源码已经上传[https://gitee.com/facebaby_L/instrument_recognition.git]https://gitee.com/facebaby_L/instrument_recognition.git)
//边缘检测
void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )
InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。
OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和类型。
double类型的threshold1,第一个滞后性阈值【低阈值】。值越大,找到的边缘越少
double类型的threshold2,第二个滞后性阈值【高阈值】。
int类型的apertureSize,表示应用Sobel算子的孔径大小,其有默认值3。
bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false。
低于阈值1的像素点会被认为不是边缘;
高于阈值2的像素点会被认为是边缘;
在阈值1和阈值2之间的像素点,若与第2步得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘。
//霍夫圆检测
void HoughCircles( CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1=100, double param2=100, int min_radius=0, int max_radius=0 );
//霍夫直线检测
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
//找到两个平面之间的转换矩阵,返回值为矩阵。
Mat findHomography(InputArray srcPoints,InputArray dstPoints,int method = 0)
//透视变换
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR)
如果知道输出图像的四点坐标,也可以调用GetPerspectiveTransform
//由四对点计算透射变换
perspectiveTransform(vector obj_corners1(4), vector scene_corners1(4), InputArray M)
因为不论是霍夫圆还是霍夫直线检测,都要先进行边缘提取,那么就涉及到一个参数选择问题,为了更好的选择参数,我们利用如下的参数选取代码,来确定得到最佳边缘分割的参数。
#include
#include
#include
#include
using namespace std;
using namespace cv;
// Define the Mat parameters
Mat g_srcImage, g_dstImage, g_midImage;
// Define a vector to collect all detected lines
vector<Vec4i> g_lines;
int g_nthreshold = 100;
static void showImage1(int, void*);
static void showImage2(int, void*);
int thred1 = 23;
int thred2 = 55;
// 23 55
int main()
{
// Load origin image
g_srcImage = imread("2.jpg");
// Show origin image
namedWindow("Dst Image", 1);
imshow("Origin Image", g_srcImage);
// Create a Tracebar
createTrackbar("threshold1", "Dst Image", &thred1, 500, showImage1);
createTrackbar("threshold2", "Dst Image", &thred2, 500, showImage2);
// Canny Detect and Gray am image
Canny(g_srcImage, g_midImage, thred1, thred2, 3);
cvtColor(g_midImage, g_dstImage, CV_GRAY2BGR);
showImage1(thred1, 0);
showImage2(thred2, 0);
// Show the dst image
imshow("Dst Image", g_dstImage);
waitKey(0);
return 0;
}
static void showImage1(int thred1, void*) {
// Canny Detection
Canny(g_srcImage, g_midImage, thred1, thred2, 3);
imshow("Dst Image", g_midImage);
}
static void showImage2(int thred2, void*) {
// Canny Detection
Canny(g_srcImage, g_midImage, thred1, thred2, 3);
imshow("Dst Image", g_midImage);
}
首先上不加摄像头,直接识别模板的代码。
#include
#include
#include
#include
#include "math.h"
using namespace std;
using namespace cv;
int main()
{
Mat orgImage,rayImage,canImage;
orgImage = imread("1.jpg");
if (orgImage.empty())
{
cout << "could not find Image" << endl;
return -1;
}
imshow("【原始图】", orgImage);
cvtColor(orgImage, rayImage, CV_BGR2GRAY);//转化边缘检测后的图为灰度图
GaussianBlur(rayImage, rayImage, Size(9, 9), 2, 2);
vector<Vec3f> circles;
// 霍夫圆
HoughCircles(rayImage, circles, CV_HOUGH_GRADIENT, 1, rayImage.rows, 100, 200, 0, 0);
//霍夫直线 canny边缘提取
Canny(orgImage, canImage, 250, 200, 3);
vector<Vec4f> lines;
//'1'生成极坐标时候的像素扫描步长,'CV_PI/180'生成极坐标时候的角度步长,'20'最小直线长度,'5'最大间隔(能构成一条直线)
HoughLinesP(canImage, lines, 1, CV_PI / 180, 100, 20, 5);
Point center(cvRound(circles[0][0]), cvRound(circles[0][1]));
Point destination;
int radius = cvRound(circles[0][2]);
float length = 0.0;
float max_length = 0.0;
// 找出所有直线中最长的一条
for (size_t i = 0; i < lines.size(); i++)
{
Vec4f plines = lines[i]; // 一个plines里边是四个点一条直线
if (plines[0] > center.x - radius && plines[0] < center.x + radius &&
plines[1] > center.y - radius && plines[1] < center.y + radius &&
plines[2] > center.x - radius && plines[2] < center.x + radius &&
plines[3] > center.y - radius && plines[3] < center.y + radius)
{
// line(orgImage, Point(plines[0],plines[1]), Point(plines[2], plines[3]), color, 1, LINE_AA);
length = sqrt((plines[2] - plines[0])*(plines[2] - plines[0]) + (plines[3] - plines[1])*(plines[3] - plines[1]));
if (length > max_length)
{
max_length = length;
if ((plines[0]-center.x)*(plines[0] - center.x)+ (plines[1] - center.y)*(plines[1] - center.y)
>(plines[2] - center.x)*(plines[2] - center.x) + (plines[3] - center.y)*(plines[3] - center.y))
{
destination.x = plines[0];
destination.y = plines[1];
}
else
{
destination.x = plines[2];
destination.y = plines[3];
}
}
}
}
line(orgImage, center, destination, Scalar(0, 0, 255), 3, LINE_AA);
//绘制圆心
circle(orgImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
//绘制圆轮廓
circle(orgImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);
imshow("【效果图】", orgImage);
waitKey(0);
return 0;
}
利用模板匹配进行摄像头识别代码如下:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
double pi = 3.1415926;
double dtheta = 100 / pi;// 一弧度的分度值
double return_degree(Point starting_Point, Point ending_Point, double dtheta);
int main()
{
VideoCapture capture(0);
//namedWindow("识别结果", WINDOW_NORMAL);
Mat srcImage1 = imread("1.jpg"), resVideo, procVideo;
std::vector<cv::KeyPoint> keypoints_object1, keypoints_scene;//vector模板类,存放任意类型的动态数组
cv::Mat descriptors_object1, descriptors_scene;
while (true)
{
capture >> resVideo;
imshow("【原始图】", resVideo);
cv::Ptr<cv::Feature2D>surf_f2d = cv::xfeatures2d::SURF::create(500);//选择SURF特征
surf_f2d->detectAndCompute(srcImage1, cv::Mat(), keypoints_object1, descriptors_object1);//检测关键点并计算描述符
surf_f2d->detectAndCompute(resVideo, cv::Mat(), keypoints_scene, descriptors_scene);
cv::FlannBasedMatcher matcher;//基于Flann的描述符匹配器
std::vector<cv::DMatch> matches1;
matcher.match(descriptors_object1, descriptors_scene, matches1);//从查询集中查找每个描述符的最佳匹配
double max_dist1 = 0, min_dist1 = 100;//最小距离和最大距离
for (int i = 0; i < descriptors_object1.rows; i++)//计算出关键点之间距离的最大值和最小值
{
double dist = matches1[i].distance;
if (dist < min_dist1) min_dist1 = dist;
if (dist > max_dist1) max_dist1 = dist;
}
std::vector<cv::DMatch> good_matches1, good_matches2, nomatches;//存下匹配距离小于3*min_dist的点对
for (int i = 0; i < descriptors_object1.rows; i++)
{
if (matches1[i].distance < 2 * min_dist1)
{
good_matches1.push_back(matches1[i]);
}
}
std::vector<cv::Point2f> obj1, obj2, scene;
for (unsigned int i = 0; i < good_matches1.size(); i++)//从匹配成功的匹配对中获取关键点
{
obj1.push_back(keypoints_object1[good_matches1[i].queryIdx].pt);
scene.push_back(keypoints_scene[good_matches1[i].trainIdx].pt);
}
if (true/*good_matches1.size()>50*/) {
//std::vector obj_corners1(4), scene_corners1(4);//从待测图片中获取角点
//obj_corners1[0] = cvPoint(0, 0);
//obj_corners1[1] = cvPoint(srcImage1.cols, 0);
//obj_corners1[2] = cvPoint(srcImage1.cols, srcImage1.rows);
//obj_corners1[3] = cvPoint(0, srcImage1.rows);
//Mat Homography1 = findHomography(obj1, scene, RANSAC);//计算透视变换
//perspectiveTransform(obj_corners1, scene_corners1, Homography1);//进行透视变换
Mat Homography1 = findHomography(scene, obj1, RANSAC);//计算透视变换
warpPerspective(resVideo, resVideo, Homography1, resVideo.size(), INTER_LINEAR);//待测图片线性插值扭正
imshow("【扭正图】", resVideo); // drawMatches(srcImage1, keypoints_object1, resVideo, keypoints_scene,//绘制出匹配到的关键点
// good_matches1, resVideo, cv::Scalar::all(-1), cv::Scalar::all(-1),
// std::vector(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
//line(resVideo, scene_corners1[0] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[1] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
//line(resVideo, scene_corners1[1] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[2] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
//line(resVideo, scene_corners1[2] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[3] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
//line(resVideo, scene_corners1[3] + cv::Point2f(static_cast(srcImage1.cols), 0), scene_corners1[0] + cv::Point2f(static_cast(srcImage1.cols), 0), cv::Scalar(255, 0, 123), 4);
cvtColor(resVideo, procVideo, COLOR_BGR2GRAY);
GaussianBlur(procVideo, procVideo, Size(9, 9), 2, 2);
medianBlur(procVideo, procVideo, 7);
vector<Vec3f> circles;
HoughCircles(procVideo, circles, CV_HOUGH_GRADIENT, 1, procVideo.rows/10, 10, 100, 0, 0);
if (!circles.empty())
{
Canny(procVideo, procVideo, 50, 80, 3); // Canny边缘提取,test测试最佳系数
vector<Vec4f>lines;
//imshow("边缘检测",procVideo);
HoughLinesP(procVideo, lines, 1, CV_PI / 180, 50, 30, 5); // 霍夫直线
Point center(cvRound(circles[0][0]), cvRound(circles[0][1]));
Point destination;
double radius = cvRound(circles[0][2]);
double length = 0.0;
double max_length = 0.0;
//指针识别
for (size_t i = 0; i < lines.size(); i++)
{
Vec4f plines = lines[i]; // 一个plines里边是四个点一条直线
if (plines[0] > center.x - radius && plines[0] < center.x + radius &&
plines[1] > center.y - radius && plines[1] < center.y + radius &&
plines[2] > center.x - radius && plines[2] < center.x + radius &&
plines[3] > center.y - radius && plines[3] < center.y + radius)
{
// line(orgImage, Point(plines[0],plines[1]), Point(plines[2], plines[3]), color, 1, LINE_AA);
length = sqrt((plines[2] - plines[0])*(plines[2] - plines[0]) + (plines[3] - plines[1])*(plines[3] - plines[1]));
if (length > max_length)
{
max_length = length;
if ((plines[0] - center.x)*(plines[0] - center.x) + (plines[1] - center.y)*(plines[1] - center.y)
> (plines[2] - center.x)*(plines[2] - center.x) + (plines[3] - center.y)*(plines[3] - center.y))
{
destination.x = plines[0];
destination.y = plines[1];
}
else
{
destination.x = plines[2];
destination.y = plines[3];
}
}
}
}
// 求夹角
double degree = return_degree(center, destination, dtheta);
string s = to_string(degree);
// 绘制指针
line(resVideo, center, destination, Scalar(0, 0, 255), 3, LINE_AA);
// 绘制圆心
circle(resVideo, center, 3, Scalar(0, 255, 0), -1, 8, 0);
// 绘制圆轮廓
circle(resVideo, center, radius, Scalar(155, 50, 255), 3, 8, 0);
/*line(resVideo, scene_corners1[0], scene_corners1[1], cv::Scalar(255, 0, 123), 4);
line(resVideo, scene_corners1[1], scene_corners1[2], cv::Scalar(255, 0, 123), 4);
line(resVideo, scene_corners1[2], scene_corners1[3], cv::Scalar(255, 0, 123), 4);
line(resVideo, scene_corners1[3], scene_corners1[0], cv::Scalar(255, 0, 123), 4);*/
// 显示数值
putText(resVideo, s, Point(resVideo.rows / 3, resVideo.cols / 2), FONT_HERSHEY_PLAIN, 2, (0, 255, 255), 1, 8);
imshow("【效果图】", resVideo);
}
waitKey(1);
}
}
return 0;
}
double return_degree(Point starting_Point, Point ending_Point, double dtheta)
{
double k = abs((double)(ending_Point.y - starting_Point.y) / (double)(ending_Point.x - starting_Point.x));
double theta = atan(k);
if (starting_Point.x > ending_Point.x && starting_Point.y > ending_Point.y)
{
return (10 + theta*dtheta);
}
else if (starting_Point.x < ending_Point.x && starting_Point.y < ending_Point.y)
{
return (10 + (pi - theta)*dtheta);
}
else if (starting_Point.x < ending_Point.x && starting_Point.y < ending_Point.y)
{
return (10 + (pi + theta)*dtheta);
}
else if (starting_Point.x > ending_Point.x && starting_Point.y < ending_Point.y)
{
return (10 - theta*dtheta);
}
}