opencv 去除孤立点以及findContours()和connectedComponentsWithStats()详解

findContours()和connectedComponentsWithStats()两个函数可以分别实现去除图像孤立点的功能

opencv 去除孤立点以及findContours()和connectedComponentsWithStats()详解_第1张图片​​​​​​​

 

connectedComponentsWithStats()函数原理是检测像素的连通区域(连通图应该不用介绍-.-),上图中每个白色斑点区域都属于一个个连通区域,当然文字部分也是,通过这个函数检测到每个连通区域后,再对不同区域的面积进行筛选(孤立点的面积肯定远远小于文字的面积),就可以去掉这些孤立点,函数用法如下:

num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(src, connectivity=8, ltype=None)

函数参数:

src:传入需要处理的图片,要求为二值图像,上面用例的图像已经是二值图像了,如果不是的话需要加一步二值化

connectivity:可选值为4或8,也就是使用4连通还是8连通,就是这个区别,简单易懂

opencv 去除孤立点以及findContours()和connectedComponentsWithStats()详解_第2张图片

 ltype:这个参数跟输出有关,可以先默认,不用管

返回值:

num_labels:所有连通域的数目
labels:这个返回值很关键,主要就用到这个,ltype参数默认为none的情况下,输出的labels是一个和原图一样大小的矩阵,原图中检测到的连通图的位置,对应的labels矩阵值为1,其余值为0,算是一种特殊的标记
stats:记录了每个连通区域的信息,是一个5列的矩阵,每一行对应一个连通区域,分别为连通区域外接矩形的x、y、width、height和面积,例如stats[0][4]就是第一个连通区域的面积
centroids:连通域的中心点,没什么大用

这样就可以通过stats对连通域进行筛选,再通过labels确定筛选出的区域坐标,在进行各种想进行操作,比如这个去除孤立点,直接让该区域像素值置为背景颜色0,就去掉了

再说findContours()函数

contours, hierarch = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)

这个相对简单一点,是检测输入图像的轮廓,最后同样通过面积进行筛选

函数参数:

img: 输入图像,最好也是二值图像;

第二个: 表示轮廓的检索模式,有四种(有兴趣的朋友可以自己搜一下区别,这里不多做赘述了-.-)

第三个:轮廓的近似办法,有三种(同上)

返回值:

coutours:一个list,list中每个元素都是图像中的一个轮廓信息,用numpy中的ndarray表示,配合cv.contourArea() 函数可以直接得到轮廓面积,配合cv.drawContours()函数可以对某个区域进行操作,比如上述的直接让该区域像素值置为背景颜色0(这俩函数也不多做赘述了,一搜一大堆)

hierarchy:轮廓间的层次关系,为三维数组,形状为(1,n,4),用不上的参数统统简略

两个方法,个人更推荐第一个,连通区域的检测比轮廓检测要精准很多,就是慢点,毕竟有for循环

这是代码,Img1()和Img2()分别对应两个方法:

import cv2.cv2 as cv
import numpy as np

def Img1(src):
    num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(src, connectivity=8, ltype=None)
    img = np.zeros((src.shape[0], src.shape[1]), np.uint8)    #创建个全0的黑背景
    for i in range(1, num_labels):
        mask = labels == i             #这一步是通过labels确定区域位置,让labels信息赋给mask数组,再用mask数组做img数组的索引
        if stats[i][4] > 300:         #300是面积 可以随便调
            img[mask] = 255
            img[mask] = 255
            img[mask] = 255           #面积大于300的区域涂白留下,小于300的涂0抹去
        else:
            img[mask] = 0
            img[mask] = 0
            img[mask] = 0
    return img

def Img2(img):
    contours, hierarch = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
    area = []
    for i in range(len(contours)):
        area.append(cv.contourArea(contours[i]))   #计算轮廓所占面积
        if area[i] < 300:                   #轮廓面积,可以自己随便调
            cv.drawContours(img,[contours[i]],0,0,-1)         #该轮廓区域填0
            continue
    return img

src = cv.imread('01.png',0)
cv.imshow('input',src)
cv.waitKey(0)
src = Img1(src)
cv.imshow('output', src)
cv.waitKey()

你可能感兴趣的:(python,opencv)