服务器之家

服务器之家 > 正文

OpenCV 圆与矩形识别的方法

时间:2021-07-31 15:07     来源/作者:走在冷风中

最近一个项目用到了图像识别,之前从未接触过OpenCV,经过各种找教程,终于是搞懂了一些。

整个具体流程大概是获取图像-->图像二值化,灰度图(cvtColor)-->图像降噪(GaussianBlur)->轮廓识别(cvFindContours)-->形状判断。

大多数教程很专业,各种参数分析看不懂,经过各种搜索终于是搞懂了。

识别圆

在识别圆方面,OpenCV有内置的方法:霍夫圆变化:

?
1
HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);

参数分析:

edges:灰度图像

circles: std::vector<Vec3f> circles;数组,用来存储圆的坐标信息

CV_HOUGH_GRADIENT:Hough 变换方式,目前只支持CV_HOUGH_GRADIENT, which is basically 21HT, described in [Yuen03].默认用这个

1.5:累加器图像的分辨率,1的时候是与获取到的图像相同,1.5就是1.5倍

10:圆与圆的最小距离,两个圆心距离如果在范围内则被认定为1个圆

200:100-200两个参数选就够了

100:默认100,数值越低识别圆越不精确(圆的数量识别变多可能有个弧线就被识别是圆)

最后两个参数分别是识别 圆的最小,最大的面积。

矩形识别

矩形识别并没有内置方法,需要自己手写。

最主要的方法是二值化。通过二值化来调节识别的强度。

?
1
cvThreshold(tgray, gray, 75, 250, CV_THRESH_BINARY);

参数分析:

src:原始数组 (单通道 , 8-bit of 32-bit 浮点数)。

dst:输出数组,必须与 src 的类型一致,或者为 8-bit。

threshold:阈值

max_value:使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值。

threshold_type:阈值类型

threshold_type=CV_THRESH_BINARY:如果 src(x,y)>threshold ,dst(x,y) = max_value; 否则,dst(x,y)=0;

threshold_type=CV_THRESH_BINARY_INV:如果 src(x,y)>threshold,dst(x,y) = 0; 否则,dst(x,y) = max_value.

threshold_type=CV_THRESH_TRUNC:如果 src(x,y)>threshold,dst(x,y) = max_value; 否则dst(x,y) = src(x,y).

threshold_type=CV_THRESH_TOZERO:如果src(x,y)>threshold,dst(x,y) = src(x,y) ; 否则 dst(x,y) = 0。

threshold_type=CV_THRESH_TOZERO_INV:如果 src(x,y)>threshold,dst(x,y) = 0 ; 否则dst(x,y) = src(x,y).

效果图如下:

在矩形识别里面的二值化图:

OpenCV 圆与矩形识别的方法

圆识别:

OpenCV 圆与矩形识别的方法

源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <WINSOCK2.H>
#include<iostream>
#include<thread>
#include <winsock2.h>
#include <stdio.h>
#include<string>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
#include<vector>
 
using namespace cv;
 
 
   //////////////////////////////////////////////////////////////////
   //函数功能:用向量来做COSα=两向量之积/两向量模的乘积求两条线段夹角
   //输入:  线段3个点坐标pt1,pt2,pt0,最后一个参数为公共点
   //输出:  线段夹角,单位为角度
   //////////////////////////////////////////////////////////////////
double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0)
{
 double dx1 = pt1->x - pt0->x;
 double dy1 = pt1->y - pt0->y;
 double dx2 = pt2->x - pt0->x;
 double dy2 = pt2->y - pt0->y;
 double angle_line = (dx1*dx2 + dy1 * dy2) / sqrt((dx1*dx1 + dy1 * dy1)*(dx2*dx2 + dy2 * dy2) + 1e-10);//余弦值
 return acos(angle_line) * 180 / 3.141592653;
}
//////////////////////////////////////////////////////////////////
//函数功能:采用多边形检测,通过约束条件寻找矩形
//输入:  img 原图像
//     storage 存储
//     minarea,maxarea 检测矩形的最小/最大面积
//     minangle,maxangle 检测矩形边夹角范围,单位为角度
//输出:  矩形序列
//////////////////////////////////////////////////////////////////
CvSeq* findSquares4(IplImage* img, CvMemStorage* storage, int minarea, int maxarea, int minangle, int maxangle, int(&temp)[30])
{
 CvSeq* contours;//边缘
 int N = 6; //阈值分级
 CvSize sz = cvSize(img->width & -2, img->height & -2);
 IplImage* timg = cvCloneImage(img);//拷贝一次img
 IplImage* gray = cvCreateImage(sz, 8, 1); //img灰度图
 IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); //金字塔滤波3通道图像中间变量
 IplImage* tgray = cvCreateImage(sz, 8, 1); ;
 CvSeq* result;
 double s, t;
 int sk = 0;
 CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage);
 
 cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height));
 //金字塔滤波
 cvPyrDown(timg, pyr, 7);
 cvPyrUp(pyr, timg, 7);
 //在3个通道中寻找矩形
 for (int c = 0; c < 3; c++) //对3个通道分别进行处理
 {
 cvSetImageCOI(timg, c + 1);
 cvCopy(timg, tgray, 0); //依次将BGR通道送入tgray    
 for (int l = 0; l < N; l++)
 {
  //不同阈值下二值化
  cvThreshold(tgray, gray, 75, 250, CV_THRESH_BINARY);
  cvShowImage("111", gray);
  cvFindContours(gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
  while (contours)
  { //多边形逼近      
  result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
 
  //如果是凸四边形并且面积在范围内
  if (result->total == 4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > minarea && fabs(cvContourArea(result, CV_WHOLE_SEQ)) < maxarea && cvCheckContourConvexity(result))
  {
 
   s = 0;
   //判断每一条边
   for (int i = 0; i < 5; i++)
   {
   if (i >= 2)
   //角度     
    t = fabs(angle((CvPoint*)cvGetSeqElem(result, i), (CvPoint*)cvGetSeqElem(result, i - 2), (CvPoint*)cvGetSeqElem(result, i - 1)));
    s = s > t ? s : t;
   }
   }
   //这里的S为直角判定条件 单位为角度
   if (s > minangle && s < maxangle)
   {
   for (int i = 0; i < 4; i++)
    cvSeqPush(squares, (CvPoint*)cvGetSeqElem(result, i));
   CvRect rect = cvBoundingRect(contours, 1);    // 获取矩形边界框
   CvPoint p1;
   p1 = cvPoint(rect.x + rect.width / 2, rect.y + rect.height / 2);  //矩形中心坐标
   std::cout << "X:" << p1.x << "Y:" << p1.y << std::endl;
   }
  }
  contours = contours->h_next;
  }
 }
 std::cout << "圆的数量是"<<sk << std::endl;
 temp[26] = sk;
 
 
 sk = 0;
 }
 cvReleaseImage(&gray);
 cvReleaseImage(&pyr);
 cvReleaseImage(&tgray);
 cvReleaseImage(&timg);
 
 return squares;
}
//////////////////////////////////////////////////////////////////
//函数功能:画出所有矩形
//输入:  img 原图像
//     squares 矩形序列
//     wndname 窗口名称
//输出:  图像中标记矩形
//////////////////////////////////////////////////////////////////
void drawSquares(IplImage* img, CvSeq* squares, const char* wndname)
{
 CvSeqReader reader;
 IplImage* cpy = cvCloneImage(img);
 CvPoint pt[4];
 int i;
 cvStartReadSeq(squares, &reader, 0);
 for (i = 0; i < squares->total; i += 4)
 {
 CvPoint* rect = pt;
 int count = 4;
 memcpy(pt, reader.ptr, squares->elem_size);
 CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
 memcpy(pt + 1, reader.ptr, squares->elem_size);
 CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
 memcpy(pt + 2, reader.ptr, squares->elem_size);
 CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
 memcpy(pt + 3, reader.ptr, squares->elem_size);
 CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
 //cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
 cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(rand() & 255, rand() & 255, rand() & 255), 1, CV_AA, 0);//彩色绘制
 }
 cvShowImage("22", cpy);
 cvReleaseImage(&cpy);
}
 
void SendMessageOne()
{
 //开起摄像头
 VideoCapture capture;
 capture.open(0);
 Mat edges; //定义转化的灰度图
 if (!capture.isOpened())
 namedWindow("【效果图】", CV_WINDOW_NORMAL);
 const char* winn = "1111";
 if (!capture.isOpened())
 //namedWindow(winn, CV_WINDOW_NORMAL);
 CvMemStorage* storage = 0;
 CvMemStorage* storage = 0;
 storage = cvCreateMemStorage(0);
 while (1)
 {
 int Y=0, J=0;
 Mat frame;
 capture >> frame;
 IplImage img0 = frame;
 //drawSquares(&img0, findSquares4(&img0, storage, 100, 2000, 80, 100, a), winn);
 //cvClearMemStorage(storage); //清空存储
 Mat E = frame(Range(1, 320), Range(1, 240));
 cvtColor(frame, edges, CV_BGR2GRAY);
 //高斯滤波
 GaussianBlur(edges, edges, Size(7, 7), 2, 2);
 std::vector<Vec3f> circles;//存储每个圆的位置信息
      //霍夫圆
 HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 10, 100, 100, 0, 50);
 for (size_t i = 0; i < circles.size(); i++)
 {
  Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
  int radius = cvRound(circles[i][2]);
  //std::cout << "圆的X是" << circles[i][0] << "圆的Y是" << circles[i][1] << std:: endl;
  //绘制圆轮廓
  circle(frame, center, radius, Scalar(155, 50, 255), 3, 8, 0);
  int R = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[2];//R
  int G = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[1];//G
  int B = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[0];//B
  int num = R + G + B;
  std::cout << "圆心颜色是" << num << std::endl;
        }
 
 imshow("【效果图】", frame);
 waitKey(30);
 }
}
 
int main()
{
 std::thread *a = new std::thread(SendMessageOne);
 a->join();
 
 return 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/qq_35957011/article/details/79109326

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021德云社封箱演出完整版 2021年德云社封箱演出在线看
2021德云社封箱演出完整版 2021年德云社封箱演出在线看 2021-03-15
返回顶部