C++ OpenCV实现物体尺寸测量示例详解

 更新时间:2022年01月10日 14:31:13   作者:Zero___Chen  
本文主要介绍了利用OpenCV对物体的尺寸进行测量,即先定位到待测物体的位置,然后测量物体的宽高。感兴趣的同学可以跟随小编一起学习学习

前言

本文将使用OpenCV C++ 进行物体尺寸测量。具体来说就是先定位到待测物体的位置,然后测量物体的宽高。

一、图像透视矫正

原图如图所示。本案例的需求是测量图片中两张卡片的尺寸。首先,我们得定位到两张卡片的位置。第一步,我们首先得将白色A4纸切割出来,这样方便定位到两张卡片所在位置。这里用到的算法是图像透视矫正,具体可以参考OpenCV C++案例实战四《图像透视矫正》

//图像矫正
void getWarp(Mat src, Mat &Warp)
{
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	Mat thresh;
	threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
	Mat open;
	morphologyEx(thresh, open, MORPH_OPEN, kernel);

	vector<vector<Point>>contours;
	findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	vector<vector<Point>>conPoly(contours.size());
	vector<Point>srcPts;

	//找到最大轮廓
	int MaxIndex = 0;
	double Area = 0;
	for (int i = 0; i < contours.size(); i++)
	{
		double area = contourArea(contours[i]);
		if (area > Area)
		{
			Area = area;
			MaxIndex = i;
		}
	}

	//获取矩形四个角点
	double peri = arcLength(contours[MaxIndex], true);
	approxPolyDP(contours[MaxIndex], conPoly[MaxIndex], 0.02*peri, true);

	srcPts = { conPoly[MaxIndex][0],conPoly[MaxIndex][1],conPoly[MaxIndex][2],conPoly[MaxIndex][3] };

	int T_L, B_L, B_R, T_R;
	int width = src.cols / 2;
	int height = src.rows / 2;
	for (int i = 0; i < srcPts.size(); i++)
	{
		if (srcPts[i].x < width && srcPts[i].y < height)
		{
			T_L = i;
		}
		if (srcPts[i].x < width && srcPts[i].y > height)
		{
			B_L = i;
		}
		if (srcPts[i].x > width && srcPts[i].y > height)
		{
			B_R = i;
		}
		if (srcPts[i].x > width && srcPts[i].y < height)
		{
			T_R = i;
		}
	}

	double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
	double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
	double MaxWidth = max(UpWidth, DownWidth);

	double UpHeight = EuDis(srcPts[T_L], srcPts[B_L]);
	double DownHeight = EuDis(srcPts[T_R], srcPts[B_R]);
	double MaxHeight = max(UpHeight, DownHeight);

	//透视变换进行图像矫正
	Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
	Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };

	Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
	warpPerspective(src, Warp, M, Point(MaxWidth, MaxHeight));

}

效果如图所示。接下来,我们需要定位两张卡片所在位置,寻找特征。

二、物体定位

//获取物体坐标
void FindPts(Mat &Warp, vector<vector<Point>>&TargetPts)
{

	Mat gray;
	cvtColor(Warp, gray, COLOR_BGR2GRAY);

	Mat thresh;
	threshold(gray, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	Mat open;
	morphologyEx(thresh, open, MORPH_OPEN, kernel);

	vector<vector<Point>>contours;
	findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	vector<vector<Point>>conPoly(contours.size());
	//定位卡片四个角点
	for (int i = 0; i < contours.size(); i++)
	{
		double area = contourArea(contours[i]);

		if (area > 1000)
		{
			double peri = arcLength(contours[i], true);

			approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);

			vector<Point>temp;
			temp = { conPoly[i][0],conPoly[i][1], conPoly[i][2], conPoly[i][3] };

			TargetPts.push_back(temp);
		}
	}
}

如图所示。通过上面代码段,我们已经定位出卡片的四个角点。接下来,只需根据角点位置就可以计算卡片的宽高了。

三、尺寸测量

//计算距离
void DrawAndCompute(Mat &Warp, vector<vector<Point>>&TargetPts)
{
	for (int i = 0; i < TargetPts.size(); i++)
	{
		for (int j = 0; j < TargetPts[i].size(); j++)
		{
			//尺寸测量
			Point PtA = Point(TargetPts[i][j]);
			Point PtB = Point(TargetPts[i][(j + 1) % TargetPts[i].size()]);
			double dis = round(EuDis(PtA, PtB) * 100) / 100;

			//效果显示
			circle(Warp, TargetPts[i][j], 5, Scalar(0, 255, 0), -1);
			line(Warp, PtA, PtB, Scalar(0, 0, 255), 2);
			char text[20];
			sprintf_s(text, "%.2f", dis);
			Point point = Point((PtA.x + PtB.x) / 2, (PtA.y + PtB.y) / 2);
			putText(Warp, text, point, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 255), 2);
		}
	}
}

四、效果显示

五、源码

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

//欧式距离
double EuDis(Point pt1, Point pt2)
{
	return sqrt((pt2.x - pt1.x)*(pt2.x - pt1.x) + (pt2.y - pt1.y)*(pt2.y - pt1.y));
}

//图像矫正
void getWarp(Mat src, Mat &Warp)
{
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	Mat thresh;
	threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
	Mat open;
	morphologyEx(thresh, open, MORPH_OPEN, kernel);

	vector<vector<Point>>contours;
	findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	vector<vector<Point>>conPoly(contours.size());
	vector<Point>srcPts;

	//找到最大轮廓
	int MaxIndex = 0;
	double Area = 0;
	for (int i = 0; i < contours.size(); i++)
	{
		double area = contourArea(contours[i]);
		if (area > Area)
		{
			Area = area;
			MaxIndex = i;
		}
	}

	//获取矩形四个角点
	double peri = arcLength(contours[MaxIndex], true);
	approxPolyDP(contours[MaxIndex], conPoly[MaxIndex], 0.02*peri, true);

	srcPts = { conPoly[MaxIndex][0],conPoly[MaxIndex][1],conPoly[MaxIndex][2],conPoly[MaxIndex][3] };

	int T_L, B_L, B_R, T_R;
	int width = src.cols / 2;
	int height = src.rows / 2;
	for (int i = 0; i < srcPts.size(); i++)
	{
		if (srcPts[i].x < width && srcPts[i].y < height)
		{
			T_L = i;
		}
		if (srcPts[i].x < width && srcPts[i].y > height)
		{
			B_L = i;
		}
		if (srcPts[i].x > width && srcPts[i].y > height)
		{
			B_R = i;
		}
		if (srcPts[i].x > width && srcPts[i].y < height)
		{
			T_R = i;
		}
	}

	double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
	double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
	double MaxWidth = max(UpWidth, DownWidth);

	double UpHeight = EuDis(srcPts[T_L], srcPts[B_L]);
	double DownHeight = EuDis(srcPts[T_R], srcPts[B_R]);
	double MaxHeight = max(UpHeight, DownHeight);

	//透视变换进行图像矫正
	Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
	Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };

	Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
	warpPerspective(src, Warp, M, Point(MaxWidth, MaxHeight));

}

//获取物体坐标
void FindPts(Mat &Warp, vector<vector<Point>>&TargetPts)
{

	Mat gray;
	cvtColor(Warp, gray, COLOR_BGR2GRAY);

	Mat thresh;
	threshold(gray, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	Mat open;
	morphologyEx(thresh, open, MORPH_OPEN, kernel);

	vector<vector<Point>>contours;
	findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	vector<vector<Point>>conPoly(contours.size());
	//定位卡片四个角点
	for (int i = 0; i < contours.size(); i++)
	{
		double area = contourArea(contours[i]);

		if (area > 1000)
		{
			double peri = arcLength(contours[i], true);

			approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);

			vector<Point>temp;
			temp = { conPoly[i][0],conPoly[i][1], conPoly[i][2], conPoly[i][3] };

			TargetPts.push_back(temp);
		}
	}
}

//计算距离
void DrawAndCompute(Mat &Warp, vector<vector<Point>>&TargetPts)
{
	for (int i = 0; i < TargetPts.size(); i++)
	{
		for (int j = 0; j < TargetPts[i].size(); j++)
		{
			//尺寸测量
			Point PtA = Point(TargetPts[i][j]);
			Point PtB = Point(TargetPts[i][(j + 1) % TargetPts[i].size()]);
			double dis = round(EuDis(PtA, PtB) * 100) / 100;

			//效果显示
			circle(Warp, TargetPts[i][j], 5, Scalar(0, 255, 0), -1);
			line(Warp, PtA, PtB, Scalar(0, 0, 255), 2);
			char text[20];
			sprintf_s(text, "%.2f", dis);
			Point point = Point((PtA.x + PtB.x) / 2, (PtA.y + PtB.y) / 2);
			putText(Warp, text, point, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 255), 2);
		}
	}
}

int main()
{
	Mat src = imread("src.jpg");
	if (src.empty())
	{
		cout << "No Image!" << endl;
		system("pause");
		return -1;
	}

	Mat Warp;
	getWarp(src, Warp);

	vector<vector<Point>>TargetPts;	
	FindPts(Warp, TargetPts);

	DrawAndCompute(Warp, TargetPts);

	imshow("Warp", Warp);
	waitKey(0);
	destroyAllWindows();
	system("pause");
	return 0;
}

总结

本文使用OpenCV C++ 进行物体尺寸测量,关键步骤有以下几点。

1、图像透视矫正。方便定位物体所在位置。

2、物体定位。定位所需物体位置,获取特征。

3、根据已知特征进行计算。

以上就是C++ OpenCV实现物体尺寸测量示例详解的详细内容,更多关于C++ OpenCV物体尺寸测量的资料请关注脚本之家其它相关文章!

相关文章

  • c++11之std::async 和std::thread的区别小结

    c++11之std::async 和std::thread的区别小结

    std::async和std::thread都是C++11中提供的线程库,它们都可以用于创建新线程,本文主要介绍了c++11之std::async 和std::thread的区别小结,感兴趣的可以了解一下
    2024-02-02
  • C++ 中使用lambda代替 unique_ptr 的Deleter的方法

    C++ 中使用lambda代替 unique_ptr 的Deleter的方法

    这篇文章主要介绍了C++ 中使用lambda代替 unique_ptr 的Deleter的方法,需要的朋友可以参考下
    2017-04-04
  • C++中Overload,Override,Hide之间的区别

    C++中Overload,Override,Hide之间的区别

    重载overload,这个概念是大家熟知的。在同一可访问区内被声名的几个具有不同参数列的(参数的类型、个数、顺序不同)同名函数,程序会根据不同的参数列来确定具体调用哪个函数,这种机制就是重载
    2013-09-09
  • c++访问修饰符与继承关系详解

    c++访问修饰符与继承关系详解

    C++提供了三个修饰符来限定类成员的被访问权限,分别是public、protected、private,通过限定访问权限,可以达到程序编写者想要解决的安全问题和权限问题,本文给大家介绍c++访问修饰符与继承关系,感兴趣的朋友一起看看吧
    2023-10-10
  • 详解C++图搜索算法之双端队列广搜

    详解C++图搜索算法之双端队列广搜

    这篇文章主要为大家介绍一下C++图搜索算法中的双端队列广搜,文中通过例题详细介绍了双端队列广搜的使用方法,感兴趣的可以了解一下
    2022-06-06
  • 黑客帝国数字雨效果VC6源代码分享

    黑客帝国数字雨效果VC6源代码分享

    这篇文章主要介绍了黑客帝国数字雨效果VC6源代码分享,本文直接给出实现代码,Win7下编译通过,效果很酷,需要的朋友可以参考下
    2015-02-02
  • C++中调用复制(拷贝)函数的三种情况总结

    C++中调用复制(拷贝)函数的三种情况总结

    这篇文章主要介绍了C++中调用复制(拷贝)函数的三种情况总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C语言二维数组应用之扫雷游戏

    C语言二维数组应用之扫雷游戏

    这篇文章主要为大家详细介绍了C语言二维数组应用之扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C++20中的结构化绑定类型示例详解

    C++20中的结构化绑定类型示例详解

    这篇文章主要为大家介绍了C++20中的结构化绑定类型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • C语言中函数栈帧的创建和销毁的深层分析

    C语言中函数栈帧的创建和销毁的深层分析

    在C语言中,每一个正在运行的函数都有一个栈帧与其对应,栈帧中存储的是该函数的返回地址和局部变量。从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等
    2022-04-04

最新评论