Efficient Inference in Fully Connected CRFs with Gaussian Edge Potentials - NIPS2011
全连接 CRFs,dense CRFs 的 Cython-based Python 封装.
1. 安装
Ubuntu 中可以采用 pip 安装:
sudo pip install pydensecrf这里需要采用 Cython 较新版本,比如 Cython>=0.22.
2. 简单使用
对于图像,pydensecrf 库的最简单的使用方式是 DenseCRF2D 类.
import numpy as np
import pydensecrf.densecrf as dcrf
    
d = dcrf.DenseCRF2D(640, 480, 5)  # width, height, nlabels2.1. 一元潜在关系 Unary potential
设置固定的一元关系(Unary potential):
U = np.array(...)     # Get the unary in some way.
print(U.shape)        # -> (5, 480, 640)
print(U.dtype)        # -> dtype('float32')
U = U.reshape((5,-1)) # Needs to be flat.
d.setUnaryEnergy(U)
# Or alternatively: d.setUnary(ConstUnary(U))其中,U 是 negative log-probabilites. 如果采用的概率为 py,则需要先进行 U=-np.log(py) 处理. nlabels 维应该在 reshape 前.
print(U.shape)  # -> (480, 640, 5)
U = U.transpose(2, 0, 1).reshape((5,-1))Unary potential 的设定:
- [1] - 手工或其它方式生成的 hard labeling. 
from pydensecrf.utils import unary_from_labels - [2] - 计算的概率分布,如深度网络的 softmax 输出. 
from pydensecrf.utils import unary_from_softmax 
2.2. 成对多元关系 Pairwise potentials
常用的成对关系添加:
# This adds the color-independent term, features are the locations only.
d.addPairwiseGaussian(sxy=(3,3), 
                      compat=3, 
                      kernel=dcrf.DIAG_KERNEL, 
                      normalization=dcrf.NORMALIZE_SYMMETRIC)
# This adds the color-dependent term, i.e. features are (x,y,r,g,b).
# im is an image-array, e.g. im.dtype == np.uint8 and im.shape == (640,480,3)
d.addPairwiseBilateral(sxy=(80,80), 
                       srgb=(13,13,13), 
                       rgbim=im, 
                       compat=10, 
                       kernel=dcrf.DIAG_KERNEL,
                       normalization=dcrf.NORMALIZE_SYMMETRIC)对应的简化形式:
d.addPairwiseGaussian(sxy=3, compat=3)
d.addPairwiseBilateral(sxy=80, srgb=13, rgbim=im, compat=10)
3. Demo.py
3.1. dense_crf 函数
dense_crf 函数定义:
import numpy as np
import pydensecrf.densecrf as dcrf
def dense_crf(img, output_probs):
    h = output_probs.shape[0]
    w = output_probs.shape[1]
    output_probs = np.expand_dims(output_probs, 0)
    output_probs = np.append(1 - output_probs, output_probs, axis=0)
    d = dcrf.DenseCRF2D(w, h, 2)
    U = -np.log(output_probs)
    U = U.reshape((2, -1))
    U = np.ascontiguousarray(U)
    img = np.ascontiguousarray(img)
    
    U = U.astype(np.float32)
    d.setUnaryEnergy(U) # Unary 
    d.addPairwiseGaussian(sxy=20, compat=3)  # 
    d.addPairwiseBilateral(sxy=30, srgb=20, rgbim=img, compat=10)
    Q = d.inference(5)
    Q = np.argmax(np.array(Q), axis=0).reshape((h, w))
    return Q3.2. 测试 demo
#image - 原始图片,hxwx3,采用 PIL.Image 读取
#seg_map - 假设为语义分割的 mask, hxw, np.array 形式.
import numpy as np
import matplotlib.pyplot as plt
final_mask = dense_crf(np.array(image).astype(np.uint8), seg_map)
plt.subplot(1, 3, 1)
plt.imshow(image)
plt.subplot(1, 3, 2)
plt.imshow(seg_map)
plt.subplot(1, 3, 3)
plt.imshow(final_mask)
plt.show()4. Github使用 - tensorflow-deeplab-resnet/inference_crf.py
    #---------------------------------CRF
    import sys
    import pydensecrf.densecrf as dcrf
    from pydensecrf.utils import compute_unary, create_pairwise_bilateral, \
        create_pairwise_gaussian, softmax_to_unary
    import skimage.io as io
    softmax = processed_probabilities.transpose((2, 0, 1))  # processed_probabilities:CNN 预测概率
    # softmax_to_unary 的输入是概率值的负对数
    unary = softmax_to_unary(softmax)
    # The inputs should be C-continious -- we are using Cython wrapper
    unary = np.ascontiguousarray(unary)  #(21, n)
    d = dcrf.DenseCRF(img.shape[0] * img.shape[1], 21)
    d.setUnaryEnergy(unary)
    # This potential penalizes small pieces of segmentation that are
    # spatially isolated -- enforces more spatially consistent segmentations
    feats = create_pairwise_gaussian(sdims=(10, 10), shape=img.shape[:2])
    d.addPairwiseEnergy(feats, compat=3,
                        kernel=dcrf.DIAG_KERNEL,
                        normalization=dcrf.NORMALIZE_SYMMETRIC)
    # This creates the color-dependent features --
    # because the segmentation that we get from CNN are too coarse
    # and we can use local color features to refine them
    feats = create_pairwise_bilateral(sdims=(50, 50), schan=(20, 20, 20),
                                      img=img, chdim=2)
    d.addPairwiseEnergy(feats, compat=10,
                        kernel=dcrf.DIAG_KERNEL,
                        normalization=dcrf.NORMALIZE_SYMMETRIC)
    #迭代次数,对于IMG_1702(2592*1456)这张图,迭代5 16.807087183s 迭代20 37.5700438023s
    Q = d.inference(5)
    res = np.argmax(Q, axis=0).reshape((img.shape[0], img.shape[1]))5. Github使用 - carvana-challenge/util/crf.py
import time
import numpy as np
import torch
from torch.autograd import Variable
import pydensecrf.densecrf as dcrf
from pydensecrf.utils import compute_unary, create_pairwise_bilateral, create_pairwise_gaussian, unary_from_softmax
def run_crf(masks):
    print("You're using CRF. Are you sure? In our previous experiments, it has never improved the performance. ")
    crf_masks = np.zeros(masks.data.size())  # shape: (batch_size, 1, height, width)
    for img_idx in range(len(img_name)):
        img  =  images.data[img_idx].cpu().numpy()  # shape: (3, height, width)
        prob = outputs.data[img_idx].cpu().numpy()  # shape: (1, height, width)
        crf_masks[img_idx] = crf(img, prob)
        # convert CRF results back into Variable in GPU
        masks = Variable(torch.from_numpy(crf_masks).float(), volatile=True)
        if torch.cuda.is_available():
            masks = masks.cuda()
    return masks
def crf(img, prob):
    '''
    input:
      img: numpy array of shape (num of channels, height, width)
      prob: numpy array of shape (1, height, width), neural network last layer sigmoid output for img
    output:
      res: (1, height, width)
    Modified from:
      http://warmspringwinds.github.io/tensorflow/tf-slim/2016/12/18/image-segmentation-with-tensorflow-using-cnns-and-conditional-random-fields/
      https://github.com/yt605155624/tensorflow-deeplab-resnet/blob/e81482d7bb1ae674f07eae32b0953fe09ff1c9d1/inference_crf.py
    '''
    func_start = time.time()
    img = np.swapaxes(img, 0, 2)
    # img.shape: (width, height, num of channels)
    num_iter = 5
    prob = np.swapaxes(prob, 1, 2)  # shape: (1, width, height)
    # preprocess prob to (num_classes, width, height) since we have 2 classes: car and background.
    num_classes = 2
    probs = np.tile(prob, (num_classes, 1, 1))  # shape: (2, width, height)
    probs[0] = np.subtract(1, prob) # class 0 is background
    probs[1] = prob                 # class 1 is car
    d = dcrf.DenseCRF(img.shape[0] * img.shape[1], num_classes)
    unary = unary_from_softmax(probs)  # shape: (num_classes, width * height)
    unary = np.ascontiguousarray(unary)
    d.setUnaryEnergy(unary)
    # This potential penalizes small pieces of segmentation that are
    # spatially isolated -- enforces more spatially consistent segmentations
    feats = create_pairwise_gaussian(sdims=(10, 10), shape=img.shape[:2])
    d.addPairwiseEnergy(feats, compat=3,
                        kernel=dcrf.DIAG_KERNEL,
                        normalization=dcrf.NORMALIZE_SYMMETRIC)
    # Note that this potential is not dependent on the image itself.
    # This creates the color-dependent features --
    # because the segmentation that we get from CNN are too coarse
    # and we can use local color features to refine them
    feats = create_pairwise_bilateral(sdims=(50, 50), schan=(20, 20, 20),
                                      img=img, chdim=2)
    d.addPairwiseEnergy(feats, compat=10,
                        kernel=dcrf.DIAG_KERNEL,
                        normalization=dcrf.NORMALIZE_SYMMETRIC)
    Q = d.inference(num_iter)  # set the number of iterations
    res = np.argmax(Q, axis=0).reshape((img.shape[0], img.shape[1]))
    # res.shape: (width, height)
    res = np.swapaxes(res, 0, 1)  # res.shape:    (height, width)
    res = res[np.newaxis, :, :]   # res.shape: (1, height, width)
    func_end = time.time()
    # print('{:.2f} sec spent on CRF with {} iterations'.format(func_end - func_start, num_iter))
    # about 2 sec for a 1280 * 960 image with 5 iterations
    return res
30 comments
demo里面输入的seg_map怎么得到呢?
您好,请问一下,这里的compat参数是什么意思呢?compat=3和compat=10到底有什么区别啊?
请问下我的原图是黑白图,该怎么改呢?
黑白图是单通道的还是三通道的,分割后的 mask 是二值的吗?
黑白图是单通道啊?分割后的mask我是二值的,(0,1),请问怎么解决呢?
单通道是可以的,分割后的 mask 采用二值化之前的那个概率图.
请问下你有写过处理这种黑白图吗?还有就是像create_pairwise_gaussian,.addPairwiseEnergy函数里面的参数代表啥呢?在哪里可以查到呢?谢谢啦
具体实现可参考其源码,如:https://github.com/lucasb-eyer/pydensecrf/tree/master/pydensecrf/densecrf
好的,非常感谢!请问下一般迭代多少次效果比较好呢?
deeplabv2 里一般是 crf 层是 3 次迭代.
确定 crf 的输入即可,比如原图img 和分割预测的 mask 概率图.
理论上是可以的,但我没有试过. 不过tf应该也有 crf 的相关实现,deeplabv2 的相关 tf 复现项目,可以找找看.
用了你的demo.py,一个二分类问题,不知道为何最后输出的final mask 都是0,木有1了
我也遇到了这种情况,请问您找到原因了吗
我也出现了这种情况,请问找到原因了吗
有可能出现
博主你好~请问这个pydensecrf是没有封装训练的相应函数吗
应该只是用于后处理的函数.
请问如果想在depth prediction里用CRF,这个库该如何使用呢?
CRF 作为一种概率图模型,图像而言是对像素及像素间的关系建立图模型. 对于 depth prediction,自己涉及不多,如果需要使用 CRF,尝试建立原始 img 和对应的 prob 间的关系,再输入到 CRF 中.
谢谢博主 学习了
seg_map 在文中简单说明了下:#seg_map - 假设为语义分割的 mask, hxw, np.array 形式
seg_map能说具体点吗,到底是啥?
您好,最近正在学习语义分割 看到您的demo里面输入的map,请问这个是怎么得到的,是数据集里面有吗?谢谢!
您好 最近正在学习语义分割 看到您的demo里面输入的seg_map,请问这个是怎么得到的 是数据集里面有吗?谢谢
博主您好 ,最近刚开始学习 语义分割关于测试demo里面的seg-map是怎么得到啊,有数据咩?谢谢
我也想知道seg-map具体是啥