参考源码:https://github.com/lucasb-eyer/pydensecrf
转载:https://www.cnblogs.com/wanghui-garcia/p/10761612.html
安装pydensecrf
sudo pip install pydensecrf
使用说明:
"""2类 crf"""
def dense_crf_2d(img, output_probs): # img 为H,*W*C 的原图,output_probs 为 输出概率 sigmoid 输出(h,w),#seg_map - 假设为语义分割的 mask, hxw, np.array 形式.
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)
d.setUnaryEnergy(U)
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 Q
"""
测试 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()
多类自定义Densecrf
from pydensecrf.utils import compute_unary, create_pairwise_bilateral, \
create_pairwise_gaussian, softmax_to_unary, unary_from_softmax,unary_from_labels
"""
Getting a Unary
得到 unary potentials有两种常见的方法:
1)由人类或其他过程产生的硬标签。该方法由from pydensecrf.utils import unary_from_labels实现
2)由概率分布计算得到,例如深度网络的softmax输出。即我们之前先对图片使用训练好的网络预测得到最终经过softmax函数得到的分类结果,
这里需要将这个结果转成一元势
"""
def dense_crf(img, processed_probabilities):
softmax = processed_probabilities # processed_probabilities:CNN 预测概率 经过 softmax [n_label,H,W]
# print(softmax.shape)
# exit()
#1)Getting a Unary
#1.直接调用函数
unary = unary_from_softmax(softmax)
#2.自己生成一元势函数
# The inputs should be C-continious -- we are using Cython wrapper
# unary = -np.log(output_probs)
# unary = unary.reshape((21, -1))
# unary = np.ascontiguousarray(unary) # (21, n)
d = dcrf.DenseCRF(img.shape[0] * img.shape[1], 21) # h,w,n_class
d.setUnaryEnergy(unary)
# This potential penalizes small pieces of segmentation that are
# spatially isolated -- enforces more spatially consistent segmentations
# Pairwise potentials(二元势)
feats = create_pairwise_gaussian(sdims=(3, 3), 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=(80, 80), schan=(13, 13, 13),
img=img, chdim=2)
d.addPairwiseEnergy(feats, compat=10,
kernel=dcrf.DIAG_KERNEL,
normalization=dcrf.NORMALIZE_SYMMETRIC)
# 快捷方法
# d.addPairwiseGaussian(sxy=3, compat=3)
# d.addPairwiseBilateral(sxy=80, srgb=13, rgbim=img, compat=10)
# 迭代次数,对于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]))
return res
调用densecrf:
pred_mask_np = dense_crf(np.array(img_raw).astype(np.uint8),output_probs) # np,np (C,H,W)
对于图像来说,最简单的使用该库的方法是使用DenseCRF2D类:
import numpy as np import pydensecrf.densecrf as dcrf
d = dcrf.DenseCRF2D(640, 480, 5) # width, height, nlabels
你可以使用下面的方法设置固定的一元势
一元势即网络预测得到的结果,进行-np.log(py)等操作
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)
记住U应该是负的log概率,所以如果你用概率py,别忘了执行U = -np.log(py)
需要在一元势上进行reshape是我想要修复的API缺陷,但是如果不引入对numpy的显式依赖,我不知道如何解决这个问题。
注意,nlabel维度是这里reshape之前的第一个维度;如果不是这样的话,你可能需要在reshape之前把nlabel移到前面,即U.shape的结果应该为(5, 480, 640),就像这样:
print(U.shape) # -> (480, 640, 5) U = U.transpose(2, 0, 1).reshape((5,-1))
得到 unary potentials有两种常见的方法:
1)由人类或其他过程产生的硬标签。该方法由from pydensecrf.utils import unary_from_labels实现
2)由概率分布计算得到,例如深度网络的softmax输出。即我们之前先对图片使用训练好的网络预测得到最终经过softmax函数得到的分类结果,这里需要将这个结果转成一元势
对此,请参阅from pydensecrf.utils import unary_from_softmax
1)unary_from_labels(labels, n_labels, gt_prob, zero_unsure=True)函数的使用
简单分类器,该分类器50%确定注释(即从训练好的网络预测img后得到的结果)是正确的。(与推理示例中相同)。
参数:
2)unary_from_softmax(sm, scale=None, clip=1e-5)函数的使用
将softmax类概率转换为一元势(每个节点的NLL)。
即我们之前先对图片使用训练好的网络预测得到最终经过softmax函数得到的分类结果,这里需要将这个结果转成一元势
参数
在这里因为scale=None,clip=None,所以这个函数的作用其实只进行了下面的操作:
-np.log(sm).reshape([num_cls, -1]).astype(np.float32)
构建好一元势后需要调用:
d.setUnaryEnergy(U)
将该一元势添加到CRF中
二维情况下,增加最常见的二元势有两种实用方法:
二元势即用于描述像素点和像素点之间的关系,鼓励相似像素分配相同的标签,而相差较大的像素分配不同的标签。这个相似的定义与颜色值srgb和实际相对距离sxy相关,所以CRF能够使图片尽量在边界处分割。
# 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)
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)
im即image
参数映射到本文中的参数如下:高斯情况下的sxy为$\theta_{\gamma}$(即Θγ),双边情况下,sxy和srgb分别映射到$\theta_{\alpha}$(即Θα)和$\theta_{\beta}$(即ΘΒ)。names是“x/y标准偏差”(x/y standard-deviation,sxy)和“rgb标准偏差”(rgb standard-deviation,srgb)的简写,公式为:
1)Non-RGB bilateral
一个重要的警告是,addPairwiseBilateral只适用于RGB图像,即三个通道。如果您的数据与这个简单但常见的情况不同,则需要使用util .create_pairwise_bilateral函数计算你自己的二元能源;有关详细信息,请参阅 generic non-2D case案例。
在examples文件夹中以笔记本的形式提供了一个example of working with Non-RGB data例子。
可见Example of DenseCRF with non-RGB data
compat参数可以是以下任何一种:
这些是label-compatibilites µ(xi, xj)的参数可能学到的东西。例如,他们可以指出把鸟的像素误认为天空并不像把猫误认为天空那么糟糕。数组应该有nlabel或(nlabel,nlabel)作为shape和一个float32数据类型。
kenel参数的可能值有:
CONST_KERNEL
DIAG_KERNEL
(the default)FULL_KERNEL
NO_NORMALIZATION
NORMALIZE_BEFORE
NORMALIZE_AFTER
NORMALIZE_SYMMETRIC
(the default)Q = d.inference(5)
然后MAP预测是:
map = np.argmax(Q, axis=0).reshape((640,480))
如果你对类概率Q感兴趣,你会注意到Q是一个包装好的特征矩阵。本项目的特征包装器实现缓冲接口,可以简单地转换为numpy数组,如下:
proba = np.array(Q)
如果出于某种原因,你想手动运行推理循环,你可以这样做:
Q, tmp1, tmp2 = d.startInference() for i in range(5): print("KL-divergence at {}: {}".format(i, d.klDivergence(Q))) d.stepInference(Q, tmp1, tmp2)
DenseCRF类可用于一般(非2d)denseCRFs。它的用法与上面完全一样,只是缺少了特定于2d的二元势addPairwiseGaussian和addPairwiseBilateral。
相反,您需要使用通用的addPairwiseEnergy方法,如下所示:
d = dcrf.DenseCRF(100, 5) # npoints, nlabels
feats = np.array(…) # Get the pairwise features from somewhere.
print(feats.shape) # -> (7, 100) = (feature dimensionality, npoints)
print(feats.dtype) # -> dtype(‘float32’)
dcrf.addPairwiseEnergy(feats)
此外,你还可以传递兼容性、内核参数和标准化参数,就像在二维高斯和双边情况下一样。
势函数计算为w*exp(-0.5 * |f_i - f_j|^2)。
当然,首先你要先使用create_pairwise_gaussian和create_pairwise_bilateral,然后才能使用addPairwiseEnergy
1.create_pairwise_gaussian(sdims, shape)函数的使用
创建二元高斯势的Util函数。这适用于所有的图像尺寸。对于2D例子,他等价于DenseCRF2D.addPairwiseGaussian的操作
参数:
2.create_pairwise_bilateral(sdims, schan, img, chdim=-1)
创造二元双边势的Util函数。这适用于所有的图像尺寸。对于2D例子,等价于DenseCRF2D.addPairwiseBilateral。
参数:
d = dcrf.DenseCRF(img.shape[1] * img.shape[0], n_labels)# get unary potentials (neg log probability) # 得到一元势(即去负对数),labels为对所有像素值标注label后的数组,label类型n_labels=2, U = unary_from_labels(labels, n_labels, gt_prob=0.7, zero_unsure=HAS_UNK) #U.shape为(2, 76800),即(n_labels,len(labels)) d.setUnaryEnergy(U) # This creates the color-independent features and then add them to the CRF feats = create_pairwise_gaussian(sdims=(3, 3), shape=img.shape[:2]) d.addPairwiseEnergy(feats, compat=3, kernel=dcrf.DIAG_KERNEL, normalization=dcrf.NORMALIZE_SYMMETRIC) # This creates the color-dependent features and then add them to the 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)
用户@markusnagel编写了几个numpy函数,将两个经典的二维图像二元势(高斯和双边)推广到任意维数:create_pairwise_gaussian和create_pairwise_bilateral。
你可以从from pydensecrf.utils import create_pairwise_gaussian访问它们,然后查看它们的文档去了解如何使用它们。
更多:http://manutdzou.github.io/2018/07/11/dense-crf.html
https://www.aiuai.cn/aifarm418.html#3.Demo.py
https://blog.csdn.net/u012759136/article/details/52434826