最近用到象限标定位,去网上找资料发现这方面的介绍太少了,就用了传统的方法(形状+颜色)进行检测,后期如果发现更好的方法,再来更新。象限标样式如下图所示
思想是:在每帧图像中进行Hough圆检测,遍历检测到的每一个圆,进行透视变换到一个新图上,在每一个圆中,检测是否同时有黄色和黑色区域,且面积达到一定值,如果有,则是象限标,显示在最终结果图上,并获取圆心,否则不做处理,对最终获取的两个象限表进行距离计算,得出成像画面中的距离值。
一.打开摄像头循环读取帧
程序如下:
#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;
}
}
}
}
二.进行Hough圆检测
#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;
}
}
}
思想:在每帧图像上进行Hough圆检测,会检测出来许多圆,如下图所示,后续如何过滤出需要的圆?通过颜色检测,所以这里需要在每帧图像上遍历所有的圆,在每个圆内进行颜色检测。所以这里用到了透视变换。
三.颜色检测
在进行颜色检测之前,需要提取出黄黑的HSV值。提取代码如下所示。
#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;
}
}
}
黄色提取结果如下图所示
将黄色与黑色的HSV阈值带入代码中。
四.最终代码
#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;
}
}
}
运行结果如图所示。
这个代码改进的部分还有许多,这里先进行一下记录。后续有时间改进后再来进行补充。