使用 OpenCV 替換圖像的背景

一. 業(yè)務(wù)背景

在我們的某項(xiàng)業(yè)務(wù)中,需要通過自研的智能硬件“自動化”地拍攝一組組手機(jī)的照片,這些照片有時候因?yàn)楣庹盏囊蛩匦枰紤]將背景的顏色整體替換掉,然后再呈現(xiàn)給 C 端用戶。這時就有背景替換的需求了。

二. 技術(shù)實(shí)現(xiàn)

使用 OpenCV ,通過傳統(tǒng)的圖像處理來實(shí)現(xiàn)這個需求。

方案一:

首先想到的是使用 K-means 分離出背景色。

大致的步驟如下:

將二維圖像數(shù)據(jù)線性化

使用 K-means 聚類算法分離出圖像的背景色

將背景與手機(jī)二值化

使用形態(tài)學(xué)的腐蝕,高斯模糊算法將圖像與背景交匯處高斯模糊化

替換背景色以及對交匯處進(jìn)行融合處理

k-平均算法(英文:k-means clustering)源于信號處理中的一種向量量化方法,現(xiàn)在則更多地作為一種聚類分析方法流行于數(shù)據(jù)挖掘領(lǐng)域。k-平均聚類的目的是:把 n 個點(diǎn)(可以是樣本的一次觀察或一個實(shí)例)劃分到k個聚類中,使得每個點(diǎn)都屬于離他最近的均值(此即聚類中心)對應(yīng)的聚類,以之作為聚類的標(biāo)準(zhǔn)。這個問題將歸結(jié)為一個把數(shù)據(jù)空間劃分為Voronoi cells的問題。

K-means 算法思想為:給定n個數(shù)據(jù)點(diǎn){x1,x2,…,xn},找到K個聚類中心{a1,a2,…,aK},使得每個數(shù)據(jù)點(diǎn)與它最近的聚類中心的距離平方和最小,并將這個距離平方和稱為目標(biāo)函數(shù),記為Wn,其數(shù)學(xué)表達(dá)式為:

K-means 算法基本流程:

初始的 K 個聚類中心。

按照距離聚類中心的遠(yuǎn)近對所有樣本進(jìn)行分類。

重新計算聚類中心,判斷是否退出條件:

兩次聚類中心的距離足夠小視為滿足退出條件;

不退出則重新回到步驟2。

intmain(){? ? Mat src = imread("test.jpg");if(src.empty()) {printf("could not load image...\n");return-1;? ? }? ? imshow("origin", src);// 將二維圖像數(shù)據(jù)線性化Mat data;for(inti =0; i < src.rows; i++) {//像素點(diǎn)線性排列for(intj =0; j < src.cols; j++)? ? ? ? {? ? ? ? ? ? Vec3b point = src.at(i, j);? ? ? ? ? ? Mat tmp = (Mat_(1,3) << point[0], point[1], point[2]);? ? ? ? ? ? data.push_back(tmp);? ? ? ? }? ? }// 使用K-means聚類intnumCluster =4;? ? Mat labels;? ? TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT,10,0.1);? ? kmeans(data, numCluster, labels, criteria,4, KMEANS_PP_CENTERS);// 背景與手機(jī)二值化Mat mask = Mat::zeros(src.size(), CV_8UC1);intindex = src.rows *2+2;//獲取點(diǎn)(2,2)作為背景色intcindex = labels.at(index);/*? 提取背景特征 */for(introw =0; row < src.rows; row++) {for(intcol =0; col < src.cols; col++) {? ? ? ? ? ? index = row * src.cols + col;intlabel = labels.at(index);if(label == cindex) {// 背景mask.at(row, col) =0;? ? ? ? ? ? }else{? ? ? ? ? ? ? ? mask.at(row, col) =255;? ? ? ? ? ? }? ? ? ? }? ? }? ? imshow("mask", mask);// 腐蝕 + 高斯模糊:圖像與背景交匯處高斯模糊化Mat k = getStructuringElement(MORPH_RECT, Size(3,3), Point(-1,-1));? ? erode(mask, mask, k);? ? GaussianBlur(mask, mask, Size(3,3),0,0);// 更換背景色以及交匯處融合處理RNGrng(12345);? ? Vec3b color;//設(shè)置的背景色color[0] =255;//rng.uniform(0, 255);color[1] =255;// rng.uniform(0, 255);color[2] =255;// rng.uniform(0, 255);Matresult(src.size(), src.type());doublew =0.0;//融合權(quán)重intb =0, g =0, r =0;intb1 =0, g1 =0, r1 =0;intb2 =0, g2 =0, r2 =0;for(introw =0; row < src.rows; row++) {for(intcol =0; col < src.cols; col++) {intm = mask.at(row, col);if(m ==255) {? ? ? ? ? ? ? ? result.at(row, col) = src.at(row, col);// 前景}elseif(m ==0) {? ? ? ? ? ? ? ? result.at(row, col) = color;// 背景}else{/* 融合處理部分 */w = m /255.0;? ? ? ? ? ? ? ? b1 = src.at(row, col)[0];? ? ? ? ? ? ? ? g1 = src.at(row, col)[1];? ? ? ? ? ? ? ? r1 = src.at(row, col)[2];? ? ? ? ? ? ? ? b2 = color[0];? ? ? ? ? ? ? ? g2 = color[1];? ? ? ? ? ? ? ? r2 = color[2];? ? ? ? ? ? ? ? b = b1 * w + b2 * (1.0- w);? ? ? ? ? ? ? ? g = g1 * w + g2 * (1.0- w);? ? ? ? ? ? ? ? r = r1 * w + r2 * (1.0- w);? ? ? ? ? ? ? ? result.at(row, col)[0] = b;? ? ? ? ? ? ? ? result.at(row, col)[1] = g;? ? ? ? ? ? ? ? result.at(row, col)[2] = r;? ? ? ? ? ? }? ? ? ? }? ? }? ? imshow("final", result);? ? waitKey(0);return0;}復(fù)制代碼

方案二:

方案一的算法并不是對所有手機(jī)都有效,對于一些淺色的、跟背景顏色相近的手機(jī),該算法會比較無能為力。

于是換一個思路:

使用 USM 銳化算法對圖像增強(qiáng)

再用純白色的圖片作為背景圖,和銳化之后的圖片進(jìn)行圖像融合。

圖像銳化是使圖像邊緣更加清晰的一種圖像處理方法。

USM(Unsharpen Mask) 銳化的算法就是對原圖像先做一個高斯模糊,然后用原來的圖像減去一個系數(shù)乘以高斯模糊之后的圖像,然后再把值 Scale 到0~255的 RGB 素值范圍之內(nèi)。基于 USM 銳化的算法可以去除一些細(xì)小的干擾細(xì)節(jié)和噪聲,比一般直接使用卷積銳化算子得到的圖像銳化結(jié)果更加真實(shí)可信。

intmain(){? ? Mat src = imread("./test.jpg");if(src.empty()) {printf("could not load image...\n");return-1;? ? }? ? namedWindow("src", WINDOW_AUTOSIZE);? ? imshow("origin", src);? ? Mat blur_img, usm;? ? GaussianBlur(src, blur_img, Size(0,0),25);? ? addWeighted(src,1.5, blur_img,-0.5,0, usm);? ? imshow("usm", usm);? ? Mat roi = Mat(Size(src.cols,src.rows), CV_8UC3, Scalar(255,255,255));? ? Mat dst;? ? addWeighted(usm,1.275, roi,0.00015,0, dst);? ? imshow("final", dst);? ? waitKey(0);return0;}復(fù)制代碼

其中,addWeighted 函數(shù)是將兩張大小相同、類型相同的圖片進(jìn)行融合。數(shù)學(xué)公式如下:

dst = src1*alpha + src2*beta + gamma;復(fù)制代碼

三. 總結(jié)

其實(shí),我嘗試過用 OpenCV 多種方式實(shí)現(xiàn)該功能,也嘗試過使用深度學(xué)習(xí)實(shí)現(xiàn)。目前還沒有最滿意的效果。后續(xù),我會更偏向于使用深度學(xué)習(xí)來實(shí)現(xiàn)該功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容