前言
大家好呀,前兩天烈陽天道1上映了,不知道大家看沒看呢,里面還有一小段彥穿越蟲洞與猴哥相遇的畫面,彥女王啊啊啊~~

所以我去網上爬了二百來張我大學的風景畫,然后找了以前存的彥女王的圖片,生成了一幅蒙太奇畫像,然后我兩個熱愛的就合體啦!
先看一下什么是蒙太奇影像吧,其實你肯定見過,只不過不知道叫蒙太奇而已:
一張大的圖片,是由很多小的圖片拼接而成的這種,就是蒙太奇影像啦(或者叫馬賽克拼圖),我要做的就是把我大學的風景圖拼成彥的圖片,
綜述實作思路
文末附有python代碼,本文為我寫的C++代碼
1:讀取檔案夾內的風景圖片集,將每張剪裁到90*45大小并存入Mat容器內
2:將圖片模板(彥的照片)擴大為1600*2700大小
3:計算圖片集的直方圖并將結果存到MatND容器內(直方圖容器)
4:雙重for回圈以90*45的步長遍歷圖片模板計算各個區域的直方圖,并將區域直方圖與圖片集的直方圖進行比對,得到相似度最高的風景圖片,將該風景圖片替換模板對應區域
5:將4步得到的蒙太奇圖與原模板圖線性相加,得到更為逼真的效果
是不是很簡單的程序?但就這么個程序,我調了一天的bug,然后被迫深入理解了C++的動態回識訓制… … 建議感興趣的小伙伴自己實作一下,
以下代碼在主函式內順次復制粘貼即可
1:讀取圖片集并預處理
//【1】圖片集的采集與處理
int Images_number = 256;//圖片集中圖片的數量
int step_x = 80; //將圖片剪裁為80*45大小
int step_y = 45;
Mat srcImage;
vector<Mat> load_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;
主要用到了利用sprintf函式得到圖片集有規律的命名,然后for回圈依次將imread到的圖片push_back添加到Mat容器內就可以了,
二百多張圖片如何快速整齊的命名,,,看鏈接,
https://jingyan.baidu.com/article/b87fe19e4834a95218356814.html
2:調整原模板圖的尺寸大小
//【2】將原模板圖擴大到合適尺寸
Mat originalImage, showImage;
originalImage = imread("彥.jpg");
imshow("彥", originalImage);
resize(originalImage, showImage, Size(1600, 2700));
沒啥好說的,就在基本保持原先長寬比例的條件下,讓長寬都是90*45(圖片集被裁剪的大小)的整數倍就好了,
3:計算圖片集直方圖
//【3】計算圖片集的直方圖
int width = showImage.cols; //模板圖的長寬
int height = showImage.rows;
Mat frame;
vector<MatND> hsit_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);
}
和第一步套路類似,回圈計算了圖片集所有圖的直方圖并存盤到了一個直方圖容器內,
4:遍歷彥模板圖,計算每一小塊的直方圖并對比,替換
//【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;
首先兩個步長分別為step_y和step_x的回圈是遍歷原圖模板各個小塊區域,并計算該塊的直方圖,
之后用一個for回圈在第三步得到的直方圖容器內,尋找與該塊直方圖匹配度最高的風景圖片,然后將該風景圖片替換原圖對應區域,
到此運行完畢我們就可以得到一個不太完美的蒙太奇圖片了:
雖然有些粗糙,但已經可以看出來了叭!效果可以調節裁剪的大小或者原圖模板的大小來改善,但以我代碼設定的引數,執行完都要好幾分鐘,如果有什么優化建議可以交流哈,
來看看女王的腿!
嗯,,竟然是用碑組成的,
我們還可以通過第五步,將上圖左圖與右圖加權得到更為逼真的蒙太奇畫像,
5:將效果圖與原圖加和
Mat dstImage;
addWeighted(showImage, 0.4, montageImage, 0.6, 0, dstImage);
imwrite("結果圖.jpg", dstImage);
imwrite("show.jpg", montageImage);
加和后的效果圖:
效果是不是好一些了?可以更改引數使效果更好,由于我還有毛概論文沒寫,就不瞎搞了~
THE END
本文靈感來自知乎@三木青年,代碼都是我自己敲的,網上關于蒙太奇的大概只有我用的C++了,python代碼可以看知乎鏈接:
https://zhuanlan.zhihu.com/p/168667043
同時歡迎大家關注公眾號【Opencv視覺實踐】,分享有趣好玩漲知識的案例哦,
這么騷的一篇文,你們真的不想來評論區和我說兩句?看在彥的面子上給個贊吧!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255637.html
標籤:AI
