上一篇学习了ROI的用法,本节接着上一篇,继续添加水印,但是要去掉讨厌的水印的背景。
关于添加水印,网上有一篇讲的很清楚:
http://www.cnblogs.com/mfryf/archive/2012/03/08/2385304.html
不过他的图像是png格式的,本身是透明背景,本篇将采用普通jpg格式图片做水印。
第一步,分析 copyTo 函数:
我的图像是白色背景的(用mspaint绘制:) )
首先,通过 cvtColor 把 water_mark 图像变成灰度图 grey:
关于门限类型的定义 Opencv解释的很清楚:
/* Threshold types */
enum
{
CV_THRESH_BINARY =0, /* value = value > threshold ? max_value : 0 */
CV_THRESH_BINARY_INV =1, /* value = value > threshold ? 0 : max_value */
CV_THRESH_TRUNC =2, /* value = value > threshold ? threshold : value */
CV_THRESH_TOZERO =3, /* value = value > threshold ? value : 0 */
CV_THRESH_TOZERO_INV =4, /* value = value > threshold ? 0 : value */
CV_THRESH_MASK =7,
CV_THRESH_OTSU =8 /* use Otsu algorithm to choose the optimal threshold value;
combine the flag with one of the above CV_THRESH_* values */
};
这个是叠加式(覆盖式)水印,即直接覆盖掉原来的图像,如果想使用半透明的水印,我这里采用了一个比较土的办法,构造了一个彩色的 color_mask,跟 water_mark 做“与”操作,使 water_mark 背景部分像素值RGB分量全部变成0,再跟 image 做 addWeighted 操作,即可。
color_mask 的构造过程:通过 split 对 water_mark 按通道分割成RGB三个单通道图像,每个图像都拷贝成单通道的 mask,然后重新合并 merge成彩色蒙版。
关于添加水印,网上有一篇讲的很清楚:
http://www.cnblogs.com/mfryf/archive/2012/03/08/2385304.html
不过他的图像是png格式的,本身是透明背景,本篇将采用普通jpg格式图片做水印。
第一步,分析 copyTo 函数:
//! copies those matrix elements to "m" that are marked with non-zero mask elements.
void copyTo( OutputArray m, InputArray mask ) const;
关注mask参数。要去掉水印的背景,需要使用mask,对不想修改的部分不做修改(上面的注释很清楚,就是说只对非0的位置做操作)。
那么如何构造这个mask?那么我需要对water_mark图像做二值化,生成一个黑白的mask,其中想留下的水印部分是非0(白色255),需要叠加操作;背景是0(黑色0),不需要叠加操作。
我的图像是白色背景的(用mspaint绘制:) )
首先,通过 cvtColor 把 water_mark 图像变成灰度图 grey:
Mat gray(water_mark);
cvtColor(gray, gray, CV_RGB2GRAY);
再通过二值化函数 threshold 把灰度图 gray 转化为黑白图 mask,其中 最后一个参数 type 要看情况而定,我这里是 CV_THRESH_BINARY_INV,表明做一个反转。(如果不反转一下,嘿嘿,那就成了叠加背景和镂空水印了。)
Mat mask(gray);
cv::threshold(gray, mask, 128, 255, CV_THRESH_BINARY_INV);
关于门限类型的定义 Opencv解释的很清楚:
/* Threshold types */
enum
{
CV_THRESH_BINARY =0, /* value = value > threshold ? max_value : 0 */
CV_THRESH_BINARY_INV =1, /* value = value > threshold ? 0 : max_value */
CV_THRESH_TRUNC =2, /* value = value > threshold ? threshold : value */
CV_THRESH_TOZERO =3, /* value = value > threshold ? value : 0 */
CV_THRESH_TOZERO_INV =4, /* value = value > threshold ? 0 : value */
CV_THRESH_MASK =7,
CV_THRESH_OTSU =8 /* use Otsu algorithm to choose the optimal threshold value;
combine the flag with one of the above CV_THRESH_* values */
};
效果图:
这个是叠加式(覆盖式)水印,即直接覆盖掉原来的图像,如果想使用半透明的水印,我这里采用了一个比较土的办法,构造了一个彩色的 color_mask,跟 water_mark 做“与”操作,使 water_mark 背景部分像素值RGB分量全部变成0,再跟 image 做 addWeighted 操作,即可。
color_mask 的构造过程:通过 split 对 water_mark 按通道分割成RGB三个单通道图像,每个图像都拷贝成单通道的 mask,然后重新合并 merge成彩色蒙版。
Mat color_mask;
std::vector<Mat> planes;
split(water_mark, planes);
std::vector<Mat>::iterator it = planes.begin();
for(; it != planes.end(); ++it)
{
mask.copyTo(*it);
}
merge(planes, color_mask);
cv::bitwise_and(water_mark, color_mask, water_mark);
addWeighted(roi, 1.0, water_mark, 0.5, 0, roi); //alpha add
效果图: