Administrator
发布于 2023-11-11 / 10 阅读 / 0 评论 / 0 点赞

计算机视觉 专题

一文看懂计算机视觉-CV(基本原理+2大挑战+8大任务+4个应用) (easyai.tech)

计算机视觉的2大挑战

  • 特征难以提取

  • 需要计算的数据量巨大

计算机视觉的 8 大任务

图像分类

目标检测

语义分割

实例分割

视频分类

人体关键点检测

场景文字识别

目标跟踪

卷积神经网络

一文看懂卷积神经网络-CNN(基本原理+独特价值+实际应用)- 产品经理的人工智能学习库 (easyai.tech)

2大特点

  1. 有效的将大数据量的图片降维成小数据

  2. 有效的保留图片特征,符合图片处理原则

解决了什么问题?

  1. 图片需要处理的数据量太大、导致成本很高、效率很低

  2. 数字化时很难保留原有的特征,导致准确率不高

人类视觉原理

  1. 摄入像素

  2. 初步处理:识别边缘、方向

  3. 进行抽象:大脑判断,是圆形还是什么图形

  4. 高层抽象:满足气球的特征,是气球

pixels->edges->object parts->object model

卷积神经网络-CNN 的基本原理

3个部分

  1. 卷积层:提取局部特征

  2. 池化层:降低参数数量

  3. 全连接层:类似传统神经网络(训练集和测试集,判断结果,输出结果)

卷积

使用一个过滤器(卷积核)来过滤图像的各个小区域,从而得到这些小区域的特征值。(有各种不同的卷积核)

卷积层运算过程

池化层

下采样,他可以大大降低数据的维度。

池化层相比卷积层可以更有效的降低数据维度(毕竟卷积核很小),这么做不但可以大大减少运算量,还可以有效的避免过拟合。

池化层过程

全输出层

全连接层用处理好的数据进行神经网络运算,输出结果。

卷积神经网络一般不止3层

卷积层 – 池化层- 卷积层 – 池化层 – 卷积层 – 全连接层

LeNet-5网络结构

CNN 的实际应用:
  1. 图片分类、检索

  2. 目标定位检测

  3. 目标分割

  4. 人脸识别

  5. 骨骼识别

cmake配置opencv

【精选】Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异)_cmake配置opencv-CSDN博客

cmake_minimum_required(VERSION 3.0.0)
project(demo2 VERSION 0.1.0 LANGUAGES C CXX)

# Find OpenCV package
set(OpenCV_INCLUDE_DIRS C:\\SnowChar\\software\\opencv\\opencv\\build\\include\\opencv2)
set(OpenCV_DIR C:\\SnowChar\\software\\opencv\\opencv\\build\\x64\\vc16\\lib)
set(OpenCV_LIBRARIES C:\\SnowChar\\software\\opencv\\opencv\\build\\x64\\vc16\\lib\\opencv_world481.lib)
find_package(OpenCV REQUIRED) #添加lib
include_directories(${OpenCV_INCLUDE_DIRS}) #添加头文件include

message(${OpenCV_INCLUDE_DIRS})
message("1")
message(${OpenCV_DIR})
message("2")
message(${OpenCV_LIBRARIES})
message("3")



include(CTest)
enable_testing()

add_executable(demo2 main.cpp)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES})

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

  1. lib 库

  2. inlude 头文件

  3. 链接 主的库

后面加d的库是debug模式

Windows-MinGW-CMake-OpenCV 配置 - 知乎 (zhihu.com)

Mat类

Mat类用于存储数据,利用自动内存管理技术很好的解决了内存自动释放的问题,当变量不再需要时立即释放内存。

代码清单2-1 创建Mat类
cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread(“test.jpg”); //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b=a; //复制矩阵头,并命名为b

Mat里放的是矩阵的指针,所以改变b,a也会被改变

类型

表2-1 OpenCV中的数据类型与取值范围

数据类型

具体类型

取值范围

CV_8U

8位无符号整数

0—255

CV_8S

8位符号整数

-128—127

CV_16U

16位无符号整数

0-65535

CV_16S

16位符号整数

-32768—32767

CV_32S

32位符号整数

-2147483648—2147483647

CV_32F

32位浮点整数

-FLT_MAX—FLT_MAX, INF, NAN

CV_64F

64位浮点整数

-DBL_MAX—DBL_MAX, INF, NAN

通道

代码清单2-3 通过OpenCV数据类型创建Mat类
cv::Mat a(640,480,CV_8UC3) //创建一个640*480的3通道矩阵用于存放彩色图像
cv::Mat a(3,3,CV_8UC1) //创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a(3,3,CV_8U) //创建单通道矩阵C1标识可以省略

8U代表 8位 ,灰度0~225

C3代表3通道 ,R,G,B

Mat类构造与赋值

(2)根据输入矩阵尺寸和类型构造

代码清单2-5 利用矩阵尺寸和类型参数构造Mat类

cv::Mat::Mat( int  rows, int  cols,  int  type          )

  • rows:构造矩阵的行数

  • cols:矩阵的列数

  • type:矩阵中存储的数据类型。此处除了CV_8UC1、CV_64FC4等从1到4通道以外,还提供了更多通道的参数,通过CV_8UC(n)中的n来构建多通道矩阵,其中n最大可以取到512.


代码清单2-7 用Size()结构构造Mat示例

cv::Mat a(Size(480, 640), CV_8UC1); //构造一个行为640,列为480的单通道矩阵

cv::Mat b(Size(480, 640), CV_32FC3); //构造一个行为640,列为480的3通道矩

(3)利用已有矩阵构造

代码清单2-8 利用已有矩阵构造Mat类

cv::Mat::Mat( const Mat & m);

m:已经构建完成的Mat类矩阵数据。

(1)构造时赋值

代码清单2-11 在构造时赋值的方法

cv::Mat::Mat(int  rows,

               int  cols,

               int  type,

               const Scalar & s

               )

  • rows:矩阵的行数

  • cols:矩阵的列数

  • type:存储数据的类型

  • s:给矩阵中每个像素赋值的参数变量,例如Scalar(0, 0, 255)。

该种方式是在构造的同时进行赋值,将每个元素想要赋予的值放入Scalar结构中即可,这里需要注意的是,用此方法会将图像中的每个元素赋值相同的数值,例如Scalar(0, 0, 255)会将每个像素的三个通道值分别赋值0,0,255。我们可以使用如下的形式构造一个已赋值的Mat类

代码清单2-12 在构造时赋值示例

cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0,0,255));//创建一个3通道矩阵,每个像素都是0,0,255

cv::Mat b(2, 2, CV_8UC2, cv::Scalar(0,255));//创建一个2通道矩阵,每个像素都是0,255

cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); //创建一个单通道矩阵,每个像素都是255

(2)枚举赋值法


这种赋值方式是将矩阵中所有的元素都一一枚举出,并用数据流的形式赋值给Mat类。具体赋值形式如代码清单2-13所示。

代码清单2-13 利用枚举法赋值示例

cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);

cv::Mat b = (cv::Mat_<double>(2, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2);


(3)循环赋值

与通过枚举法赋值方法相类似,循环法赋值也是对矩阵中的每一位元素进行赋值,但是可以不在声明变量的时候进行赋值,而且可以对矩阵中的任意部分进行赋值。具体赋值形式如代码清单2-14所示。

代码清单2-14 利用枚举法赋值示例

cv::Mat c = cv::Mat_<int>(3, 3); //定义一个3*3的矩阵

for (int i = 0; i < c.rows; i++) //矩阵行数循环

{

for (int j = 0; j < c.cols; j++) //矩阵列数循环

{

c.at<int>(i, j) = i+j;

}

}


(4)类方法赋值


在Mat类里提供了可以快速赋值的方法,可以初始化指定的矩阵。例如生成单位矩阵、对角矩阵、所有元素都为0或者1的矩阵等。具体使用方法如代码清单2-15所示。

代码清单2-15 利用类方法赋值示例

cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);

cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);

cv::Mat c = cv::Mat::diag(b);

cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);

cv::Mat e = cv::Mat::zeros(4, 2, CV_8UC3);

上面代码中,每个函数作用及参数含义分别如下:

  • eye():构建一个单位矩阵,前两个参数为矩阵的行数和列数,第三个参数为矩阵存放的数据类型与通道数。如果行和列不相等,则在矩阵的 (1,1),(2,2),(3,3)等主对角位置处为1。

  • diag():构建对角矩阵,其参数必须是Mat类型的1维变量,用来存放对角元素的数值。

  • ones():构建一个全为1的矩阵,参数含义与eye()相同。

  • zeros():构建一个全为0的矩阵,参数含义与eye()相同。

  • 对角矩阵 (diagonal matrix)是一个 主对角线 之外的元素皆为0的 矩阵 。 对角线上的元素可以为0或其他值。 对角线上元素相等的对角矩阵称为 数量矩阵 ;对角线上元素全为1的对角矩阵称为 单位矩阵

OpenCV_孤舟簔笠翁的博客-CSDN博客

OTSU算法

缺陷,光照的阴影对OTSU算法影响很大

均衡化

CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );

	Mat src = imread("F:/code/images/aero1.jpg");
	if (src.empty()) {
		printf("fail to read");
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);

	//直方图均衡化
	Mat gray, dst;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);
	equalizeHist(gray, dst);//只能接受灰度图
	imshow("eh-demo", dst);

	//直方图计算

	//设定像素取值范围
	int histSize = 256;
	float range[] = { 0,255 };
	const float* histRanges = { range };

	Mat gray_hist, dst_hist;

	calcHist(&gray, 1, 0, Mat(), gray_hist, 1, &histSize, &histRanges, true, false);
	calcHist(&dst, 1, 0, Mat(), dst_hist, 1, &histSize, &histRanges, true, false);

	//创建直方图画布并归一化处理
	Mat histImage = Mat::zeros(Size(600, 400), CV_8UC3);
	int margin = 50;
	int nm = histImage.rows - 2 * margin;
	float bh = (float)(histImage.cols - 2 * margin) / (float)histSize;

	normalize(gray_hist, gray_hist, 0, nm, NORM_MINMAX, -1, Mat());//使用最小值和最大值进行归一化处理,最小值是0,最大值是nm
	normalize(dst_hist, dst_hist, 0, nm, NORM_MINMAX, -1, Mat());

	//绘制前后两个统计点
	for (int i = 0; i < histSize - 1; i++) {
		line(histImage, Point(i * bh + margin, nm - gray_hist.at<float>(i) + margin),
			Point((i + 2) * bh + margin, nm - gray_hist.at<float>(i + 1) + margin), Scalar(255, 0, 0), 1, LINE_AA);
		line(histImage, Point(i * bh + margin, nm - dst_hist.at<float>(i) + margin),
			Point((i + 2) * bh + margin, nm - dst_hist.at<float>(i + 1) + margin), Scalar(0, 255, 0), 1, LINE_AA);
	}
	imshow("histImage", histImage);

均值滤波

CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
                        Size ksize, Point anchor = Point(-1,-1),
                        int borderType = BORDER_DEFAULT );

	Mat src = imread("F:/code/images/test1.png");
	if (src.empty()) {
		printf("fail to read");
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);

	//手工进行图像卷积计算
	//均值滤波
	int width = src.cols;
	int height = src.rows;
	Mat result = src.clone();

	for (int row = 1; row < height - 1; row++) {
		for (int col = 1; col < width - 1; col++) {
			// 3*3 卷积核 
			int sb = result.at<Vec3b>(row, col)[0] + result.at<Vec3b>(row - 1, col - 1)[0] + result.at<Vec3b>(row - 1, col)[0] + result.at<Vec3b>(row - 1, col + 1)[0] + result.at<Vec3b>(row, col - 1)[0] + result.at<Vec3b>(row, col + 1)[0] + result.at<Vec3b>(row + 1, col - 1)[0] + result.at<Vec3b>(row + 1, col)[0] + result.at<Vec3b>(row + 1, col + 1)[0];

			int sg = result.at<Vec3b>(row, col)[1] + result.at<Vec3b>(row - 1, col - 1)[1] + result.at<Vec3b>(row - 1, col)[1] + result.at<Vec3b>(row - 1, col + 1)[1] + result.at<Vec3b>(row, col - 1)[1] + result.at<Vec3b>(row, col + 1)[1] + result.at<Vec3b>(row + 1, col - 1)[1] + result.at<Vec3b>(row + 1, col)[1] + result.at<Vec3b>(row + 1, col + 1)[1];

			int sr = result.at<Vec3b>(row, col)[2] + result.at<Vec3b>(row - 1, col - 1)[2] + result.at<Vec3b>(row - 1, col)[2] + result.at<Vec3b>(row - 1, col + 1)[2] + result.at<Vec3b>(row, col - 1)[2] + result.at<Vec3b>(row, col + 1)[2] + result.at<Vec3b>(row + 1, col - 1)[2] + result.at<Vec3b>(row + 1, col)[2] + result.at<Vec3b>(row + 1, col + 1)[2];

			result.at<Vec3b>(row, col)[0] = sb / 9;
			result.at<Vec3b>(row, col)[1] = sg / 9;
			result.at<Vec3b>(row, col)[2] = sr / 9;
		}
	}
	imshow("mean filter My", result);
	
	//调用API : blur 进行均值滤波
	Mat dst;
	blur(src, dst, Size(3,3), Point(-1,-1), BORDER_DEFAULT);
	imshow("mean filter API", dst);

CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth,
                             Size ksize, Point anchor = Point(-1,-1),
                             bool normalize = true,
                             int borderType = BORDER_DEFAULT );

Mat dst;
	bluer(src, dst, -1, Size(5, 5), Point(-1, -1), true, BORDER_DEFAULT);
	imshow("box blur", dst);在这里插入图片描述

高斯滤波

CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

	//高斯模糊
	Mat dst;
	GaussianBlur(src, dst, Size(15, 15), 0);
	imshow("gaussian blur", dst);

锐化Schar算子
	Mat src = imread("F:/code/images/ttt.png");
	if (src.empty()) {
		printf("fail to read");
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);

	// robot gradient 计算
	Mat robot_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
	Mat robot_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);

	Mat grad_x, grad_y;
	filter2D(src, grad_x, CV_32F, robot_x, Point(-1, -1), 0, BORDER_DEFAULT);
	filter2D(src, grad_y, CV_32F, robot_y, Point(-1, -1), 0, BORDER_DEFAULT);
	convertScaleAbs(grad_x, grad_x);
	convertScaleAbs(grad_y, grad_y);
	Mat result;
	add(grad_x, grad_y, result);//L1梯度
	imshow("robot gradient", result);

	// sobel and scharr
	Sobel(src, grad_x, CV_32F, 1, 0);
	Sobel(src, grad_y, CV_32F, 0, 1);
	convertScaleAbs(grad_x, grad_x);
	convertScaleAbs(grad_y, grad_y);
	Mat result2;
	addWeighted(grad_x, 0.5, grad_y, 0.5, 0, result2);
	imshow("sobel gradient", result2);


	Scharr(src, grad_x, CV_32F, 1, 0);
	Scharr(src, grad_y, CV_32F, 0, 1);
	convertScaleAbs(grad_x, grad_x);
	convertScaleAbs(grad_y, grad_y);
	Mat result3;
	addWeighted(grad_x, 0.5, grad_y, 0.5, 0, result3);
	imshow("scharr gradient", result3);

形态学运算开闭

	Mat src = imread("F:/code/images/hist_02.jpg");
    namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);

    //二值图
    Mat gray, binary;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
    namedWindow("binary", WINDOW_AUTOSIZE);
    imshow("binary", binary);

    //腐蚀
    Mat elementRect = getStructuringElement(MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));//矩形
    erode(binary, binary, elementRect);
    namedWindow("erode", WINDOW_AUTOSIZE);
    imshow("erode", binary);

    //膨胀
    Mat elementRect2 = getStructuringElement(MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));//矩形
    dilate(binary, binary, elementRect2);
    namedWindow("dilate", WINDOW_AUTOSIZE);
    imshow("dilate", src);


评论