OpenCV (学长走路背景)
Eva.Q Lv9

小作业——继续跟踪

这次的背景有一只学长路过

所以不能用动态检测,只能用颜色检测啦!!!

(ps. 动态检测是啥,其实我也没了解很多,我以为大家都在用颜色检测,后来才知道学长学姐上一个小作业是用动态检测的,所以问学长要了动态检测的代码

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
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
#include <iostream>

using namespace cv;
using namespace std;

const int Train = 50;
int main()
{
Ptr<BackgroundSubtractorMOG2> mog = createBackgroundSubtractorMOG2(100, 25, false); // 背景消去
//bgsubtractor->setVarThreshold(20);
Mat foreGround;
Mat backGround;
int trainCounter = 0;
bool dynamicDetect = true;

namedWindow("src", WINDOW_FREERATIO);
namedWindow("foreground", WINDOW_FREERATIO);

VideoCapture cap("Resources/task3.mp4");
if (!cap.isOpened())
{
cout << "Fail to open!"<< endl;
return -1;
}
Mat src;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); // 开操作去噪声
bool stop = false;
while (!stop)
{
cap >> src;
if (src.empty())
break;

if (dynamicDetect)
{
mog->apply(src, foreGround, 0.005);
morphologyEx(foreGround, foreGround, MORPH_OPEN, kernel);
//图像处理过程
medianBlur(foreGround, foreGround, 3);
dilate(foreGround, foreGround, Mat(), Point(-1, -1), 3);
erode(foreGround, foreGround, Mat(), Point(-1, -1), 6);
dilate(foreGround, foreGround, Mat(), Point(-1, -1), 3);
imshow("foreground", foreGround);
if (trainCounter < Train)//训练期间所得结果为不准确结果,不应作为后续
{
Mat findc;
foreGround.copyTo(findc);
vector<vector<Point>> contours;
findContours(findc, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); //寻找轮廓

//targets.clear();
const int maxArea = 200;
size_t s = contours.size();
for (size_t i = 0; i < s; i++)
{
double area = abs(contourArea(contours[i]));
if (area > maxArea)
{
Rect mr = boundingRect(Mat(contours[i]));
rectangle(src, mr, Scalar(0, 0, 255), 2, 8, 0);
//targets.push_back(mr);
}
}
//string text;
char text[50];
sprintf_s(text, "background training -%d- ...", trainCounter);
putText(src, text, Point(50, 50), 3, 1, Scalar(0, 255, 255), 2, 8, false);
//delete[] text;

}
else
{
//detects.clear();
Mat findc;
foreGround.copyTo(findc);
vector<vector<Point>> contours;
cv::findContours(findc, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
const int maxArea = 200;
size_t s = contours.size();
RNG rng;
for (size_t i = 0; i < s; i++)
{
double area = abs(contourArea(contours[i]));
if (area > maxArea)
{
Scalar sca_color = Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
Rect mr = boundingRect(Mat(contours[i]));
rectangle(src, mr, sca_color, 2, 8, 0);
//可以对动态目标进行相应操作
}
}
}
trainCounter++;
}
imshow("src", src);
if (waitKey(30) == 27) //Esc键退出
{
stop = true;
}
}
return 0;
}

另附相关资料

https://blog.csdn.net/m0_37901643/article/details/72841289

%完了动态检测,还是学习一波颜色检测吧 ~ ~

冲冲冲!!!

Code

(根据之前颜色检测的代码来魔改

首先 需要用 colorPicker 检测出需要检测的颜色的 HSV 三个分量的最大最小值。

然后就可以改代码了

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
#include<opencv2/imgcodecs.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<iostream>
#include<opencv2/core.hpp>

using namespace std;
using namespace cv;

Mat img;
vector<vector<int>> newPoints;

vector<vector<int>> myColors{ {0,142,120,179,255,255}, // 橙色(hmin smin vmin hmax smax vmax)
{0,0,0,0,0,0} }; // 绿色(hmin smin vmin hmax smax vmax)

vector<Scalar> myColorValues{ {255,0,255}, // 蓝色
{0,255,0} }; // 绿色

//! 获取轮廓
Point getContours(Mat imgDil) { // imgDil是传入的扩张边缘的图像用来查找轮廓,img是要在其上绘制轮廓的图像
vector<vector<Point>> contours; // 轮廓检测到的轮廓。每个轮廓线存储为一个点的向量

vector<Vec4i> hierarchy; // 包含关于映像拓扑的信息 typedef Vec<int, 4> Vec4i;具有4个整数值

//在二值图像中查找轮廓。该函数利用该算法从二值图像中提取轮廓
findContours(imgDil, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2); // img:要绘制轮廓在什么图片上,contours:要绘制的轮廓,-1定义要绘制的轮廓号(-1表示所有轮廓),Saclar表示轮廓颜色,2表示厚度

vector<vector<Point>> conPoly(contours.size()); // conploy的数量应小于contours
vector<Rect> boundRect(contours.size());

Point myPoint(0, 0);

//过滤器:通过轮廓面积来过滤噪声
for (int i = 0; i < contours.size(); i++) { // 遍历检测到的轮廓
double area = contourArea(contours[i]);

// cout << area << endl;

string objectType;
if (area > 10) { // 轮廓面积>10才绘制
// 计算轮廓周长或凹坑长度。该函数计算了曲线长度和封闭的周长。
double peri = arcLength(contours[i], true); // 计算封闭轮廓周长
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true); // 以指定的精度近似多边形曲线。第二个参数conPloy[i]存储近似的结果,是输出。

boundRect[i] = boundingRect(conPoly[i]); // 计算边界矩形

myPoint.x = boundRect[i].x;
myPoint.y = boundRect[i].y + boundRect[i].height / 2;

if (conPoly[i].size() > 4)
putText(img, "Small Ball", Point(boundRect[i].x, boundRect[i].y - 10), FONT_HERSHEY_PLAIN, 3, Scalar(0, 255, 0), 6);

/*绘制边界矩形*/
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5); // tl():topleft矩形左上角坐标 br():bottom right矩形右下角坐标
drawContours(img, conPoly, i, Scalar(255, 0, 0), 2);
}
}
return myPoint;
}


vector<vector<int>> findColor(Mat img) {
Mat imgHSV;
cvtColor(img, imgHSV, COLOR_BGR2HSV); // 转换图像到HSV空间,在其中查找颜色更加容易

for (int i = 0; i < myColors.size(); i++)
{
Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);
Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);
Mat mask;
inRange(imgHSV, lower, upper, mask); // 定义颜色下限和上限,因为由于照明和不同的阴影,颜色的值将不完全相同,会是一个值的范围
// imshow(to_string(i), mask);
Point myPoint = getContours(mask);
if (myPoint.x != 0 && myPoint.y != 0) { // 没检测到东西的时候就不加入新点
newPoints.push_back({ myPoint.x,myPoint.y,i }); // i为颜色索引
}
}
return newPoints;

}

void drawOnCanvas(vector<vector<int>> newPoints, vector<Scalar> myColorValues) {
for (int i = 1; i < newPoints.size(); i++) {
line(img, Point(newPoints[i - 1][0], newPoints[i - 1][1]), Point(newPoints[i][0], newPoints[i][1]), Scalar(0, 255, 255), 10);
}
}

int main() {
VideoCapture cap("D:/Summer Holiday Practice/opencv learning/Resources/TrackBall2.mp4");
if (!cap.isOpened()) {
cout << "fail to open!" << endl;
return -1;
}
char c;
bool stop = false;

namedWindow("Image", WINDOW_FREERATIO);
while (c = waitKey(1))
{
if (c == 27)
break;
if (c == 'p')
{
stop = !stop;
}
if (!stop)
{
cap >> img;

newPoints = findColor(img);
drawOnCanvas(newPoints, myColorValues);
imshow("Image", img);
}
}
return 0;
}

追踪效果如图:

学了点儿啥呢

主要还是在 colorPicker 的使用上

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
Mat imgHSV, mask;
int hmin = 0, smin = 0, vmin = 0;
int hmax = 179, smax = 255, vmax = 255;

void main() {

namedWindow("Trackbars", (640, 200));
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
createTrackbar("Hue Max", "Trackbars", &hmax, 179);
createTrackbar("Sat Min", "Trackbars", &smin, 255);
createTrackbar("Sat Max", "Trackbars", &smax, 255);
createTrackbar("Val Min", "Trackbars", &vmin, 255);
createTrackbar("Val Max", "Trackbars", &vmax, 255);

while (true) {
string path = "D:/Summer Holiday Practice/opencv learning/Resources/ball2.png";
Mat img = imread(path);

/*// 转换单通道
if (img.channels() == 4) {
cv::cvtColor(img, imgHSV, COLOR_BGRA2GRAY);
}
else if (img.channels() == 3) {
cv::cvtColor(img, imgHSV, COLOR_BGR2GRAY);
}
else if (img.channels() == 2) {
cv::cvtColor(img, imgHSV, COLOR_BGR5652GRAY);
}
else if (img.channels() == 1) {// 单通道的图片直接就不需要处理
imgHSV = img;
}
else { // 负数,说明图有问题 直接返回
return;
}*/

cvtColor(img, imgHSV, COLOR_BGR2HSV);

Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);

inRange(imgHSV, lower, upper, mask);

cout << hmin << ',' << smin << ',' << vmin << ',' << hmax << ',' << smax << ',' << vmax << endl;
namedWindow("Image", WINDOW_NORMAL);
namedWindow("Image mask", WINDOW_NORMAL);
imshow("Image", img);
imshow("Image mask", mask);
waitKey(1);
}
}
Path

一是路径书写上,我总是会遇到无法正常打开图片或者视频的情况,且大部分情况下,都与路径书写有关,也试过把相对路径改成绝对路径。

经过实践后,只能说,答案如下, D:/Summer Holiday Practice/opencv learning/Resources/ball2.png

cvtColor

之前 cvtColor 一直报错,后来去找资料,资料上说 cvtColor 不能处理灰度图,上面代码中注释掉的那一段,是在判断图片的通道数,并进行转换。

虽然我并不是因为这个原因,但这也是值得注意的一个地方吧。

最终结果视频

  • Post title:OpenCV (学长走路背景)
  • Post author:Eva.Q
  • Create time:2021-08-06 09:20:21
  • Post link:https://qyy/2021/08/06/OPENCV/OpenCVProject2/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.