OTSU阈值分割+孔洞填充+海陆分离

1. OTSU阈值分割

参考1. https://zhuanlan.zhihu.com/p/124944108

OTSU算法(大津算法)的详细步骤:

  1. 假设初始有个阈值 T 0 T_0 T0,并将图像分为两个部分(阈值分割),前景F和背景B。
  2. 假设像素的总个数为N,前景像素个数为 N f N_f Nf,背景像素个数为 N b N_b Nb
  3. 假设图像的总灰度级为 L − 1 L-1 L1,每个灰度级的像素个数为 N i N_i Ni,那么满足如下的公式:
    P f = ∑ i = 0 i = T 0 N i N P_f = \sum ^{i=T_0} _{i=0} \frac {N_i}{N} Pf=i=0i=T0NNi
    P b = ∑ i = T 0 i = L − 1 N i N P_b = \sum ^{i=L-1} _{i=T_0} \frac {N_i}{N} Pb=i=T0i=L1NNi
  4. 前景和背景的灰度平均值分别为:
    M f = ∑ i = 0 i = T 0 i ∗ P i P f M_f = \sum ^{i=T_0} _{i=0} i * \frac {P_i}{P_f} Mf=i=0i=T0iPfPi
    M b = ∑ i = T 0 i = L − 1 i ∗ P i P b M_b = \sum ^{i=L-1} _{i=T_0} i * \frac {P_i}{P_b} Mb=i=T0i=L1iPbPi
  5. 整个图像的灰度平均值为:
    M = P f ∗ M f + P b ∗ M b M = P_f * M_f + P_b * M_b M=PfMf+PbMb
  6. 那么,前景和背景之间的类间方差为:
    δ 2 = P f ∗ ( M f − M ) 2 + P b ∗ ( M b − M ) 2 \delta ^2 = P_f * (M_f - M)^2 + P_b * (M_b - M)^2 δ2=Pf(MfM)2+Pb(MbM)2
  7. 大津算法的目的就是求得一个阈值,使得第六步的类间方差最大。
  8. 至于怎么求解,可以用遍历的方法,或者采用其他优化算法,找出那个类间方差最大的阈值来分割即可。

参考2. https://zhuanlan.zhihu.com/p/95034826

g:类间方差(那个灰度的g最大,那个灰度就是需要的阈值t)
g = w 0 ∗ ( u 0 − u ) 2 + w 1 ∗ ( u 1 − u ) 2 g = w_0 * (u_0 - u) ^2 + w_1 * (u_1 - u) ^2 g=w0(u0u)2+w1(u1u)2
根据上面的关系,可以推出:
g = w 0 ∗ w 1 ∗ ( u 0 − u 1 ) 2 g = w_0 * w_1 * (u_0 - u_1) ^2 g=w0w1(u0u1)2
然后,遍历每一个灰度值,找到这个灰度值对应的g,找到最大的g对应的t。

参考3. https://www.cnblogs.com/yinliang-liang/p/9293310.html

解释阈值分割法

参考4. https://blog.csdn.net/u010128736/article/details/52801310

#coding:utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt

image = cv2.imread("E:/python/cv/OTSU/test.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

plt.subplot(131), plt.imshow(image, "gray")
plt.title("source image"), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.hist(image.ravel(), 256)
plt.title("Histogram"), plt.xticks([]), plt.yticks([])
ret1, th1 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)  #方法选择为THRESH_OTSU
plt.subplot(133), plt.imshow(th1, "gray")
plt.title("OTSU,threshold is " + str(ret1)), plt.xticks([]), plt.yticks([])
plt.show()

2. 去除小区域

加入高斯滤波,代码如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt

image = cv2.imread("H:/wj/pictures/test.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

#plt.subplot(131), plt.imshow(image, "gray")
#plt.title("source image"), plt.xticks([]), plt.yticks([])
#plt.subplot(132), plt.hist(image.ravel(), 256)
#plt.title("Histogram"), plt.xticks([]), plt.yticks([])
blur = cv2.GaussianBlur(image, (5,5), 0)    # 高斯滤波
ret1, th1 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)  #方法选择为THRESH_OTSU
#plt.subplot(111), plt.imshow(th1, "gray")
#plt.title("OTSU,threshold is " + str(ret1)), plt.xticks([]), plt.yticks([])
#plt.show()

#cv2.imwrite("H:/wj/pictures/test_re.jpg", th1)  #保存OTSU处理的图片

# 去除小区域
contours,hierarch=cv2.findContours(th1,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)):
    area = cv2.contourArea(contours[i])
    if area < 700:
        cv2.drawContours(th1,[contours[i]],0,0,-1)
cv2.imwrite("H:/wj/pictures/test_sArea.jpg", th1)

执行效果如下:
在这里插入图片描述

3. 去除孔洞

参考1. https://www.cnblogs.com/denny402/p/5166258.html

消除孤立区域,导入skimage

参考2. https://www.cnblogs.com/picassooo/p/12024471.html

Python-OpenCV实现二值图像孔洞填充

import cv2
import numpy as np
 
def FillHole(mask):
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    len_contour = len(contours)
    contour_list = []
    for i in range(len_contour):
        drawing = np.zeros_like(mask, np.uint8)  # create a black image
        img_contour = cv2.drawContours(drawing, contours, i, (255, 255, 255), -1)
        contour_list.append(img_contour)
 
    out = sum(contour_list)
    return out
 
if __name__ == '__main__':
    mask_in = cv2.imread('H:/wj/pictures/test_sArea.jpg', 0)
    mask_out = FillHole(mask_in)
    cv2.imwrite('H:/wj/pictures/test_sArea+erase.jpg', mask_out)

执行效果如下:
在这里插入图片描述

参考3. https://cloud.tencent.com/developer/article/1016690

python下使用cv2.drawContours填充轮廓颜色
代码:

import cv2
import os

img = cv2.imread("H:/wj/pictures/001159.jpg")

# 定义海陆分离函数
def segmentFunc(img):
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 一、高斯滤波 滤波之后001159效果不佳
    #blur = cv2.GaussianBlur(gray_img, (5, 5), 0)
    
    # 二、otsu阈值
    (t, thresh) = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    # 三、形态学操作
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
    
    # 四、彩色图像的otsu分割
    
    #   1.颜色空间转换:BGR转RGB
    #result = cv2.cvtColor(cv2.bitwise_and(img, img, mask=thresh), cv2.COLOR_BGR2RGB)
    result = cv2.bitwise_and(img, img, mask=thresh)
    
    #   2.保存otsu分割的彩色照片
    #cv2.imwrite("H:/wj/pictures/001159_otsu.jpg", result)
    
    # 五、去除小连通域和大连通域
    h, w, _ = result.shape
    
    #   1.找到边界
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    
    #   2.需要搞一个list给cv2.drawContours()
    c_max = []
    for i in range(len(contours)):
        cnt = contours[i]
        area = cv2.contourArea(cnt)
        
        # 3.处理掉小区域和大区域
        if(area < (h/50 * w/50) or area > (h/3 * w/3)):
            c_min = []
            c_min.append(cnt)
            # thickness不为-1时,表示画轮廓线,thickness的值表示线的宽度。
            cv2.drawContours(thresh, c_min, -1, (0, 0, 0), thickness=-1)
            continue
        c_max.append(cnt)
        
    cv2.drawContours(thresh, c_max, -1, (255, 255, 255), thickness=-1)
    
    # 4.保存去除小连通域和大连通域的二值化图片
    #cv2.imwrite("H:/wj/pictures/001159_area.jpg", thresh)
    
    # 六、将彩色图片去除小连通域和大连通域
    final = cv2.bitwise_and(result, result, mask=thresh)
    return final

# 定义批量处理图片函数
def imgProcess(filePath):
    for filename in os.listdir(filePath):
        img = cv2.imread(filePath + "/" + filename)
        final = segmentFunc(img)
        
        path = os.path.dirname(filePath) + "/result"
        if os.path.exists(path) == False:
            os.makedirs(path)
        cv2.imwrite(path + "/" + filename, final)
        
# 主函数测试
if __name__ == "__main__":
    imgProcess("H:/wj/pictures")

效果:
在这里插入图片描述在这里插入图片描述

4. 去除大连通域会导致大目标的漏检

otsu.py仅去除小连通域

import cv2
import os

def ostu(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 高斯滤波
    blur = cv2.GaussianBlur(gray, (5,5), 0)    
    
    # 1. 方法选择为THRESH_OTSU
    ret1, th1 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)  
    
    #保存OTSU处理的图片
#    cv2.imwrite("H:/wj/pictures/000014_otsu_bin.jpg", th1)  
    
    # 3. 去除孤立区域
    h, w, _ = image.shape
    # Find Contour
    contours, hierarchy = cv2.findContours( th1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    
    # 需要搞一个list给cv2.drawContours()才行!!!!!
    c_max = []
    for i in range(len(contours)):
        cnt = contours[i]
        area = cv2.contourArea(cnt)
    
        # 处理掉小的轮廓区域,这个区域的大小自己定义。
        if(area < (h/40*w/40)):
            c_min = []
            c_min.append(cnt)
            # thickness不为-1时,表示画轮廓线,thickness的值表示线的宽度。
            cv2.drawContours(th1, c_min, -1, (0,0,0), thickness=-1)
            continue
        #
        c_max.append(cnt)
        
    cv2.drawContours(th1, c_max, -1, (255, 255, 255), thickness=-1)
    
    final = cv2.bitwise_and(image, image, mask=th1)
    return final
#    img = th1
#    return img

def imgs_process(filePath):
    for filename in os.listdir(filePath):
        img = cv2.imread(filePath + "/" + filename)
        img = ostu(img)
        
        path_ostu = os.path.dirname(filePath) + "/ostu"
        if os.path.exists(path_ostu) == False:
            os.makedirs(path_ostu)
        cv2.imwrite(path_ostu + "/" + filename, img)

        
if __name__ == "__main__":
    imgs_process("H:/wj/pictures")

效果:
在这里插入图片描述
在这里插入图片描述

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值