目录
一、目标识别(我的绿色水杯)
1.头文件中各个量的定义
头文件(general.h)的代码:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
double fps;
string FPS;
Mat src, hsv_img, mask_img, element;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
VideoCapture capture(0);
Point2f P[4];
RotatedRect rect1, rect2;
double t1;
double t2;
Point origin;
int font_face = FONT_HERSHEY_COMPLEX;
double font_scale = 2;
int thickness = 2;
其中第三段是图片处理时所用变量,第四段是计算fps时所用的变量,第五段是显示帧频时所用的变量。
2.图片处理
我是先将图片转为HSV格式,再将图片用inRange进行一波筛选,将图片中绿色的部分挑出来变为白色,其他颜色变为黑色。我后来发现直接这样做效果不太好,然后用dilate+erode做了一个闭运算,消除了一些噪点。
代码:
capture >> src;
cvtColor(src, hsv_img, COLOR_BGR2HSV);
inRange(hsv_img, Scalar(35, 40, 40), Scalar(77, 255, 255), mask_img);
element = getStructuringElement(MORPH_RECT, Size(10, 10), Point(-1, -1));
dilate(mask_img, mask_img, element, Point(-1, -1), 3);
erode(mask_img, mask_img, element, Point(-1, -1), 3);
3.水杯轮廓绘制
我是使用findContours函数来找到所有轮廓,然后逐个遍历,找到最大的轮廓,求最小外接矩形。然后逐个比较,找到面积最大的外接矩形。然后将矩形的四点赋值给一个PointArray,用line描出最大外接矩形的轮廓。
代码:
findContours(mask_img, contours, 3, 1, Point(0, 0));
for (int i = 0; i < contours.size(); i++)
{
if (i == 0)
rect1 = minAreaRect(contours[i]);
else
rect2 = minAreaRect(contours[i]);
if (rect1.size.height * rect1.size.width < rect2.size.height * rect2.size.width)
rect1 = rect2;
}
rect1.points(P);
for (int j = 0; j <= 3; j++)
{
line(src, P[j], P[(j + 1) % 4], Scalar(0, 255, 255), 5);
}
二、显示帧频
1.帧频的计算
我用的是在循环开始时求一次时间,结束前求一次时间,用两时间差除以一个转换系数得到帧频
代码:
t1 = (double)getTickCount();
......
t2 = (double)getTickCount();
fps = (t2 - t1) * 1000 / (getTickFrequency());
2.帧频转为2位小数
我用的是将double用to_string转为string,然后遍历,当遍历到小数点时,开始计数,计数大于等于2时结束遍历,把之前遍历到的char写到一个新的string里面去。
具体代码在后面具体展示。
3.帧频的类和调用
代码:
类:
class Fps
{
double _fps;
public:
Fps(double fps)
{
_fps = fps;
};
string to_2f()
{
string fps = to_string( _fps);
int i, j = 0;
string result = "";
bool isfloat = false;
for (i = 0; j < 2; i++)
{
if (isfloat)
j++;
result += fps[i];
if (fps[i] == '.')
isfloat = true;
}
return result;
};
};
调用:
fps = (t2 - t1) * 1000 / (getTickFrequency());
Fps f1(fps);
FPS = f1.to_2f();
origin.x = 0;
origin.y = 400;
putText(src, FPS, origin, font_face, font_scale, Scalar(0, 255, 255), thickness, 8, 0);
三、所有代码
1.general.h
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
double fps;
string FPS;
Mat src, hsv_img, mask_img, element;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
VideoCapture capture(0);
Point2f P[4];
RotatedRect rect1, rect2;
double t1;
double t2;
Point origin;
int font_face = FONT_HERSHEY_COMPLEX;
double font_scale = 2;
int thickness = 2;
2.ConsoleApplication2.cpp
#include <iostream>
#include <opencv2\opencv.hpp>
#include <string>
#include "general.h"
using namespace std;
using namespace cv;
class Fps
{
double _fps;
public:
Fps(double fps)
{
_fps = fps;
};
string to_2f()
{
string fps = to_string( _fps);
int i, j = 0;
string result = "";
bool isfloat = false;
for (i = 0; j < 2; i++)
{
if (isfloat)
j++;
result += fps[i];
if (fps[i] == '.')
isfloat = true;
}
return result;
};
};
int main()
{
while (true)
{
t1 = (double)getTickCount();
capture >> src;
cvtColor(src, hsv_img, COLOR_BGR2HSV);
inRange(hsv_img, Scalar(35, 40, 40), Scalar(77, 255, 255), mask_img);
element = getStructuringElement(MORPH_RECT, Size(10, 10), Point(-1, -1));
dilate(mask_img, mask_img, element, Point(-1, -1), 3);
erode(mask_img, mask_img, element, Point(-1, -1), 3);
findContours(mask_img, contours, 3, 1, Point(0, 0));
for (int i = 0; i < contours.size(); i++)
{
if (i == 0)
rect1 = minAreaRect(contours[i]);
else
rect2 = minAreaRect(contours[i]);
if (rect1.size.height * rect1.size.width < rect2.size.height * rect2.size.width)
rect1 = rect2;
}
rect1.points(P);
for (int j = 0; j <= 3; j++)
{
line(src, P[j], P[(j + 1) % 4], Scalar(0, 255, 255), 5);
}
t2 = (double)getTickCount();
fps = (t2 - t1) * 1000 / (getTickFrequency());
Fps f1(fps);
FPS = f1.to_2f();
origin.x = 0;
origin.y = 400;
putText(src, FPS, origin, font_face, font_scale, Scalar(0, 255, 255), thickness, 8, 0);
cv::imshow("mask", mask_img);
cv::imshow("src", src);
cv::waitKey(1);
}
}
3.效果图
四、总结与评价
显然这个程序的缺点很多,比如对背景的要求苛刻,识别的准确度不高,水杯装水后无法准确识别,必须要手拿杯底以免干扰等等,但是我个人认为总的来说还是比较不错的一个成果,毕竟我接触计算机视觉到现在也就十几天的时间。然后就是如果要识别别的颜色的水杯,要在主程序里面把inRange中的两个Scalar改成你要识别的颜色范围,注意是hsv格式下的!!!