废话不多说,直接上栗子!
1.边缘检测、轮廓查找、轮廓筛选、绘制最小外接矩、尺寸测量和标注尺寸,代码如下:
//开发环境:VS2015+OPENCV3.2
#include
#include
#include
using namespace cv;
using namespace std;
#define PHY_LEN 72.0 //参照物较长边边长的物理长度
//文本绘制
void PaintText(Mat& img, char* text, Point origin)
{
int fontface = CV_FONT_HERSHEY_SIMPLEX; //字体
double fontscale = 0.5; //尺寸因子,值越大文字越大
Scalar textcolor = Scalar(0, 0, 255); //文本颜色
int thickness = 2; //线宽
int linetype = 8; //线型
//int baseline;
获取文本框的长宽
//Size text_size = getTextSize(text, fontface, fontscale, thickness, &baseline);
putText(img, text, origin, fontface, fontscale, textcolor, thickness, linetype);
}
void fineMinAreaRect(Mat &threshold_output, Mat &src)
{
bool flag = false; //查找比例尺标志
double pp = 0.0; //比例因子:每像素代表的实际物理尺寸
vector> contours;
vector hierarchy;
//寻找轮廓,输入必须为二值图像
findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0)); //CV_CHAIN_APPROX_SIMPLE
//第一轮筛选:移除过短的轮廓
int cmin = 100; //最小轮廓长度
int cmax = 5000; //最大轮廓长度
vector>::const_iterator itc = contours.begin();
while (itc != contours.end())
{
if (itc->size() < cmin || itc->size() > cmax)
itc = contours.erase(itc);
else
++itc;
}
//第二轮筛选:剔除面积小于指定阈值的轮廓
//计算轮廓的面积
for (int i = 0; i < (int)contours.size(); i++)
{
double g_dConArea = fabs(contourArea(contours[i], true));
cout << "轮廓面积: " << g_dConArea << endl;
}
//剔除小面积轮廓
//vector >::const_iterator itc = contours.begin();
itc = contours.begin();
for (; itc != contours.end();)
{
double g_dConArea = contourArea(*itc);
if (g_dConArea < 200)
{
itc = contours.erase(itc);
}
else
{
++itc;
}
}
//对每个找到的轮廓创建可倾斜的边界框
vector minRect(contours.size());
for (int i = 0; i < contours.size(); i++)
{
minRect[i] = minAreaRect(Mat(contours[i]));
}
//绘出轮廓及其可倾斜的边界框
//Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
//先遍历寻找参照物,计算出比例因子
for (int i = 0; i < contours.size(); i++)
{
Point2f rect_points[4];
minRect[i].points(rect_points);
//寻找比例尺,四顶点均位于左半图
if (!flag && rect_points[0].x < src.cols / 2 && rect_points[1].x < src.cols / 2 && rect_points[2].x < src.cols / 2
&& rect_points[3].x < src.cols / 2)
{
flag = true;
double length1 = sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2));
double length2 = sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2));
if (length1 <= length2) {
pp = PHY_LEN / length2;
break;
}
else {
pp = PHY_LEN / length1;
break;
}
}
}
double length; //矩形边长
Point paint_point; //中点位置
char text[20] = { 0 }; //文本字符串数组
//再遍历绘制外接矩形
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(255, 255, 255); //白色轮廓线
//绘制轮廓
drawContours(src, contours, i, color, 1, 8, vector(), 0, Point());
Point2f rect_points[4];
minRect[i].points(rect_points);
//for (int j = 0; j < 4; j++) {
// line(drawing, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 255, 0), 2, 8); //绿色矩形框
//}
//分别绘制矩形的四条边
//第一条边
line(src, rect_points[0], rect_points[1], Scalar(0, 255, 0), 2, 8); //绿色矩形框
length = pp* (sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2))); //计算长度
sprintf(text, "%.3lfmm", length); //转为字符串
paint_point.x = (rect_points[0].x + rect_points[1].x) / 2; //寻找中点
paint_point.y = (rect_points[0].y + rect_points[1].y) / 2;
PaintText(src, text, paint_point); //文本标注
//第二条边
line(src, rect_points[1], rect_points[2], Scalar(255, 0, 0), 2, 8); //蓝色矩形框
length = pp* (sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2)));
sprintf(text, "%.3lfmm", length);
paint_point.x = (rect_points[1].x + rect_points[2].x) / 2;
paint_point.y = (rect_points[1].y + rect_points[2].y) / 2;
PaintText(src, text, paint_point);
//第三、四条边
line(src, rect_points[2], rect_points[3], Scalar(0, 255, 0), 2, 8);
line(src, rect_points[3], rect_points[0], Scalar(255, 0, 0), 2, 8);
}
//结果显示
imshow("测量结果", src);
}
int main()
{
Mat src, m_src,src_gray, edge;
src = imread("F:/opencv_workspace/img/greein33.jpg"); //加载
imshow("原始图片", src);
GaussianBlur(src, src, Size(3, 3), BORDER_DEFAULT); //高斯模糊
//blur(src, src, Size(3, 3)); //均值滤波
//medianBlur(src, src, 3); //中值滤波
//imshow("滤波效果", src);
bilateralFilter(src, m_src, 25, 25 * 2, 25 / 2); //保留边缘效果最好,不支持in-place操作
//对比度增强(很重要,否则部分背光区域边缘轮廓无法找到)
Mat kernel = (Mat_(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(m_src, m_src, -1, kernel, Point(-1, -1), 0);
//定义核
//Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
//进行形态学操作
//morphologyEx(m_src, m_src, MORPH_OPEN, element); //开运算:可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积
//imshow("开运算", m_src);
//int s = 3 * 2 + 1;
//Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
//dilate(m_src, m_src, structureElement, Point(-1, -1), 1); //膨胀
//erode(m_src, m_src, structureElement); //腐蚀
cvtColor(m_src, src_gray, CV_BGR2GRAY); //转为灰度图像
Canny(src_gray, edge, 150, 450, 3); //边缘检测,得到的是二值图像
imshow("边缘检测", edge);
//形态学闭运算
//进行形态学操作
//morphologyEx(edge, edge, MORPH_CLOSE, element);
fineMinAreaRect(edge, src); //寻找最小外接矩形
waitKey(0);
return(0);
}
运行效果:
2.同例1,只是将轮廓筛选改为了只保留最大面积的轮廓,代码如下:
#include
#include
#include
using namespace cv;
using namespace std;
#define PHY_LEN 114.0 //参照物较长边边长的物理长度
//文本绘制
void PaintText(Mat& img, char* text, Point origin)
{
int fontface = CV_FONT_HERSHEY_SIMPLEX; //字体
double fontscale = 0.5; //尺寸因子,值越大文字越大
Scalar textcolor = Scalar(0, 0, 255); //文本颜色
int thickness = 2; //线宽
int linetype = 8; //线型
//int baseline;
获取文本框的长宽
//Size text_size = getTextSize(text, fontface, fontscale, thickness, &baseline);
putText(img, text, origin, fontface, fontscale, textcolor, thickness, linetype);
}
void fineMinAreaRect(Mat &threshold_output, Mat &src)
{
bool flag = false; //查找比例尺标志
double pp = 0.0; //比例因子:每像素代表的实际物理尺寸
vector> contours;
vector hierarchy;
//寻找轮廓,输入必须为二值图像
findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0)); //CV_CHAIN_APPROX_SIMPLE
// //第一轮筛选:移除过短的轮廓
//int cmin = 100; //最小轮廓长度
//int cmax = 5000; //最大轮廓长度
//vector>::const_iterator itc = contours.begin();
//while (itc != contours.end())
//{
// if (itc->size() < cmin || itc->size() > cmax)
// itc = contours.erase(itc);
// else
// ++itc;
//}
vector>::const_iterator itc = contours.begin();
//计算轮廓的面积
for (int i = 0; i < (int)contours.size(); i++)
{
double g_dConArea = fabs(contourArea(contours[i], true));
cout << "轮廓面积: " << g_dConArea << endl;
}
//遍历查找最大面积轮廓
itc = contours.begin();
double MaxArea = fabs(contourArea(*itc));
for (itc = contours.begin()+1; itc != contours.end();)
{
double g_dConArea = fabs(contourArea(*itc));
if (g_dConArea > MaxArea)
{
MaxArea = g_dConArea;
itc = contours.erase(itc-1);
itc++;
}
else {
itc = contours.erase(itc);
}
}
//查看检测到的最大轮廓面积
for (int i = 0; i < (int)contours.size(); i++)
{
double max_dConArea = fabs(contourArea(contours[i], true));
cout << "最大轮廓面积: " << max_dConArea << endl;
}
//对每个找到的轮廓创建可倾斜的边界框
vector minRect(contours.size());
for (int i = 0; i < contours.size(); i++)
{
minRect[i] = minAreaRect(Mat(contours[i]));
}
//绘出轮廓及其可倾斜的边界框
//Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
//先遍历寻找参照物,计算出比例因子
for (int i = 0; i < contours.size(); i++)
{
Point2f rect_points[4];
minRect[i].points(rect_points);
//寻找比例尺,四顶点均位于左半图
if (!flag && rect_points[0].x < src.cols / 2 && rect_points[1].x < src.cols / 2 && rect_points[2].x < src.cols / 2
&& rect_points[3].x < src.cols / 2)
{
flag = true;
double length1 = sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2));
double length2 = sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2));
if (length1 <= length2) {
pp = PHY_LEN / length2;
break;
}
else {
pp = PHY_LEN / length1;
break;
}
}
}
double length; //矩形边长
Point paint_point; //中点位置
char text[20] = { 0 }; //文本字符串数组
//再遍历绘制外接矩形
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(255, 255, 255); //白色轮廓线
//绘制轮廓
drawContours(src, contours, i, color, 1, 8, vector(), 0, Point());
Point2f rect_points[4];
minRect[i].points(rect_points);
//for (int j = 0; j < 4; j++) {
// line(drawing, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 255, 0), 2, 8); //绿色矩形框
//}
//分别绘制矩形的四条边
//第一条边
line(src, rect_points[0], rect_points[1], Scalar(0, 255, 0), 2, 8); //绿色矩形框
length = pp* (sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2))); //计算长度
sprintf(text, "%.3lfmm", length); //转为字符串
paint_point.x = (rect_points[0].x + rect_points[1].x) / 2; //寻找中点
paint_point.y = (rect_points[0].y + rect_points[1].y) / 2;
PaintText(src, text, paint_point); //文本标注
//第二条边
line(src, rect_points[1], rect_points[2], Scalar(255, 0, 0), 2, 8); //蓝色矩形框
length = pp* (sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2)));
sprintf(text, "%.3lfmm", length);
paint_point.x = (rect_points[1].x + rect_points[2].x) / 2;
paint_point.y = (rect_points[1].y + rect_points[2].y) / 2;
PaintText(src, text, paint_point);
//第三、四条边
line(src, rect_points[2], rect_points[3], Scalar(0, 255, 0), 2, 8);
line(src, rect_points[3], rect_points[0], Scalar(255, 0, 0), 2, 8);
}
//结果显示
imshow("测量结果", src);
}
int main()
{
Mat src, src_gray, edge;
src = imread("F:/opencv_workspace/img/m_img/m5.jpg"); //加载
//resize(src, src, Size(src.cols / 2, src.rows / 2)); //等比例缩放,必须等比例缩放,否则尺寸错误
imshow("原始图片", src);
GaussianBlur(src, src, Size(3, 3), BORDER_DEFAULT); //高斯模糊
//blur(src, src, Size(3, 3)); //均值滤波
//medianBlur(src, src, 3); //中值滤波
//imshow("滤波效果", src);
//对比度增强(很重要,否则部分背光区域边缘轮廓无法找到)
Mat kernel = (Mat_(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(src, src, -1, kernel, Point(-1, -1), 0);
//定义核
Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
//进行形态学操作
morphologyEx(src, src, MORPH_OPEN, element); //开运算:可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积
//imshow("开运算",src);
//int s = 3 * 2 + 1;
//Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
//dilate(src, src, structureElement, Point(-1, -1), 1); //膨胀
//erode(src, src, structureElement); //腐蚀
cvtColor(src, src_gray, CV_BGR2GRAY); //转为灰度图像
Canny(src_gray, edge, 30, 90, 3); //边缘检测,得到的是二值图像
//imshow("边缘检测", edge);
//形态学闭运算
//进行形态学操作
morphologyEx(edge, edge, MORPH_CLOSE, element);
//threshold(src_gray, edge, 175, 255, CV_THRESH_BINARY); //二值化
//imshow("二值化效果", edge);
fineMinAreaRect(edge, src); //寻找最小外接矩形
waitKey(0);
return(0);
}
运行效果: