图像腌膜Mask的常规操作你真的信手拈来吗?

2020-06-05 10:27:26 浏览数 (2)

我对图像腌膜的含义一直有些模糊,今天写了几行代码,证明了我这模糊的印象倒是正确的。今天借一个给图片添加水印的小例子,给大家总结一些图像腌膜的常规操作。

首先先了解一下图像腌膜的定义:。。。

额,找了一圈好像并没有啥特别正式的官方定义/我记得曾在书上看到过来着~

其实腌膜可以抽象为一个黑白相间的图,或者膜,黑色的像素值为0,白色为1。将这个膜和你原本的图像重合到一起,黑色区域被忽略,仅剩下白色区域,就是这样。

就像把白色区域的图像抠出来一样,”抠图“就是腌膜Mask最常干的事。

废话少说,咱们开始咱们的小实践吧。

一:普通加水印:图像加和

先看一下今天要处理的水印图,一张白底红字的logo图:

然后再看一下我们的底图:

我们要吧logo,也就是水印加到左上角去,首先我们想到的就是在左上角掏个和logo图片一样大小的ROI区域出来,然后直接将logo添加到ROI区域里,来看代码:

代码语言:javascript复制
#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中:

代码语言:javascript复制
  Mat mask;//定义腌膜
  cvtColor(logoImage, mask, COLOR_BGR2GRAY);//将logo转成灰度图

处理后得到如下灰度图:

2,对腌膜Mask这个图像矩阵进行取反操作:

代码语言:javascript复制
bitwise_not(mask, mask);//对mask图像取反,白色(255)变成黑色(0)

对于上面操作我们需要多加解释一下。

图像的基本运算有很多种,比如两幅图像可以相加相减、相乘、相除、位运算、平方根、对数、绝对值等;

图像也可以放大、缩小、旋转,还可以截取其中的一部分作为ROI(感兴趣区域)进行操作,各个颜色通道还可以分别提取及对各个颜色通道进行各种运算操作。

所以我们自然可以对图像进行与,或,非,异或等操作啦。

代码语言:javascript复制

 //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
代码语言:javascript复制
引用自:https://blog.csdn.net/u011028345/article/details/77278467

为什么要对我们处理后得灰度图进行取反操作呢?因为我们开头说过,在腌膜中黑色无效,白色有效,而我们得灰度图的底色确实白色的,字确实黑色的,所以进行取反操作,结果如下:

3,对取反后的图进行阈值化操作,抠出五个字:

代码语言:javascript复制
threshold(mask, mask, 100, 255, THRESH_BINARY);//对mask进行二值化,将mask进一步处理

得到如下图所示:

4,进行图像融合

通过上面步骤,我们得到了如上图所示的黑底白字的腌膜了,下面就把腌膜加入到图像融合中去。

代码语言:javascript复制
 Mat imgROI = srcImage(Rect(20, 20, logoImage.cols, logoImage.rows));
 logoImage.copyTo(imgROI, mask); //将logo拷贝到imgROI上,掩码为不为0的部分起作用,为0的部分不起作用

可以看到copyTo方法中,有第二个参数mask,我们只需要传给他就可以了,很多现成的函数接口都有图像腌膜这个参数,现在你知道怎么用了吧。

来看看效果图:

是不是变成水印了呢?

三:添加非矩形的Mask区域

我们选取ROI区域一般都是用矩形,所以圈出来的区域都是矩形的,如何添加非矩形的呢?

首先我们读取我们的logo图片,为了观察方便我们换了如下一个logo:

代码语言:javascript复制
Mat logo2Image = imread("logo2.jpg");//读取logo2

需要强调的是,接下来我们的操作都是基于这张logo的大小来进行的:

1,在原图开出logo大小的ROI区域:

代码语言:javascript复制
Mat imgROI = srcImage(Rect(20, 20, logo2Image.cols, logo2Image.rows));

2,创建一个同logo一样大的纯黑的腌膜mask:

代码语言:javascript复制
Mat mask = Mat::zeros(logo2Image.size(), CV_8UC1);

3,在黑色腌膜上画出一个白色内填充的圆:

代码语言:javascript复制
circle(mask, Point(mask.rows / 2, mask.cols / 2), 120, Scalar(255), -1, 8);

4,最后就还是一样的操作,将logo复制到原图ROI区域中去,并申明腌膜:

代码语言:javascript复制
logo2Image.copyTo(imgROI, mask);//将logo拷贝到imgROI上,掩码为不为0的部分起作用,为0的部分不起作用

运行效果如下图:

虽然有些丑,但好看又不能拿来找对象,所以就这样叭。

到此,会添加圆形了,其他非规则的形状就要通过关键点来进行确定了,我们只需要将上述步骤中的第三步:在黑色腌膜上画圆变成画不规则多边形就行了,如下:

代码语言:javascript复制
 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

0 人点赞