c++ identify quadrant mark

        I recently used quadrant positioning. I searched for information on the Internet and found that there were too few introductions in this area, so I used the traditional method (shape + color) for detection. If a better method is found later, I will update it. The quadrant mark style is as shown below

        The idea is: perform Hough circle detection in each frame of image, traverse each detected circle, perform perspective transformation to a new image, and in each circle, detect whether there are yellow and black areas at the same time, and the area reaches a certain value , if there is one, it is a quadrant mark, which is displayed on the final result map, and the center of the circle is obtained. Otherwise, no processing is performed, and the distance between the two quadrant tables finally obtained is calculated to obtain the distance value in the imaging screen.

 1. Open the camera and read frames in a loop

The procedure is as follows:

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void main()
{
	Mat frame;
	VideoCapture cap(0);   //0:打开本机摄像头;1:打开外接摄像头
	if (cap.isOpened())
	{
		cap.set(CAP_PROP_FRAME_WIDTH, 1080);  // 设置摄像头的分辨率为1080*960
		cap.set(CAP_PROP_FRAME_HEIGHT, 960);
		while (true)
		{
			cap.read(frame);
			namedWindow("帧", WINDOW_AUTOSIZE);
			imshow("帧", frame);
			if (waitKey(100) == 27)  // 按ESC退出;
			{
				break;
			}

		}
	}

}

2. Perform Hough circle detection

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void main()
{
	Mat frame, img_gray, img_blur;
	VideoCapture cap(0);   //0:打开本机摄像头;1:打开外接摄像头
	if (cap.isOpened())
	{
		cap.set(CAP_PROP_FRAME_WIDTH, 1080);  // 设置摄像头的分辨率为1080*960
		cap.set(CAP_PROP_FRAME_HEIGHT, 960);
	}

	vector<Vec3f>circles;
	Vec3f circle;
	Mat img_cont;
	Mat matrix, imgWarp;
	while (true)
	{
		cap.read(frame);
		img_cont = frame.clone();
		cvtColor(frame, img_gray, COLOR_BGR2GRAY);  // 灰度变换
		medianBlur(img_gray, img_blur, 3);  // 中值滤波

		//hough圆检测
		HoughCircles(img_blur, circles, HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50);
		for (size_t i = 0; i < circles.size(); i++)  // 遍历每帧图像所有的圆
		{
			circle = circles[i];
			cv::circle(img_cont, Point(circle[0], circle[1]), circle[2], Scalar(255, 0, 0)); // 画圆
			cv::circle(img_cont, Point(circle[0], circle[1]), 2, Scalar(255, 0, 0));   //画圆心
			float w = circle[2];
			float h = circle[2]; // 透视图的宽高
			//获取每个圆的外接矩形
			Point2f src[4] = { {circle[0] - circle[2],circle[1] - circle[2]},  //矩形左上角坐标
								{circle[0] + circle[2],circle[1] - circle[2]}, //矩形右上角坐标
								{circle[0] - circle[2],circle[1] + circle[2]},  //矩形左下角
								{circle[0] + circle[2],circle[1] + circle[2]},  //矩形右下角

			};
			Point2f dir[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
			matrix = getPerspectiveTransform(src, dir); // 透视变换,实现两张图像同一个点的对应关系
			warpPerspective(frame, imgWarp, matrix, Point(w, h)); // 将检测的圆画在imgWarp上
		}

		cv::namedWindow("帧", WINDOW_AUTOSIZE);
		cv::imshow("帧", frame);

		cv::namedWindow("Hough圆检测", WINDOW_AUTOSIZE);
		cv::imshow("Hough圆检测", img_cont);

		cv::namedWindow("透视图", WINDOW_AUTOSIZE);
		cv::imshow("透视图", imgWarp);
		if (waitKey(100) == 27)  // 按ESC退出;
		{
			break;
		}

		}

}

Idea: Performing Hough circle detection on each frame of image will detect many circles, as shown in the figure below. How to filter out the required circles later? Through color detection, it is necessary to traverse all circles on each frame of image and perform color detection within each circle. So perspective transformation is used here.

3. Color detection

Before color detection, the HSV values ​​of yellow and black need to be extracted. The extraction code is shown below.

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void main()
{
	VideoCapture cap(0);
	if (cap.isOpened())
	{
		cap.set(CAP_PROP_FRAME_WIDTH, 1080);
		cap.set(CAP_PROP_FRAME_HEIGHT, 960);
	}
	Mat frame, img_HSV,mask;
	int hmin = 0, smin = 110, vmin = 150;
	int hmax = 255, smax = 255, vmax = 255;

	namedWindow("HSV调节", (640, 640));
	createTrackbar("Hue Min", "HSV调节", &hmin, 255);
	createTrackbar("Hue Max", "HSV调节", &hmax, 255);
	createTrackbar("Sat Min", "HSV调节", &smin, 255);
	createTrackbar("Sat Max", "HSV调节", &smax, 255);
	createTrackbar("val Min", "HSV调节", &vmin, 255);
	createTrackbar("val Max", "HSV调节", &vmax, 255);

	while (true)
	{
		cap.read(frame);
		cvtColor(frame, img_HSV, COLOR_BGR2HSV); // 进行HSV变换
		Scalar lower(hmin, smin, vmin);
		Scalar upper(hmax, smax, vmax);
		inRange(img_HSV, lower, upper, mask);

		namedWindow("帧", WINDOW_AUTOSIZE);
		imshow("帧", frame);

		namedWindow("HSV", WINDOW_AUTOSIZE);
		imshow("HSV", img_HSV);

		namedWindow("mask", WINDOW_AUTOSIZE);
		imshow("mask", mask);
		if (waitKey(100) == 27)
		{
			break;
		}
	}
}

The yellow extraction result is shown in the figure below

 Bring the HSV thresholds for yellow vs. black into the code.

4. Final code

#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//int hmin = 0, smin = 110, vmin = 153;
//int hmax = 255, smax = 240, vmax = 255;
int area_y, area_d;

RNG rng(12345); // 产生随机颜色

int main()
{
	VideoCapture cap(1);
	if (cap.isOpened())
	{
		cap.set(CAP_PROP_FRAME_WIDTH, 1080);
		cap.set(CAP_PROP_FRAME_HEIGHT, 960);
	}
	Mat frame,img_HSV,mask_yellow,mask_dark,mask,img_gray,img_thresh;

	//namedWindow("阈值调节", (640, 640));
	//createTrackbar("Thresh min", "阈值调节", &Thresh_min, 255);
	//createTrackbar("Thresh max", "阈值调节", &Thresh_max, 255);

	/*namedWindow("Trackbars", (640, 200));
	createTrackbar("Hue Min", "Trackbars", &hmin, 255);
	createTrackbar("Hue Max", "Trackbars", &hmax, 255);
	createTrackbar("sat Min", "Trackbars", &smin, 255);
	createTrackbar("sat Max", "Trackbars", &smax, 255);
	createTrackbar("val Min", "Trackbars", &vmin, 255);
	createTrackbar("val Max", "Trackbars", &vmax, 255);*/

	Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

	vector<Vec3f>circles;
	Vec3f circle;
	typedef Point_<double> Point2d;
	vector<Point2d> Mypoints;
	Point2d a, b;
	vector<double> center_dis;

	Mat matrix;
	Mat img_cont;
	Mat img_result;
	Mat img_blur, imgWarp;
	while (cap.isOpened())
	{
		cap.read(frame);
		img_cont = frame.clone();
		img_result = frame.clone();

		cvtColor(frame, img_gray, COLOR_BGR2GRAY);
		medianBlur(img_gray, img_blur, 3);//中值滤波

		//霍夫圆检测
		vector<Vec3f>circles;
		HoughCircles(img_blur, circles, HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50);
		for (size_t i = 0; i < circles.size(); i++)//每帧图像,遍历所有圆
		{
			//画出所有的圆
			circle = circles[i];
			cv::circle(img_cont, Point(circle[0], circle[1]), circle[2], Scalar(255, 0, 0)); // 画圆
			cv::circle(img_cont, Point(circle[0], circle[1]), 2, Scalar(255, 0, 0));   // 圆心
			cout << "circle:" << circle << endl;
			float w = circle[2];
			float h = circle[2];  // 透视图像宽高;

			Point2f src[4] = { {circle[0] - circle[2], circle[1] - circle[2]},
								{circle[0] + circle[2],circle[1] - circle[2]},
								{circle[0] - circle[2],circle[1] + circle[2]},
								{circle[0] + circle[2], circle[1] + circle[2]} };//在每帧图像中,每检测到一个圆获取外界矩形坐标

			Point2f dir[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} }; //
			matrix = getPerspectiveTransform(src, dir);   // 透视变换,实现两张图片同一个点的对应关系
			warpPerspective(frame, imgWarp, matrix, Point(w, h));  //将检测到的圆显示在img_Warp

			//cvtColor(imgWarp, img, COLOR_GRAY2BGR);
			cvtColor(imgWarp, img_HSV, COLOR_BGR2HSV);

			Scalar lower_yellow(21, 116, 144);//象限标黄色区域HSV
			Scalar upper_yellow(84, 175, 255);

			Scalar lower_dark(0, 0, 0);//象限标黑色区域HSV
			Scalar upper_dark(180, 255, 73);

			//在圆上进行HSV
			inRange(img_HSV, lower_yellow, upper_yellow, mask_yellow);   //实现二值化功能,将两个阈值内的像素值设置为白色,不在阈值区间内的像素值设置为黑色
			inRange(img_HSV, lower_dark, upper_dark, mask_dark);
			bitwise_or(mask_yellow, mask_dark, mask);  //按位或

			vector<vector<Point>>contours_y;   // 是一个向量,每一组point点集就是一个轮廓
			vector<Vec4i>hierarchy_y;
			vector<vector<Point>>contours_d;   // 是一个向量,每一组point点集就是一个轮廓
			vector<Vec4i>hierarchy_d;

			findContours(mask_yellow, contours_y, hierarchy_y, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);   // 连通域查找
			findContours(mask_dark, contours_d, hierarchy_d, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
			drawContours(imgWarp, contours_y, -1, color, 1);
			drawContours(imgWarp, contours_d, -1, color, 1);
			area_y = 0;
			area_d = 0;
			for (int i = 0; i < contours_y.size(); i++)
			{
				area_y = contourArea(contours_y[i]); // 计算轮廓的面积
				area_y += area_y;

			}
			for (int i = 0; i < contours_d.size(); i++)
			{
				area_d = contourArea(contours_d[i]); // 计算轮廓的面积
				area_d += area_d;

			}
			cout << "area_y:" << area_y << "area_d:" << area_d << endl;
			if (area_y > 4 and area_d > 4)
			{
				cv::circle(img_result, Point(circle[0], circle[1]), circle[2], Scalar(0, 0, 255)); // 画圆
				cv::circle(img_result, Point(circle[0], circle[1]), 2, Scalar(0, 0, 255));   // 圆心
				Mypoints.push_back(Point(circle[0],circle[1]));
			}
			
		}
		cout << "符合条件的所有圆:" << Mypoints << endl;
		for (int i = 0; i < Mypoints.size(); i++)
		{
			if (i % 2 == 0)  //偶数,第一次为0
			{
				a = Mypoints[i];
			}
			else if (i % 2 != 0)  //奇数
			{
				b = Mypoints[i];
			}
			center_dis.push_back(pow(pow(b.x - a.x, 2) + pow(b.y - a.y, 2),0.5));
			cout << "距离" << center_dis[0] << endl;
			
		}

		namedWindow("原图",WINDOW_AUTOSIZE);
		imshow("原图", frame);

		//namedWindow("透视图", WINDOW_AUTOSIZE); //提取出圆单独显示
		//imshow("透视图", imgWarp);

		//namedWindow("mask", WINDOW_AUTOSIZE);
		//imshow("mask", mask);

		//namedWindow("HSV", WINDOW_AUTOSIZE); // 在提取出来的圆上进行HSV检测
		//imshow("HSV", img_HSV);

		namedWindow("contours", WINDOW_AUTOSIZE);
		imshow("contours", img_cont);

		namedWindow("结果", WINDOW_AUTOSIZE);
		imshow("结果", img_result);

		if (waitKey(10) == 27)
		{
			break;
		}
	
	}
}

 The running results are shown in the figure.

 There are many improvements to this code, so let’s record them first. I will add more later when I have time to improve it.

Guess you like

Origin blog.csdn.net/weixin_50847463/article/details/132531501