简介
图片的蒙太奇效果,一般称为马赛克图。由很多小图拼接成一个大图。在商业宣传后,宣传自己同时也可以顺便宣传诸多合作商,颇受欢迎。教程展示利用opencv来完成马赛克图的制作。效果图如下:
前提准备
- VS2017的安装 ,参考教程:vs2017安装教程;
- OPENCV3.4.1的安装 ,参考教程:vs2017安装opencv教程;
- 准备一张目标图片和一堆用于马赛克的图片;
思路
- 修改那些提前准备的一堆图片的大小到马赛克般大小。
- 计算每一张图片的颜色均值。
- 修改目标图片的长宽,使之长宽都是马赛克大小的整数倍。
- 一马赛克大小为单位遍历目标图片同时计算这个区域的颜色均值 ,然后和提前准备的用于替换的马赛克图片的均值进行比较,用均值差最接近的替换目标图片。
- 用全替换好的图片和最开始准备的目标图片做一个融合,效果更加逼真。
代码解析
思路一代码:
这一部分主要是修改了用于替换的图片的大小,同时计算了每张图片的颜色均值,然后我定义了一个map类,用均值作为键值,路径作为槽值,为了后文替换目标图片部分做准备
思路二代码:
这是计算颜色均值的源码
思路三代码:
图一就是对目标图片的大小进行一个小小的修改,让他可以由整数个马赛克图来替换而成。
图二是对mat类图片大小修改的源码,xy参数是对传入的image基于(x,y)点向右下取width、height大小的图片。
思路四代码:
for (int i = 0; i < src.rows / height; i++)
{
for (int j = 0; j < src.cols / width; j++)
{
static int n = 0;
float error = 0;
float lasterror = 10000000;
string path = "";
Mat tempPieces = spliteTest(src, j * width, i * height, width, height);
float ave = cal_mean_stddev(tempPieces);
map<int, string>::iterator strmap_iter = strMap.begin();
for (; strmap_iter != strMap.end(); strmap_iter++)
{
if (fabsf(ave - strmap_iter->first) < 1)
{
path = strmap_iter->second;
break;
}
error = fabsf(ave - strmap_iter->first);
if (error < lasterror)
{
lasterror = error;
path = strmap_iter->second;
}
//cout << strmap_iter->first << ' ' << strmap_iter->second << endl;
}
Mat tempimg = imread(path);
mergeTest(src, tempimg, j * width, i * height, width, height);
}
}
这部分用简单的算法,以马赛克大小为单位遍历整张目标图,然后计算每部分的颜色均值,然后与我们之前map类中存的键值作比较,然后取颜色均值相近的来替换。
思路五代码:
最后用结果图和原始图片做一个融合,这样的目的是有的时候我们准备的替换图片数量过少或者颜色太单一,这样会导致目标图上有一些颜色我们是没有准备的从而效果图差异较大。融合也就是用alpha和beta这两个比例系数对mat矩阵的三通道乘上一个系数缩放。具体原理可以百度addWeighted函数源码。
工程使用说明
**最后为大家准备了整个工程的源码,做一个简单说明如下图:
我们只需要配置下代码里这些内容即可。
这里不在过多的说明,可以看下我的文件路径。
运行结果
原图(拿女朋友开刀吧hhhhh):
没加图片融合的效果图:
加图片融合的效果图:
工程地址
链接:http://t.cn/Ais8oaxV 提取码:4hqm 使用前得安装vs2017和OpenCV。双击.sln打开vs的工程,然后根据提示改下代码里面初始化部分就可以了
总结
其实有思路的话还挺好写的。。。过程中最曲折的就是mat类的col和row。。。行和列永远也分不清楚,我是服了,动不动就遍历图片超出了长宽,然后就各种异常,这可能就是vs的魅力吧。
enjoy it~