日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

颜色协调模型Color Harmoniztion

發(fā)布時(shí)間:2023/12/13 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 颜色协调模型Color Harmoniztion 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

最近做換臉,在膚色調(diào)整的那一塊,看到一個(gè)有意思的文章,復(fù)現(xiàn)一波玩玩。不過(guò)最后一步掉鏈子了,有興趣的可以一起討論把鏈子補(bǔ)上。

主要是github上大佬的那個(gè)復(fù)現(xiàn)代碼和原文有點(diǎn)差異,而且代碼復(fù)雜度過(guò)高,閱讀費(fèi)勁,這里為了清晰理解理論知識(shí),就一步一步按照論文的每個(gè)章節(jié)走,不過(guò)有很大一部分代碼都借鑒大佬了。

國(guó)際慣例,參考文獻(xiàn):

論文《Color Harmonization》

opencv超像素分割

大佬的實(shí)現(xiàn)

簡(jiǎn)介

這篇論文主要干啥呢,看下圖

注意小姑涼的衣服的顏色從左邊的原圖變成了右邊的變化了的圖片。

那么論文的工作就是:在盡量保持原色的基礎(chǔ)上,調(diào)整圖片顏色,使得圖片整體顏色看起來(lái)更加協(xié)調(diào)。

基本原理就是將RGB圖像轉(zhuǎn)成HSV,然后調(diào)整色相(hue)直方圖。下圖中左邊的彩色圓環(huán)代表原始圖片的色相直方圖,右邊為論文調(diào)整后的色相直方圖。

感覺(jué)這個(gè)論文用來(lái)去除雜色應(yīng)該挺有用,不過(guò)更好玩的是能交互式自動(dòng)調(diào)色。

【注】要知道色度hue的范圍是0°~360°0°\sim360°0°360° ,opencv里面提取出來(lái)的是0°~180°0°\sim 180°0°180°

流程復(fù)現(xiàn)

環(huán)境python3、opencv-python==3.4.2.16、opencv-contrib-python==3.4.2.16

注意如果找不到ximgproc,手動(dòng)卸載opencv-contrib-python再重新安裝,自動(dòng)卸載安裝無(wú)法解決問(wèn)題。

顏色模板

首先,文章介紹了8種比較適合用于調(diào)色的模板,注意灰色區(qū)域的大小固定,但是位置不是固定的,可以繞著圓心旋轉(zhuǎn)。

當(dāng)圖像的色相都落在灰色區(qū)域,就是顏色處理的比較好的圖像。

文末介紹了每個(gè)模板的灰色區(qū)域面積比:

  • 模板中大的扇形區(qū)域:V,Y,X占26%的面積,角度是93.6°93.6°93.6°

  • 模板中小的扇形區(qū)域:i,L,I,Y占5%的面積,角度是18°18°18°

  • 模板L占22%面積,角度是79.0°79.0°79.0°

  • 模板T占50%面積,角度是180°180°180°

  • 模板I,X,Y的兩個(gè)扇形區(qū)的夾角是180°180°180°

  • 模板L兩個(gè)扇形區(qū)的夾角是90°90°90°

不過(guò)大佬已經(jīng)把這部分列出來(lái)了,兩個(gè)數(shù),第一個(gè)是扇形區(qū)域的中心,第二個(gè)是扇形區(qū)域的寬度:

#定義模板,分別定義的中心與邊界偏轉(zhuǎn)角度 HueTemplates = {"i" : [( 0.00, 0.05)],"V" : [( 0.00, 0.26)],"L" : [( 0.00, 0.05), ( 0.25, 0.22)],"mirror_L": [( 0.00, 0.05), (-0.25, 0.22)],"I" : [( 0.00, 0.05), ( 0.50, 0.05)],"T" : [( 0.25, 0.50)],"Y" : [( 0.00, 0.26), ( 0.50, 0.05)],"X" : [( 0.00, 0.26), ( 0.50, 0.26)], }

隨便顯示一個(gè)瞅瞅

#預(yù)覽模板 def show_temp(template_name,template_alpha):canvas = np.zeros((canvas_h, canvas_w, 3)) #畫布cv2.circle(canvas, (yc, xc), circle_r, (255,255,255), -1) #畫圓for t in HueTemplates[template_name]:center = t[0]*360 + template_alphawidth = t[1]*360start = center - width/2end = center + width/2cv2.ellipse(canvas,(yc,xc),(circle_r,circle_r),0,start,end,(0,0,0),-1,cv2.LINE_AA)cv2.circle(canvas, (yc, xc), 10, (0,0,0), -1,cv2.LINE_AA) #畫中心點(diǎn)canvas = np.array(canvas,np.uint8) canvas = cv2.cvtColor(canvas,cv2.COLOR_BGR2RGB)return canvas plt.imsave('./temp/show_temp.png',show_temp('X',90),cmap='gray')

色相直方圖

就是單純的將圖像的H通道提取出來(lái),看看像素個(gè)數(shù)。

def count_hue_histogram(X):N = 360H = X[:, :, 0].astype(np.int32) * 2 H_flat = H.flatten()histo = np.zeros(N)for i in range(N):histo[i] = np.sum(H_flat==i);return histo

然后把它畫到一個(gè)環(huán)上

canvas_h = 600 #畫布高度 canvas_w = 600 #畫布寬度 yc = int(canvas_h/2) #圓心位置y xc = int(canvas_w/2) #圓心位置x circle_r = 250 #半徑 def draw_polar_histogram(histo):N = 360histo = histo.astype(float)histo /= np.max(histo)histo *= circle_rcanvas = np.zeros((canvas_h, canvas_w, 3)) #畫布cv2.circle(canvas, (yc, xc), circle_r, (255,255,255), -1) #畫圓for i in range(N):theta = -i * np.pi / 180 #各個(gè)hue的弧度count = histo[i] #各個(gè)hue的數(shù)目#當(dāng)前hue的柱子y1 = yc - int(circle_r * np.sin(theta))x1 = xc + int(circle_r * np.cos(theta))y2 = yc - int((circle_r-histo[i]) * np.sin(theta))x2 = xc + int((circle_r-histo[i]) * np.cos(theta))color_HSV = np.zeros((1,1,3), dtype=np.uint8)color_HSV[0,0,:] = [int(i/2),255,255] #每個(gè)角度的Hcolor_BGR = cv2.cvtColor(color_HSV, cv2.COLOR_HSV2BGR) #將HSV轉(zhuǎn)換為BGRB = int(color_BGR[0,0,0])G = int(color_BGR[0,0,1])R = int(color_BGR[0,0,2])cv2.line(canvas, (x1,y1), (x2,y2), (B,G,R), 3,cv2.LINE_AA) #畫柱子canvas = cv2.circle(canvas, (yc, xc), 5, (0,0,0), -1) #圓心canvas = np.array(canvas,np.uint8)canvas = cv2.cvtColor(canvas,cv2.COLOR_BGR2RGB)return canvas

測(cè)試代碼

img_rgb = cv2.imread('peacock.png') img_hsv = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2HSV) h_hist = count_hue_histogram(img_hsv) img_hue = img_hsv[...,0].copy()*2.0 img_sat = img_hsv[...,1].copy()/2.55 hist_img = draw_polar_histogram(h_hist) plt.imsave('./temp/hist_img.png',hist_img)

圖像評(píng)分

正式進(jìn)入論文第3章節(jié),根據(jù)圖像的色相與每個(gè)模板扇形區(qū)域的邊界的最小距離,以及每個(gè)像素的飽和度,來(lái)計(jì)算得分,公式為
F(x,(m,α))=∑p∈X∣∣H(p)?ETm(α)(p)∣∣?S(p)F(x,(m,\alpha))=\sum_{p\in X}||H(p)-E_{T_m(\alpha)}(p)||\cdot S(p) F(x,(m,α))=pX?H(p)?ETm?(α)?(p)?S(p)
X代表圖像,p代表圖像每個(gè)像素位置的色度值(Hue),ETm(α)(p)E_{T_m(\alpha)}(p)ETm?(α)?(p)代表色度值p與扇形區(qū)域最接近的邊界,S(p)S(p)S(p)代表HSV中的飽和度S

接下來(lái)先看某個(gè)模板中,0°~360°0°\sim360°0°360°每個(gè)色相與邊界的最短距離(角度值差),如果色相在模板的扇形區(qū)域內(nèi),距離為000

#圓弧距:在hue圓上的角度差 def deg_distance(a, b):d1 = np.abs(a - b)d2 = np.abs(360-d1)d = np.minimum(d1, d2)return d #是否在區(qū)域內(nèi) def in_border(h,center,width):return deg_distance(h,center)<width/2 #區(qū)域外的h,計(jì)算最近邊界的圓弧距 def dist_to_border(h,border):H1=deg_distance(h,border[0]) H2=deg_distance(h,border[1]) H_dist2bdr = np.minimum(H1,H2)return H_dist2bdr #計(jì)算當(dāng)前模板的每個(gè)hue值的加權(quán) def hue_weight(temp_name,alpha):hweight = []h = np.arange(360)for t in HueTemplates[temp_name]:center = t[0]*360 + alpha #中心位置width = t[1]*360 #寬度border = [center - width/2,center + width/2] #起止位置temp_dist = dist_to_border(h,border) #色相與當(dāng)前邊界的距離temp_dist[in_border(h,center,width)]=0 hweight.append(temp_dist)hweight = np.array(hweight)hweight = hweight.min(axis=0)return hweight

接下來(lái)計(jì)算第一項(xiàng):
∣∣H(p)?ETm(α)(p)∣∣||H(p)-E_{T_m(\alpha)}(p)|| H(p)?ETm?(α)?(p)
直接將圖像H值換成對(duì)應(yīng)的最近邊界距離即可:

# 利用template的hue權(quán)重對(duì)圖像的hue直方圖進(jìn)行加權(quán) def h_dist(img_h,temp_name,alpha):score = np.zeros((img_h.shape[0],img_h.shape[1]),dtype=np.float32)hw = hue_weight(temp_name,alpha)for i in range(360):score[img_h==i]= hw[i]return score

再跟飽和度一起,把圖像與每個(gè)模板對(duì)應(yīng)的得分都計(jì)算出來(lái)

#計(jì)算最小的模板得分 def cal_scores(img_hue,img_s):scores = np.zeros((len(HueTemplates.keys()),360),dtype=np.float32)temp_keys = list(HueTemplates.keys())for i in range(len(temp_keys)):#遍歷模板print('temp_keys',temp_keys[i])for alpha in range(360):#每個(gè)旋轉(zhuǎn)角度scores[i,alpha]=np.sum(np.multiply(h_dist(img_hue,temp_keys[i],alpha),img_s))return scores

提取最好的那個(gè)模板和角度,使得當(dāng)前的圖像得分最大

# 計(jì)算每個(gè)模板的得分 hue_scores = cal_scores(img_hue,img_sat) #得到最好的template和alpha [best_m,betst_alpha] = np.unravel_index(np.argmin(hue_scores),hue_scores.shape) ''' temp_keys i temp_keys V temp_keys L temp_keys mirror_L temp_keys I temp_keys T temp_keys Y temp_keys X '''

把最好的模板、旋轉(zhuǎn)角度與對(duì)應(yīng)圖像色相直方圖放一起顯示一波瞅瞅

#畫圖 hist_img = draw_polar_histogram(h_hist) temp_keys = list(HueTemplates.keys()) print(temp_keys[best_m],betst_alpha) temp_img = show_temp(temp_keys[best_m],betst_alpha) overlay_img = cv2.addWeighted(temp_img,0.2,hist_img,1-0.2,0) plt.figure(figsize=(8,8)) plt.imshow(overlay_img) plt.imsave('./temp/overlay_img.png',overlay_img) ''' T 46 '''

圖像分割

上面有些模板有兩個(gè)對(duì)稱扇形,這兩個(gè)扇形代表互補(bǔ)色,也就是顏色差距很大,但是當(dāng)我們把扇形外面的像素壓到扇形里面去的時(shí)候,中間的一些顏色到兩個(gè)扇形的距離差不多,這時(shí)候就會(huì)出現(xiàn)相似的顏色被壓到了完全不同的顏色。如下圖所示

看(d)圖,兩個(gè)箭頭從同一個(gè)位置出發(fā),但是這個(gè)位置的色相到兩個(gè)扇形的距離差不多,這時(shí)候如果隨機(jī)分配顏色,就會(huì)出現(xiàn)孔雀脖子上相似的顏色被壓到完全不同的兩個(gè)顏色里面去了。

這時(shí)候,我們就需要對(duì)每個(gè)像素指定他所壓縮的扇形,并且要保證圖像比較連續(xù)的區(qū)域的顏色不要被壓的不連續(xù)了,那么就可以先對(duì)圖像每個(gè)像素進(jìn)行二分類,當(dāng)有兩個(gè)扇形的時(shí)候,像素標(biāo)簽為0或10或101 ,分別表示應(yīng)該被劃分到哪個(gè)區(qū)域。

文章的算法貌似比較耗時(shí),大佬使用了opencv里面的一個(gè)超像素塊劃分方法,參考文檔看前言。這個(gè)超像素塊劃分有點(diǎn)類似于目標(biāo)分割,達(dá)到的效果如下:

原圖戳這里

可以看出來(lái),連續(xù)區(qū)域基本被劃分到一塊了。每一塊都被標(biāo)記為一個(gè)單獨(dú)的類別。

我們現(xiàn)在要把這一對(duì)類別合并起來(lái),變成兩個(gè)類別。

分割流程大致可以分為這樣

先不看顏色的連續(xù)性,單純計(jì)算每個(gè)像素的色相到哪個(gè)扇形區(qū)域最近,先為每個(gè)像素單獨(dú)打標(biāo)簽

#先計(jì)算每個(gè)像素到哪個(gè)扇形區(qū)域距離最短 def class_pix(h,temp_name,alpha):# 先計(jì)算當(dāng)前模板下,每個(gè)hue對(duì)應(yīng)哪個(gè)扇區(qū)img_label=np.zeros((h.shape[0],h.shape[1]),dtype=np.int32)hlabels = []hue_stand=np.arange(360)#扇區(qū)里外一起處理,某個(gè)扇區(qū)內(nèi)的距離自己的扇區(qū)邊界更近for t in HueTemplates[temp_name]:center = t[0]*360 + alpha #中心位置width = t[1]*360 #寬度border = [center - width/2,center + width/2] #起止位置temp_dist = dist_to_border(hue_stand,border) #hue與當(dāng)前扇區(qū)邊界的距離,不要排除扇區(qū)內(nèi)的hue的計(jì)算hlabels.append(temp_dist)hlabels = np.array(hlabels)hlabel = hlabels.argmin(axis=0)#再把圖像的hue轉(zhuǎn)換為對(duì)應(yīng)扇區(qū)標(biāo)簽for i in range(360):img_label[h==i]=hlabel[i]return img_label

然后再統(tǒng)計(jì)opencv劃分的每個(gè)超像素塊里面的像素標(biāo)簽?zāi)膫€(gè)多,就把當(dāng)前超像素的標(biāo)簽置為誰(shuí):

#制作每個(gè)像素的標(biāo)簽,確定每個(gè)像素往哪個(gè)扇區(qū)shift def split_img(img_hsv,img_h,temp_name,alpha):num_superpixels=200h_cls = class_pix(img_hue,temp_name,alpha)SEEDS = cv2.ximgproc.createSuperpixelSEEDS(img_hsv.shape[1],img_hsv.shape[0],img_hsv.shape[2],num_superpixels,10)SEEDS.iterate(img_hsv,4)V=np.zeros((img_hue.shape))N=V.shape[0]grid_num = SEEDS.getNumberOfSuperpixels() #超像素塊個(gè)數(shù)labels = SEEDS.getLabels() #原圖每個(gè)像素的屬于哪個(gè)超像素塊(標(biāo)簽)for i in range(grid_num):P=[[],[]]s=np.average(h_cls[labels==i]) #當(dāng)前超像素塊的所有hue的標(biāo)簽的均值if(s>0.5):#像素塊大部分朝哪個(gè)扇區(qū),就確定是哪個(gè)扇區(qū)s=1else:s=0h_cls[labels==i]=sreturn h_cls

這兩個(gè)函數(shù)就保證了,顏色又連續(xù),每個(gè)顏色被壓縮的距離又最小,色差變化不會(huì)太大。

壓色相

每個(gè)像素往哪個(gè)扇形塊壓,我們都知道了,那就執(zhí)行最后一步,把像素按照下式壓到對(duì)應(yīng)扇形區(qū)域去
H′(p)=C(p)+w2(1?Gσ(∣∣H(p)?C(p)∣∣))H'(p)=C(p)+\frac{w}{2}(1-G_\sigma(||H(p)-C(p)||)) H(p)=C(p)+2w?(1?Gσ?(H(p)?C(p)))
上式中C(p)代表扇形區(qū)域的中心,www代表扇形區(qū)域的寬度,GσG_\sigmaGσ?代表標(biāo)準(zhǔn)高斯函數(shù),均值為0,方差為σ\sigmaσ,作者建議這個(gè)方差σ\sigmaσ 等于w2\frac{w}{2}2w?

那么先把高斯函數(shù)寫出來(lái)唄:
1(2π)σe?12(x?μσ)2\frac{1}{\sqrt{(2\pi)}\sigma}e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2} (2π)?σ1?e?21?(σx?μ?)2

#標(biāo)準(zhǔn)正態(tài)分布采樣 def normalized_gaussian(X,mu,sigma):X=np.asarray(X).astype(np.float64)sigma=np.asarray(sigma).astype(np.float64)mu=X-muM2=np.multiply(mu,mu)S2=np.multiply(sigma,sigma)return (1/(sigma*np.sqrt(2*np.pi)))*np.exp(-M2/(2*S2))

最后一步,根據(jù)H′(p)H'(p)H(p)的公式,壓色相值

# 對(duì)每個(gè)標(biāo)簽塊分別shift到對(duì)應(yīng)扇區(qū) def hue_shift(img_hsv,temp_name,alpha):img_hue = img_hsv[...,0].copy()*2.0new_img_hue = np.zeros_like(img_hue)h_cls = split_img(img_hsv,img_hue,temp_name,alpha) #確定每個(gè)像素對(duì)應(yīng)的扇區(qū)i=0for t in HueTemplates[temp_name]: #每個(gè)扇區(qū)分別處理center = t[0]*360 + alphawidth = t[1]*360mask = (h_cls==i) #當(dāng)前扇區(qū)所對(duì)應(yīng)的所有標(biāo)簽temp_hue = img_hue*mask #排除其他扇區(qū)超像素塊的干擾center_dist = deg_distance(temp_hue,center)G = normalized_gaussian(center_dist,0,width/2)new_h = center + (1-G)*width/2new_img_hue=new_img_hue+new_h*maski=i+1new_img_hsv = img_hsv.copy()new_img_hsv[...,0]=new_img_hueresult_img = cv2.cvtColor(new_img_hsv,cv2.COLOR_HSV2RGB)return result_img

搞定!!

可視化看看結(jié)果

temp_name=temp_keys[best_m] alpha=betst_alpha plt.imshow(hue_shift(img_hsv,temp_name,alpha))

嗯?真心綠。

其實(shí)還有更好玩的,自己隨便指定一個(gè)template和alpha,能玩一天,比如

temp_name='X'#temp_keys[best_m] alpha=betst_alpha plt.imshow(hue_shift(img_hsv,temp_name,alpha)) plt.imsave('./temp/result1.png',hue_shift(img_hsv,temp_name,alpha))

問(wèn)題

最后一步的實(shí)現(xiàn)應(yīng)該有問(wèn)題,和大佬的代碼有點(diǎn)出入,可能我對(duì)論文的最后一步理解有遺漏,后面再來(lái)查缺補(bǔ)漏吧。有大神看論文以后,也可以在這個(gè)基礎(chǔ)上完善一下。

博客代碼下載:

鏈接:https://pan.baidu.com/s/1DavXVylYx3Sd0K-LygnBoQ
提取碼:ub0n

本文已經(jīng)同步到微信公眾號(hào)中,公眾號(hào)與本博客將持續(xù)同步更新運(yùn)動(dòng)捕捉、機(jī)器學(xué)習(xí)、深度學(xué)習(xí)、計(jì)算機(jī)視覺(jué)算法,敬請(qǐng)關(guān)注

總結(jié)

以上是生活随笔為你收集整理的颜色协调模型Color Harmoniztion的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。