C++ OpenCV 把一張圖藏到另外一張圖中(圖像加水印)

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格式保存,有損壓縮導致像素值恢復時候發生變動,導致噪聲很大。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章