在VS中使用Opencv
每个项目单独导入opencv(在项目属性的包含目录与库目录的)
链接器导入对应版本的lib

Background:
0 = black
255 = white
8 bits = 256 levels

1. 导入图像视频和网络摄像头

常用头文件

1
2
3
4
5
6
7
8
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
// #include <opencv2/imgcodecs.hpp>:这个头文件包含了用于图像编码和解码的函数,例如读取和保存图像文件(如 JPEG、PNG 等)。
// #include <opencv2/highgui.hpp>:该头文件提供了高级图形用户界面功能,比如创建窗口、显示图像、处理鼠标和键盘事件等。
// #include <opencv2/imgproc.hpp>:此头文件包含了大量的图像处理函数,如图像滤波、边缘检测、图像变换等。
// #include <iostream>:这是标准输入输出流的头文件,用于在控制台进行输入输出操作。

导入图像
(注意c++和python的imread格式不同)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

///////////////// Images //////////////////////
void main() {

string path = "Resources/test.png";
Mat img = imread(path);//图像要写成 matrix data type
imshow("Image", img);
waitKey(0);
}

视频播放

1
2
3
4
5
6
7
8
9
10
11
12
13
/////////////////  Video  //////////////////////
void main() {

string path = "Resources/test_video.mp4";
VideoCapture cap(path);
Mat img;
while (true) {
cap.read(img);

imshow("Image", img);
waitKey(20);//图片播放delay的时间,单位时间为ms
}
}

摄像头

1
2
3
4
5
6
7
8
9
10
11
12
/////////////////  Webcam  //////////////////////
void main() {

VideoCapture cap(0);//Id number of camera
Mat img;
while (true) {
cap.read(img);

imshow("Image", img);
waitKey(1);//图片播放delay的时间,单位时间为ms
}
}

2. 图像处理基础功能

vs不能单文件运行,与python不同,当多文件并存,要么是注释,要么是排除(就是文件图标上会显示红叉)
后续会学习轨迹框的使用,通过轨迹框得到最佳检测

包括灰度,模糊,膨胀和腐蚀

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
/////////////////Basic Function/////////////////
void main() {

string path = "Resources/test.png";
Mat img = imread(path);
Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;//c++中的数据都要先定义

cvtColor(img, imgGray, COLOR_BGR2GRAY);//意为 BGR to GRAY
//等同于imgGray = cvtColor(img, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3,3), 3, 0);//guassian blur高斯模糊
Canny(imgBlur, imgCanny, 50,150);
//一般用canny edge detector 前要模糊处理
//不模糊的话会有多余的线条

//通过膨胀和腐蚀处理边缘,二者叠加使用
Mat kernel = getStructuringElement(MORPH_RECT, Size(5,5));//Size用奇数
dilate(imgCanny, imgDil, kernel);//kernel作为膨胀的内核,要用矩阵type
erode(imgDil, imgErode, kernel);

imshow("Image", img);
imshow("ImgGray", imgGray);
imshow("ImgBlur", imgBlur);
imshow("ImgCanny", imgCanny);
imshow("ImgDilation", imgDil);
imshow("ImgErode", imgErode);
waitKey(0);
}

3. 调整大小及裁剪图像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/////////////////Resize and Crop/////////////////
void main() {

string path = "Resources/test.png";
Mat img = imread(path);
Mat imgResize, imgCrop;

cout << img.size() << endl;
resize(img, imgResize, Size(640, 480));//拉伸大小
resize(img, imgResize, Size(), 0.5, 0.5);//按比例缩放

Rect roi(100, 100, 300, 250);//用于在图像或二维空间里描述矩形区域
imgCrop = img(roi);

imshow("Image", img);
imshow("ImgResize", imgResize);
imshow("ImgCrop", imgCrop);

waitKey(0);
}

4. 绘制形状与文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/////////////////Draw Shapes and Text/////////////////
void main() {

Mat img(512, 512, CV_8UC3, Scalar(255, 255, 255));
//CV_8UC3:这是一个用于指定矩阵元素类型的标志。具体含义如下:
//8U:表示每个元素是一个无符号的 8 位整数(范围从 0 到 255)。
//C3:表示矩阵有 3 个通道。在图像处理中,3 个通道通常对应 BGR 颜色空间,分别代表蓝色(Blue)、绿色(Green)和红色(Red)。
//Scalar 是 OpenCV 中用于表示多通道值的类

circle(img, Point(256, 256), 155, Scalar(0, 0, 0),FILLED);//由圆心,半径确定
rectangle(img, Point(256, 256), Point(0, 0), Scalar(255, 52, 255), 2);
line(img, Point(256, 256), Point(0, 0), Scalar(100, 255, 255), 2);

putText(img, "GGGGG", Point(50,50), FONT_HERSHEY_DUPLEX, 2, Scalar(0, 0, 0), 2);
//设置了字体和比例大小

imshow("Image", img);

5.扭曲图像

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
/////////////////Warp Images/////////////////

float w = 250, h = 350;
Mat matrix, imgWarp;

void main() {

string path = "Resources/cards.jpg";
Mat img = imread(path);

Point2f src[4] = { {529,142}, {771,190}, {405,395}, {674,457} };//source points
Point2f dst[4] = { {0.0f,0.0f}, {w,0.0f}, {0.0f,h}, {w,h} };//destination points

matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, imgWarp, matrix, Point(w, h));

for (int i = 0; i < 4;i++)//用for来标记出选择的点
{
circle(img, src[i], 10, Scalar(0, 69, 255), FILLED);
}

imshow("Image", img);
imshow("ImgWarp", imgWarp);
waitKey(0);
}

6.图像色彩检测

通过滑动条,等显示的只剩下目标颜色,此时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
/////////////////Color Detection/////////////////

Mat imgHSV, mask, imgHSV1;
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;

void main() {

string path = "Resources/lambo.png";
Mat img = imread(path);

//hsv space 色相饱和度值
cvtColor(img, imgHSV, COLOR_BGR2HSV);

namedWindow("Trackbars", (640, 200));
createTrackbar("Hue Min", "Trackbars", &hmin, 179);//更改hmin
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) {
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(imgHSV, lower, upper, mask);
//mask 值最终输出,蒙版

imshow("Image", img);
imshow("ImgWarp", imgHSV);
imshow("ImgMask", mask);
waitKey(1);//waitKey(0) 会使程序一直阻塞等待按键事件,滑动条无法实时响应操作。应该使用 waitKey(1) 让程序持续运行并处理滑动条的变化。
}
}

7.图像形状检测

边缘检测出轮廓再来对应
这里的函数,预处理格式要熟悉,注意规范使用

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

using namespace cv;
using namespace std;

/////////////////Shape Detection/////////////////

void getContours(Mat imgDil, Mat img) {

//需要定义轮廓,是矢量
vector<vector<Point>> contours;//向量包含向量包含点
vector<Vec4i> hierarchy;
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//存储对象信息
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);

for (int i = 0;i < contours.size();i++)
{

int area = contourArea(contours[i]);
vector<vector<Point>> conPloy(contours.size());//角点,也应该写出向量格式
vector<Rect> boundRect(contours.size());//括号里指的是变量数量
string objectType;

if (area > 1000)
{
float peri = arcLength(contours[i], true);//使用轮廓找到弧长
approxPolyDP(contours[i], conPloy[i], 0.02 * peri, true);//Define a new array, 寻找角点再判断是几边形
//drawContours(img, conPloy, i, Scalar(255, 0, 255), 2);

boundRect[i] = boundingRect(conPloy[i]);

//tl = top_left;br = bottom_right

//为了找到哪个是哪个图形——显示文字
int objCor = (int)conPloy[i].size();// create integer objects corner

if (objCor == 3) { objectType = "Tri"; }
if (objCor == 4) {
//再来细分出矩形中的正方形。长宽就由轮廓width与height来表示
//要把vector强制类型转换成float
float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
if(aspRatio> 0.95&& aspRatio <1.05)objectType = "Square";//要考虑到误差
else objectType = "Rect"; }
if (objCor > 4) { objectType = "Circle"; }

putText(img, objectType, { boundRect[i].x, boundRect[i].y - 5 }, FONT_HERSHEY_DUPLEX, 0.75, Scalar(0, 0, 0));
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(255, 30, 30), 5);
}
}

}

void main() {

string path = "Resources/shapes.png";
Mat img = imread(path);
Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;

//Preprocessing
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);//开始设置(55,155)使有图形识别不出来
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
dilate(imgCanny, imgDil, kernel);//加粗更好

getContours(imgDil, img);

imshow("Image", img);
//imshow("ImgGray", imgGray);
//imshow("ImgBlur", imgBlur);
//imshow("ImgCanny", imgCanny);
//imshow("ImgDil", imgDil);
waitKey(0);

}

8.人脸识别

这里需要用到大模型

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
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>//物品识别要另外加入的头文件
#include <iostream>

using namespace cv;
using namespace std;
/////////////////Face Detection/////////////////
void main() {

string path = "Resources/test.png";
Mat img = imread(path);
Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;

CascadeClassifier faceCascade;
faceCascade.load("Resources/haarcascade_frontalface_default.xml");
//这个xml文件是训练好的大模型
//use this xml file to load our cascade and use it to find the faces

//if (faceCascade.empty()) { cout << "XML file not loaded" << endl; }//检测是否load成功

//create bounding boxes
vector <Rect> faces;
faceCascade.detectMultiScale(img, faces, 1.1, 10);

for (int i = 0; i < faces.size(); i++)
{
rectangle(img, faces[i].tl(), faces[i].br(), Scalar(0, 0, 0), 5);
//和上一个,用矩形圈定图形一样,矩形的坐标从向量中获得
}

//Preprocessing
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);//开始设置(55,155)使有图形识别不出来
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
dilate(imgCanny, imgDil, kernel);//加粗更好


imshow("Image", img);
//imshow("ImgGray", imgGray);
//imshow("ImgBlur", imgBlur);
//imshow("ImgCanny", imgCanny);
//imshow("ImgDil", imgDil);
waitKey(0);

}