OpenCV計算機視覺開發實踐:基于Qt C++ - 商品搜索 - 京東
實現數字水印的相關函數
用OpenCV來實現數字水印功能,需要使用一些位操作函數,我們需要先了解一下這些函數。
1. bitwise_and函數
bitwise_and函數是OpenCV中的位運算函數之一,用于對兩幅二值圖像進行按位與操作。具體來說,對于每個像素,將兩幅輸入圖像相應位置的像素值分別進行按位與運算,輸出的結果圖像的對應像素值即為這兩幅輸入圖像對應像素值的按位與結果。
bitwise_and函數的語法如下:
void bitwise_and(InputArray src1, InputArray src2,OutputArray dst, InputArray mask = noArray());
其中,src1和src2表示要進行按位與操作的兩幅輸入圖像;mask是可選參數,如果指定了掩膜,則只對掩膜對應位置的像素進行按位與操作;dst表示按位與運算的結果。
【例14.1】創建空白圖像進行按位與操作
?? 打開Qt Creator,新建一個控制臺項目,項目名稱是test。
?? 在main.cpp中輸入如下代碼:
#include "opencv2/opencv.hpp"
using namespace cv;int main()
{// 空白圖像創建Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);// 在圖像內添加矩陣rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);imshow("m1", m1);imshow("m2", m2);Mat dst;bitwise_and(m1, m2, dst); // 進行與操作,結果存于dst中imshow("result", dst);waitKey();return 0;
}
通過bitwise_and函數就能得到與操作的結果dst。
?? 運行程序,結果如圖14-1所示。
圖14-1
再看個示例,對現有圖片進行按位與操作,代碼如下:
【例14.2】對現成圖像進行按位與操作
?? 打開Qt Creator,新建一個控制臺項目,項目名稱是test。
?? 在main.py中輸入如下代碼:
#include "opencv2/opencv.hpp"
using namespace cv;int main()
{Mat dog,cat,img_and;resize(imread("cat.png"),cat, Size(400, 360));resize(imread("dog.png"),dog, Size(400, 360));bitwise_and(cat,dog,img_and); // 與運算 1 & 1 = 1, 其它為0imshow("result",img_and);waitKey(0);return 0;
}
?? 保存程序并運行,結果如圖14-2所示。
可以看出,與運算的結果是使圖像變小,最后的圖像也會偏暗。
2. bitwise_or函數
在OpenCV中進行或運算使用bitwise_or函數,其聲明如下:
void bitwise_or(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray());
其中輸入參數src1和src2可為灰度圖或彩色圖,src1和src2大小需相同;輸出參數dst存放或運算的結果,尺寸和類型與src保持一致;掩膜mask可通俗理解為一個遮罩,只對mask設定的有效區域進行操作。
圖14-2
或運算 0 | 0 = 0,其他情況為1。下面將貓和狗的圖片進行或運算。
【例14.3】對現成圖像進行按位或操作
?? 打開Qt Creator,新建一個控制臺項目,項目名稱是test。
?? 在main.cpp中輸入如下代碼:
#include "opencv2/opencv.hpp"
using namespace cv;int main()
{Mat dog,cat,img_and;resize(imread("cat.png"),cat, Size(400, 360));resize(imread("dog.png"),dog, Size(400, 360));bitwise_or(cat,dog,img_and); // 或運算 1 & 1 = 1,其他為0imshow("result",img_and);waitKey(0);return 0;
}
?? 保存程序并運行,結果如圖14-3所示。
圖14-3
可以看出,或運算的結果是使圖像變大,最后的圖像也偏亮。
代碼實現數字水印
前面講解了數字水印的嵌入過程和提取過程,步驟比較清晰。本節將根據這些步驟,通過代碼來實現數字水印的嵌入和提取。
【例14.4】實現數字水印的嵌入和提取
?? 打開Qt Creator,新建一個控制臺項目,項目名稱是test。
?? 在main.cpp中輸入如下代碼:
#include "opencv2/opencv.hpp"
using namespace cv;int main()
{Mat gray1H7,dst,getWatermark;Mat src= imread("src.bmp",0);// 讀取水印圖像Mat watermark= imread("watermark.bmp",0);// 將水印圖像內的正值處理為1,以方便嵌入,相當于把水印圖像變成二值圖像for(int i=0;i<watermark.rows;i++){for(int j=0;j<watermark.cols;j++){if(watermark.at<uchar>(i,j)>0)watermark.at<uchar>(i,j)=1;}}// 讀取原始載體圖像的行和列int r=src.rows;int c=src.cols;// ----------------------嵌入過程------------------------// 生成元素值都是254的數組Mat t254 = Mat::ones(r, c, CV_8UC1) * 254;// 獲取gray1H7圖像的高七位bitwise_and(src,t254,gray1H7);// 將watermark嵌入gray1H7內bitwise_or(gray1H7,watermark,dst);// ----------------------提取過程--------------------------// 生成元素值都是1的數組Mat t1= Mat::ones(r, c, CV_8UC1);// 從目標載體圖像內提取水印圖像bitwise_and(dst,t1,getWatermark);// 將水印圖像內的正值處理為255,以方便顯示for(int i=0;i<getWatermark.rows;i++){for(int j=0;j<getWatermark.cols;j++){if(getWatermark.at<uchar>(i,j)>0)getWatermark.at<uchar>(i,j)=255;}}// ---------顯示結果--------imshow("srcImg",src);imshow("watermark",watermark*255); // 當前watermark內最大值為1imshow("dstImg",dst);imshow("getWatermark",getWatermark);waitKey();destroyAllWindows();waitKey(0);return 0;
}
可以看出,上述代碼是按照嵌入過程的步驟和提取過程的步驟來實現的。我們把一副水印圖像(watermark.bmp)嵌入原始載體圖像(src.bmp)中變為目標載體圖像(也稱含水印的載體圖像)dst,然后從dst中提取出水印數據存于getWatermark中,最后顯示4種圖像。
?? 運行程序,結果如圖14-4所示。
從圖14-4中可以看到,原始載體圖像(src)和含水印的載體圖像(dst)肉眼是看不出區別的。下面再看水印圖像,如圖14-5所示。
圖14-4
圖14-5
在圖14-5中,左邊是原來的數字水印圖像,右邊是從目標載體圖像中提取出來的數字水印圖像,可以發現兩者并沒有變化。這樣我們就實現了把一副數字水印圖像嵌入載體圖像再提取出的過程。