小果祈祷中...

培训1——OpenCV基础学习笔记

本文是基于本人在电子科技大学ROBOCON 比赛LImiti战队期间学习笔记

环境:

系统:ubuntu 20.04LTS

电脑配置:intel 13900K+nvidia GPU 3080 10g +32g RAM +2T SSD

依赖:nvidia驱动 +cuda11.8+cudnn 8.6.5

0. 安装OpenCV

(详细安装过程csdn上搜索即可)

我安装的版本是opencv 4.2 /4.7 /4.8 c++

可以都编译好,需要哪个版本的时候

1
sudo make install 

就行。

输入

1
sudo apt-get install libopencv-dev

或者 使用源码安装

如果在终端运行的操作

1
2
3
4
5
mkdir build
cd build
cmake ..
make
./xxxx(the excutable file that you create)

内嵌到vscode 下载插件:c/c++ cmake tools c/c++ compile run



1. 任务1——Mat

在电脑中随便选择一张图片,使用imread读取该图片,并使用imshow可视化该图片

  • 要求:掌握OpenCV基础Mat类型,使用cmake编译并运行


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <opencv2/opencv.hpp>//包含库

    int main() {
    // 读取图片
    cv::Mat image = cv::imread("/home/zy/pictures/12.jpg");

    // 检查是否成功读取图片
    if (image.empty()) {//empty返回值为1,执行以下程序
    std::cerr << "无法读取图片" << std::endl;
    return -1;//error
    }

    // 创建一个窗口并显示图片
    cv::imshow("Image", image);

    // 等待用户按下任意键后关闭窗口
    cv::waitKey(0);

    return 0;
    }


OpenCV(Open Source Computer Vision Library)开源计算机视觉库中的 cv::Mat 是一个非常重要的类,它用于表示图像和图像处理中的矩阵数据。这是OpenCV中的核心数据结构之一,用于处理图像和图像数据。以下是 cv::Mat 的一些基本解释:

1.图像和矩阵的通用容器:cv::Mat 是OpenCV中的通用容器,用于存储图像、矩阵和多维数组等数据。它可以存储单通道和多通道的图像,以及任意维度的数值矩阵。

2.数据表示:cv::Mat 表示的数据是多维的,通常是2D(图像)或3D(彩色图像)的。每个元素可以是整数、浮点数、复数等数据类型,具体取决于您的需求。

3.图像处理:cv::Mat 提供了许多函数和方法,用于在图像处理中进行各种操作,例如图像加载、保存、剪裁、调整大小、过滤、转换、绘制等。

4.通道和颜色空间:对于彩色图像,cv::Mat 可以存储多个通道,每个通道代表不同的颜色通道(如红、绿、蓝)。您可以轻松地在不同颜色空间之间转换,例如从BGR到HSV。

5.访问像素值:您可以使用 at 或 ptr 方法来访问 cv::Mat 中像素的值,这让您可以读取和修改图像中的像素数据。

6.内存管理:cv::Mat 采用智能指针管理内存,因此在大多数情况下,您无需手动释放内存。当 cv::Mat 对象超出作用域时,内存将自动释放。

7.图像大小和类型:cv::Mat 包含有关图像大小和数据类型的信息,这使得在处理图像时非常方便,您可以轻松地获取图像的宽度、高度、通道数和数据类型。

8.灰度图像和彩色图像:cv::Mat 可以用于表示灰度图像(单通道图像)和彩色图像(多通道图像)。

例如访问其像素值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <opencv2/opencv.hpp>

int main() {
// 创建一个3x3的灰度图像
cv::Mat grayImage(3, 3, CV_8UC1); // CV_8UC1表示8位无符号单通道图像

// 设置像素值
grayImage.at<uchar>(0, 0) = 100;
grayImage.at<uchar>(1, 1) = 200;
grayImage.at<uchar>(2, 2) = 50;

// 访问像素值
uchar pixelValue = grayImage.at<uchar>(1, 1);
std::cout << "Pixel Value at (1,1): " << static_cast<int>(pixelValue) << std::endl;

return 0;
}



2.任务2——摄像头和颜色

使用VideoCapture打开一个摄像头

可以是电脑自带摄像头,也可以是usb外接摄像头(如果都没有,请参考IP摄像头调用手机摄像头.md ),或者使用队里的大的相机。

实时捕获摄像头图像数据,并将图片色彩空间转换到HSV色彩空间,实时提取出摄像头图像数据中的红色、蓝色、白色

  • 要求:学会调用摄像头捕获图像,并了解RGB、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
    #include <opencv2/opencv.hpp>

    int main() {
    // 打开摄像头
    cv::VideoCapture cap("http://192.168.50.88:8081");//手机ip
    // cd::VideoCapture cap(0);//电脑自带摄像头

    // 检查摄像头是否成功打开
    if (!cap.isOpened()) {
    std::cerr << "无法打开摄像头" << std::endl;
    return -1;//error
    }

    cv::Mat frame;
    while (true) {
    // 读取一帧图像
    cap >> frame;

    cv::Mat result1;
    cv::Mat result2;
    cv::Mat result3;//初始化
    // 调整图像尺寸为更小的尺寸
    cv::Size smallSize(640, 480); // 设置目标尺寸
    cv::resize(frame, frame, smallSize);

    // 转换色彩空间为HSV
    cv::Mat hsv;
    cv::cvtColor(frame, hsv, cv::COLOR_BGR2HSV);

    // 定义红色范围
    cv::Scalar lower_red(150, 100, 100);
    cv::Scalar upper_red(180, 255, 255);

    // 定义蓝色范围
    cv::Scalar lower_blue(90, 100, 100);
    cv::Scalar upper_blue(150, 255, 255);

    // 定义白色范围
    cv::Scalar lower_white(0, 0, 200);
    cv::Scalar upper_white(255, 30, 255);//需要根据色盘来确定,
    //不同地方的取值范围不同

    // 提取红色、蓝色和白色
    cv::Mat mask_red, mask_blue, mask_white;
    cv::inRange(hsv, lower_red, upper_red, mask_red);
    cv::inRange(hsv, lower_blue, upper_blue, mask_blue);
    cv::inRange(hsv, lower_white, upper_white, mask_white);


    // 调整颜色提取结果的尺寸
    cv::resize(mask_red, mask_red, smallSize);
    cv::resize(mask_blue, mask_blue, smallSize);
    cv::resize(mask_white, mask_white, smallSize);

    cv::bitwise_and(frame, frame, result1, mask_red);
    cv::bitwise_and(frame, frame, result2, mask_blue);
    cv::bitwise_and(frame, frame, result3, mask_white);
    ////将结果加上颜色

    // 显示原始图像和颜色提取结果
    cv::imshow("Camera", frame);
    cv::imshow("Red", result1);
    cv::imshow("Blue", result2);
    cv::imshow("White", result3);


    // 等待用户按下ESC键后退出循环
    if (cv::waitKey(1) == 27) {
    break;
    }
    }

    // 关闭摄像头
    cap.release();
    cv::destroyAllWindows();

    return 0;
    }

在其中,读取每一帧的图像:

1
2
cv::Mat frame;
cap >> frame; // 读取一帧图像

转换色彩空间:

cv::cvtColor

函数用于将图像从一种色彩空间转换为另一种色彩空间。

1
cv::cvtColor(src, dst, code);

src:输入图像,要将其从一个色彩空间转换为另一个色彩空间。
dst:输出图像,转换后的图像将存储在这里。
code:色彩空间转换代码,指定要执行的具体转换。例如,cv::COLOR_BGR2GRAY 表示将BGR色彩空间转换为灰度色彩空间。

以下是一些常见的 code 值和它们的含义:

cv::COLOR_BGR2GRAY:将BGR色彩空间转换为灰度色彩空间。
cv::COLOR_BGR2HSV:将BGR色彩空间转换为HSV色彩空间。
cv::COLOR_BGR2Lab:将BGR色彩空间转换为Lab色彩空间。
cv::COLOR_HSV2BGR:将HSV色彩空间转换回BGR色彩空间。
cv::COLOR_GRAY2BGR:将灰度色彩空间转换为BGR色彩空间,通常用于将灰度图像转换为彩色图像。

1
2
3
4
cv::Mat inputImage = cv::imread("input.jpg");
cv::Mat grayImage;

cv::cvtColor(inputImage, grayImage, cv::COLOR_BGR2GRAY);

cv::inRange

函数用于在图像中提取指定范围内的像素值,并将范围内的像素设置为白色,范围外的像素设置为黑色。这个函数通常用于颜色分割和掩码操作,以便从图像中提取特定颜色的对象。

1
2
3
4
5
6
7
8
cv::inRange(src, lowerBound, upperBound, dst);
/*
src:输入图像,从该图像中提取像素值。
lowerBound:指定范围的下界。这是一个包含了颜色的向量,表示颜色的最小值。
upperBound:指定范围的上界。这是一个包含了颜色的向量,表示颜色的最大值。
dst:输出图像,结果图像,范围内的像素为白色(255),
范围外的像素为黑色(0)。
*/

示例:

1
2
3
4
5
6
7
cv::Mat inputImage = cv::imread("input.jpg");
cv::Mat mask; // 输出的掩码图像

cv::Scalar lowerRed(0, 0, 100); // 红色的下界(B, G, R)
cv::Scalar upperRed(50, 50, 255); // 红色的上界(B, G, R)

cv::inRange(inputImage, lowerRed, upperRed, mask);

cv::bitwise_and

函数用于执行位逻辑与操作,它可以将两个二进制图像进行逐位的与操作。这个函数通常用于图像处理中的掩码操作,其中一个二进制图像用于指定哪些像素需要保留,另一个用于指定哪些像素需要删除

1
2
3
4
5
6
7
8
9
cv::bitwise_and(src1, src2, dst, mask);
/*
src1:第一个输入图像。
src2:第二个输入图像。
dst:输出图像,结果图像,将 src1 和 src2 进行逐位的与操作后的图像。
mask:可选参数,用于指定逻辑与操作的掩码图像。如果提供了掩码图像,
仅当掩码图像中对应像素的值为非零时,才会执行逻辑与操作;
否则,结果像素为零。
*/



3. 任务3——形态学操作

学习一些形态学操作,如膨胀、腐蚀、开运算、闭运算等

学习边缘提取(如Canny)和findContours函数,在任务二的基础上,实时提取出红色、蓝色、白色的轮廓,且利用形态学操作消除一些噪点(或一些面积过小的色块)

  • 要求:学会基础的形态学操作,辅助消除图像噪声等
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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
VideoCapture cap(0); // 打开摄像头,您也可以使用图像文件
if (!cap.isOpened()) {
cerr << "Error: Camera not found." << endl;
return -1;
}

while (true) {
Mat frame;
cap >> frame;
if (frame.empty()) {
cerr << "Error: Frame is empty." << endl;
break;
}

Mat hsv_frame;
cvtColor(frame, hsv_frame, COLOR_BGR2HSV);

// 定义红色、蓝色和白色的颜色范围
Scalar lower_red(0, 100, 100);
Scalar upper_red(10, 255, 255);
Scalar lower_blue(100, 100, 100);
Scalar upper_blue(130, 255, 255);
Scalar lower_white(0, 0, 200);
Scalar upper_white(255, 30, 255);

Mat mask_red, mask_blue, mask_white;
inRange(hsv_frame, lower_red, upper_red, mask_red);
inRange(hsv_frame, lower_blue, upper_blue, mask_blue);
inRange(hsv_frame, lower_white, upper_white, mask_white);

Mat result;
bitwise_or(mask_red, mask_blue, result);
bitwise_or(result, mask_white, result);

// 形态学操作
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(result, result, MORPH_OPEN, kernel);
morphologyEx(result, result, MORPH_CLOSE, kernel);

// 查找轮廓
vector<vector<Point>> contours;
findContours(result, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//开,闭运算
// 绘制轮廓
Mat contour_frame = frame.clone();
drawContours(contour_frame, contours, -1, Scalar(0, 255, 0), 2);

imshow("Original Frame", frame);
imshow("Processed Frame", contour_frame);

if (waitKey(10) == 27) // 按ESC键退出
break;
}

return 0;
}

膨胀(Dilation):

膨胀操作可以扩展图像中的白色区域,通常用于填充图像中的小空洞或连接相邻对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat dilated_image;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
// 定义膨胀内核
cv::dilate(input_image, dilated_image, kernel);

cv::imshow("Input Image", input_image);
cv::imshow("Dilated Image", dilated_image);
cv::waitKey(0);

return 0;
}

cv::getStructuringElement

函数用于创建用于形态学操作(例如腐蚀和膨胀)的结构元素。在你提供的代码中,创建了一个矩形结构元素 kernel,其大小为5x5像素,用于形态学操作。

这个结构元素可以用于执行形态学操作,例如图像腐蚀和膨胀,以及其他基于结构元素的图像处理操作。

cv::MORPH_RECT 表示你创建的结构元素是一个矩形。
**cv::Size(5, 5) **指定了矩形的尺寸,宽度为5像素,高度为5像素。
你可以将这个 kernel 结构元素传递给形态学操作的函数,例如 cv::erode(腐蚀)和 cv::dilate(膨胀),以执行图像处理任务。这个结构元素将定义操作的卷积核,用于定义操作的影响范围。通常,矩形结构元素用于形态学操作,但你也可以创建其他形状的结构元素,如椭圆形或十字形,以满足特定的图像处理需求。


腐蚀(Erosion):

腐蚀操作可以减小图像中的白色区域,通常用于去除小的噪声或分离相邻对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat eroded_image;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); // 定义腐蚀内核
cv::erode(input_image, eroded_image, kernel);

cv::imshow("Input Image", input_image);
cv::imshow("Eroded Image", eroded_image);
cv::waitKey(0);

return 0;
}

开运算(Opening):

开运算是腐蚀操作后紧接着膨胀操作的组合。它通常用于去除小的噪声,以及分离相邻对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat opened_image;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); // 定义内核
cv::morphologyEx(input_image, opened_image, cv::MORPH_OPEN, kernel);

cv::imshow("Input Image", input_image);
cv::imshow("Opened Image", opened_image);
cv::waitKey(0);

return 0;
}

闭运算(Closing):

闭运算是膨胀操作后紧接着腐蚀操作的组合。它通常用于关闭对象之间的小空隙。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.png", cv::IMREAD_GRAYSCALE);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat closed_image;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); // 定义内核
cv::morphologyEx(input_image, closed_image, cv::MORPH_CLOSE, kernel);

cv::imshow("Input Image", input_image);
cv::imshow("Closed Image", closed_image);
cv::waitKey(0);

return 0;
}

4.任务4–使用鼠标指针交互来获取图片的某点的rgb值

自己学习如何制作鼠标指针回调来得到当前图片当前点的信息

’ 要求:学会可以使用鼠标回调得到当前点的的三通道(rgb或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
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

Mat image;

void onMouse(int event, int x, int y, int flags, void* param) {
if (event == EVENT_LBUTTONDOWN) {//EVENT_LBUTTONDOWN 鼠标左键
Vec3b pixel = image.at<Vec3b>(y, x); // 获取像素值
//上行代码从图像 image 中获取了鼠标单击位置 (x, y) 处的像素值。
int blue = pixel[0];
int green = pixel[1];
int red = pixel[2];

cout << "RGB at (" << x << ", " << y << "): ";
cout << "R=" << red << ", G=" << green << ", B=" << blue << endl;
}
}

int main() {
image = imread("your_image.jpg"); // 读取图像

if (image.empty()) {
cerr << "Error: Image not found." << endl;
return -1;
}

namedWindow("Image");
imshow("Image", image);

setMouseCallback("Image", onMouse, NULL); // 设置鼠标回调函数

waitKey(0);
destroyAllWindows();

return 0;
}

一旦使用 setMouseCallback 函数将窗口与回调函数关联起来,当用户在名为 “Image” 的窗口上进行鼠标操作(如单击、移动等)时,onMouse 回调函数将会被调用,并根据鼠标事件的类型执行相应的操作。这允许您实现交互式图像处理,例如在图像上单击以获取像素值或执行其他操作。


5.任务5–对图像进行均衡化处理,伽马变化,以及图像滤波等常见的变化

自己查询常见的处理方式和函数(均衡化,伽马变化,高斯模糊,以及几种常见的滤波器)

‘ 要求:学会常见对图像进行的方式和函数,包括不限于上述提到的基本算法

直方图均衡化(Histogram Equalization):

直方图均衡化用于增强图像的对比度,特别是在图像中存在大范围亮度变化时。它通过重新分布像素值来拉伸图像的直方图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.jpg", cv::IMREAD_GRAYSCALE);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat equalized_image;
cv::equalizeHist(input_image, equalized_image);

cv::imshow("Input Image", input_image);
cv::imshow("Equalized Image", equalized_image);
cv::waitKey(0);

return 0;
}

伽马变化(Gamma Correction):

伽马变化用于调整图像的亮度和对比度,以更好地显示特定亮度区域的细节。它通过应用幂函数来改变像素的强度值。

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
#include <opencv2/opencv.hpp>
#include <cmath>

int main() {
cv::Mat input_image = cv::imread("input_image.jpg", cv::IMREAD_COLOR);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

double gamma = 1.5; // 伽马值,可以调整以增强或减弱图像对比度
cv::Mat gamma_corrected_image;
cv::Mat lookup_table(1, 256, CV_8U);

for (int i = 0; i < 256; i++) {
lookup_table.at<uchar>(0, i) = cv::saturate_cast<uchar>(pow(i / 255.0, gamma) * 255.0);
}

cv::LUT(input_image, lookup_table, gamma_corrected_image);

cv::imshow("Input Image", input_image);
cv::imshow("Gamma Corrected Image", gamma_corrected_image);
cv::waitKey(0);

return 0;
}

伽马变化是通过应用以下公式来实现的:

1
output_pixel_value = input_pixel_value^gamma

其中 output_pixel_value 是变换后的像素值,input_pixel_value 是输入像素值,而 gamma 是伽马值。通过调整 gamma 值,您可以增强或减弱图像的对比度。

当 gamma < 1.0 时,图像的低亮度区域将更亮,高亮度区域将更暗,对比度降低。
当 gamma > 1.0 时,图像的低亮度区域将更暗,高亮度区域将更亮,对比度增加。
当 gamma = 1.0 时,变换无效,图像保持原样。
通过调整 gamma 值,您可以根据需要调整图像的明暗程度和对比度,以满足不同的视觉效果要求。在示例代码中,gamma 被设置为 1.5,这会导致对比度增加,使图像的亮部更亮,暗部更暗。您可以尝试不同的 gamma 值来观察不同的效果。


高斯模糊(Gaussian Blur):

高斯模糊用于降低图像噪声并平滑图像。它基于高斯滤波核来进行模糊操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.jpg", cv::IMREAD_COLOR);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat blurred_image;
cv::GaussianBlur(input_image, blurred_image, cv::Size(5, 5), 0);

cv::imshow("Input Image", input_image);
cv::imshow("Blurred Image", blurred_image);
cv::waitKey(0);

return 0;
}

cv::GaussianBlur

函数用于在图像上应用高斯模糊,以减少噪声和平滑图像。

1
cv::GaussianBlur(input_image, blurred_image, cv::Size(5, 5), 0);

input_image:这是输入图像,您希望对其进行高斯模糊处理的图像。通常,它是一个OpenCV的 cv::Mat 对象。

blurred_image:这是输出图像,它将包含经过高斯模糊处理后的图像。同样,它也是一个 cv::Mat 对象,通常会在函数调用之前进行初始化。

cv::Size(5, 5):这是高斯滤波核的大小,它决定了模糊程度。在这个示例中,使用了一个 5x5 的核。核的大小越大,模糊效果越明显,噪声减少,但图像细节也可能丧失。

0:这是高斯滤波的X和Y方向的标准差。如果将其设置为0,OpenCV将根据核的大小自动计算标准差。如果需要更精确的控制,可以指定非零值。

cv::GaussianBlur 函数会将高斯滤波应用于输入图像,然后将结果存储在输出图像 blurred_image 中。高斯模糊是一种常用的图像平滑技术,可用于去除噪声、减少细节和平滑图像。通过调整核的大小和标准差,您可以控制模糊的程度。


图像滤波(Image Filtering):

图像滤波是一种用于去噪、增强边缘、模糊等操作的常见技术。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
#include <opencv2/opencv.hpp>

int main() {
cv::Mat input_image = cv::imread("input_image.jpg", cv::IMREAD_COLOR);
if (input_image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat filtered_image;
cv::Mat kernel = (cv::Mat_<float>(3, 3) << -1, -1, -1, -1, 8, -1, -1, -1, -1); // 自定义卷积核
cv::filter2D(input_image, filtered_image, -1, kernel);

cv::imshow("Input Image", input_image);
cv::imshow("Filtered Image", filtered_image);
cv::waitKey(0);

return 0;
}

/*
cv::Mat kernel:这是自定义的卷积核,它是一个 cv::Mat 对象,
表示卷积核的权重矩阵。在这个示例中,卷积核是一个3x3的矩阵,
其中心元素为8,周围元素为-1,用于增强图像中的边缘特征。
您可以根据需要设计不同的卷积核来实现不同的滤波效果。

cv::filter2D(input_image, filtered_image, -1, kernel):
这是应用卷积操作的部分。cv::filter2D 函数用于将自定义卷积核
kernel 应用于输入图像 input_image,并将结果存储在 filtered_image 中。
参数 -1 表示输出图像的深度(与输入图像相同的深度)。

这段代码的目的是使用自定义卷积核对输入图像进行卷积,
以实现特定的图像处理效果。在这个示例中,
卷积核被设计为增强图像中的边缘特征。根据不同的卷积核设计,
您可以实现不同的滤波效果,如边缘检测、锐化、模糊等。
卷积是图像处理中的常见操作,用于提取图像的不同特征。
*/

相关检测的核:

边缘检测:

1.Sobel 卷积核:用于检测图像中的边缘,分为水平和垂直方向。

1
2
3
4
5
6
7
8
9
Horizontal Sobel Kernel:
-1 -2 -1
0 0 0
1 2 1

Vertical Sobel Kernel:
-1 0 1
-2 0 2
-1 0 1

2.Scharr 卷积核:与Sobel类似,但对边缘更敏感。

1
2
3
4
5
6
7
8
9
Horizontal Scharr Kernel:
-3 -10 -3
0 0 0
3 10 3

Vertical Scharr Kernel:
-3 0 3
-10 0 10
-3 0 3

锐化:

1.Laplacian 卷积核:用于增强图像的高频细节,从而实现锐化效果。

1
2
3
4
Laplacian Kernel:
0 1 0
1 -4 1
0 1 0

模糊:

均值模糊(平均模糊):用于平滑图像,消除噪声。

1
2
3
4
3x3 均值模糊 Kernel:
1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9

高斯模糊:用于平滑图像,但更加柔和,能够保留一些边缘细节。

1
2
3
4
3x3 高斯模糊 Kernal:
-1 -1 -1
-1 8 -1
-1 -1 -1

通常使用OpenCV的 cv::filter2D 函数来应用卷积操作。



6.霍夫找圆和角点检测

学习霍夫直线和角点的函数,

’要求:可以使用相关的函数来检测圆和角点来检测图片,找出给定图片里面的三角,圆和正方形。

霍夫圆检测:

霍夫圆检测用于在图像中检测圆形物体。您可以使用 cv::HoughCircles 函数来执行霍夫圆检测。原理概述:

需要检测的圆可以用以下数学方程表示:(x - a)^2 + (y - b)^2 = r^2,其中 (a, b) 是圆心的坐标,r 是圆的半径。

对于每个可能的 (a, b, r) 组合,可以在图像中寻找相应的边缘点。这些边缘点应当与圆上的点非常接近。

在累加器数组(Hough Accumulator)中,为每个可能的 (a, b, r) 组合投票。如果有足够多的边缘点投票给特定的 (a, b, r),则认为找到了一个圆。

最后,通过分析累加器数组来找到得票最多的圆,然后输出检测到的圆的信息,包括圆心坐标和半径。

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
#include <opencv2/opencv.hpp>

int main() {
cv::Mat image = cv::imread("input_image.jpg", cv::IMREAD_COLOR);
if (image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

cv::Mat gray_image;
cv::cvtColor(image, gray_image, cv::COLOR_BGR2GRAY);

// 霍夫圆检测
std::vector<cv::Vec3f> circles;
cv::HoughCircles(gray_image, circles, cv::HOUGH_GRADIENT, 1, gray_image.rows / 8, 200, 100, 0, 0);

// 绘制检测到的圆
for (size_t i = 0; i < circles.size(); i++) {
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
cv::circle(image, center, radius, cv::Scalar(0, 0, 255), 2);
}

cv::imshow("Detected Circles", image);
cv::waitKey(0);

return 0;
}

cv::HoughCircles

1
2
3
4
5
6
7
8
9
10
11
12
cv::HoughCircles(
input_image, // 输入灰度图像
circles, // 存储检测到的圆的容器
cv::HOUGH_GRADIENT, // 霍夫变换方法
dp, // 累加器图像的分辨率,通常设置为1
minDist, // 圆心之间的最小距离,通常设置为图像高度的八分之一
param1, // Canny边缘检测器的高阈值
param2, // 至少要有多少个投票才能认为检测到一个圆
minRadius, // 最小半径
maxRadius // 最大半径,如果设置为0,函数将自动计算
);

1
2
std::vector<cv::Vec3f> circles;
cv::HoughCircles(gray_image, circles, cv::HOUGH_GRADIENT, 1, gray_image.rows / 8, 200, 100, 0, 0);

std::vectorcv::Vec3f circles:这是一个用于存储检测到的圆的容器。每个检测到的圆都由一个 cv::Vec3f 对象表示,其中包含圆的中心坐标 (x, y) 和半径。

cv::HoughCircles 函数执行霍夫圆检测。它的参数如下:

gray_image:这是输入的灰度图像,通常是由彩色图像转换而来。

circles:这是用于存储检测到的圆的容器。

cv::HOUGH_GRADIENT:这是霍夫变换的方法,表示使用梯度法进行圆检测。

1:表示霍夫圆检测的累加器图像的分辨率,通常设置为1。

gray_image.rows / 8:圆心之间的最小距离,通常设置为图像高度的八分之一。

200:Canny边缘检测器的高阈值。

100:表示至少要有多少个投票才能认为检测到一个圆。

0:表示霍夫圆检测的最小半径。

0:表示霍夫圆检测的最大半径。如果设置为0,函数将根据图像尺寸自动计算最大半径。

cv::HoughCircles 函数会在输入的灰度图像上执行霍夫圆检测,并将检测到的圆的信息存储在 circles 容器中。您可以遍历 circles 容器来访问每个检测到的圆的位置和半径信息,然后在图像上绘制这些圆以进行可视化显示。


角点检测:

角点检测(Corner Detection)是计算机视觉中的一项重要任务,用于检测图像中的角点或角落区域。角点通常是图像中的显著特征点,具有在不同方向上发生明显弯曲的特性。角点检测的原理是寻找图像中的局部最大弯曲或局部最小特征值的位置。

原理概述:

角点通常位于图像中的边缘交汇或曲线的交点附近。这些点在不同方向上的梯度变化较大,因此梯度矩阵的特征值可以用来检测它们。

对于每个像素点,计算其周围邻域的梯度信息,通常使用Sobel、Harris或Shi-Tomasi等算子。

计算梯度矩阵的特征值(最小特征值和最大特征值)或角点响应函数。

根据特征值的阈值或相对大小,选择具有较大特征值的像素点,这些点被认为是图像中的角点。

通常还会对检测到的角点进行非极大值抑制,以去除冗余的角点。

OpenCV 提供了多种角点检测方法,其中包括 Harris 角点检测、Shi-Tomasi 角点检测和 FAST 角点检测等。

角点检测用于检测图像中的角点,例如检测三角形的顶点。您可以使用 cv::goodFeaturesToTrack 函数来执行角点检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <opencv2/opencv.hpp>

int main() {
cv::Mat image = cv::imread("input_image.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

std::vector<cv::Point2f> corners;//检测到的角点
cv::goodFeaturesToTrack(image, corners, 100, 0.01, 10);

// 绘制检测到的角点
for (size_t i = 0; i < corners.size(); i++) {
cv::circle(image, corners[i], 5, cv::Scalar(0, 0, 255), -1);
}

cv::imshow("Detected Corners", image);
cv::waitKey(0);

return 0;
}

cv::goodFeaturesToTrack 函数来执行角点检测。

1
2
3
4
5
6
7
8
9
10
11
12
void cv::goodFeaturesToTrack(
cv::InputArray image, // 输入图像,通常是灰度图像
cv::OutputArray corners, // 输出检测到的角点
int maxCorners, // 最大角点数目,要检测到的角点数量的上限
double qualityLevel, // 角点质量因子,通常在0.01到0.1之间
double minDistance, // 角点之间的最小距离,两个角点之间的距离必须大于这个值
cv::InputArray mask = cv::noArray(), // 可选的掩码,指定检测区域
int blockSize = 3, // 计算角点响应函数所使用的邻域块的大小
bool useHarrisDetector = false, // 是否使用Harris角点检测方法,如果为true,则使用Harris角点检测,否则使用Shi-Tomasi
double k = 0.04 // Harris角点检测参数,仅在useHarrisDetector为true时有效
);

**Sobel、Harris 和 Shi-Tomasi **

都是图像处理和计算机视觉领域中常用的算子,用于不同的任务,如边缘检测和角点检测。

Sobel 算子:

Sobel 算子是一种用于边缘检测的算子。它通过计算图像中每个像素点的梯度来寻找图像中的边缘。Sobel 算子通常应用于灰度图像。
Sobel 算子分为水平和垂直两个方向的核,分别用于检测图像中的水平和垂直边缘。
梯度的大小和方向可用于确定边缘的位置和强度。
Harris 角点检测:

Harris 角点检测是用于检测图像中的角点的算法。它基于像素点周围邻域的强度变化来检测角点。
Harris 角点检测使用局部窗口内的像素值来计算一个角点响应函数,该函数考虑了窗口内像素值的变化。当这个响应函数达到最大值时,表示在该位置检测到一个角点。
Harris 角点检测通常用于特征匹配和跟踪。
Shi-Tomasi 角点检测:

Shi-Tomasi 角点检测是与 Harris 角点检测类似的算法,也用于检测图像中的角点。它改进了 Harris 角点检测的角点选择准则。
Shi-Tomasi 角点检测使用最小特征值来评估角点的质量。当最小特征值大于某个阈值时,才被认为是一个角点。
Shi-Tomasi 角点检测常常被视为 Harris 角点检测的改进版本,通常在计算机视觉中更常用。


7. 透视变换

学习掌握透视变换的原理

重点掌握getPerspectiveTransformwarpPerspective两个函数,拍摄一个物体(如A4纸),并利用透视变换校正视角为正视该物体

  • 要求:学会利用透视变换获取不同的视角

透视变换是一种图像处理技术,用于将图像从一个透视或投影视图转换为另一个视图,以校正图像中的透视畸变或改变观察角度。在这个任务中,您想要拍摄一个物体(例如A4纸),并使用透视变换来校正视角,使物体呈现正视图。

以下是执行透视变换的一般步骤:

拍摄物体图像:首先,您需要拍摄物体的图像,例如A4纸。确保在拍摄时,物体位于透视图中,可能会出现一些透视畸变。

确定四个关键点:在图像上标记四个关键点,这些点对应于物体的四个角。这四个点用于定义透视变换。

计算透视变换矩阵:使用 cv::getPerspectiveTransform 函数计算透视变换矩阵。这个函数需要输入源图像上的四个关键点和目标图像上的四个对应点,然后返回变换矩阵。

应用透视变换:使用 cv::warpPerspective 函数将图像应用于透视变换矩阵,以校正透视畸变并获得正视图。

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

int main() {
cv::Mat image = cv::imread("object_image.jpg"); // 读取物体图像
if (image.empty()) {
std::cerr << "Error: Cannot read the input image." << std::endl;
return -1;
}

// 在图像上标记四个关键点(物体的四个角)
std::vector<cv::Point2f> src_points;
src_points.push_back(cv::Point2f(0, 0)); // 左上角
src_points.push_back(cv::Point2f(image.cols, 0)); // 右上角
src_points.push_back(cv::Point2f(image.cols, image.rows)); // 右下角
src_points.push_back(cv::Point2f(0, image.rows)); // 左下角

// 在目标图像上定义对应的四个点
std::vector<cv::Point2f> dst_points;
dst_points.push_back(cv::Point2f(0, 0)); // 左上角
dst_points.push_back(cv::Point2f(image.cols, 0)); // 右上角
dst_points.push_back(cv::Point2f(image.cols * 1.5, image.rows)); // 右下角
dst_points.push_back(cv::Point2f(-image.cols * 0.5, image.rows)); // 左下角

// 计算透视变换矩阵
cv::Mat perspective_matrix = cv::getPerspectiveTransform(src_points, dst_points);

// 应用透视变换
cv::Mat corrected_image;
cv::warpPerspective(image, corrected_image, perspective_matrix, image.size());

// 显示原始图像和校正后的图像
cv::imshow("Original Image", image);
cv::imshow("Corrected Image", corrected_image);

cv::waitKey(0);
return 0;
}


cv::warpPerspective

用于应用透视变换矩阵到图像,以实现透视校正或视角变换。这个函数接受输入图像和透视变换矩阵,并返回经过变换后的图像。

1
2
3
4
5
6
7
8
9
void cv::warpPerspective(
cv::InputArray src, // 输入图像,通常是要进行透视变换的图像
cv::OutputArray dst, // 输出图像,用于存储变换后的图像
cv::InputArray M, // 3x3透视变换矩阵
cv::Size dsize, // 输出图像的尺寸
int flags = cv::INTER_LINEAR, // 插值方法,通常使用线性插值
int borderMode = cv::BORDER_CONSTANT, // 边界模式
const cv::Scalar& borderValue = cv::Scalar() // 边界值
);

参数解释:

src:输入图像,通常是要进行透视变换的图像。

dst:输出图像,用于存储变换后的图像。它应该是一个预先分配的空图像。

M:3x3透视变换矩阵,通常是使用 cv::getPerspectiveTransform 函数计算得到的。

dsize:输出图像的尺寸,通常是一个 cv::Size 结构,指定输出图像的宽度和高度。

flags:插值方法,用于确定如何在变换过程中处理像素值。通常使用线性插值 (cv::INTER_LINEAR) 来获得平滑的结果。

borderMode:边界模式,用于确定如何处理超出图像边界的像素。默认是 cv::BORDER_CONSTANT,意味着超出边界的像素将使用指定的 borderValue 进行填充。

borderValue:边界值,当 borderMode 设置为 cv::BORDER_CONSTANT 时使用的像素值。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
cv::Mat input_image = cv::imread("input_image.jpg"); // 读取输入图像
cv::Mat perspective_matrix; // 透视变换矩阵,通常使用 cv::getPerspectiveTransform 计算得到

cv::Mat output_image; // 用于存储变换后的图像
cv::Size output_size(width, height); // 输出图像的尺寸

// 应用透视变换
cv::warpPerspective(input_image, output_image, perspective_matrix, output_size, cv::INTER_LINEAR, cv::BORDER_CONSTANT);

// 输出图像保存到文件或显示
cv::imwrite("output_image.jpg", output_image);

8. 查阅学习几个坐标系

自行查阅资料,了解图像坐标系、像素坐标系、相机坐标系、世界坐标系等几个坐标系之间的变换关系

要求:理解其相对的关系

在计算机视觉和图像处理领域,有几个常用的坐标系,它们之间存在一些变换关系。以下是几个常见的坐标系以及它们之间的关系:

1.图像坐标系(Image Coordinate System):

图像坐标系是最常见的坐标系之一,用于表示图像中的像素位置。它通常以图像的左上角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向。
像素坐标系(Pixel Coordinate System):

1
2
3
4
5
6
7
8
9
O————————————————————————————————————————————————————————————————>X轴
|
|
|
|
|
|
V
Y轴

2.像素坐标系

是图像坐标系的一种特殊形式,其中每个像素都有一个唯一的坐标。它通常以左上角的像素为 (0,0) 坐标,然后向右增加列数,向下增加行数。

3.相机坐标系(Camera Coordinate System):

相机坐标系用于表示相机的内部参数和外部参数。它通常以相机的光学中心为原点,X轴指向相机的右侧,Y轴指向相机的下方,Z轴指向相机的前方。相机坐标系通常用于相机校准和计算摄像机姿态等任务。
世界坐标系(World Coordinate System):

4.世界坐标系

是用于表示场景中物体位置的坐标系。它通常以场景中的某一固定点为原点,X、Y、Z 轴分别指向场景中的某个方向。世界坐标系通常用于进行三维重建和物体定位等任务

关系和变换:

图像坐标系和像素坐标系之间的关系通常是一一对应的,其中图像坐标系中的点 (x, y) 对应于像素坐标系中的点 (i, j),其中** i 和 j 是整数像素坐标**。

相机坐标系和世界坐标系之间的变换通常是通过相机的内部参数(如焦距和主点)以及外部参数(如相机位置和姿态)来实现的。这个变换关系通常称为相机的外部和内部参数矩阵。

相机坐标系中的点可以通过相机的内部和外部参数变换到世界坐标系中,从而实现从相机坐标系到世界坐标系的变换。

图像坐标系通常与相机坐标系之间也有一个变换关系,可以通过相机的投影矩阵来实现。这个变换将相机坐标系中的点投影到图像坐标系中。


9. 学习相机成像原理和对相机进行标定

自己在网上学习相机成像的原理,然后再搜索标定相机的方法,结合所学到得到相机成像的原理(一般使用的matlab来对其进行标定

要求:可以自主完成相机标定任务。

相机成像原理:

针孔相机模型:了解针孔相机模型是理解相机成像原理的关键。根据该模型,光线通过相机的针孔(光圈)进入相机并投射在感光元件(例如CCD或CMOS传感器)上,从而形成图像。

透镜和焦距:理解透镜的作用,透镜的焦距决定了相机的视场和对焦深度。

成像平面:感光元件的表面被称为成像平面,它用于捕捉光线的图像。

成像过程:了解光线如何通过透镜和针孔进入相机,以及如何在成像平面上形成图像。学习如何通过光线追踪来理解图像形成过程。

相机标定

标定目的:确定相机的内部参数和外部参数,以便在图像中准确地测量物体的尺寸和位置。

内部参数:内部参数包括焦距、主点坐标、镜头畸变等。通常,这些参数是相机制造商提供的,但在某些情况下需要进行重新标定。

外部参数:外部参数包括相机的位置和朝向。这些参数通常需要在标定过程中估计。

标定模式:选择合适的标定模式,如单目相机、双目相机或立体视觉系统。

标定板:使用特定的标定板(如棋盘格)来拍摄图像,以获取标定所需的数据。

图像采集:拍摄一系列包含标定板的图像,确保标定板在不同位置和朝向下都能够被捕捉到。

标定算法:使用相机标定算法,如Zhang’s标定方法或Tsai’s标定方法,从图像数据中估计相机的内部和外部参数。

结果验证:对标定结果进行验证,通常使用残差分析来评估标定的准确性。

应用:一旦完成相机标定,您可以将标定参数应用于图像测量、摄像机运动估计、立体视觉等领域。

在标定相机时,通常使用专门的标定工具或库,如OpenCV,它提供了一组用于相机标定的函数和工具。您可以使用这些工具来获取标定所需的参数。

请注意,相机标定是一个复杂的任务,需要一定的数学和计算机视觉知识。建议您查阅相关文档、教程和示例代码,以更深入地了解相机成像原理和标定方法,并在实践中逐步掌握这些技能。