最近接到了一個坑爹題目,是這么要求的:
仿照 《你的名字》,對天壇圖像。src.jpg進行處理。要求
(一)背景(天空)分割,替換后再融合
在自然界的圖片中,很難出現動漫中大多大多的雲彩。首先需要將背景(天空)分割出來,替換成動漫的天空,並且在很好地融合回去。
需要實現的技術:1.背景(天空)分割; 2.再融合。
(二)前景色調轉換
為了實現漫畫中具有卡通意味的前景色調,需要對前面切割下來的前景圖片進行色調轉換。需要實現的技術:3.LUT和色塊制作
幸好網上的例子比較多,老師也給了個例程,現在就一步步來執行
給了兩張圖片,是這個樣子的:
現在需要做得事情就是把雲編程動漫里面那種雲,然后把天壇摳出來換成那樣的雲。
首先要對天壇進行摳圖處理。
給出代碼如下:
/************************************************************************/ /* 1.背景(天空)分割 */ /************************************************************************/ cvtColor(matSrc, temp, COLOR_BGR2HSV); split(temp, planes); equalizeHist(planes[2], planes[2]);//對v通道進行equalizeHist merge(planes, temp); inRange(temp, Scalar(100, 43, 46), Scalar(124, 255, 255), temp); erode(temp, temp, Mat());//形態學變換,填補內部空洞 dilate(temp, temp, Mat()); imshow("kou",temp);
原理是對圖片進行了直方圖均衡化:equalizeHist(),然后根據闕值二元化,白色和黑色。接着進行形態學變換,把天壇的大致輪廓摳圖出來了。
接下來就是把我們的雲彩圖片和這個天壇融合起來了.......
記得要先進行resize,也就是尺寸控制。
/************************************************************************/ /* 2.再融合,以1的結果mask,直接將雲圖拷貝過來(之前需要先做尺度變換) */ /************************************************************************/ //尋找白色區域最大外接矩形的代碼 VP maxCountour = FindBigestContour(mask); Rect maxRect = boundingRect(maxCountour); if (maxRect.height == 0 || maxRect.width == 0) maxRect = Rect(0, 0, mask.cols, mask.rows);//特殊情況 matDst = matSrc.clone(); //注意這里的mask 需要和matCloud同樣尺寸 mask = mask(maxRect); resize(matCloud, matCloud, maxRect.size()); //seamless clone //中間位置為藍天的背景位置 Point center = Point((maxRect.x + maxRect.width) / 2, (maxRect.y + maxRect.height) / 2); Mat normal_clone; Mat mixed_clone; Mat monochrome_clone; seamlessClone(matCloud, matSrc, mask, center, normal_clone, NORMAL_CLONE); seamlessClone(matCloud, matSrc, mask, center, mixed_clone, MIXED_CLONE); seamlessClone(matCloud, matSrc, mask, center, monochrome_clone, MONOCHROME_TRANSFER);
這里有幾個問題,第一個是FindBigestContour函數,實在是不太清楚作用
VP FindBigestContour(Mat src) { int imax = 0; //代表最大輪廓的序號 int imaxcontour = -1; //代表最大輪廓的大小 std::vector<std::vector<Point>>contours; findContours(src, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); for (int i = 0; i < contours.size(); i++) { int itmp = contourArea(contours[i]);//這里采用的是輪廓大小 if (imaxcontour < itmp) { imax = i; imaxcontour = itmp; } } return contours[imax]; }
seamlessclone是一種叫做泊松融合的函數,這里用了3種融合方法。
函數原型(C++):
void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst)
官方文檔中的解釋:Checks if array elements lie between the elements of two other arrays.即檢查數組元素是否在另外兩個數組元素值之間。這里的數組通常也就是矩陣Mat或向量。請注意:該函數輸出的dst是一幅二值化之后的圖像。
seamlessClone(matCloud, matSrc, mask, center, normal_clone, NORMAL_CLONE);
seamlessClone(matCloud, matSrc, mask, center, mixed_clone, MIXED_CLONE);
seamlessClone(matCloud, matSrc, mask, center, monochrome_clone, MONOCHROME_TRANSFER);
實驗結果如下:
有幾個問題,這三個函數顯示出來的圖象其實效果差不多。是因為什么呢?這就不太清楚了,由於之前的邊緣分析算法設計比較精良的緣故嘛?
接下來進行卡通處理。
代碼如下:
/************************************************************************/ /* 3.卡通畫處理 */ /************************************************************************/ //雙邊濾波 bilateralFilter(normal_clone, temp, 5, 10.0, 2.0); //彩色直方圖均衡,將RGB圖像轉到YCbCr分量,然后對Y分量上的圖像進行直方圖均衡化 cvtColor(temp, temp, COLOR_BGR2YCrCb); split(temp, planes); equalizeHist(planes[0], planes[0]); merge(planes, temp); cvtColor(temp, temp, COLOR_YCrCb2BGR); //提高圖像飽和度 matDst = EnhanceSaturation(temp); imshow("結果圖", matDst);
這里有個秘制小函數叫EnhanceSaturation(),目的是提高圖像的飽和度,這里就不放出來了。
大致的原理是雙邊濾波+彩色直方圖均衡。
最終的結果是這樣