语义分割如何去掉小面积的区域(python/C++)

C++:

cv::Mat removeLargerSmallerRegion(cv::Mat mask, int minAreaThreshold , int maxAreaThreshold )
{
    // 查找轮廓
    std::vector<std::vector<Point>> contours;
    std::vector<cv::Vec4i> hierarchy;

    findContours(mask, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
   
    // 遍历所有轮廓,过滤掉面积小于阈值的轮廓
    int minAreaThreshold = 20; // 设置最小面积阈值
    int maxAreaThreshold  = 1000
    for (int i = 0; i < contours.size(); i++) {
        int areasValue = contourArea(contours[i], false);

        if (areasValue <= minAreaThreshold  && areasValue >= maxAreaThreshold ) 
        { // 只保留大于设定阈值的区域
            cv::Scalar color = cv::Scalar(0, 0, 0); // 区域涂黑
            drawContours(mask, contours, i, color, CV_FILLED, 8, hierarchy, 0, cv::Point(0, 0));
        }
    }
}

findContours:检测物体轮廓

findContours(Mat image, 
             List<MatOfPoint> contours, 
             Mat hierarchy, 
             int mode, 
             int method, 
             Point offset)

参数介绍:
image:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,此处使用的是,语义分割之后的mask结果图,像素值代表类别;
contours:为一个向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓;有多少轮廓,向量contours就有多少元素;
int model:定义轮廓的检索模式:
1)RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;
2)RETR_LIST :检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓;
3)RETR_CCOMP: 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层;
4)RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
int method:定义轮廓的近似方法:
1)CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量内;
2)CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;
3)CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法;
Point offset:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值。

drawContours:用于绘制轮廓的函数,根据轮廓数据在图像上绘制轮廓线条或填充轮廓。

drawContours(Mat image, 
             List<MatOfPoint> contours, 
             int contourIdx, 
             Scalar color, 
             int thickness,
             int lineType, 
             Mat hierarchy, 
             int maxLevel, 
             Point offset)

参数介绍:
第一个参数image表示目标图像,
第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,
第三个参数contourIdx****指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数lineType为线型,
第七个参数为轮廓结构信息,
第八个参数为maxLevel
第九个参数为偏移量

contourArea:计算轮廓面积
这个函数可以计算给定轮廓的面积,轮廓是由一系列二维点组成的。

double contourArea(InputArray contour, bool oriented = false);

参数介绍:
InputArray contour:输入的点集,通常是图像轮廓的顶点坐标,可以是std::vector或Mat类型。
bool oriented:是一个面向区域的标识符,如果设置为true,函数会返回一个带符号的面积值,正负取决于轮廓的方向(顺时针或逆时针)。如果为false,则以绝对值返回面积值。

注意:
1、contourArea()求的是封闭曲线内面积(通过格林公式计算),几何面积求的是最小外接矩的面积,导致contourArea()求出来的面积和宽高面积不一样,且永远小于看宽高面积。
2、由于在计算面积的时候丢失了外围像素,因此contourArea()求出来的面积比真实面积要小。

python:

方法一:通过检测轮廓来去除

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)。

方法二:检测连通区域
connectedComponentsWithStats()函数
通过这个函数检测到每个连通区域后,再对不同区域的面积进行筛选:

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

函数参数:
src:传入需要处理的图片,要求为二值图像,上面用例的图像已经是二值图像了,如果不是的话需要加一步二值化;
connectivity:可选值为4或8,也就是使用4连通还是8连通
在这里插入图片描述
返回值:
num_labels:所有连通域的数目
labels:这个返回值很关键,ltype参数默认为none的情况下,输出的labels是一个和原图一样大小的矩阵,原图中检测到的连通图的位置,对应的labels矩阵值为1,其余值为0;
stats:记录了每个连通区域的信息,是一个5列的矩阵,每一行对应一个连通区域,分别为连通区域外接矩形的x、y、width、height和面积,例如stats[0][4]就是第一个连通区域的面积
centroids:连通域的中心点

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

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
                      #面积大于300的区域涂白留下,小于300的涂0抹去
        else:
            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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ghx3110

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值