C++ OpenCV 把一張圖藏到另外一張圖中(圖像加水印)
圖像的像素值的範圍爲[0,255],8位2進製表示[00000000,11111111]
uchar 無符號8位2進制。
採用截取圖像A的最後2位(2位只能表示4種可能性),用於藏圖像B。涉及到C++的位操作: >> ,<< , & , | 四種操作即可。
此時的圖像,有2點要求:1、被藏圖像B(水印圖像)較單調,像素值較集中。2、圖像B儘可能保持與圖像A相同尺寸。3、圖像A和B竟然採用無壓縮格式儲存圖像格式(BMP格式),其他比如JPG格式是有損壓縮。
圖像A,下圖:
圖像B,下圖:
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <queue>
using namespace std;
using namespace cv;
void pic_add(Mat &image, Mat &image_water)
{
cout << image.rows << " " << image.cols << " " << image.channels() << " " << image.type() << " " << endl;
cout << "image.channels() = " << image.channels() << endl;
//cout << image_water.rows << " " << image_water.cols << " " << image_water.dims << endl;
int nr = image.rows; // number of rows
int nc = image.cols; // number of columns
for (int j = 0; j < nr; j++)
{
for (int i = 0; i < nc; i++)
{
uchar a = 0xfc;
uchar thes = 50;
image.at<Vec3b>(j, i)[0] = image.at<Vec3b>(j, i)[0] & a;
image.at<Vec3b>(j, i)[1] = image.at<Vec3b>(j, i)[1] & a;
image.at<Vec3b>(j, i)[2] = image.at<Vec3b>(j, i)[2] & a;
Vec3b p;
p[0] = image_water.at<Vec3b>(j, i)[0] >> 6;
p[1] = image_water.at<Vec3b>(j, i)[1] >> 6;
p[2] = image_water.at<Vec3b>(j, i)[2] >> 6;
image.at<Vec3b>(j, i)[0] = image.at<Vec3b>(j, i)[0] | p[0];
image.at<Vec3b>(j, i)[1] = image.at<Vec3b>(j, i)[1] | p[1];
image.at<Vec3b>(j, i)[2] = image.at<Vec3b>(j, i)[2] | p[2];
}
}
imwrite("C:\\Users\\kai\\Pictures\\test\\tiqu_20190824re111111.bmp", image);
cv::namedWindow("Original Water add Image");
cv::imshow("Original Water add Image", image);
}
int main()
{
String path = "C:\\Users\\kai\\Pictures\\test\\1.bmp";
String pathadd = "C:\\Users\\kai\\Pictures\\test\\2.bmp";
cv::Mat image = cv::imread(path, 1); //1.bmp
cv::Mat image_water = cv::imread("C:\\Users\\kai\\Pictures\\test\\2.bmp", 1);
/*載入標識,指定一個加載圖像的顏色類型,
默認值爲1,表示載入三通道的彩色圖像;
-1,imread按解碼得到的方式讀入圖像;
0,imread按單通道的方式讀入圖像,即灰白圖像。*/
if (!image.data) return 0;
Mat drawing = Mat::zeros(image.size(), CV_8UC3);//初始化一個背景爲黑色矩陣
pic_add(image, image_water);
cout << image.rows << " " << image.cols << " " << image.channels() << " " << image.type() << " " << endl;
cout << "image.channels() = " << image.channels() << endl;
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
cv::namedWindow("Original Water Image");
cv::imshow("Original Water Image", image_water);
waitKey(0);
return 0;
}
對比圖像(左邊是原圖,右邊是要加入的水印圖像)
對比圖像(左邊是原圖,右邊是加了水印的圖像),看不出差別。
提取水印圖像,只需要提取加了水印的圖像C的像素值最後2位即可。
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <queue>
using namespace std;
using namespace cv;
void extract(Mat &image)
{
Mat drawing = Mat::zeros(image.size(), CV_8UC3);//初始化一個背景爲黑色矩陣
int nr = image.rows; // number of rows
int nc = image.cols; // number of columns
for (int j = 0; j < nr; j++)
{
for (int i = 0; i < nc; i++)
{
uchar a = 0x03;
uchar b = 0xff;
uchar c = 0xc0;
drawing.at<Vec3b>(j, i)[0] = image.at<Vec3b>(j, i)[0] & a;
drawing.at<Vec3b>(j, i)[1] = image.at<Vec3b>(j, i)[1] & a;
drawing.at<Vec3b>(j, i)[2] = image.at<Vec3b>(j, i)[2] & a;
//cout << drawing.at<Vec3b>(j, i) << " ";
drawing.at<Vec3b>(j, i)[0] = drawing.at<Vec3b>(j, i)[0] << 6;
drawing.at<Vec3b>(j, i)[1] = drawing.at<Vec3b>(j, i)[1] << 6;
drawing.at<Vec3b>(j, i)[2] = drawing.at<Vec3b>(j, i)[2] << 6;
if (drawing.at<Vec3b>(j, i)[0] == c )
drawing.at<Vec3b>(j, i)[0] = 255;
if (drawing.at<Vec3b>(j, i)[1] == c )
drawing.at<Vec3b>(j, i)[1] = 255;
if (drawing.at<Vec3b>(j, i)[2] == c )
drawing.at<Vec3b>(j, i)[2] = 255;
}
}
imwrite("C:\\Users\\kai\\Pictures\\test\\tiqu_20190824.bmp", drawing);
//cvSaveImage("gray_mat.jgp", binaryAdaptive); tiqu_20190823.jpg
cv::namedWindow("Original Water extract Image");
cv::imshow("Original Water extract Image", drawing);
}
int main()
{
String result_path = "C:\\Users\\kai\\Pictures\\test\\result.bmp";
cv::Mat result_img = cv::imread(result_path, 1);
/*載入標識,指定一個加載圖像的顏色類型,
默認值爲1,表示載入三通道的彩色圖像;
-1,imread按解碼得到的方式讀入圖像;
0,imread按單通道的方式讀入圖像,即灰白圖像。*/
if (!image.data) return 0;
// display original image
cv::namedWindow("Original Image");
cv::imshow("Original Image", result_img);
cv::namedWindow("Original Water Image");
cv::imshow("Original Water Image", image_water);
extract(result_img);
cout << image.rows << " " << image.cols << " " << image.channels() << " " << image.type() << " " << endl;
cout << "image.channels() = " << image.channels() << endl;
waitKey(0);
return 0;
}
對比圖像(左邊是加了水印的圖像,右邊是提取的水印圖像)。
對比圖像(左邊是提取的水印圖像,右邊是原始的水印圖像)。兩者基本一致,紅箭頭標註的角落上還有點細微差別。
本方法加水印的方法抗噪能力不行,一旦圖像有發生——圖像像素值變化會導致水印提取結果很差。
直方圖均值化後,水印提取的結果:
加了水印的圖像以JPG格式保存,有損壓縮導致像素值恢復時候發生變動,導致噪聲很大。