大家好呀,前两天烈阳天道1上映了,不知道大家看没看呢,里面还有一小段彦穿越虫洞与猴哥相遇的画面,彦女王啊啊啊~~
所以我去网上爬了二百来张我大学的风景画,然后找了以前存的彦女王的图片,生成了一幅蒙太奇画像。然后我两个热爱的就合体啦!
先看一下什么是蒙太奇图像吧,其实你肯定见过,只不过不知道叫蒙太奇而已:
一张大的图片,是由很多小的图片拼接而成的这种,就是蒙太奇图像啦(或者叫马赛克拼图),我要做的就是把我大学的风景图拼成彦的图片。
文末附有python代码,本文为我写的C++代码
1:读取文件夹内的风景图片集,将每张剪裁到90*45大小并存入Mat容器内
2:将图片模板(彦的照片)扩大为1600*2700大小
3:计算图片集的直方图并将结果存到MatND容器内(直方图容器)
4:双重for循环以90*45的步长遍历图片模板计算各个区域的直方图,并将区域直方图与图片集的直方图进行比对,得到相似度最高的风景图片,将该风景图片替换模板对应区域
5:将4步得到的蒙太奇图与原模板图线性相加,得到更为逼真的效果
是不是很简单的过程?但就这么个过程,我调了一天的bug,然后被迫深入理解了C++的动态回收机制… … 建议感兴趣的小伙伴自己实现一下。
以下代码在主函数内顺次复制粘贴即可
//【1】图片集的采集与处理 int Images_number = 256;//图片集中图片的数量 int step_x = 80; //将图片剪裁为80*45大小 int step_y = 45; Mat srcImage; vectorload_Images;//图片集容器 char filename[30];//存储图片名字 for (int i = 0; i < Images_number; i++) { Mat dstImage; sprintf_s(filename, "./风景图/ysu (%d).jpg", i); cout << filename << endl; srcImage = imread(filename); //Mat类图像 resize(srcImage, dstImage, Size(step_x, step_y), 0, 0, INTER_NEAREST); load_Images.push_back(dstImage); } cout << "图片加载完毕" << endl; 1234567891011121314151617
主要用到了利用sprintf函数得到图片集有规律的命名,然后for循环依次将imread到的图片push_back添加到Mat容器内就可以了。
二百多张图片如何快速整齐的命名,,,看链接。
https://jingyan.baidu.com/article/b87fe19e4834a95218356814.html 1
//【2】将原模板图扩大到合适尺寸 Mat originalImage, showImage; originalImage = imread("彦.jpg"); imshow("彦", originalImage); resize(originalImage, showImage, Size(1600, 2700)); 12345
没啥好说的,就在基本保持原先长宽比例的条件下,让长宽都是90*45(图片集被裁剪的大小)的整数倍就好了。
//【3】计算图片集的直方图 int width = showImage.cols; //模板图的长宽 int height = showImage.rows; Mat frame; vectorhsit_list;//直方图容器 //计算直方图的参数准备 int bins = 128; //直条数 int hist_sizes[] = { bins,bins,bins }; //存放每个维度的直方图尺寸数组 // 均为256条宽度 float range[] = { 0,256 }; //每一维数组的取值范围 // 均为0-255高度 const float* ranges[] = { range,range,range }; int channels[] = { 0,1,2 }; //for循环计算图片集的直方图并存储 for (int i = 0; i < Images_number; i++) { /*load_Images[i].copyTo(frame);*/ MatND hsit_RGB; Mat frame; load_Images[i].copyTo(frame); calcHist(&frame, 1, channels, Mat(), hsit_RGB, 3, hist_sizes, ranges, true, false); hsit_list.push_back(hsit_RGB); } 1234567891011121314151617181920
和第一步套路类似,循环计算了图片集所有图的直方图并存储到了一个直方图容器内。
//【4】遍历,寻找最匹配的图片并替换 int number_order; for (int x = 0; x < height; x = x + step_y) { for (int y = 0; y < width; y = y + step_x) { Mat roiImage = montageImage(Rect(y, x, step_x, step_y));//Rect(y, x, step_x, step_y) //参数准备 MatND hsit_roi; double match_ = 0.0;//匹配度 calcHist(&roiImage, 1, channels, Mat(), hsit_roi, 3, hist_sizes, ranges, true, false); for (int i = 0; i < Images_number; i++) { double match; match = compareHist(hsit_roi, hsit_list[i], HISTCMP_CORREL); if (match > match_) {//寻找对比对最高的 number_order = i; match_ = match; } } load_Images[number_order].copyTo(roiImage); } } cout << "【4】遍历,寻找最匹配的图片并替换成功" << endl; 123456789101112131415161718192021
首先两个步长分别为step_y和step_x的循环是遍历原图模板各个小块区域,并计算该块的直方图。
之后用一个for循环在第三步得到的直方图容器内,寻找与该块直方图匹配度最高的风景图片,然后将该风景图片替换原图对应区域。
到此运行完毕我们就可以得到一个不太完美的蒙太奇图片了:
虽然有些粗糙,但已经可以看出来了叭!效果可以调节裁剪的大小或者原图模板的大小来改善。但以我代码设置的参数,执行完都要好几分钟,如果有什么优化建议可以交流哈。
来看看女王的腿!
嗯,,竟然是用碑组成的。
我们还可以通过第五步,将上图左图与右图加权得到更为逼真的蒙太奇画像。
Mat dstImage; addWeighted(showImage, 0.4, montageImage, 0.6, 0, dstImage); imwrite("结果图.jpg", dstImage); imwrite("show.jpg", montageImage); 1234
加和后的效果图:
效果是不是好一些了?可以更改参数使效果更好。由于我还有毛概论文没写,就不瞎搞了~
python代码可以看知乎链接:
Python代码获取加群:1136192749
此文转载文 原作者:周旋_