任务
根据语义分割网络的输出,去求以下任务的结果。(神经网络的后处理操作)
(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;
}
}