opencv轮廓内所有点的获取

文章描述了一个项目需求,涉及模块A和B的交互。模块A在图像上画出ROI并传递轮廓点给模块B,B则需要获取ROI内的所有点。文中提出了两种方法:1)测试每个点是否在区域内;2)直接填充轮廓得到内部点。第一种方法通过插值处理不连续点,然后遍历检测;第二种方法使用OpenCV的drawContours直接填充轮廓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:

        项目中有个需求点,涉及到两个模块,A、B

A:

        在图像区域绘制一个ROI,可能是任意形状的,然后A会将轮廓的一些点传给模块B,让B去计算参数

B:

        获取该ROI内的所有点数据

现在想到的有两种做法可以实现该模块

两种做法的共有部分:

        由于A提供的点有可能是不连续的,所以可能需要进行一个插值,当然这个可以做可以不做,看具体误差要求

插值基本逻辑        

struct CvPointCompare
{
    template<typename T> bool operator()(const T &p1, const T &p2)
    {
        return (fabs(static_cast<float>(p1.x - p2.x)) < 0.0001f) ? (p1.y < p2.y) : (p1.x < p2.x);
    }
};

// 输入点集合,得到闭合线或者非闭合线
void GetLineByPoints(const std::vector<CvPoint> &Pt, bool bIsCircle, std::set<cv::Point2f, CvPointCompare> &sLine)
{
    sLine.clear(); // 清除集合中的元素
    int iSum = Pt.size();

    if (iSum > 0)
    {
        if (bIsCircle) // 环形的时候
        {
            for (int i = 0; i < iSum; i++)
            {
                ((iSum - 1) == i) ? GetLineByTwoPoints(Pt[iSum - 1], Pt[0], sLine) : GetLineByTwoPoints(Pt[i], Pt[i + 1], sLine);
            }
        }
        else  // 非环形的时候
        {
            for (int i = 0; i < iSum - 1; i++)
            {
                GetLineByTwoPoints(Pt[i], Pt[i + 1], sLine);
            }
        }
        
        sLine.insert(cvPointTo32f(Pt.back())); // 插入最后一个点(防止遗漏,非闭合跟只有一个点的时候会出现遗漏)
    }
    
}


void GetTraceSelectedPoints(std::vector<CvPoint> &vBoundaryArea, std::vector<CvPoint> &vArea)
{
    // 需要自定义排序
    std::set<cv::Point2f, CvPointCompare> sOutLine; // 轮廓点集合

    GetLineByPoints(vBoundaryArea, true, sOutLine); // 获取插值后的轮阔点集合

}

然后进行后续处理

方法1:测试每个点是否在区域内部

 基本逻辑:

void GetTraceSelectedPoints(std::vector<CvPoint> &vBoundaryArea, std::vector<CvPoint> &vArea)
{
    // 需要自定义排序
    std::set<cv::Point2f, CvPointCompare> sOutLine; // 轮廓点集合

    GetLineByPoints(vBoundaryArea, true, sOutLine); // 获取插值后的轮阔点集合


    // 计算轮廓内所有点
    std::vector<cv::Point2f> vTmp(sOutLine.begin(), sOutLine.end()); // 转存到容器(set已经实现了排序过程)
    
    float fMinY = vTmp.front().y;
    float fMaxY = vTmp.front().y;
    for (cv::Point2f &pt : vTmp)
    {
        if (pt.y > fMaxY)
        {
            fMaxY = pt.y;
        }
        else if (pt.y < fMinY)
        {
            fMinY = pt.y;
        }
    }

    CvPoint LeftTop   = cvPointFrom32f(cvPoint2D32f(vTmp.front().x, fMinY));
    CvPoint RightDown = cvPointFrom32f(cvPoint2D32f(vTmp.back().x, fMaxY));
    vArea.clear();
    
    for (int y = LeftTop.y; y <= RightDown.y; y++)
    {
        for (int x = LeftTop.x; x <= RightDown.x; x++)
        {
            cv::Point2f PCur = cvPoint2D32f(x, y);
            if (!(pointPolygonTest(vTmp, PCur, false) < 0)) // 判断当前点是否在轮廓内
            {
                vArea.push_back(cvPointFrom32f(PCur));
            }
        }
    }

}

方法2:直接得到回轮廓内部点

int main()
{
	std::vector<uint8_t> v_data(500 * 500, 0);
	cv::Mat cv_back_end = cv::Mat(500, 500, CV_8UC1, v_data.data());

    // 这个位置就是模拟前面插值后的轮廓点
    std::vector<std::vector<cv::Point>> v_points = { {cv::Point(98, 2), cv::Point(40, 40), cv::Point(2, 98),   cv::Point(98, 98) } };
    
    //直接将轮廓内的所有点绘制在back_end上,这里的关键点就是thickness参数(最后一个参数)设置成了-1,设置成-1,opencv就会将轮廓内填充起来
    cv::drawContours(cv_back_end, v_points, -1, cv::Scalar(255), -1);

}

获取轮廓内的所有像素,可以按照以下步骤进行操作: 1. 使用OpenCV的函数,比如`cv2.findContours()`,来查找图像中的所有轮廓。该函数返回一个包含所有轮廓的列表。 2. 遍历列表中的每个轮廓。对于每个轮廓,使用`cv2.drawContours()`函数在一个空白图像上绘制该轮廓。 3. 创建一个与原始图像大小相同的空白图像,作为结果图像。 4. 对于新绘制轮廓的图像,遍历图像的每个像素。对于每个像素,判断它是否属于轮廓内部。 5. 如果某个像素在轮廓内部,将其像素值赋值给结果图像相应位置的像素。 6. 完成遍历后,结果图像即包含了轮廓内的所有像素。 下面是一个示例代码,使用这个方法获取图像内的轮廓像素: ```python import cv2 import numpy as np # 加载图像 image = cv2.imread('image.jpg') # 将图像转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 进行二值化处理 _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 寻找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 创建空白图像作为结果 result = np.zeros_like(image) # 遍历每个轮廓 for contour in contours: # 绘制轮廓 cv2.drawContours(result, [contour], -1, (255, 255, 255), cv2.FILLED) # 遍历结果图像的每个像素 for y in range(result.shape[0]): for x in range(result.shape[1]): # 判断像素是否在轮廓内 if result[y, x] == [255, 255, 255]: # 将轮廓内的像素赋值给结果图像 result[y, x] = image[y, x] # 显示结果图像 cv2.imshow("Result", result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这样,`result`即是包含了轮廓内所有像素的图像。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值