# ln_opencv **Repository Path**: Shine_Zhang/ln_opencv ## Basic Information - **Project Name**: ln_opencv - **Description**: opencv 学习笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-01-02 - **Last Updated**: 2025-01-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ln_opencv ### 介绍 opencv 学习笔记 环境:ubuntu 22.04 参考教程:[OpenCV 4.0 中文文档](https://apachecn.github.io/opencv-doc-zh/#/docs/4.0.0/1.1-tutorial_py_intro) ### 安装 #### 1. opencv-python安装 ``` bash # 安装命令 pip install opencv-python # 安装完成后验证 (opencv) joke@ShineZhang:~/ln_opencv/01_install$ python3 test_opencv_python.py # 验证ok opencv-python version: 4.10.0 # 如果系统上没有安装完整的 Qt 库和工具,你可能还需要安装完整的 Qt 库 sudo apt install qt5-qmake qtbase5-dev qtbase5-dev-tools ``` #### 2. opencv c/c++安装 ```bash # 安装命令 sudo apt install -y libopencv-dev # 安装完成后,OpenCV 的头文件和库文件将位于 /usr/include/opencv4/ 和 /usr/lib/x86_64-linux-gnu/ 目录,可以直接在 C 或 C++ 项目中使用 # g++编译验证代码 或者 使用cmake (opencv) joke@ShineZhang:~/ln_opencv/01_install$ g++ -o test_opencv test_opencv.cpp `pkg-config --cflags --libs opencv4` # 运行,测试图片就会被打开 (opencv) joke@ShineZhang:~/ln_opencv/01_install$ ./test_opencv ``` #### 3.其他 由于我使用的开发环境是wsl-ubuntu22.04 在使用matplotlib库时提示无法显示图像,解决方案如下: 在代码中导入库之后添加以下代码: ```python import matplotlib matplotlib.use('TkAgg') ``` ### GUI操作 #### 1.图像入门 - 图像读取 `imread` - 图像显示 `imshow` - 获取按键输入 `waitKey` - 关闭窗口 `destroyAllWindows` #### 2.视频入门 [Download Sample Videos](https://sample-videos.com/index.php) 使用wget下载上述网站中的示例视频 `big_buck_bunny_720p_1mb.mp4` 验证视频播放和保存: - 创建 `VideoCapture` 对象 `cap` 打开视频文件\摄像头 - 获取`cap.get()`的分辨率、帧率、亮度、对比度 - 读取`cap.read(frame)`视频帧,显示每一帧图像 `imshow`,构成视频播放 - 创建 `VideoWriter` 对象 `writer` ,在指定输出文件名、编码格式、帧率和分辨率的同时 `open` 打开输出文件 - 将每一个视频帧`writer.write(frame)`写入到输出文件中,构成视频保存到本地 #### 3.绘图入门 - `np.zeros`: - 用于创建全黑背景的空白图像。 - 参数 `(height, width, 3)` 定义了图像大小和通道数。 - `dtype=np.uint8` 指定像素值范围为 `0-255`。 - 绘图函数: - `cv.line`: 绘制一条直线,定义起点、终点、颜色和粗细。 - `cv.rectangle`: 绘制一个矩形,定义左上角、右下角、颜色和粗细。 - `cv.circle`: 绘制一个圆形,定义圆心、半径、颜色和粗细。 - `cv.ellipse`: 绘制一个椭圆,定义中心点、轴长度、旋转角度和起止角度。 - `cv.polylines`: 绘制多边形,输入顶点数组并指定是否闭合。 - `cv.putText`: 在图像上添加文本。 #### 4.鼠标作为画笔 1. 全局变量 - `pt1` 和 `pt2` 用于存储鼠标点击的起始点和终止点坐标。 - `is_drawing` 用于标记是否正在绘制矩形。 2. 鼠标回调函数 `mouse_callback()` 会在鼠标事件发生时被触发。它使用事件类型来判断当前鼠标操作是按下、移动还是松开,并在图像上绘制矩形。 - 事件类型: - **`EVENT_LBUTTONDOWN`**:左键按下时记录起始点坐标。 - **`EVENT_MOUSEMOVE`**:鼠标移动时,如果正在绘制,更新矩形的终点坐标,并在克隆图像上绘制矩形。 - **`EVENT_LBUTTONUP`**:左键松开时,绘制最终的矩形。 3. 创建图像 创建了一张大小为 600x800 的黑色图像。 4. 注册鼠标回调 使用 `cv2.setMouseCallback()` 注册回调函数,使得每次鼠标事件发生时都会调用 `mouse_callback()` 函数。 5. 显示图像 使用 `cv2.imshow()` 显示图像,并用 `cv2.waitKey(0)` 等待用户按下键盘任意键后关闭窗口。 #### 5.调色板 - 创建窗口和图像: 使用 namedWindow 创建一个窗口,使用 Mat::zeros 创建一个黑色图像。 - 创建轨迹栏: createTrackbar 函数用于创建四个轨迹栏: - R、G、B 用于控制 RGB 三个通道的颜色值。 - 一个开关轨迹栏用于控制图像的显示和隐藏。 - 更新图像: 在 while 循环中,获取每个轨迹栏的当前值,并根据开关的状态来更新图像。如果开关为 0,图像为黑色;如果开关为 1,根据 RGB 值更新图像颜色。 - 显示图像并处理退出: 使用 imshow 显示图像,使用 waitKey 检测按键事件,按 ESC 键退出循环并关闭窗口。 函数介绍: `cv2.createTrackbar` 用于在指定窗口中创建一个滑动条(Trackbar),用户可以通过拖动滑动条来调整其值。它的函数原型如下: ```python cv2.createTrackbar(name, winname, value, count, onChange) # 参数说明: # name:滑动条的名称(一个字符串),在窗口中显示。 # winname:要将滑动条添加到的窗口名称。这个窗口必须已经通过 cv2.namedWindow() 创建。 # value:滑动条的初始值。 # count:滑动条的最大值(滑动条的范围是从 0 到 count)。 # onChange:当滑动条的值发生变化时会调用的回调函数,通常可以传入一个简单的占位符函数(如 nothing(x),其中 x 为滑动条当前的值)。 ``` `cv2.getTrackbarPos` 用于获取指定滑动条的当前值。它的函数原型如下: ``` python cv2.getTrackbarPos(name, winname) # 参数说明: # name:滑动条的名称(与 createTrackbar 中设置的名称一致)。 # winname:包含滑动条的窗口名称。 # 返回值: # 返回滑动条的当前值(整数),范围为 0 到 count(在 createTrackbar 中设置的最大值)。 ``` #### 6.创建一个 Paint 程序,具有可调节颜色和笔刷半径的轨迹栏 - 创建窗口和图像 - 创建轨迹栏、鼠标回调 - `while`循环中根据轨迹栏的值更新画笔颜色 - 鼠标回调函数中根据鼠标的动作(按下、按下并移动)根据坐标对图像进行画实心圆 - `while`循环中实时更新图像显示 当前`opencv`版本中 `createTrackbar`使用事件驱动,在回调函数中更新rgb和画笔半径 使用`python`示例展示了事件驱动编程的原理和思想(`event_driven.py`) ### 核心操作 #### 1. 图像的基本操作 CPP版: 1. cv::imread 与 img.empty: - 使用 cv::imread 加载图像,检查 img.empty 判断是否加载成功。 2. 图像基本信息获取: - 使用 img.rows, img.cols, 和 img.channels() 获取图像的高、宽、通道数。 - 使用 img.total() 获取像素总数。 3. 访问与修改像素值: - 使用 img.at(y, x) 访问像素值。 - cv::Vec3b 是一个表示 3 个字节(通道)的数据结构。 4. 裁剪图像: - 使用 cv::Rect 定义裁剪区域并直接对 cv::Mat 子矩阵操作。 - 使用 clone() 方法确保裁剪结果是独立的图像。 5. 通道操作: v::split 和 cv::merge 进行通道分解和合并。 6. 图像边界填充: cv::copyMakeBorder 实现图像边界填充。 7. 显示与按键事件: - 使用 cv::imshow 显示图像。 - 使用 cv::waitKey 获取按键输入,监听退出键(ESC 键)。 #### 2. 图像的算术操作 加法、减法、按位运算 在c++中: - 加法、减法、按位运算:在 C++ 中使用 cv::add、cv::subtract 和 cv::bitwise_and/or/xor 函数实现,支持对图像与标量的运算。 - 乘法和除法:通过 convertTo 方法实现,alpha 为乘法系数,beta 为加法偏移。 - 纯黑色图像:通过 cv::Mat::zeros 创建与输入图像相同大小的黑色图像。 - 写文本:通过 cv::putText 实现,支持指定字体、颜色、位置等。 - 图像混合:使用 cv::addWeighted 进行权重混合。 ### 图像处理 #### 1. 颜色空间 hsv颜色空间转换 gray颜色空间转换 如何确认目标的hsv值范围: 选定目标像素点的坐标,打印该坐标的hsv值,确定目标的hsv值范围 ```python # 读取图像 image_path = "../roi_img.jpg" img = cv.imread(image_path) # 检查图像是否成功读取 if img is None: print(f"Error: Image not found or unable to open the image at path: {image_path}") exit(1) # 改变颜色空间 BGR -> Gray img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 改变颜色空间 BGR -> HSV img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) # 打印img_hsv某个像素点的hsv的值 print(img_hsv[227,112]) # 112,227是roi_img.jpg的一个像素点的坐标 print(img_hsv[257,135]) print(img_hsv[306,62]) print(img_hsv[211,87]) # 根据以上选点的打印值,确定目标的hsv值范围 # [125 24 63] # [101 54 81] # [98 88 32] # [ 0 0 22] # lower_blue = np.array([90,22,50]) # upper_blue = np.array([130,100,100]) ``` #### 2.图像几何变换 透视变换和仿射变换都是二维几何变换 进一步理解可以参考:[OpenCV学习笔记15_仿射变换与透视变换](https://blog.csdn.net/qq_40595787/article/details/121555358?ops_request_misc=%257B%2522request%255Fid%2522%253A%252247f1d19bed580c3b214e671fbbfa2d2b%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=47f1d19bed580c3b214e671fbbfa2d2b&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-121555358-null-null.142^v101^pc_search_result_base6&utm_term=%E4%BB%BF%E5%B0%84%E5%8F%98%E6%8D%A2%E5%92%8C%E9%80%8F%E8%A7%86%E5%8F%98%E6%8D%A2&spm=1018.2226.3001.4187) 仿射变换是一种线性变换,具有以下特点: 1. 直线保持直线:变换前后的直线仍然是直线。 2. 平行性保持:变换前后平行的直线仍然保持平行。 3. 可实现几何操作:包括平移、旋转、缩放、剪切(倾斜)等操作。 典型应用 仿射变换主要用于以下场景: 1. 图像对齐: - 将图像旋转到标准方向。 - 对图像进行统一的缩放或剪切处理。 2. 几何变换: - 旋转图像以更改角度。 - 缩放图像以调整分辨率。 3. 特征点检测的预处理: - 调整图像视角以匹配目标特征。 4. 特效制作: - 制作倾斜、拉伸等几何特效。 5. 图像增强: - 改变图像的对比度或透视感,改善图像视觉效果。 透视变换是一种非线性变换,通过三维投影来改变图像的视角,具有以下特点: 1. 直线保持直线:但平行关系不再保持。 2. 仿射变换的扩展:允许视角的扭曲,例如将矩形变成任意四边形。 3. 模拟三维视角:可以将图像看作是从不同的角度拍摄。 典型应用 透视变换在模拟三维效果和校正视角方面非常有用,典型场景包括: 1. 图像校正: - 矫正因拍摄角度造成的图像畸变,例如矫正书页、白板或地图拍摄的斜视图。 - 对建筑物拍摄的照片进行垂直矫正。 2. 俯视图生成: - 将相机拍摄的斜视角图片转换为正俯视图。 3. 特定区域提取: - 从图像中提取特定的四边形区域,例如裁剪一块车牌或二维码。 4. 增强现实(AR): - 将虚拟内容投影到真实世界的平面上。 5. 几何变换特效: - 制作具有真实感的变形效果,例如模拟镜头的透视变化 #### 3.图像阈值(二值化) 测试图像下载链接:[DIP3/e — 书籍图像下载](https://www.imageprocessingplace.com/DIP-3E/dip3e_book_images_downloads.htm),下载了第三、四章; 另,[Image Databases](https://www.imageprocessingplace.com/root_files_V3/image_databases.htm)中包含众多测试图像 1. 简单阈值 - 设置阈值为 127,将大于 127 的像素值置为 255,其他置为 0。此方法直接应用于整个图像。 2. 自适应阈值 - 根据图像的局部信息来计算每个像素的阈值,而不是全局固定一个阈值。这使得它能更好地处理那些具有不均匀光照条件的图像。 - 局部阈值:自适应阈值法通过考虑每个像素周围的局部区域(通常是一个窗口),并计算该区域的均值或加权均值来选择阈值。每个像素有一个根据局部信息计算出来的不同阈值。 - 计算过程: 1. 选择窗口大小:在每个像素周围选择一个固定大小的窗口(如 11x11 的矩阵)。 2. 计算局部均值:计算窗口内所有像素的均值。 3. 设置阈值:将当前像素与该局部均值进行比较。如果当前像素值大于均值,则设为前景(通常是 255),否则设为背景(通常是 0)。 3. OTSU阈值法(又名大津法) - OTSU 阈值法是一种基于图像直方图的自动阈值选择方法,适用于前景和背景的分离比较明显的情况。其核心思想是通过最大化类间方差来选择最佳阈值,使得前景与背景之间的区分度最大化。 - 类间方差(Between-Class Variance):OTSU 试图找到一个阈值,使得图像被分为前景和背景后,前景和背景的灰度值分布的差异(类间方差)最大。类间方差的最大值对应的阈值即为最佳阈值。 - 计算过程: 1. 计算图像的直方图:首先计算图像所有像素的灰度值分布。 2. 遍历所有可能的阈值:对于每个可能的阈值,计算分割后的前景和背景的类间方差。 3. 选择最佳阈值:选择使得类间方差最大的阈值作为最终的二值化阈值。 #### 4.图像平滑(又名图像滤波、图像模糊) - 均值平滑:均值替换邻域中心像素,简单快速,但对噪声敏感。 - 高斯平滑:加权平均值替换邻域中心像素,能去除噪声并保持边缘,适用于一般的噪声去除任务。 - 中值滤波:邻域内所有像素的中值替换中心像素,去除椒盐噪声效果好,保留边缘,但计算量较大。 - 双边滤波:使用一个空间高斯核和一个灰度高斯核,结合这两个核进行加权平均,距离和颜色相似性都会影响权重,去噪效果好,保留边缘,但计算复杂度较高。 - 方框滤波:在每个像素周围定义一个矩形窗口,对窗口内所有像素值进行均值化,得到新的像素值。与均值平滑类似,但可能引入更多的模糊。 - 扩展卷积:使用自定义的卷积核对图像进行卷积,改变每个像素的值。灵活,适用各种图像处理任务,如果没有合适的卷积核,可能导致结果不理想。 - 图像金字塔:会改变图像尺寸,在多个分辨率下对图像进行平滑,从高分辨率到低分辨率逐渐模糊图像,适用于多尺度图像处理任务。在 OpenCV 中,使用 cv2.pyrDown() 和 cv2.pyrUp() 函数可以分别实现金字塔的下采样和上采样。cv2.pyrDown() 将图像的分辨率降低一半,cv2.pyrUp() 则是将图像的分辨率增大一倍。 #### 5.形态学运算 - 腐蚀操作通常用于消除图像中的小的白色区域(前景物体)。它通过滑动结构元素(通常是一个小的矩形、圆形或椭圆形)在图像上,只有当结构元素完全适合在原图的前景部分时,像素值才会被保留。结果是前景物体会变小。 - 膨胀操作是腐蚀的反操作,它会扩展图像中的前景物体。它通过在图像上滑动结构元素,并将结构元素覆盖区域的像素设置为前景(通常是白色)。膨胀通常用于填充小的黑洞或连接两个前景物体。 - 开运算是先腐蚀后膨胀的组合操作`(开运算(Opening) = 腐蚀 + 膨胀)`。其作用是去除图像中的小物体或噪声。它主要用于分离物体、去除细小的噪声以及平滑物体的边界。 - 闭运算是先膨胀后腐蚀的组合操作`(闭运算(Closing) = 膨胀 + 腐蚀)`。它主要用于填充图像中的小空洞,连接物体之间的细小断裂部分,或者平滑物体的边缘。 - 形态梯度是膨胀与腐蚀的差值`(形态梯度 = 膨胀 - 腐蚀)`,常用于边缘检测。它可以帮助突出显示图像中物体的边缘,尤其是在物体边界部分。 - 顶帽操作是原始图像与开运算结果之间的差异`(顶帽 = 原始图像 - 开运算结果)`。它主要用于提取图像中的细小白色区域,通常用于亮区或小物体的提取。 - 黑帽操作是闭运算结果与原始图像之间的差异`(黑帽 = 闭运算结果 - 原始图像)`。它主要用于提取图像中的细小黑色区域,通常用于暗区或物体的凹陷部分。 #### 6.边缘\梯度\高通滤波 OpenCV 提供了多种梯度滤波器和高通滤波器,用于图像处理中的边缘检测、图像增强、噪声抑制等任务。 - OpenCV 提供的梯度滤波器和高通滤波器包括: - Sobel 滤波器:用于计算图像的水平和垂直梯度,常用于边缘检测。 - Scharr 滤波器:Sobel 的优化版本,提供更高精度的边缘检测。 - Laplacian 滤波器:用于检测图像的二阶梯度,通常用于边缘检测和细节提取。 - Canny 边缘检测:结合了多种技术,常用于边缘检测。 - 高通滤波器:用于突出图像中的细节和边缘,常通过差分操作或频域滤波来实现。 1. Sobel 滤波器 (Sobel Filter) - Sobel 滤波器用于计算图像的梯度,常用于边缘检测。它可以分别计算图像在水平(x 轴)和垂直(y 轴)方向上的梯度。Sobel 滤波器是一个离散的近似算子,可以帮助突出图像中的边缘。 2. Scharr 滤波器 (Scharr Filter) - Scharr 滤波器是 Sobel 滤波器的一种变体,具有更高的精度。它通过优化的卷积核在计算梯度时可以获得更高的信噪比。 3. Laplacian 滤波器 (Laplacian Filter) - Laplacian 滤波器是一个二阶导数算子,通常用于检测图像的边缘和快速变化区域。它通过计算像素点的拉普拉斯算子(即二阶导数)来突出显示边缘。 4. Canny 边缘检测 (Canny Edge Detection) - Canny 边缘检测算法是一种多阶段的边缘检测算法,它结合了高斯滤波、梯度计算、非极大值抑制和边缘连接等技术。它被认为是最经典且高效的边缘检测算法之一。 - Canny 边缘检测的步骤: 1. 使用高斯滤波器平滑图像。 2. 计算梯度(通常使用 Sobel 滤波器)。 3. 进行非极大值抑制。 4. 使用双阈值法和边缘连接算法确定边缘。 5. 高通滤波器 (High-pass Filter) - 高通滤波器是一个叫法或者统称,Sobel滤波器和Laplacian滤波器都可以视为高通滤波器; - 高通滤波器通过抑制低频信息,突出图像中的细节和边缘。它可以通过某些变换,例如傅里叶变换,或者直接使用差分操作来实现。 - 常见的高通滤波器: 1. 差分滤波器(比如 Sobel、Scharr)。 2. 拉普拉斯滤波器(Laplacian)。 6. 频域滤波(Frequency Domain Filtering) - 通过傅里叶变换(FFT)将图像转换到频域,然后使用高通滤波器来去除低频部分,仅保留高频部分。这通常用于图像增强、噪声去除等。 - 频域滤波的基本步骤: 1. 傅里叶变换:将图像从空间域转换到频域。频域中,低频部分通常代表图像的平滑区域,高频部分代表图像的边缘和细节。 2. 滤波操作:在频域上进行滤波操作,比如应用高通滤波器、低通滤波器等。 3. 反傅里叶变换:将处理后的频域图像转换回空间域,得到最终结果。 - 频域滤波的常见滤波器: 1. 低通滤波器:去除图像中的高频噪声,仅保留低频部分。常用于去噪。 2. 高通滤波器:去除图像中的低频部分,仅保留高频部分,常用于边缘检测和细节增强。 3. 带通滤波器:保留某一频率范围的信号,通常用于特定的频率增强。 #### 7. 图像轮廓 在 OpenCV 中,轮廓(Contour)相关的知识点非常丰富,主要涵盖轮廓的检测、处理、分析和绘制等操作。 ##### 1. 轮廓检测 - 轮廓检测主要基于图像中像素的灰度变化等信息来进行,步骤如下: 1. 图像预处理,包括灰度化处理、滤波操作; 2. 边缘检测, 常用Canny算子初步获得边缘图像; 3. 轮廓查找,基于得到的边缘图像,利用一些算法来连接边缘像素形成轮廓,Opencv中常用基于层次结构的轮廓查找算法 - 应用: 1. 目标识别与定位,通过检测物体的轮廓,可以大致确定物体在图像中的位置以及物体的形状等信息,从而识别出不同类型的目标。比如在工业生产线上,检测产品零部件的轮廓来判断其摆放姿态是否正确、型号是否符合要求等,帮助实现自动化的产品分拣和质量检测。 2. 图像分割,轮廓可以作为分割图像不同区域的依据,将图像中感兴趣的物体轮廓提取出来后,就可以把物体所在区域与背景区域或者其他物体所在区域区分开来,便于后续对特定物体单独进行分析、处理,像医学图像中分割出病变组织的轮廓,进而对病变的大小、形状等特征做进一步研究。 3. 形状匹配,对比检测到的轮廓形状和已知的标准形状轮廓,来判断物体是否符合特定的形状要求,常用于安防监控领域,例如识别监控画面中出现的特定形状物体(如人形轮廓等),判断是否有异常情况发生;也用于图形验证码识别等场景中,通过匹配轮廓形状来识别验证码中的图形元素。 4. 在一些场景下,通过检测出不同物体的轮廓,然后对轮廓数量进行统计,就可以得知物体的个数,比如在农业领域统计果园里果实的数量、在交通场景中统计道路上车辆的数量等。 - 示例: ``` python import cv2 # 读取图像,这里以读取同目录下名为test.jpg的图像为例,你可以替换为自己的图像路径 image = cv2.imread('test.jpg') # 将图像转换为灰度图像,因为很多轮廓检测操作在灰度图上进行更方便 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 进行高斯滤波,去除噪声,使后续轮廓检测更准确 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 使用Canny边缘检测算法获取边缘图像 edged = cv2.Canny(blurred, 30, 150) # 查找轮廓,cv2.RETR_EXTERNAL表示只检测最外层轮廓,cv2.CHAIN_APPROX_SIMPLE对轮廓点进行压缩简化 contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 绘制轮廓,这里将检测到的轮廓绘制到原始彩色图像上,颜色为红色(BGR格式下(0, 0, 255)),线条粗细为2像素 cv2.drawContours(image, contours, -1, (0, 0, 255), 2) # 显示结果图像 cv2.imshow('Contour Detection Result', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ##### 2. 轮廓分析 [04_img_ps/cpp/src/07_img_outline.cpp](04_img_ps/cpp/src/07_img_outline.cpp) 中展示了一个应用:识别图片[box.png](04_img_ps/box.png)中的的蓝色盒子 ![alt text](04_img_ps/Blue Rectangles Detection.png) 1. 图像矩,(Image Moments)是对图像中像素分布特征进行量化描述的一组数学特征量,也可以说是归一化的灰度图像的二维随机变量的概率密度,是一个统计学特征,它是基于图像的灰度值以及像素的坐标位置等信息计算得出的, 常见的图像矩包括零阶矩、一阶矩、二阶矩等。 - 零阶矩:它反映了图像的总灰度值(或者说图像的面积,对于二值图像而言),其计算通常是对图像所有像素的灰度值进行求和(在二值图像中,相当于统计白色像素点的数量)。 - 一阶矩:一阶矩与图像的质心位置相关,通过计算图像灰度值与对应坐标的加权和等方式,来确定图像在水平和垂直方向上的质心坐标,进而可以了解图像中物体大致的中心位置。 - 二阶矩:二阶矩可用于描述图像的方向、离心率等特征,它涉及到图像灰度值与坐标乘积的更复杂计算,比如通过计算协方差矩阵等,进而分析图像的形状变化趋势等情况。 2. 轮廓面积,使用`cv2.contourArea`函数计算轮廓的面积; 3. 轮廓周长,使用` cv2.arcLength` 函数计算轮廓的周长; 4. 轮廓近似(Contour Approximation),是一种将复杂轮廓简化为具有较少顶点的近似轮廓的技术,主要基于 [Douglas-Peucker](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) 算法,使用`cv2.approxPolyDP(curve, epsilon, closed[, approxCurve])`函数实现,具体步骤如下: - 初始条件:将轮廓的起始点和结束点作为直线段的两个端点,计算轮廓上所有点到该直线段的距离。 - 寻找最远点:找出距离该直线段最远的点,若该距离大于预先设定的阈值,则该点为必须保留的点。 - 递归操作:将曲线划分为两段,分别是从起始点到最远点,以及从最远点到结束点。对这两段曲线重复上述步骤,直到没有距离超过阈值的点为止。 5. 凸包(Convex Hull),是一个包含轮廓上所有点的最小凸多边形,简单来说,如果你将轮廓上的所有点看作是平面上的一组钉子,用一根橡皮筋环绕这些钉子,橡皮筋形成的形状就是凸包。凸包的特点是其内部任意两点的连线都在凸包的内部,不会有凹陷部分。在 OpenCV 中,用于计算凸包的主要函数是 `cv2.convexHull`。 - 可以使用`cv2.isContourConvex`函数检查曲线是否为凸多边形。它只是返回True还是False - 应用方向:凹凸性判断、特征提取、目标检测简化表示、姿态估计、碰撞检测(游戏) 6. 边界矩形 - 直角矩形(Bounding Rectangle),直角矩形也被称为外接矩形(Bounding Rectangle),是指能够完全包围轮廓或一组点的最小矩形,其边与图像的坐标轴平行。`cv2.boundingRect(points)`:该函数用于计算轮廓或点集的外接直角矩形, - `points`:输入的点集,通常是由 `cv2.findContours` 函数找到的轮廓点集。 - 返回值:一个元组 `(x, y, w, h)`,其中 `x` 和 `y` 是矩形左上角的坐标,`w` 是矩形的宽度,`h` 是矩形的高度。 - 旋转矩形(Rotated Rectangle),旋转矩形是指能够完全包围轮廓或一组点的最小矩形,但它可以旋转一定的角度,以最小化矩形的面积。`cv2.minAreaRect(points)`:该函数用于计算轮廓或点集的最小旋转矩形。 - `points`:输入的点集,通常是由 `cv2.findContours` 函数找到的轮廓点集。 - 返回值:一个元组 `(center, (width, height), angle)`,其中 `center` 是矩形的中心坐标(以浮点数表示),`(width, height)` 是矩形的尺寸,`angle` 是旋转角度(以度为单位,正值表示逆时针旋转)。 7. 最小外圆(Minimum Enclosing Circle),在 OpenCV 中,最小外圆是指能够完全包围轮廓或一组点的最小圆形。c`v2.minEnclosingCircle(points)`:该函数用于计算轮廓或点集的最小外圆。 - points:输入的点集,通常是由 `cv2.findContours` 函数找到的轮廓点集。 - 返回值:一个元组 `(center, radius)`,其中 `center` 是圆心的坐标(以浮点数表示),radius 是圆的半径。 8. 拟合椭圆(Fitting an Ellipse),拟合椭圆是指根据轮廓或一组点找到一个最接近的椭圆,该椭圆可以通过轮廓或点集的分布来估计。`cv2.fitEllipse(points)`:该函数用于根据轮廓或点集拟合椭圆。 - points:输入的点集,通常是由 `cv2.findContours` 函数找到的轮廓点集。 - 返回值:一个元组 `(center, (major_axis, minor_axis), angle)`,其中 `center` 是椭圆中心的坐标(以浮点数表示),`(major_axis, minor_axis)` 是椭圆长轴和短轴的长度,`angle` 是椭圆旋转的角度(以度为单位,顺时针方向)。 9. 拟合线(Fitting a Line),拟合线是指根据一组点找到一条最能代表这些点分布趋势的直线。`cv2.fitLine(points, distType, param, reps, aeps)`:该函数用于根据一组点拟合直线。 - 函数参数和返回值 - `points`:输入的点集,通常是由` cv2.findContours` 函数找到的轮廓点集或其他点集。 - `distType`:距离类型,用于计算点到直线的距离,例如 `cv2.DIST_L2` 表示使用欧几里得距离。 - `param`:距离参数,对于某些距离类型可能需要此参数,通常设置为 0。 - `reps`:径向的精度参数,一般设置为 0.01。 - `aeps`:角度精度参数,一般设置为 0.01。 - 返回值:一个数组 `(vx, vy, x0, y0)`,其中 `(vx, vy) `是直线的方向向量,`(x0, y0) `是直线上的一个点。 ##### 3. 轮廓属性 矩、质心,面积,周长,凸包,边界矩形,外接圆、轮廓的近似也属于属性,但在上面已经提到。 1. 长宽比 (Aspect Ratio) - 定义:轮廓的长宽比是指轮廓的外接矩形的宽度与高度之比。 - 用途:长宽比可以帮助区分不同形状的物体,例如判断物体是接近正方形还是矩形,或者是长条形的。 - 函数:`cv2.minAreaRect()` - 用法:该函数返回一个最小外接矩形的信息(包括中心、长宽和旋转角度)。长宽比是矩形宽度与高度的比值。 2. 范围 (Extent) - 定义:范围是轮廓面积与其外接矩形面积的比值。 - 用途:该特征可以用来判断轮廓是否接近紧凑,或者是否有大量的空白区域。范围值较小表示物体比较不规则,较大则表示物体的形状接近外接矩形。 - 函数:`cv2.contourArea()` 和 `cv2.boundingRect()` - 用法:计算轮廓面积与外接矩形面积的比值。 3. 固实性 (Solidity) - 定义:固实性是轮廓面积与其凸包面积的比值。 - 用途:固实性可以帮助判断轮廓是否完整或有孔洞。例如,固实性接近1表示轮廓完整,接近0则表示轮廓内部有空洞或缺失。 - 函数:`cv2.contourArea()` 和` cv2.convexHull()` - 用法:计算轮廓面积与其凸包面积的比值。 4. 等效直径 (Equivalent Diameter) - 定义:等效直径是一个圆形的直径,其面积与轮廓的面积相同。 - 用途:该特征可以用来估计物体的大小。通过等效直径,可以将复杂的轮廓简化为一个圆形,方便比较不同轮廓的大小。 - 函数:`cv2.contourArea()` - 用法:通过轮廓面积计算等效直径。 5. 方向 (Orientation) - 定义:方向是轮廓的主轴的角度,它描述了轮廓的朝向。 - 用途:方向可以帮助识别物体的旋转状态,或者判断物体的倾斜角度。 - 函数:`cv2.fitEllipse()` - 用法:该函数拟合一个椭圆到轮廓,并返回椭圆的旋转角度。 6. 遮罩和像素点 (Mask and Pixels) - 定义:遮罩是一个与轮廓相匹配的二值图像,表示轮廓区域,像素点则是轮廓内的所有像素坐标。 - 用途:通过遮罩和像素点,可以提取轮廓区域的像素信息,进行进一步的分析或处理。 - 函数:`cv2.drawContours()` 和 `cv2.findContours()` - 用法:通过 `cv2.drawContours()` 创建轮廓的遮罩,像素点通过 `cv2.findContours()` 提取。 7. 最大值、最小值及其位置 (Min/Max Values and Positions) - 定义:最大值和最小值分别是轮廓区域内的最大亮度值和最小亮度值,位置是对应的坐标。 - 用途:通过最大值和最小值,可以了解轮廓区域的亮度变化情况,最小值和最大值的位置则可以提供轮廓的边界信息。 - 函数:`cv2.minMaxLoc()` - 用法:可以用于获取轮廓区域内的最大值、最小值及其位置。 8. 平均颜色或平均强度 (Mean Color or Mean Intensity) - 定义:轮廓区域内所有像素的颜色或强度的平均值。 - 用途:平均颜色或强度可以用来描述轮廓区域的亮度或颜色特征,常用于颜色分析或物体识别。 - 函数:`cv2.mean()` - 用法:计算轮廓区域的平均颜色或强度。 9. 极端点 (Extreme Points) - 定义:极端点指的是轮廓的最左、最右、最上和最下的点,通常通过轮廓的边界来确定。 - 用途:极端点可以用于计算轮廓的边界框,也可用来对轮廓进行旋转、平移等几何变换的操作 - 函数:`cv2.convexHull()` - 用法:计算轮廓的极端点(最左、最右、最上、最下的点)。 ##### 4. 更多功能 1. 凸包缺陷 - 当一个轮廓不是凸形时,它可能有一些“凹陷”部分,这些凹陷部分就是凸包缺陷。凸包缺陷通常由一个点(缺陷的深度点)和凸包上的两个点(起点和终点)定义。 - 在凸包缺陷中,每个缺陷可以用以下三个点描述: 1. 起点(Start Point):凸包上凹陷开始的点。 2. 终点(End Point):凸包上凹陷结束的点。 3. 深度点(Defect Point):凹陷区域内离凸包最远的点。 - 凸包缺陷的用途:形状分析、手势识别、物体检测 - OpenCV 中的凸包缺陷计算: - 计算轮廓:用 `cv2.findContours() `获取轮廓。 - 计算凸包:用 `cv2.convexHull() `获取凸包。 - 计算缺陷:用 `cv2.convexityDefects()` 获取凸包缺陷。 - 参考:[凸缺陷 convexityDefects](https://blog.csdn.net/m0_49302377/article/details/130931581) 2. 点多边形测试(Point Polygon Test) - 点多边形测试是指判断一个点与某个轮廓(或多边形)的空间关系,通常用来判断点是否位于多边形内部、外部,或者在边界上。 - OpenCV 提供了 `cv2.pointPolygonTest()` 函数,可以直接进行点多边形测试,它返回的距离为:当点在轮廓外时为负;当点在轮廓内时为正;如果点在轮廓上,则返回零。 - 用途: 1. 计算点到轮廓边界的最近距离,应用于形状分析; 2. 判断一个点是否位于某个感兴趣区域(ROI)内 3. 匹配形状(Shape Matching) - OpenCV 提供的形状匹配函数主要是 `cv::matchShapes()`。它基于形状描述符计算两个轮廓的相似性。 - 形状匹配使用了 Hu 矩(Hu Moments),它是基于图像矩的一组七个不变特征,可以用于描述形状的特征: - 与缩放、旋转、平移无关。 - 可以通过计算 Hu 矩来比较形状。 - 应用场景:目标检测、形状分类、图像分割后形状分析 ##### 5. 轮廓层次(Contour Hierarchy) - 相关解释可以参考: - [OpenCV学习笔记-轮廓的层次结构](https://wendao.blog.csdn.net/article/details/80472043?fromshare=blogdetail&sharetype=blogdetail&sharerId=80472043&sharerefer=PC&sharesource=qq_32939413&sharefrom=from_link) - [轮廓层次](https://apachecn.github.io/opencv-doc-zh/#/docs/4.0.0/4.9-tutorial_py_contours) - 在 OpenCV 中,轮廓层次是通过 cv::findContours 函数返回的一个附加输出,该输出是一个数组,其中每个元素对应一个轮廓的层次信息。 - 轮廓层次是一个多层次的数据结构,其中每个轮廓都有四个值,通常表示为:`[Next, Previous, FirstChild, Parent]` - Next: 当前轮廓的下一个轮廓的索引(如果存在)。 - Previous: 当前轮廓的前一个轮廓的索引(如果存在)。 - FirstChild: 当前轮廓的第一个子轮廓的索引(如果存在)。 - Parent: 当前轮廓的父轮廓的索引(如果存在)。 - 轮廓层次的主要应用是在检测和分析嵌套形状时。比如,当你需要区分不同层级的轮廓,或者处理轮廓嵌套的情况时,轮廓层次非常有用。常见的应用场景包括:嵌套轮廓的分析、复杂形状的分割与处理、面向对象的检测、过滤和提取特定类型的轮廓、图像修复与填充 #### 8. 直方图 直方图经常使用matplotlib显示,所以先不考虑cpp版本 直方图的定义:图像的直方图是图像中每个像素值的频率分布。假设一个灰度图像的像素值范围从 0 到 255,那么它的直方图就是一个 256 维的向量,其中每个元素表示对应灰度值的像素数量。 ##### 1.绘制直方图 opencv使用 `cv.calcHist()` 函数查找直方图 ``` python cv2.calcHist(images, channels, mask, histSize, ranges) ``` - images:输入图像,传入时应该用中括号 `[]` 括起来。例如:`[img]` - channels:传入图像的通道,传入时应该用中括号 `[]` 括起来。例如:`[0]` 表示传入的是灰度图像 - mask:掩膜,一般用 `None` 表示没有使用掩膜 - histSize:BINS 的数量,用中括号 `[]` 括起来。例如:`[256]` - ranges:像素值的范围,一般用 `[0, 256]` 表示 0 到 255 - 返回值:返回的是一个二维数组,其中第一维表示 BINS 的数量,第二维表示像素值的范围。 ![hist](04_img_ps/hist.png "hist") ##### 2.直方图均衡化 直方图均衡化(Histogram Equalization)是一种增强图像对比度的技术,目的是使得图像的灰度分布更加均匀,通常用于改善图像的细节,使得图像中亮部和暗部的细节更加可见。 均衡化过程: - 对图像的直方图进行重分布,使得图像的像素值更均匀地分布在整个灰度范围 [0, 255] 上。 - 计算图像的累积分布函数(CDF,Cumulative Distribution Function),它反映了每个灰度级的累计像素数。 - 通过累积分布函数将原图像的灰度值映射到新的灰度值,使得像素值的分布更加均匀,从而达到增强图像对比度的目的。 直方图均衡化的作用:增强图像对比度、提高细节可见性、自动化增强 函数:`cv2.equalizeHist()`,src: 输入图像,必须是单通道图像(灰度图),并且其数据类型必须为 np.uint8(即像素值在 0-255 之间)。 均衡化前后: ![equalizeHist](04_img_ps/equalizeHist.png) 图像效果: ![equalizeHist2](04_img_ps/equalizeHist2.png) 但是均衡化后,会出现大量细节信息丢失,如部分区域亮度过低导致丢失,部分区域亮度过高导致丢失,所以需要使用自适应直方图均衡化。 ##### 3.自适应直方图均衡化(CLAHE) 自适应直方图均衡化(CLAHE, Contrast Limited Adaptive Histogram Equalization)是一种局部图像增强技术,旨在提高图像的局部对比度,同时避免全局直方图均衡化可能导致的过度增强问题。 主要通过以下步骤实现: 1. 局部处理:将图像划分为若干个小块(称为 "tiles"),每个小块内分别进行直方图均衡化。每个小块(区域)的直方图单独均衡,减少了全局均衡化带来的问题。 2. 对比度限制(Clip Limit):为了避免直方图均衡化时过度增强某些区域的对比度,CLAHE 设定了一个对比度限制(clipLimit)。如果某个区域的局部直方图的某个灰度值的频数超过了设定的阈值(clipLimit),则会对这个区域进行裁剪,避免局部对比度过高。然后,通过插值方法调整使得灰度值的频数符合要求。 3. 插值:在块之间进行插值处理,确保各个小块之间平滑过渡,避免出现不自然的边界。 可以使用 OpenCV 的 cv2.createCLAHE() 函数,它可以自适应地对图像进行直方图均衡化,从而提高图像的对比度。 函数:`cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))`: - clipLimit: 对比度限制,控制局部增强的程度。值越大,局部增强的效果越强。 - tileGridSize: 块大小,图像被分割为块,每个块单独进行直方图均衡化。块越小,局部均衡化效果越明显。 ![CLAHE](04_img_ps/CLAHE.png "CLAHE") ##### 4. 直方图反投影(Back Projection)