1、什么是條件隨機(jī)向量場(chǎng)CRF?

上述模型表示,以
表示觀測(cè)序列,以
表示隱含的狀態(tài)序列,那么觀察值
與
。
2、圖像分割中的應(yīng)用全連接CRF
??由于CRF具有考慮上下文關(guān)系的特性,因此一般的CRF可以用在圖像去噪,平滑圖像的過(guò)程。但是圖像分割過(guò)程中產(chǎn)生了分類目標(biāo)邊界模糊的問(wèn)題,為了得到更精確的最終分類結(jié)果,通常要進(jìn)行一些圖像后處理??紤]到圖像像素之間的強(qiáng)相關(guān)性,因此提出了全連接CRF來(lái)解決這一問(wèn)題。

??全連接CRFs與稀疏CRFs的最大差別在于:每個(gè)像素點(diǎn)都與所有的像素點(diǎn)相連接構(gòu)成連接邊。為了簡(jiǎn)化計(jì)算,獻(xiàn)《Efficient Inference in Fully Connected CRFs with Gaussian Edge Potentials》給出了快速推理算法。接著我們就簡(jiǎn)單講解具體的求解算法:
首先,假設(shè)圖像中的每一點(diǎn)都符合吉布斯分布
其中是觀測(cè)值,
是能量函數(shù),也是我們優(yōu)化的目標(biāo),
是歸一化因子。
??能量函數(shù)由一元?jiǎng)莺瘮?shù)和二元?jiǎng)莺瘮?shù)構(gòu)成,如下公式所示:
其中的一元?jiǎng)莺瘮?shù)用于衡量像素點(diǎn)的類別概率,自卷積神經(jīng)網(wǎng)絡(luò)網(wǎng)絡(luò)的后端輸出。二元?jiǎng)莺瘮?shù)用于描述像素點(diǎn)和像素點(diǎn)之間的關(guān)系,鼓勵(lì)相似像素分配相同的標(biāo)簽,而相差較大的像素分配不同的標(biāo)簽。這個(gè)相似的定義與顏色值rgb和實(shí)際相對(duì)距離xy相關(guān),所以CRF能夠使圖片盡量在邊界處分割。
二元?jiǎng)莺瘮?shù)公式如下:
其中叫做標(biāo)簽兼容項(xiàng)(Label Compatibility),它約束了像素間傳導(dǎo)的條件,只有相同標(biāo)簽(label)條件下,能量才可以相互傳導(dǎo)。
是特征函數(shù),以特征的形式表示了不同像素之前的“親密度”,第一項(xiàng)被稱作表面核,第二項(xiàng)被稱作平滑核,公式如下:
??在全連接CRFs進(jìn)行影像后處理的實(shí)際操作中,一元?jiǎng)菽転楦怕史植紙D,即由模型輸出的特征圖經(jīng)過(guò)softmax函數(shù)運(yùn)算得到的結(jié)果;二元?jiǎng)菽苤械奈恢眯畔⒑皖伾畔⒂稍加跋裉峁?。?dāng)能量
越小時(shí),預(yù)測(cè)的類別標(biāo)簽X就越準(zhǔn)確,我們通過(guò)迭代最小化能量函數(shù),得到最終的后處理結(jié)果。
3、代碼理解
import numpy as np
import pydensecrf.densecrf as dcrf
try:
from cv2 import imread, imwrite
except ImportError:
# 如果沒(méi)有安裝OpenCV,就是用skimage
from skimage.io import imread, imsave
imwrite = imsave
from pydensecrf.utils import unary_from_labels, create_pairwise_bilateral, create_pairwise_gaussian
"""
original_image_path 原始圖像路徑
predicted_image_path 之前用自己的模型預(yù)測(cè)的圖像路徑
CRF_image_path 即將進(jìn)行CRF后處理得到的結(jié)果圖像保存路徑
"""
def CRFs(original_image_path,predicted_image_path,CRF_image_path):
print("original_image_path: ",original_image_path)
img = imread(original_image_path)
# 將predicted_image的RGB顏色轉(zhuǎn)換為uint32顏色 0xbbggrr
anno_rgb = imread(predicted_image_path).astype(np.uint32) #shape為(240, 320, 3)
anno_lbl = anno_rgb[:,:,0] + (anno_rgb[:,:,1] << 8) + (anno_rgb[:,:,2] << 16) #shape變?yōu)榱?240, 320)
# 將uint32顏色轉(zhuǎn)換為1,2,...
# 如果是分兩類,colors會(huì)產(chǎn)生三種像素值,如:[0,16384,4227072];
# labels為anno_lbl矩陣中像素值在colors中的索引,并以列表形式儲(chǔ)存, 因此labels的值只有0,1,2三種;shape為(240*320,)
colors, labels = np.unique(anno_lbl, return_inverse=True)
# 如果你的predicted_image里的黑色(0值)不是待分類類別,表示不確定區(qū)域,即將分為其他類別
# 那么就取消注釋以下代碼
#HAS_UNK = 0 in colors
#if HAS_UNK:
#colors = colors[1:]
# 創(chuàng)建從predicted_image到32位整數(shù)顏色的映射。
colorize = np.empty((len(colors), 3), np.uint8)
colorize[:,0] = (colors & 0x0000FF)
colorize[:,1] = (colors & 0x00FF00) >> 8
colorize[:,2] = (colors & 0xFF0000) >> 16
# 計(jì)算predicted_image中的類數(shù)。
n_labels = len(set(labels.flat))
#n_labels = len(set(labels.flat)) - int(HAS_UNK) ##如果有不確定區(qū)域,用這一行代碼替換上一行
###########################
### 設(shè)置CRF模型 ###
###########################
use_2d = False
# use_2d = True
###########################################################
##不是很清楚什么情況用2D
##作者說(shuō)“對(duì)于圖像,使用此庫(kù)的最簡(jiǎn)單方法是使用DenseCRF2D類”
##作者還說(shuō)“DenseCRF類可用于通用(非二維)密集CRF”
##但是根據(jù)我的測(cè)試結(jié)果一般情況用DenseCRF比較對(duì)
#########################################################33
if use_2d:
# 使用densecrf2d類
d = dcrf.DenseCRF2D(img.shape[1], img.shape[0], n_labels)
# 得到一元?jiǎng)荩ㄘ?fù)對(duì)數(shù)概率)
# U.shape為(2, 76800),即(n_labels,len(labels))
U = unary_from_labels(labels, n_labels, gt_prob=0.2, zero_unsure=None)
#U = unary_from_labels(labels, n_labels, gt_prob=0.2, zero_unsure=HAS_UNK)## 如果有不確定區(qū)域,用這一行代碼替換上一行
d.setUnaryEnergy(U)
# 增加了與顏色無(wú)關(guān)的術(shù)語(yǔ),只是位置-----會(huì)懲罰空間上孤立的小塊分割,即強(qiáng)制執(zhí)行空間上更一致的分割
d.addPairwiseGaussian(sxy=(3, 3), compat=3, kernel=dcrf.DIAG_KERNEL,
normalization=dcrf.NORMALIZE_SYMMETRIC)
# 增加了顏色相關(guān)術(shù)語(yǔ),即特征是(x,y,r,g,b)-----使用局部顏色特征來(lái)細(xì)化它們
d.addPairwiseBilateral(sxy=(80, 80), srgb=(13, 13, 13), rgbim=img,compat=10,
kernel=dcrf.DIAG_KERNEL,
normalization=dcrf.NORMALIZE_SYMMETRIC)
'''
addPairwiseGaussian函數(shù)里的sxy為公式中的 $\theta_{\gamma}$,
addPairwiseBilateral函數(shù)里的sxy、srgb為$\theta_{\alpha}$ 和 $\theta_{\beta}$
'''
else:
# 使用densecrf類
d = dcrf.DenseCRF(img.shape[1] * img.shape[0], n_labels)
# 得到一元?jiǎng)荩ㄘ?fù)對(duì)數(shù)概率)
U = unary_from_labels(labels, n_labels, gt_prob=0.5, zero_unsure=None)
#U = unary_from_labels(labels, n_labels, gt_prob=0.7, zero_unsure=HAS_UNK)## 如果有不確定區(qū)域,用這一行代碼替換上一行
d.setUnaryEnergy(U)
# 這將創(chuàng)建與顏色無(wú)關(guān)的功能,然后將它們添加到CRF中
feats = create_pairwise_gaussian(sdims=(3, 3), shape=img.shape[:2])
d.addPairwiseEnergy(feats, compat=8,kernel=dcrf.DIAG_KERNEL,
normalization=dcrf.NORMALIZE_SYMMETRIC)
# 這將創(chuàng)建與顏色相關(guān)的功能,然后將它們添加到CRF中
feats = create_pairwise_bilateral(sdims=(80, 80), schan=(13, 13, 13),
img=img, chdim=2)
d.addPairwiseEnergy(feats, compat=10,
kernel=dcrf.DIAG_KERNEL,
normalization=dcrf.NORMALIZE_SYMMETRIC)
####################################
### 做推理和計(jì)算 ###
####################################
# 進(jìn)行5次推理
Q = d.inference(10)
# 找出每個(gè)像素最可能的類
MAP = np.argmax(Q, axis=0)
# 將predicted_image轉(zhuǎn)換回相應(yīng)的顏色并保存圖像
MAP = colorize[MAP,:]
imwrite(CRF_image_path, MAP.reshape(img.shape))
print("CRF圖像保存在",CRF_image_path,"!")
4、一元?jiǎng)莺瘮?shù) Unary potential
一元?jiǎng)菁淳W(wǎng)絡(luò)預(yù)測(cè)得到的結(jié)果,進(jìn)行-np.log(py)等操作
U = np.array(...) # Get the unary in some way.
print(U.shape) # -> (2, 240, 320)
print(U.dtype) # -> dtype('float32')
U = U.reshape((5,-1)) # Needs to be flat.
d.setUnaryEnergy(U)
# Or alternatively: d.setUnary(ConstUnary(U))
#注意,nlabel維度是這里reshape之前的第一個(gè)維度;如果不是這樣的話,你可能需要在reshape之前把nlabel移到前面,即U.shape的結(jié)果應(yīng)該為(5, 480, 640),就像這樣:
print(U.shape) # -> (480, 640, 5)
U = U.transpose(2, 0, 1).reshape((5,-1))
得到 unary potentials有兩種常見(jiàn)的方法:
1)由人類或其他過(guò)程產(chǎn)生的硬標(biāo)簽。該方法由from pydensecrf.utils import unary_from_labels實(shí)現(xiàn)
2)由概率分布計(jì)算得到,例如深度網(wǎng)絡(luò)的softmax輸出。即我們之前先對(duì)圖片使用訓(xùn)練好的網(wǎng)絡(luò)預(yù)測(cè)得到最終經(jīng)過(guò)softmax函數(shù)得到的分類結(jié)果,這里需要將這個(gè)結(jié)果轉(zhuǎn)成一元?jiǎng)?br>
對(duì)此,請(qǐng)參閱from pydensecrf.utils import unary_from_softmax
1)unary_from_labels(labels, n_labels, gt_prob, zero_unsure=True)函數(shù)的使用
簡(jiǎn)單分類器,該分類器50%確定注釋(即從訓(xùn)練好的網(wǎng)絡(luò)預(yù)測(cè)img后得到的結(jié)果)是正確的。(與推理示例中相同)。
參數(shù):
labels: numpy.array;標(biāo)簽label映射,即數(shù)據(jù)的形狀的數(shù)組,其中每個(gè)唯一值對(duì)應(yīng)于一個(gè)標(biāo)簽,一種像素值對(duì)應(yīng)一種標(biāo)簽。
n_labels: int;標(biāo)簽的總數(shù)。如果zero_unsure參數(shù)為True(默認(rèn)值),這個(gè)數(shù)字不應(yīng)該包括' 0 '標(biāo)簽,因?yàn)? 0 '不是一個(gè)標(biāo)簽!
gt_prob: float;基本事實(shí)的確定性(必須在(0,1)之內(nèi))。
zero_unsure: bool;如果“True”,則將標(biāo)簽值“0”視為“可能是任何東西”,即具有此值的項(xiàng)將得到一致的一元概率,不將其當(dāng)作標(biāo)簽。如果“False”,不要特別對(duì)待值“0”,而是像對(duì)待任何其他類一樣對(duì)待它。
2)unary_from_softmax(sm, scale=None, clip=1e-5)函數(shù)的使用
將softmax類概率轉(zhuǎn)換為一元?jiǎng)?每個(gè)節(jié)點(diǎn)的NLL),即我們之前先對(duì)圖片使用訓(xùn)練好的網(wǎng)絡(luò)預(yù)測(cè)得到最終經(jīng)過(guò)softmax函數(shù)得到的分類結(jié)果,這里需要將這個(gè)結(jié)果轉(zhuǎn)成一元?jiǎng)荨?/p>
參數(shù):
sm: numpy.array,第一個(gè)維度是類的softmax的輸出,其他所有維度都是flattend。這意味著“sm.shape[0] == n_classes”。
scale: float,softmax輸出的確定性(默認(rèn)為None),需要值在(0,1]。如果不為None,則softmax輸出被縮放到從[0,scale]概率的范圍。
clip: float,將概率裁剪到的最小值。這是因?yàn)橐辉瘮?shù)是概率的負(fù)對(duì)數(shù),而log(0) = inf,所以我們需要把0概率裁剪成正的值。
在這里因?yàn)?code>scale=None,clip=None,所以這個(gè)函數(shù)的作用其實(shí)只進(jìn)行了下面的操作:
-np.log(sm).reshape([num_cls, -1]).astype(np.float32)
構(gòu)建好一元?jiǎng)莺笮枰{(diào)用, 將該一元?jiǎng)萏砑拥紺RF中:
d.setUnaryEnergy(U)
5、參考
??[1] 如何輕松愉快地理解條件隨機(jī)場(chǎng)(CRF)?
??[2] 圖像語(yǔ)義分割之FCN和CRF
??[3] https://hit-computer.github.io/2017/06/10/CRF/
??[4] DeepLearning-500-questions第九章 圖像分割d
??[5] https://zhuanlan.zhihu.com/p/64854535
??[6] pydensecrf的使用