所使用的开发环境如下
针对图片水印
一般来说yuv图,可以通过读取本地的.yuv文件来获取,但是这样获取到的可能是yuv文件序列,本blog主要用来演示算法,只需要一张yuv图,而且最好是yuv420的图,因此,直接从rgb转过来是最合适的,OpenCV能够快速实现这样的转换
所要读取的图像文件为l_hires.jpg,这是一张rgb通道的图,转换到yuv的代码如下
#include "iostream"
using namespace std;
#include "opencv/cv.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/video/video.hpp"
using namespace cv;
#pragma comment(lib,"opencv_world343d.lib")
int main(int argc, char *argv[])
{
// l_hires.jpg为被嵌图,需要先转换为yuv数据
char *img_path = "l_hires.jpg";
cv::Mat img;
img = cv::imread(img_path, 1); // 读取图片
cv::imshow("load image", img);
////RBG转YUV
cv::Mat yuv_img;
cv::cvtColor(img, yuv_img, CV_BGR2YUV_I420);
cv::imshow("yuvImg", yuv_img);
cv::waitKey(100000); // 等待时间,这里等待时间最好别填零,要不打开的窗口秒退
return 0;
}
yuv的图像跟rgb是不一样的,uv的排列是这样的一种形式(这里说的是yuv420)
要嵌入的水印图为logo.png,但是这里是没有考虑其alpha通道的,所以在嵌入完毕后,可以看到是透明背景会变为白色,
主要思路如下:
首先需要在读取的时候指定为读取灰度图,然后得到图像的uchar数据,再覆盖到yuv图中y分量的对应位置上
代码实现部分如下
#include "iostream"
using namespace std;
#include "opencv/cv.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/video/video.hpp"
using namespace cv;
#pragma comment(lib,"opencv_world343d.lib")
int main(int argc, char *argv[])
{
// l_hires.jpg为被嵌图,需要先转换为yuv数据
char *img_path = "l_hires.jpg";
cv::Mat img;
img = cv::imread(img_path, 1); // 读取图片
cv::imshow("load image", img);
////RBG转YUV
cv::Mat yuv_img;
cv::cvtColor(img, yuv_img, CV_BGR2YUV_I420);
cv::imshow("yuvImg", yuv_img);
// 把yuv的数据全部放到img_data中
int index = 0;
int image_size = yuv_img.cols * yuv_img.rows;
unsigned char* img_data = new unsigned char[image_size];
for (int i = 0; i < yuv_img.rows; i++)
{
for (int j = 0; j < yuv_img.cols; j++)
{
img_data[index] = yuv_img.at(i, j); // 从yuv中拿数据,只取出来y分量,所以宽高是一开始rgb的img
index++;
}
}
cv::Mat logo_img_rgb, logo_yuv_img;
logo_img_rgb = cv::imread("logo.png", 0);
cv::imshow("logo_img_rgb", logo_img_rgb);
// 得到水印图的unsigned char*数据
image_size = logo_img_rgb.cols * logo_img_rgb.rows;
unsigned char* logo_img_data = new unsigned char[image_size];
index = 0;
for (int i = 0; i < logo_img_rgb.rows; i++)
{
for (int j = 0; j < logo_img_rgb.cols; j++)
{
logo_img_data[index] = logo_img_rgb.at(i, j); // 从yuv中拿数据,但是宽高是一开始的img
index++;
}
}
// 用logo图的图像数据覆盖被嵌入图的数据
index = 0;
for (int i = 0; i < logo_img_rgb.rows; i++) // 嵌入y分量
{
memcpy(img_data + yuv_img.cols * index, logo_img_data + logo_img_rgb.cols * index, logo_img_rgb.cols);
index++;
}
// 看看完整的yuv数据是否还在
cv::Mat img_res(yuv_img.rows, yuv_img.cols, CV_8UC1);
index = 0;
for (int i = 0; i < yuv_img.rows; i++)
{
for (int j = 0; j < yuv_img.cols; j++)
{
img_res.at(i, j) = (uchar)img_data[index];
index++;
}
}
cv::imshow("img_res", img_res);
// 转回rgb看看
cv::Mat img_res_rgb;
cv::cvtColor(img_res, img_res_rgb, CV_YUV2BGR_I420); // imread 1的时候才会有BGR
cv::imshow("img_res_rgb", img_res_rgb);
cv::waitKey(100000); // 等待时间,这里等待时间最好别填零,要不打开的窗口秒退
return 0;
}
由于嵌入的logo图是灰度图,所以出来的颜色参考了原图的颜色,但是效果已经得到了
只要了解了嵌入y分量的原理,如果在了解uv的存放形式,那么剩下的事情,就是取出rgb的数据,分别转换为yuv后,再嵌入到主图的uv分量中就完成效果了
实现步骤如下
实现代码如下(主要难点是理解图像的偏移即可)
#include "iostream"
using namespace std;
#include "opencv/cv.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/video/video.hpp"
using namespace cv;
#pragma comment(lib,"opencv_world343d.lib")
int main(int argc, char *argv[])
{
// l_hires.jpg为被嵌图,需要先转换为yuv数据
char *img_path = "l_hires.jpg";
cv::Mat img;
img = cv::imread(img_path, 1); // 读取图片
cv::imshow("load image", img);
////RBG转YUV
cv::Mat yuv_img;
cv::cvtColor(img, yuv_img, CV_BGR2YUV_I420);
cv::imshow("yuvImg", yuv_img);
// 把yuv的数据全部放到img_data中
int index = 0;
int image_size = yuv_img.cols * yuv_img.rows;
unsigned char* img_data = new unsigned char[image_size];
for (int i = 0; i < yuv_img.rows; i++)
{
for (int j = 0; j < yuv_img.cols; j++)
{
img_data[index] = yuv_img.at(i, j); // 从yuv中拿数据,只取出来y分量,所以宽高是一开始rgb的img
index++;
}
}
cv::Mat logo_img_rgb = cv::imread("logo.png", 1);
cv::imshow("logo_img_rgb", logo_img_rgb);
cv::Mat logo_yuv_img;
cv::cvtColor(logo_img_rgb, logo_yuv_img, CV_BGR2YUV_I420); // imread 1的时候才会有BGR
cv::imshow("logo_yuv_img", logo_yuv_img);
// 把水印的yuv数据存放到logo_yuv_data中
int image_size3 = logo_yuv_img.cols * logo_yuv_img.rows;
unsigned char* logo_yuv_data = new unsigned char[image_size3];
index = 0;
for (int i = 0; i < logo_yuv_img.rows; i++)
{
for (int j = 0; j < logo_yuv_img.cols; j++)
{
logo_yuv_data[index] = logo_yuv_img.at(i, j); // 从yuv中拿数据,但是宽高是一开始的img
index++;
}
}
// 如果需要嵌入一个rgb的分量,响应的需要先把水印的logo图从rgb转到yuv,然后分别插入不同的分量中
index = 0;
for (int i = 0; i < logo_img_rgb.rows; i++) // 嵌入y分量
{
memcpy(img_data + yuv_img.cols * index, logo_yuv_data + logo_img_rgb.cols * index, logo_img_rgb.cols);
index++;
}
//int y = yuv_img.rows * 2 / 3;
int y = img.rows * img.cols;
int y2 = logo_img_rgb.rows * logo_img_rgb.cols;
index = 0;
for (int i = 0; i < logo_img_rgb.rows / 2; i++) // 嵌入u分量
{
// img.rows * yuv_img.cols 表示跳过y分量,他的长度为原始img的宽和高img.rows,img的宽yuv不发生改变,img.cols与yuv_img.cols相等
memcpy(img_data + y + yuv_img.cols / 2 * index, logo_yuv_data + y2 + logo_img_rgb.cols / 2 * index, logo_img_rgb.cols / 2);
index++;
}
int v = yuv_img.rows * 5 / 6 * yuv_img.cols;
int v2 = logo_yuv_img.rows * 5 / 6 * logo_yuv_img.cols;
index = 0;
for (int i = 0; i < logo_img_rgb.rows / 2; i++) // 嵌入v分量
{
// img.rows * yuv_img.cols 表示跳过y、u分量,跳过的长度为y+u的数据长度,一共是5/6的高,拷贝的宽仅仅为原来的1/2
memcpy(img_data + v + yuv_img.cols / 2 * index, logo_yuv_data + v2 + logo_img_rgb.cols / 2 * index, logo_img_rgb.cols / 2);
index++;
}
// 看看完整的yuv数据是否还在
cv::Mat img_res(yuv_img.rows, yuv_img.cols, CV_8UC1);
index = 0;
for (int i = 0; i < yuv_img.rows; i++)
{
for (int j = 0; j < yuv_img.cols; j++)
{
img_res.at(i, j) = (uchar)img_data[index];
index++;
}
}
cv::imshow("img_res", img_res);
// 转回rgb看看
cv::Mat img_res_rgb;
cv::cvtColor(img_res, img_res_rgb, CV_YUV2BGR_I420); // imread 1的时候才会有BGR
cv::imshow("img_res_rgb", img_res_rgb);
cv::waitKey(100000); // 等待时间,这里等待时间最好别填零,要不打开的窗口秒退
return 0;
}
数字水印技术概览
opencv把jpg图片转化成yuv数据_opencv把Mat转换成yuv
opencv yuv420与Mat互转
YUV叠加
OpenCV YUV 与 RGB的互转(草稿)
OpenCV实现RGB与YUV的转换
opencv cvcvtcolor函数 将RGB转为YUV
YUV420P格式图像叠加,拼接
Opencv7:Mat与unsigned char类型的相互转换
opencv3/C++视频中叠加透明图片的实现
NV12图像格式叠加(水印原理演示)