[学习笔记] MRF 入门

MRF马尔科夫随机场入门

Intro

MRF是一种广泛应用于图像分割的模型,当然我看到MRF的时候并不是因为分割,而是在图像生成领域,有的paper利用MRF模型来生成图像,因此入门一下MRF,并以分割模型为例记一下代码。

Model

Target

在图像分割中,我们的任务是给定一张图像,输出每个像素的标签。因此我们就是要得到在给定图片特征之下,标签概率最大化时所对应的标签。

因此可以这么建模:
\[ \hat{\omega} = arg \max_{\omega \in \Omega} P(\omega|f) \]
其中w表示标签,f表示图像特征,求最大后验概率。

根据贝叶斯理论,上式右边可以写成:
\[ P(\omega|f) = \frac{P(f|\omega)P(\omega)}{P(f)} \]
其中,P(f)是常量,因为当一张图片确定之后,P(f)便确定了。因此,上式只取决于分子部分。

因此,我们的任务中只需要对分子的两个部分进行定义即可。

Neighbors

像素Neighbors的定义很简单,就是这个像素周围的其他像素。

举例而言,下图分别是中心点像素的四邻域和八邻域。

Hammersley-Clifford Theorem

Hammersley-Clifford Theorem表明,条件随机场里的p(w)一定是满足吉布斯分布的,所以定义下式:
\[ P(\omega) = \frac{1}{Z}exp(-U(\omega)) = \frac{1}{Z}exp(- \sum_{c \in C} V_c(\omega)) \]
其中,Z作为normalization项,\(Z = exp(-U(\omega))\),U定义为势能,而等号最右边将U变成了V的求和,在后面我们会说到,这里其实是每个原子团的势能的求和。

Clique

Clique就是我们上面提到的“团”的概念。集合\(c\)\(S\)的原子团当且仅当c中的每个元素都与该集合中的其他元素相邻。那么Clique就是所有\(c\)的并集。
\[ C = c_1 \cup c_2 \cdots c_n \]

举例而言:

一个像素的四邻域及他自己组成的集合的原子团可以分为singleton和doubleton如图所示。

Clique Potential

翻译过来就是势能,用\(V(w)\)表示,描述的是一个Clique的能量。

那么,一个像素的领域的势能就是每个团的能量的和。
\[ U(\omega) = \sum_{c \in C} V_c(w) \]
其中c表示原子团,c表示Clique,V是如何定义的呢?

在图像分割中,可以以一阶团为例,
\[ V_c(\omega) = \beta \delta(w,w_s) = \left\{\begin{aligned}\beta &&w = w_s \\-\beta && w \neq w_s \\\end{aligned}\right. \]
到这里,\(P(\omega)\)的所有变量解释完了,下一步是计算\(P(f|\omega)\)

\(P(f|\omega)\)的计算

\(P(f|\omega)\)是服从高斯分布的,也就是说,如果我们知道了这个像素的标签是什么,那么他的像素值应该服从这个标签下的条件概率的高斯分布。
\[ P(f_s|\omega_s) = \frac{1}{\sqrt{2\pi}\sigma_{w_s}}exp(-\frac{(f_s - \mu_{\omega_s})^2}{2\sigma^2_{\omega_s}}) \]
计算每个类别的像素均值和方差,带入公式,即得条件概率。

最后,就是最大化\(P(\omega)P(f|\omega)\),以对数形式转化为求和的形式去优化,最大化\(log(P(\omega)) + log(P(f|\omega))\).

Coding

import numpy as np
import cv2 as cv
import copy
class MRF():
    def __init__(self,img,max_iter = 100,num_clusters = 5,init_func = None,beta = 10):
        self.max_iter = max_iter
        self.kernels = np.zeros(shape = (8,3,3))
        self.beta = beta
        self.num_clusters = num_clusters
        for i in range(9):
            if i < 4:
                self.kernels[i,i//3,i%3] = 1
            elif i > 4:
                self.kernels[i-1,(i-1)//3,(i-1)%3] = 1
        self.img = img
        if init_func is None:
            self.labels = np.random.randint(low = 1,high = num_clusters + 1,size = img.shape,dtype = np.uint8)

    def __call__(self):
        #print(self.labels)
        #print(self.img)
        img = self.img.reshape((-1,))
        for iter in range(self.max_iter):
            p1 = np.zeros(shape = (self.num_clusters,self.img.shape[0] * self.img.shape[1]))
            for cluster_idx in range(self.num_clusters):
                temp = np.zeros(shape = (self.img.shape))
                for i in range(8):
                    res = cv.filter2D(self.labels,-1,self.kernels[i,:,:])
                    temp[(res == (cluster_idx + 1))] += self.beta
                    temp[(res == (cluster_idx + 1))] -= self.beta
                    #temp += (res == (cluster_idx + 1)).astype(np.int)
                temp = np.exp(-temp)
                #temp = temp/8.0
                p1[cluster_idx,:] = temp.reshape((-1,))
            p1 = p1 / np.sum(p1,axis = 0)
            p1[p1 == 0] = 1e-3
            mu = np.zeros(shape = (self.num_clusters,))
            sigma = np.zeros(shape = (self.num_clusters,))
            for i in range(self.num_clusters):
                #mu[i] = np.mean(self.img[self.labels == (i+1)])
                data = self.img[self.labels == (i+1)]
                if np.sum(data) > 0:
                    mu[i] = np.mean(data)
                    sigma[i] = np.var(data)
                else:
                    mean = 0
                    sigma[i] = 1
                #print(sigma[i])
            #sigma[sigma == 0] = 1e-3
            p2 = np.zeros(shape = (self.num_clusters,self.img.shape[0] * self.img.shape[1]))
            for i in range(self.img.shape[0] * self.img.shape[1]):
               for j in range(self.num_clusters):
                   #print(sigma[j])
                   p2[j,i] = -np.log(np.sqrt(2*np.pi)*sigma[j]) -(img[i]-mu[j])**2/2/sigma[j];
            self.labels = np.argmax(np.log(p1) + p2,axis = 0) + 1
            self.labels = np.reshape(self.labels,self.img.shape).astype(np.uint8)
            print("-----------start-----------")
            print(p1)
            print("-" * 20)
            print(p2)
            print("----------end------------")
            #print("iter {} over!".format(iter))
            #self.show()
            #print(self.labels)
    def show(self):
        img = copy.copy(self.labels)
        img[img == 1] = 0
        img[img == 2] = 80
        img[img == 3] = 160
        img[img == 4] = 240
        #img = self.labels / (self.num_clusters) * 255
        
        cv.imshow("res",img.astype(np.uint8))
        cv.waitKey(0)
if __name__ == "__main__":
    img = cv.imread("/home/xueaoru/图片/test002.jpg")
    
    img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    img = img/255.
    #img = np.random.rand(64,64)
    #img = cv.resize(img,(256,256))
    mrf = MRF(img = img,max_iter = 20,num_clusters = 2)
    mrf()
    mrf.show()
    #print(mrf.kernels)

Input:

Output(num_clusters = 4):

Output(num_clusters = 2):

猜你喜欢

转载自www.cnblogs.com/aoru45/p/12079722.html