yy怎么一直在模板相关信息圆柱钢模板优势是什么?企业网站建设模板和定制化有什么区别呢?拼命加载中,做直播网站多少钱,wordpress 站长工具,网站备案表文章目录传统图像处理分割阈值分割一个应用场景opencv库中的阈值分割固定阈值THRESH_OTSU 大津法阈值自适应阈值传统图像处理分割
现在提到图像分割#xff0c;很多人会直接想到当前火爆的深度学习的各种分割网络#xff0c;比如实例分割#xff0c;语义分割等。其实在传统…
文章目录传统图像处理分割阈值分割一个应用场景opencv库中的阈值分割固定阈值THRESH_OTSU 大津法阈值自适应阈值传统图像处理分割
现在提到图像分割很多人会直接想到当前火爆的深度学习的各种分割网络比如实例分割语义分割等。其实在传统的图像处理领域也有一些分割算法这些算法在通用的分割上来说没有深度网络的普适性好但是在某些特殊场景是一个更轻量级的解决方案。也是图像处理学习过程中的毕竟之路。
阈值分割
阈值分割是一个基本的图像处理算法思路很简单 将图像中每个像素的像素值和阈值进行比较高于阈值的是一类低于阈值的是一类。 这样的算法一般有两种应用场景
提取前景或者背景二值化处理
一个应用场景
我在实际工作中碰到了一个实际场景我觉得就是可以用这个阈值分割来解决的而不需要使用比较重的深度网络来解决。
问题是要定位一个试管中试剂的位置信息设备中拍摄的图像如下图所示
从图中可以分析这条分界线实际上是很容易判断出来的而且前景和背景的像素值差距非常大所以我当时考虑的就是直接用阈值分割来实现。
opencv库中的阈值分割
在opencv库中用于阈值分割的函数是cv2.threshold. 该函数的接受4个参数
src, 图像数据为单通道图像。thresh阈值。maxval最大像素值type阈值分割方法。
最后的这个参数是因为阈值分割有很多中方法在opencv的官网上描述的非常清楚 实际上就是根据像素值与阈值的关系来获取最终目标图像的像素值规则。这里就不一个一个说了。我提一下我自己用到的几个方法以及在这个试管工作中的效果。 另外我还用到一些其他的方法把试管的感兴趣区域确定和提取出来了这里就说了和这一篇文章的关系不大。
固定阈值
上面提到了因为分界线非常明显所以我想着直接尝试出一个固定的阈值来做分割看能不能达到自己想要的效果。
代码如下
if __name__ __main__:img cv2.imread(images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 如果大于阈值赋值为255小于阈值赋值为0ret, binary cv2.threshold(grey, 60, 255, cv2.THRESH_BINARY)cv2.imshow(result, binary)cv2.waitKey()cv2.destroyAllWindows()效果不太理想最上面的这个界限不是很清楚(虽然通过拟合可以基本确定)而且中间的一些杂志部分去除的不是很好(后续需要用到所以在这一步想一次搞定)。 所以再试试其他的方法看看。
尝试了TRUNC和TOZERO的方式如下图
我理解实际上就是把目标图像的前景和背景的像素值拉的不是很大而已对我想要的效果帮助不大。
而且我通过修改阈值把60这个值改大改小效果仍然不佳就只能谋求其他方法了。
THRESH_OTSU 大津法阈值
这个分割方法和上述的分割方法不一样上述的分割方法的阈值是固定的而这个方法的阈值时需要通过一些计算才能得到的这种计算方式就是大津法。 大津法的原理和思路如下(单通道)
大津法的思路就是要找到一个阈值根据这个阈值划分成两类A, B之后这A, B两类像素值的方差最大。那么问题就变成两个 如何计算AB两类像素的方差。使用最快速的方法找到让方差最大的那个阈值。 一张M * N大小的图像假设阈值为k。那么大于k的像素点(如果是BINARY的方法则表示前景否则就是反过来下同)的个数N0N_0N0,小于k的像素点的个数为N0N_0N0。那么前景分类的像素点在整张图像中的占比就为ω0N0M∗N\omega_0\frac{N_0}{M * N}ω0M∗NN0背景的就是ω1N1M∗N\omega_1\frac{N_1}{M * N}ω1M∗NN1.大于k的像素点的平均灰度值可计为u0u_0u0小于的平均灰度值可计为u1u_1u1整张图像的平均灰度为uuu那么现在可以回答上面的第一个问题两类像素的方差定位为 gω0(u0−u)2ω1(u1−u)2g \omega_0(u_0-u)^2 \omega_1(u_1-u)^2gω0(u0−u)2ω1(u1−u)2大津法就是要找到这个最大的g值。那么第二个问题来了怎么去算这个g值。最简单的办法就是遍历让k从0到255依次计算这个g值哪个最大阈值就取那个k。opencv是不是这么实现的不知道有兴趣的可以去翻翻源码。
通过大津法计算得到阈值之后还要确定是使用哪种分类方法所以代码可以写成
if __name__ __main__:img cv2.imread(images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 先使用大津法计算阈值然后再分类# 如果大于阈值赋值为255小于阈值赋值为0ret, binary cv2.threshold(grey, 60, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)cv2.imshow(result, binary)cv2.waitKey()cv2.destroyAllWindows()图是下图
我感觉上面的那条分界线更加分明和分布平均一点。
自适应阈值
上面的几种方法里的思路里基本上都是一个阈值在应用与整个图像的分割不管是自己手动设的还是通过某种方法计算出来的。但是一旦确定后这个阈值就会应用到整张图像中去。 在opencv中还提供了一种阈值分割方法这个阈值在整张图像的分割操作中是变化的是 根据该像素周边的邻居像素(neighborhood pix)值来确定该像素的比较阈值然后再根据之前的阈值比较逻辑来确定是前景类还是后台类。
opencv中的这个函数是adaptiveThreshold这个函数有下面几个参数
src源图像为灰度图。maxValue, 最大值adaptiveMethod根据邻居像素如何计算这个阈值也就是说确定这个邻域像素的计算方法。 ADAPTIVE_THRESH_MEAN_C邻域的像素计算平均值作为阈值(减去一个偏移量)ADAPTIVE_THRESH_GAUSSIAN_C邻域的像素通过一个高斯核作为掩码来计算权重值作为阈值(减去一个偏移量)。根据不同的blockSizeopencv会调用getGaussianKernel这个来获取这个高斯核有兴趣可以具体了解下高斯核的生成原理和逻辑。 thresholdType确定阈值之后的分类方式就支持两种THRESH_BINARY(大于阈值取255小于取0)THRESH_BINARY_INV(大于阈值取0小于取255)blockSize, 这个邻域的边长一般是357.和图像中的卷积核类似。C上面计算阈值过程中提到的偏移量。
我在我的工程里试了一下
if __name__ __main__:img cv2.imread(/Users/zoulei/files/personal/blog/images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 如果大于阈值赋值为255小于阈值赋值为0# ret, binary cv2.threshold(grey, 60, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)# 自适应阈值分割binary cv2.adaptiveThreshold(grey, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, 2)cv2.imshow(result, binary)cv2.waitKey()cv2.destroyAllWindows()效果如下
试过蛮多的blockSize基本上都是这个效果。和我想要的效果完全不符合所以这个方法对我来说没什么用。 这个方法应该是用于做对比度较大的图像杂质分割会比较有用我这个工程里暂时没有用到有兴趣的朋友可以去试一下。
当然最后我自己的解决方案是选择用OTSU大津法的阈值分割方法再加上对分界线的一些拟合方法和一些逻辑判断来解决我自己的问题。