我对图像腌膜的含义一直有些模糊,今天写了几行代码,证明了我这模糊的印象倒是正确的。今天借一个给图片添加水印的小例子,给大家总结一些图像腌膜的常规操作。
首先先了解一下图像腌膜的定义:。。。
额,找了一圈好像并没有啥特别正式的官方定义/我记得曾在书上看到过来着~
其实腌膜可以抽象为一个黑白相间的图,或者膜,黑色的像素值为0,白色为1。将这个膜和你原本的图像重合到一起,黑色区域被忽略,仅剩下白色区域,就是这样。
就像把白色区域的图像抠出来一样,”抠图“就是腌膜Mask最常干的事。
废话少说,咱们开始咱们的小实践吧。
一:普通加水印:图像加和
先看一下今天要处理的水印图,一张白底红字的logo图:
然后再看一下我们的底图:
我们要吧logo,也就是水印加到左上角去,首先我们想到的就是在左上角掏个和logo图片一样大小的ROI区域出来,然后直接将logo添加到ROI区域里,来看代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage, logoImage, dstImage;
srcImage = imread("1.jpg");//读取底图
logoImage = imread("logo1.jpg");//读入logo图片
//【1】ROI区域与原图直接混合
Mat imgROI = srcImage(Rect(20, 20, logoImage.cols, logoImage.rows)); //Rect方法定义ROI,注意宽是:cols;高是rows。
logoImage.copyTo(imgROI);//将logo图片拷贝到img的ROI上(注意copyTo函数要求两图像大小和类型都相同,否则无效)
imshow("混合后", srcImage);
waitKey(0);
return 0;
}
运行效果如下:
可以看到白色的底并不是我们想要的,所以就需要用到腌膜的相关抠图,将五个红色的字”抠“出来。
二:抠图操作加水印
首先我们要完成的操作就是将这五个字识别出来,也就是抠出来,对于我们这个logo自然很简单啦,只需要转灰度,然后阈值化操作一下就可以了:
1,我们先定义一个腌膜Mask,然后将logo图像转为灰度图像存入到Mask中:
Mat mask;//定义腌膜
cvtColor(logoImage, mask, COLOR_BGR2GRAY);//将logo转成灰度图
处理后得到如下灰度图:
2,对腌膜Mask这个图像矩阵进行取反操作:
bitwise_not(mask, mask);//对mask图像取反,白色(255)变成黑色(0)
对于上面操作我们需要多加解释一下。
图像的基本运算有很多种,比如两幅图像可以相加相减、相乘、相除、位运算、平方根、对数、绝对值等;
图像也可以放大、缩小、旋转,还可以截取其中的一部分作为ROI(感兴趣区域)进行操作,各个颜色通道还可以分别提取及对各个颜色通道进行各种运算操作。
所以我们自然可以对图像进行与,或,非,异或等操作啦。
//bitwise_and、bitwise_or、bitwise_xor、bitwise_not这四个按位操作函数。
void bitwise_and(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 & src2
void bitwise_or(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 | src2
void bitwise_xor(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 ^ src2
void bitwise_not(InputArray src, OutputArray dst,InputArray mask=noArray());//dst = ~src
- bitwise_and:对二进制数据进行“与”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“与”操作,1&1=1,1&0=0,0&1=0,0&0=0
- bitwise_or:对图像(灰度图像或彩色图像均可)每个像素值进行二进制“或”操作,1|1=1,1|0=1,0|1=1,0|0=0
- bitwise_xor:对图像(灰度图像或彩色图像均可)每个像素值进行二进制“异或”操作,1^1=0,1^0=1,0^1=1,0^0=0
- bitwise_not:对图像(灰度图像或彩色图像均可)每个像素值进行二进制“非”操作,~1=0,~0=1
引用自:https://blog.csdn.net/u011028345/article/details/77278467
为什么要对我们处理后得灰度图进行取反操作呢?因为我们开头说过,在腌膜中黑色无效,白色有效,而我们得灰度图的底色确实白色的,字确实黑色的,所以进行取反操作,结果如下:
3,对取反后的图进行阈值化操作,抠出五个字:
threshold(mask, mask, 100, 255, THRESH_BINARY);//对mask进行二值化,将mask进一步处理
得到如下图所示:
4,进行图像融合
通过上面步骤,我们得到了如上图所示的黑底白字的腌膜了,下面就把腌膜加入到图像融合中去。
Mat imgROI = srcImage(Rect(20, 20, logoImage.cols, logoImage.rows));
logoImage.copyTo(imgROI, mask); //将logo拷贝到imgROI上,掩码为不为0的部分起作用,为0的部分不起作用
可以看到copyTo方法中,有第二个参数mask,我们只需要传给他就可以了,很多现成的函数接口都有图像腌膜这个参数,现在你知道怎么用了吧。
来看看效果图:
是不是变成水印了呢?
三:添加非矩形的Mask区域
我们选取ROI区域一般都是用矩形,所以圈出来的区域都是矩形的,如何添加非矩形的呢?
首先我们读取我们的logo图片,为了观察方便我们换了如下一个logo:
Mat logo2Image = imread("logo2.jpg");//读取logo2
需要强调的是,接下来我们的操作都是基于这张logo的大小来进行的:
1,在原图开出logo大小的ROI区域:
Mat imgROI = srcImage(Rect(20, 20, logo2Image.cols, logo2Image.rows));
2,创建一个同logo一样大的纯黑的腌膜mask:
Mat mask = Mat::zeros(logo2Image.size(), CV_8UC1);
3,在黑色腌膜上画出一个白色内填充的圆:
circle(mask, Point(mask.rows / 2, mask.cols / 2), 120, Scalar(255), -1, 8);
4,最后就还是一样的操作,将logo复制到原图ROI区域中去,并申明腌膜:
logo2Image.copyTo(imgROI, mask);//将logo拷贝到imgROI上,掩码为不为0的部分起作用,为0的部分不起作用
运行效果如下图:
虽然有些丑,但好看又不能拿来找对象,所以就这样叭。
到此,会添加圆形了,其他非规则的形状就要通过关键点来进行确定了,我们只需要将上述步骤中的第三步:在黑色腌膜上画圆变成画不规则多边形就行了,如下:
vector<vector<Point>> contour;
vector<Point> pts;
pts.push_back(Point(30, 45));
pts.push_back(Point(100, 15));
pts.push_back(Point(300, 145));
pts.push_back(Point(330, 240));
pts.push_back(Point(50, 250));
contour.push_back(pts);
drawContours(mask, contour, 0, Scalar::all(255), -1);
创建多个点,然后进行轮廓绘制就好了,drawContours这个函数最后传的参数-1为线宽,当为正时为宽度,为负则为向内填充,和画圆函数类似。
THE END