ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一个案例展示opencv在医学图像病灶分割任务的常用操作

2021-11-02 18:04:08  阅读:241  来源: 互联网

标签:p1 案例 max Point opencv cnts 病灶 pts


任务

根据语义分割网络的输出,去求以下任务的结果。(神经网络的后处理操作)
(1)画出病灶边界
(2)画出贴近的椭圆
(3)过滤病灶内的噪声
(4)计算病灶的不规则周长
(5)计算病灶面积
(6)画出径线

原图

预处理

假设网络为一个二分类网络,网络的输出是一个二维的tensor,每个像素的值为0~1的置信度。先预处理操作,通过设置阈值把每个像素转换为0或者255,分别代表背景和目标,生成mask图像。储存为cv::Mat 8UC1的模式,图片格式为8位单通道,2^8 = 256, 对应每个像素的取值范围为0 ~ 255。

原图

CODE

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

// 输入直线的两个端点,计算直线的中点
cv::Point calMidpoint(Point2f p1, Point2f p2){
    Point p;
    p.x = int((p1.x + p2.x) / 2);
    p.y = int((p1.y + p2.y) / 2);
    return p;
}

int main()
{
    try {
    	// 这是上面原图的三通道版本的图像,实际原图应该为单通道(灰度)图
        Mat img = imread("test.jpg");
        // 创建储存检测目标的边界的点的集合(会存在多个边界)
        vector<vector<Point>> cnts;
        vector<Vec4i> hierarchy;
        Mat img2;
        // 过滤病灶内的噪声,但是因为侵蚀操作会影响病灶边界产生变化,Size(10, 10)根据实际情况调整
        Mat kernel = getStructuringElement(MORPH_RECT, Size(10, 10));
        morphologyEx(img, img, MORPH_CLOSE, kernel);
        // 将RGB三通道转单通道(灰度)
        cvtColor(img, img2, CV_RGB2GRAY);
        // 设置阈值将所有像素的值变成0或者255
        threshold(img2, img2, 100, 255, THRESH_BINARY);
        // 寻找病灶的边界
        // 第4和第5个参数可查看这两个枚举类型的说明cv::RetrievalModes和cv::ContourApproximationModes
        findContours(img2, cnts, hierarchy, cv::RetrievalModes::RETR_LIST, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
        
        if (cnts.size() > 0) {
        	// 查找点(Point)最多的一组点集作为边界
            vector<Point> contour;
            int max_index = -1;
            int max_size = 0;
            for (int i = 0; i < cnts.size(); i++) {
                if(cnts[i].size() > max_size){
                    max_size = cnts[i].size();
                    max_index = i;
                }
            }
            // 根据这组点集找到最合适的拟合椭圆(非外接椭圆)
            RotatedRect rect = fitEllipse(cnts[max_index]);
            // 获得这个拟合椭圆的外接矩形的四个角点
            Point2f pts[4];
            // The order is bottomLeft, topLeft, topRight, bottomRight.
            rect.points(pts);
			// 算出椭圆长轴和短轴的端点
            Point long_p1 = calMidpoint(pts[0], pts[3]);
            Point long_p2 = calMidpoint(pts[1], pts[2]);
            Point short_p1 = calMidpoint(pts[0], pts[1]);
            Point short_p2 = calMidpoint(pts[2], pts[3]);
			// 画出径线
			// Tips:这种方法不适合特定场景,且径线的端点也不一点落在病灶边界上
            line(img, long_p1, long_p2, Scalar(0, 0, 255));
            line(img, short_p1, short_p2, Scalar(0, 0, 255));
            // 根据边界计算病灶面积
            double area = contourArea(cnts[max_index]);
            // 根据边界计算,闭合边界的周长
            double length = arcLength(cnts[max_index], true);
            cout << "area:" << area << ", length" << length << endl;
            // 根据边界画出轮廓
            drawContours(img, cnts, max_index, Scalar(0, 255, 0));
            imshow("processed", img);
            waitKey(0);
        } else {
            cout << "no contours." << endl;
        }
    }
    catch (cv::Exception &e) {
        const char* err_msg = e.what();
        cout << "exception caught: " << err_msg << endl;
    }
}

效果

输出

标签:p1,案例,max,Point,opencv,cnts,病灶,pts
来源: https://blog.csdn.net/a2824256/article/details/121103776

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有