图像读取与显示
// 读取图片
Mat img;
string img_path = "C:\\Users\\my_file\\Desktop\\data\\4.bmp";
img = imread(img_path);
if (img.empty())
{
cout << "请确认图像文件名是否正确" << endl;
return 0;
}
else
{
cout << "图片读取成功!" << endl;
}
// 显示根据原图片大小生成窗口
int img_height;
int img_width;
img_height = img.cols; // 获取图片的行和列
img_width = img.rows;
printf("图片的高度%d\n", img_height);
printf("图片的宽度%d\n", img_width);
// namedWindow("原图",0); //创建窗口
// 因显示不全所以缩放
Mat resized_down;
resize(img, resized_down, Size(img_width, img_height), INTER_LINEAR);
图像预处理–灰度化二值化
// ----------------------------------------------------图像预处理-------------------------------------------------------------
//
// 灰度化 -> 二值化 -> 去噪 -> 边缘检测 ->
// 1.灰度化并显示
Mat img_2_gray;
Mat img_2_gray_rd;
namedWindow("灰度图", 0); //创建窗口
// namedWindow("9x9滤波灰度图", 0);
cvtColor(img, img_2_gray, COLOR_BGR2GRAY);
resize(img_2_gray, img_2_gray_rd, Size(img_2_gray.rows, img_2_gray.cols), INTER_LINEAR);
imshow("灰度图", img_2_gray_rd);
waitKey(0);
Mat bin_img, bin_img_rd;
threshold(img_2_gray, bin_img, 0, 255, 0 | THRESH_OTSU); //二值化
namedWindow("二值化后图像", 0);
resize(bin_img, bin_img_rd, Size(bin_img.rows, bin_img.cols), INTER_LINEAR);
imshow("二值化后图像", bin_img_rd);
waitKey(0);
图像预处理–去噪
// 2.去噪 -------------------------------------------中值滤波------------------------------------------------------------
Mat grayResult9, grayResult9_rd;
medianBlur(bin_img, grayResult9, 9);
namedWindow("9x9滤波灰度图", 0);
resize(grayResult9, grayResult9_rd, Size(grayResult9.rows, grayResult9.cols), INTER_LINEAR);
imshow("9x9滤波灰度图", grayResult9_rd);
waitKey(0);
// 内核
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
int cl_iterations = 1; // 设置闭运算次数为4次
// 进行-------------------------------------------------闭运算------------------------------------------------------------
Mat closedImage, closedImage_rd;
morphologyEx(grayResult9, closedImage, MORPH_CLOSE, kernel, Point(-1, -1), cl_iterations);
namedWindow("闭运算结果图", 0);
resize(closedImage, closedImage_rd, Size(closedImage.rows, closedImage.cols), INTER_LINEAR);
imshow("闭运算结果图", closedImage_rd);
waitKey(0);
边缘检测
使用canny算子
// 3.--------------------------------------------------边缘检测-----------------------------------------------------------
// 边缘检测
Mat cannyout, cannyout_rd;
Canny(closedImage, cannyout, thresh, thresh * 2, 3); // y一般为1:2或1:3
namedWindow("边缘检测结果图", 0);
resize(cannyout, cannyout_rd, Size(cannyout.rows, cannyout.cols), INTER_LINEAR);
imshow("边缘检测结果图", cannyout);
// String filename = "边缘检测.jpg";
// imwrite(filename, cannyout);
waitKey(0);
边缘检测以及轮廓绘制
为了生成掩膜图,绘制轮廓时绘制最外层轮廓,将层级低的轮廓过滤掉
//------------------------------------------------------轮廓绘制---------------------------------------------------------
vector<vector<Point>> contours, Contours_temp; //
double con_area; // 存放轮廓面积
vector<Vec4i> hierarchy; // 存放轮廓层次
// 创建一个空白的图像来绘制轮廓
// 提取轮廓
Mat contourImage = Mat::zeros(img.size(), CV_8UC3);
findContours(cannyout, Contours_temp, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // 查找全部的轮廓
// findContours(cannyout, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); // 查找全部的轮廓
printf("find %d contours", Contours_temp.size());
// 4.绘制原始轮廓
drawMyContours("未筛选轮廓图", img, Contours_temp, true);
// String filename1 = "未筛选轮廓图.jpg";
// imwrite(filename1, img);
// 5.筛选轮廓
// 初始化迭代器
//vector<vector<Point>>::iterator itc = contours.begin();
//vector<Vec4i>::iterator itc_hierarchy = hierarchy.begin();
// 5.1使用层级结构筛选轮廓
//int i = 0;
//while (itc_hierarchy != hierarchy.end())
//{
// //验证轮廓大小
// if (hierarchy[i][2] > 0 || hierarchy[i][3] > 0) // 存在子轮廓/父轮廓
// {
// itc = contours.erase(itc);
// itc_hierarchy = hierarchy.erase(itc_hierarchy);
// }
// else
// {
// ++i;
// ++itc;
// ++itc_hierarchy;
// }
//}
printf("%d contours remaining after hierarchy filtering\n", Contours_temp.size());
for (size_t t = 0; t < Contours_temp.size(); t++)
{
// 计算轮廓面积
double area = contourArea(Contours_temp[t]);
// cout << "Contour " << t << " Area: " << area << endl;
if (area > 200) //500
{
contours.push_back(Contours_temp[t]);
}
}
// 绘制级别筛选后的轮廓
drawMyContours("绘制最外层轮廓", img, contours, true);
// String filename2 = "最外层轮廓.jpg";
// imwrite(filename2, img);
生成掩膜图
绘制好轮廓后将轮廓内部填充为一个灰度值,掩膜和背景区分开来
//-------------------------------------------------------掩膜填充-------------------------------------------------------
// 初始化Mat 掩膜容器
Mat mask = Mat::zeros(img.size(), CV_8UC1);
Mat mask_rd;
Mat mask_small = Mat::zeros(img.size(), CV_8UC1);
Mat mask_small_rd;
Mat mask_large = Mat::zeros(img.size(), CV_8UC1);
Mat mask_large_rd;
Mat mask_largeround = Mat::zeros(img.size(), CV_8UC1);
Mat mask_largeround_rd;
Mat normmask = Mat::zeros(img.size(), CV_8UC1);
Mat normmask_rd;
Mat count; // 存储抠图
// 裁图
/* for (size_t t = 0; t < contours.size(); t++)
{
Mat tempImage = Mat::zeros(img.size(), img.type());
drawContours(tempImage, contours, t, Scalar(255, 255, 255), -1);
// 使用掩膜图(tempImage)提取原始图像中的区域
Mat maskedRegion;
img.copyTo(maskedRegion, tempImage);
// 保存裁剪后的单个图像
String filename = "cropped_image_" + std::to_string(t) + ".jpg";
count = t;
imwrite(filename, maskedRegion);
}*/
// 填充轮廓
for (size_t t = 0; t < contours.size(); t++)
{
drawContours(mask, contours, t, Scalar(255, 225, 225), -1); // FILLED 与-1等同
}
// String filename3 = "填充轮廓-掩膜图.jpg";
// imwrite(filename3, mask);
namedWindow("填充轮廓-掩膜图", 0);
resize(mask, mask_rd, Size(mask.rows, mask.cols), INTER_LINEAR);
imshow("填充轮廓-掩膜图", mask_rd);
waitKey(0);
根据轮廓特征分类并抠图
抠图就需要将掩膜图放在原图上push_back
//----------------------------------------------------通过设置阈值分类----------------------------------------------------
vector<vector<Point>> smallContours; // 体积过小轮廓
vector<vector<Point>> largeContours; // 粘连轮廓
vector<vector<Point>> largeroundContours; // 圆型体积大轮廓
vector<vector<Point>> normalContours; // 正常
vector<double>area; // 面积
vector<double>roundness; // 圆形度
vector<double>len; // 周长
// 区分
for (size_t t = 0; t < contours.size(); t++)
{
// 计算面积和周长
double con_area = contourArea(contours[t]);
double con_len = arcLength(contours[t], true);
double con_round = (4 * CV_PI * con_area) / (con_len * con_len);
area.push_back(con_area);
len.push_back(con_len);
roundness.push_back(con_round);
int class_num;
cout << "contour " << t << " Area: " << area[t] << endl;
cout << "contour " << t << " Perimeter: " << len[t] << endl;
cout << "contour " << t << " Roundness: " << roundness[t] << endl;
// 高斯分布
if (area[t] < 12000 && roundness[t] < 0.8820) // 体积过小 包含形状不规则
{
class_num = 0;
add_key_to_pilldata(mysql, area[t], class_num);
smallContours.push_back(contours[t]);
}
else if (area[t] > 20000 && len[t] > 550 && roundness[t] < 0.8) // 粘连
{
class_num = 0;
add_key_to_pilldata(mysql, area[t], class_num);
largeContours.push_back(contours[t]);
}
else if (area[t] > 14000 && len[t] > 470 && roundness[t] >= 0.78) // 体积过大圆形
{
class_num = 0;
add_key_to_pilldata(mysql, area[t], class_num);
largeroundContours.push_back(contours[t]);
}
else
{
class_num = 1;
add_key_to_pilldata(mysql, area[t], class_num);
normalContours.push_back(contours[t]);
}
//if ((area[t] > 11000 && area[t] < 14000 ) && (roundness[t] > 0.80 && roundness[t] <= 0.9))
//{
// normalContours.push_back(contours[t]);
//}
//else if (area[t] < 11000 )
//{
// smallContours.push_back(contours[t]);
//}
//else if (area[t] > 12000)
//{
// largeroundContours.push_back(contours[t]);
//}
}
getchar();
//for (size_t t = 0; t < normalContours.size(); t++)
//{
// double con_area = contourArea(contours[t]);
// double con_len = arcLength(contours[t], true);
// double con_round = (4 * CV_PI * con_area) / (con_len * con_len);
// 归类
for (size_t t = 0; t < smallContours.size(); t++)
{
drawContours(mask_small, smallContours, t, Scalar(255, 225, 225), -1); // FILLED 与-1等同
}
// String filename4 = "体积过小药丸掩膜图.jpg";
// imwrite(filename4, mask_small);
namedWindow("体积过小掩膜图", 0);
resize(mask_small, mask_small_rd, Size(mask_small.rows, mask_small.cols), INTER_LINEAR);
imshow("体积过小掩膜图", mask_small_rd);
waitKey(0);
for (size_t t = 0; t < largeContours.size(); t++)
{
drawContours(mask_large, largeContours, t, Scalar(255, 225, 225), -1); // FILLED 与-1等同
}
// String filename5 = "粘连药丸掩膜图.jpg";
// imwrite(filename5, mask_large);
namedWindow("粘连掩膜图", 0);
resize(mask_large, mask_large_rd, Size(mask_large.rows, mask_large.cols), INTER_LINEAR);
imshow("粘连掩膜图", mask_large_rd);
for (size_t t = 0; t < normalContours.size(); t++)
{
drawContours(normmask, normalContours, t, Scalar(255, 225, 225), -1); // FILLED 与-1等同
}
// String filename6 = "圆形大药丸掩膜图.jpg";
// imwrite(filename6, mask_largeround);
namedWindow("正常掩膜图", 0);
resize(normmask, normmask_rd, Size(normmask.rows, normmask.cols), INTER_LINEAR);
imshow("正常掩膜图", normmask_rd);
for (size_t t = 0; t < largeroundContours.size(); t++)
{
drawContours(mask_largeround, largeroundContours, t, Scalar(255, 225, 225), -1); // FILLED 与-1等同
}
// String filename6 = "圆形大药丸掩膜图.jpg";
// imwrite(filename6, mask_largeround);
namedWindow("圆形掩膜图", 0);
resize(mask_largeround, mask_largeround_rd, Size(mask_largeround.rows, mask_largeround.cols), INTER_LINEAR);
imshow("圆形掩膜图", mask_largeround_rd);
// 抠分完类的图------粘连
Mat nianimg;
Mat nianimg_rd;
img.copyTo(nianimg, mask_large);
namedWindow("粘连抠图", 0);
resize(nianimg, nianimg_rd, Size(nianimg.rows, nianimg.cols), INTER_LINEAR);
imshow("粘连抠图", nianimg_rd);
//
Mat largeround_img;
Mat largeround_img_rd;
img.copyTo(largeround_img, mask_largeround);
namedWindow("圆形大抠图", 0);
resize(largeround_img, largeround_img_rd, Size(largeround_img.rows, largeround_img.cols), INTER_LINEAR);
imshow("圆形大抠图", largeround_img_rd);
Mat small_img;
Mat small_img_rd;
img.copyTo(small_img, mask_small);
namedWindow("体积过小抠图", 0);
resize(small_img, small_img_rd, Size(small_img.rows, small_img.cols), INTER_LINEAR);
imshow("体积过小抠图", small_img_rd);
Mat normal_img;
Mat normal_img_rd;
img.copyTo(normal_img, normmask);
namedWindow("正常抠图", 0);
resize(normal_img, normal_img_rd, Size(normal_img.rows, normal_img.cols), INTER_LINEAR);
imshow("正常抠图", normal_img_rd);
// String filename7 = "体积小药丸抠图.jpg";
// imwrite(filename7, small_img);
// 保存
// String filename = "粘连.jpg";
// imwrite(filename, nianimg);
waitKey(0);
return 0;