opencv计算机视觉基础教程

opencv计算机视觉基础教程
🖥️ OpenCV计算机视觉基础教程
📖 1.1 OpenCV简介
1.1.1 OpenCV主要功能及模块介绍
功能 模块 描述
内置数据结构和输入/输出 core 提供基本数据结构,如Image、Point、Rectangle等,支持图像文件的读写。
图像处理操作 imgproc 提供图像过滤、几何变换、绘图、色彩空间转换等功能。
图形用户界面操作 highgui 支持创建窗口、显示图像或视频、响应键盘和鼠标事件等。
视频分析 video 分析视频中的运动、目标跟踪和视频稳定处理。
3D重建 calib3d 根据2D图像创建3D场景。
特征提取 features2d 检测和提取2D图像中的对象特征。
对象检测 objdetect 在图像中检测给定图像的位置。
机器学习 ml 包含多种机器学习算法,如k近邻、k均值聚类、支持向量机、神经网络等。
深度学习 DNN 支持深度学习框架,如Caffe、TensorFlow、Torch、Darknet等。
计算摄影 photo 改善相机拍摄的图像,如高动态范围成像、全景图像等。
形态分析 shape 识别对象形状、分析形状之间的相似性等。
人脸检测和识别 face 实现人脸检测、特征检测和识别功能,使用Haar级联分类器和深度学习算法。
表面匹配 surface_matching 提供3D对象识别和姿态估计算法。
文本检测和识别 text 用于识别和检测图像中的文本,相关应用如车牌识别、内容数字化等。
1.1.2 OpenCV的版本
版本 发布年份 主要更新内容
OpenCV 1.0 2006年10月 首次发布,增加Python模块、图像修复功能、对JPEG2000和EXR支持等。
OpenCV 2.0 2009年9月 修复Windows安装包、增加新的Python接口。
OpenCV 3.0 2015年6月 不再向后兼容,修复200多处错误,增加并行处理功能。
OpenCV 4.0 2018年 成为C++ 11库,删除1.x版本中的许多C函数,增加对Mask-RCNN模型的支持。
OpenCV 4.5.0 2020年10月 许可证更改为Apache 2,增加对OpenCL多个上下文的支持。
OpenCV 4.5.2 2021年4月 持续支持OpenCV 3.X版本更新。
1.1.3 OpenCV-Python
OpenCV-Python是由原始OpenCV C++实现的Python包装器,是OpenCV库的Python接口。
优点:
代码运行速度与原始C/C++代码一样快。
用Python编写代码更容易,具有语法简洁、易于学习等特点。
注意:
OpenCV-Python需要使用NumPy库,图像数据存储在NumPy数组中。
🔧 1.2 配置开发环境
1.2.1 安装Python
从Python官网下载安装Python。
1.2.2 安装NumPy
执行命令:pip install numpy 安装NumPy包。
1.2.3 安装OpenCV-Python
安装OpenCV-Python有三种方式:
pip安装方式
安装官方预编译包方式
源代码安装方式
1.2.4 安装Visual Studio Code
Visual Studio Code(简称“VS Code”)是微软提供的一个免费的集成开发工具。
特点:支持智能感知、运行、调试、内置Git等。
在Windows中安装VS Code。
📚 1.3 使用OpenCV文档和示例
1.3.1 查看OpenCV文档
在OpenCV官网查看文档。
1.3.2 查看OpenCV-Python示例
OpenCV官方在线文档的“Examples”页面中列出示例的C++代码目录。
获取示例代码可访问OpenCV发布页面,下载源代码,示例代码在samples/python文件夹中。
🧪 1.4 实验
1.4.1 实验1:配置虚拟开发环境
实验目的:掌握Python+OpenCV应用开发虚拟环境的配置方法。
实验内容:
创建Python虚拟环境。
在虚拟环境中安装NumPy和OpenCV-Python包。
移动虚拟环境文件夹的位置,测试虚拟环境能否正常使用。
1.4.2 实验2:在VS Code中运行示例
实验目的:掌握使用VS Code运行OpenCV示例的方法。
实验内容:在VS Code中运行边缘检测示例edge.py。
🖥️ OpenCV计算机视觉基础教程
📚 第2章 图像处理基础
本章主要内容:
NumPy简介
图像基础操作
图像运算
实验
📖 2.1 NumPy简介
本节主要内容:
数据类型
创建数组
数组形状
索引、切片和迭代
数组运算
2.1.1 数据类型
NumPy比Python支持更多的数据类型,具体如下:
数据类型 说明
np.bool_ 布尔值(True或False),存储为字节
np.byte 由平台定义
np.ubyte 由平台定义
np.short 由平台定义
np.ushort 由平台定义
np.intc 由平台定义
np.uintc 由平台定义
np.int_ 由平台定义
np.uint 由平台定义
np.longlong 由平台定义
np.ulonglong 由平台定义
np.half 半精度浮点数:1位符号、5位指数、10位尾数
np.single 平台定义的单精度浮点数:通常为1位符号、8位指数、23位尾数
np.double 平台定义的双精度浮点数:通常为1位符号、11位指数、52位尾数
np.longdouble 平台定义的扩展精度浮点数
np.csingle 复数,由两个单精度浮点数(实部和虚部)表示
np.cdouble 复数,由两个双精度浮点数(实部和虚部)表示
np.clongdouble 复数,由两个扩展精度浮点数(实部和虚部)表示
NumPy数据类型别名
NumPy还提供了一组表示固定大小的数据类型别名,具体如下:
数据类型别名 说明
np.int8 带符号的8位整数(-128~127)
np.int16 带符号的16位整数(-32768~32767)
np.int32 带符号的32位整数(-231~231-1)
np.int64 带符号的64位整数(-263~263-1)
np.uint8 无符号的8位整数(0~255)
np.uint16 无符号的16位整数(0~65535)
np.uint32 无符号的32位整数(0~2^32-1)
np.uint64 无符号的64位整数(0~2^64-1)
np.intp 用于索引的整数
np.uintp 足够大以容纳指针的整数
np.float32 32位单精度浮点数
np.float64、np.float_ 与Python的float类型的精度匹配
np.complex64 复数,由两个32位浮点数(实部和虚部)表示
np.complex128、np.complex_ 与Python的complex类型的精度匹配
在Python中使用NumPy数据类型的示例代码
import numpy as np # 导入NumPy包,np是按惯例使用的名称,也可为其他名称
a = np.int8(123) # 定义一个整数
type(a) # 查看其数据类型
2.1.2 创建数组
使用array()函数创建数组
NumPy的array()函数可将Python中类似数组的数据结构(如列表和元组)转换为数组,示例代码如下:
a = np.array([1, 2, 3]) # 将列表转换为数组
print(a) # 输出数组

输出: [1 2 3]

type(a) # 查看数组的数据类型
a = np.array((1, 2, 3)) # 将元组转换为数组
a = np.array(([1, 2, 3], [4, 5, 6])) # 将嵌套数据转换为数组
print(a) # 输出数组

输出: [[1 2 3]

[4 5 6]]

📊 NumPy数组创建与操作

1. 创建数组

1.1 使用 np.array() 函数创建数组

  • 示例代码:
    a = np.array(([1, 2, 3], [4, 5]))  # 将不规则的数据转换为数组
    print(a)
    

注意:在将嵌套的多维数据转换为数组时,同维度数据的个数应该相同;否则,NumPy会将其作为一个Python对象放入数组。

1.2 使用 zeros() 函数创建数组

  • 创建指定形状的数组,数组元素默认值为0,数据类型默认为float。
  • 示例代码:
    np.zeros((2, 3))          # 创建2行3列的二维数组
    np.zeros((2, 5), dtype=int)  # 用dtype参数指定数组元素的数据类型
    np.zeros((2, 3, 4))      # 创建三维数组
    

1.3 使用 arange() 函数创建数组

  • 创建元素值按规则递增的数组,类似于Python的 range() 函数。
  • 示例代码:
    np.arange(5)              # 元素取值范围为[0, 4]
    np.arange(-2, 5)          # 元素取值范围为[-2, 4]
    np.arange(5.6)            # 数组元素的数据类型默认与参数一致
    np.arange(-2, 2, dtype=float)  # 用dtype参数指定数组元素的数据类型
    

1.4 使用 linspace(a, b, c) 函数创建数组

  • 创建由参数 c 指定元素数量的数组,其第一个元素为 a,最后一个元素为 b,相邻元素的差值为 b−ac−1\frac{b-a}{c-1}c1ba
  • 示例代码:
    np.linspace(1, 5, 6)
    

1.5 使用 indices() 函数创建数组

  • 创建一个有两个元素的一维数组,每个元素都是一个指定形状的数组,其元素值表示该维的变化。
  • 示例代码:
    np.indices((3, 4))  # 数组元素是一个大小为3×4的数组
    

1.6 使用 ones() 函数创建数组

  • ones() 函数用于创建元素值为1的数组(单位矩阵)。
  • 示例代码:
    np.ones((5,), dtype=int)  # 创建一维数组,元素值为整数1
    np.ones((5,))              # 创建一维数组,元素值为浮点数1.0
    np.ones((2, 5))            # 创建大小为2×5的二维数组
    

2. 数组的形状

2.1 查看和改变数组的形状

  • 数组对象的 shape 属性可用于查看或改变数组的形状。
  • 示例代码:
    a = np.arange(12)        # 创建一维数组,其中共有12个元素
    a.shape                  # 查看数组形状
    a.shape = (2, -1)        # 更改数组形状为2行,-1表示每行中的元素个数自动计算
    

2.2 reshape() 方法

  • reshape() 方法可更改数组形状,并返回更改后的新数组。
  • 示例代码:
    a.reshape((3, -1))  # 更改数组形状,返回新数组
    

2.3 resize() 方法

  • resize() 方法的 refcheck 参数为 False 时,可在改变形状的同时更改元素个数。
  • 示例代码:
    a.resize((3, 4))            # 更改形状
    a.resize((2, 3), refcheck=False)  # 更改形状并减少元素个数
    a.resize((2, 5), refcheck=False)  # 增加元素个数
    

2.4 np.ravel() 函数

  • 将数组转换为一维数组。
  • 示例代码:
    np.ravel(a)                    # 默认行优先
    np.ravel(a, order='F')         # 列优先
    

3. 数组索引、切片和迭代

3.1 一维数组操作

  • 示例代码:
    rng = np.random.default_rng()    # 获得随机数生成器
    a = rng.integers(10, size=8)
    a[0]            # 索引:第1个元素
    a[-1]           # 索引:最后1个元素
    a[2:5]         # 切片
    for x in a:    # 迭代
        print(x, end=' ')
    

3.2 多维数组操作

  • 用以逗号分隔的多个值进行索引。
  • 示例代码:
    rng = np.random.default_rng()
    a = rng.integers(10, size=(2, 5))  # 创建一个大小为2×5的数组
    a[0, 0]            # 索引:第1行第1个元素
    a[1, 0]            # 索引:第2行第1个元素
    a[0, :3]           # 切片:第1行前3个元素
    for x in a:       # 迭代
        print(x)
    

4. 数组运算

4.1 算术与比较运算

  • NumPy数组与常量执行算术运算和比较运算时,会对每个数组元素执行计算。
  • 示例代码:
    a = np.arange(5)
    a + 5                        # 每个元素加上5
    a - 5                        # 每个元素减去5
    a * 5                        # 每个元素乘以5
    a ** 2                       # 每个元素求平方
    a / 2                        # 每个元素除以2,结果为浮点数
    a // 2                       # 每个元素除以2,结果为整数
    a < 2.5                      # 每个元素执行比较运算
    

4.2 矩阵运算

  • 两个数组执行算术运算时,“*”运算符用来计算元素乘积,“@”运算符和 dot() 方法用来计算矩阵乘积。
  • 示例代码:
    a = np.array([[1, 2], [3, 4]])
    b = np.array([[10, 0], [0, 10]])
    a + b                # 矩阵加法
    b - a                # 矩阵减法
    a @ b                # 矩阵乘法
    a.dot(b)             # 矩阵乘法
    a.T                   # 矩阵转置
    

4.3 赋值运算

  • NumPy支持 +=*= 等赋值运算,且会用计算结果覆盖原数组。
  • 示例代码:
    a += 10
    a *= 2
    

4.4 数组计算方法

  • NumPy为数组提供了一些执行计算的方法。
  • 示例代码:
    a.min()            # 求最小元素值
    a.max()            # 求最大元素值
    a.sum()            # 求所有元素和
    

4.5 使用 axis 参数

  • 可设置 axis 参数以便按指定的轴执行计算。
  • 示例代码:
    a.max(axis=0)        # 返回最大值所在的行
    a.max(axis=1)        # 返回每一行中的最大值
    a.sum(axis=0)        # 按列执行加法
    a.sum(axis=1)        # 按行执行加法
    
    

🖼️ 图像处理基础

1. 图像读取

1.1 读取图像的方式

在OpenCV中,使用 cv2.imread() 函数读取图像,常用的参数选项包括:

参数描述
cv2.IMREAD_GRAYSCALE将图像转换为单通道灰度图像
cv2.IMREAD_COLOR将图像转换为3通道BGR彩色图像
cv2.IMREAD_UNCHANGED保留原图像的所有通道和透明度信息
cv2.IMREAD_REDUCED_GRAYSCALE_4将图像转换为单通道灰度图像并减小为1/4的尺寸
cv2.IMREAD_REDUCED_COLOR_4将图像转换为3通道BGR彩色图像并减小为1/4的尺寸
cv2.IMREAD_REDUCED_GRAYSCALE_8将图像转换为单通道灰度图像并减小为1/8的尺寸
cv2.IMREAD_REDUCED_COLOR_8将图像转换为3通道BGR彩色图像并减小为1/8的尺寸
cv2.IMREAD_IGNORE_ORIENTATION不根据EXIF方向标志旋转图像

示例代码

import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_REDUCED_GRAYSCALE_4)  # 读取图像
print(img.shape)  # 输出图像的形状
print(img.size)   # 输出图像的大小
2. 图像写入
2.1 使用imwrite()写图像
OpenCV的 cv2.imwrite() 函数用于将NumPy数组中的图像写入文件。
示例代码
import cv2
import numpy
img = numpy.zeros((50, 50), dtype=numpy.uint8)  # 创建大小为50×50的黑色正方形图像
cv2.imwrite('mypic2-1.jpg', img)  # 将图像存入文件
3. 图像显示
3.1 使用imshow()显示图像
cv2.imshow() 函数用于在窗口中显示图像,参数包括窗口名称和图像数组。
示例代码
import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_REDUCED_COLOR_2)  # 读取图像并减小尺寸为1/2
cv2.imshow('lena', img)  # 显示图像
3.2 使用waitKey()等待用户输入
cv2.waitKey([delay]) 函数等待用户输入,返回值为按下键的ASCII码。
示例代码
key = 0
while key != 27:  # 按Esc键时终止循环
    key = cv2.waitKey()  # 等待按键
cv2.destroyWindow('lena')  # 关闭图像窗口
4. 视频处理
4.1 读、写、播放视频
OpenCV提供了 VideoCapture 和 VideoWriter 类用于视频处理。
视频播放步骤
	创建 VideoCapture 对象,指定视频文件或摄像头作为数据源。
	调用 read() 方法获取视频帧。
	使用 imshow() 显示帧或使用 VideoWriter 写入视频文件。
播放视频示例代码
import cv2
vc = cv2.VideoCapture('test2-5.mp4')  # 创建VideoCapture对象
while True:
    success, frame = vc.read()  # 读下一帧
    if not success:  # 视频结束时跳出循环
        break
    cv2.imshow('myvideo', frame)  # 显示帧图像
    key = cv2.waitKey(25)  # 延迟时间
    if key == 27:  # 按Esc键退出
        break
vc.release()  # 关闭视频
4.2 将视频写入文件
将视频写入文件的过程与播放视频类似。
示例代码
import cv2
vc = cv2.VideoCapture('test2-5.mp4')
vw = cv2.VideoWriter('test2-6out.avi', cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), 30, (640, 480))  # 设置保存视频的文件名与格式
while True:
    success, frame = vc.read()  # 读下一帧
    if not success:
        break
    vw.write(frame)  # 将帧写入文件
vc.release()  # 关闭视频
5. 图像通道操作
5.1 拆分通道
使用数组索引或 cv2.split() 函数拆分图像通道。
示例代码(数组索引)
import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_REDUCED_COLOR_2)
b = img[:, :, 0]  # B通道
g = img[:, :, 1]  # G通道
r = img[:, :, 2]  # R通道
示例代码(使用cv2.split()import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_REDUCED_COLOR_2)
b, g, r = cv2.split(img)  # 拆分通道
5.2 合并图像通道
使用 cv2.merge() 函数合并图像通道。
示例代码
img = cv2.merge([b, g, r])  # 合并B、G、R通道
6. 图像运算
6.1 加法运算
使用 + 运算符或 cv2.add() 函数执行图像加法运算。
示例代码
img3 = img1 + img2  # 使用+运算符
img4 = cv2.add(img1, img2)  # 使用cv2.add()
6.2 加权加法运算
使用 cv2.addWeighted() 函数执行图像的加权加法运算。
示例代码
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma)  # 加权加法
以上是本次讲座的主要内容,涵盖了图像的读取、写入、显示、视频处理、图像通道操作及图像运算的基本知识。
🖥️ 位运算
2.3.3 位运算简介
在图像处理领域,位运算是对图像像素进行直接操作的有效方法。OpenCV库提供了多种图像位运算函数,这些函数能够处理图像的基本操作。
OpenCV位运算函数
函数名	说明
cv2.bitwise_and(src1, src2[, mask])	当mask对应的位不为0时,执行按位与操作。
cv2.bitwise_or(src1, src2[, mask])	当mask对应的位不为0时,执行按位或操作。
cv2.bitwise_not(src1[, mask])	当mask对应的位不为0时,执行按位取反操作。
cv2.bitwise_xor(src1, src2[, mask])	当mask对应的位不为0时,执行按位异或操作。
示例代码
以下代码展示了如何使用OpenCV进行图像的位运算:
import cv2

src1 = cv2.imread('lena.jpg', cv2.IMREAD_REDUCED_COLOR_2)      # 读取图像
src2 = cv2.imread('opencvlog.jpg', cv2.IMREAD_REDUCED_COLOR_2)  # 读取图像

img3 = cv2.bitwise_and(src1, src2)  # 按位与
img4 = cv2.bitwise_or(src1, src2)   # 按位或
img5 = cv2.bitwise_not(src1)        # 按位取反
img6 = cv2.bitwise_xor(src1, src2)  # 按位异或

cv2.imshow('lena', src1)             # 显示原图像
cv2.imshow('log', src2)               # 显示原图像
cv2.imshow('lenaandlog', img3)        # 显示按位与图像
cv2.imshow('lenaorlog', img4)         # 显示按位或图像
cv2.imshow('lenanotlog', img5)        # 显示按位取反图像
cv2.imshow('lenaxorlog', img6)        # 显示按位异或图像

cv2.waitKey(0)
2.4 实验
本节主要内容
	实验1:为任务图像打码
	实验2:创建图像掩模
2.4.1 实验1:为人物图像打码
	实验目的:掌握图像的读取和显示,以及更改图像像素的方法。
	实验内容:使用IDLE编写一个程序,用黑色矩形框遮挡人物眼部。
	实验过程:具体的代码和实现步骤将在课堂上讨论。
2.4.2 实验2:创建图像掩模
	实验目的:掌握图像运算,利用图像运算操作图像。
	实验内容:使用图像掩模执行位运算,取出掩模内的图像。
	实验过程:具体的代码和实现步骤将在课堂上讨论。
🖥️ OpenCV计算机视觉基础教程
📖 第3章 图形用户界面
本章主要内容:
	窗口控制
	绘图
	响应鼠标事件
	使用跟踪栏
	实验
________________________________________
🪟 3.1 窗口控制
3.1.1 创建和关闭窗口
创建窗口
	cv2.imshow() 函数用于显示图像。如果指定的窗口不存在,则会创建一个窗口,窗口大小由图像大小决定,且不能更改。
	cv2.namedWindow() 函数用于创建窗口,其基本格式如下:
	cv2.namedWindow(winname[, flags])
	参数说明:
	winname:窗口名称
	flags:表示窗口属性的常量
	cv2.WINDOW_NORMAL:用户可以调整窗口大小。
	cv2.WINDOW_AUTOSIZE:默认值,用户无法调整窗口大小。
	cv2.WINDOW_FULLSCREEN:窗口全屏显示。
	cv2.WINDOW_GUI_EXPANDED:窗口可显示状态栏和工具栏。
	cv2.WINDOW_FREERATIO:窗口显示图片时无比例限制。
	cv2.WINDOW_KEEPRATIO:窗口由图像的比例决定。
示例代码
import numpy
import cv2
img = numpy.zeros((240, 320), dtype=numpy.uint8)  # 创建黑色图像
img[70:170, 110:210] = 255  # 设置白色区域
cv2.namedWindow('test3-1', cv2.WINDOW_NORMAL)  # 创建普通窗口
cv2.imshow('test3-1', img)  # 在窗口中显示图像
cv2.waitKey(0)
关闭窗口
	cv2.destroyAllWindows():关闭所有窗口,示例代码如下:
cv2.destroyAllWindows()
	cv2.destroyWindow(winname):关闭指定名称的窗口,示例代码如下:
cv2.destroyWindow('test3-1')
3.1.2 调整窗口大小
	cv2.resizeWindow() 函数用于更改窗口大小,基本格式如下:
cv2.resizeWindow(winname, size)
	参数说明:
	winname:窗口名称
	size:表示窗口大小的二元组
示例代码
import cv2
img = cv2.imread('lena.jpg')  # 读取图像
s = img.shape
cv2.imshow('lena', img)  # 显示图像
key = cv2.waitKey(500)
cv2.resizeWindow('lena', (s[0] // 2, s[1] // 2))  # 更改窗口大小
cv2.waitKey(0)
________________________________________
🎨 3.2 绘图
3.2.1 绘制直线
	cv2.line() 函数用于绘制直线,语法格式如下:
cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
	参数说明:
	img:用于绘制图像的图像。
	pt1:直线起点坐标。
	pt2:直线终点坐标。
	color:直线颜色(BGR模型)。
	thickness:线条粗细,默认值为1,设置为-1表示绘制填充图形。
	lineType:线条类型,默认值为 cv2.LINE_8。
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8)  # 创建一幅黑色图像
cv2.line(img, (0, 0), (320, 200), (255, 0, 0), 5)  # 画蓝色对角线
cv2.line(img, (320, 0), (0, 200), (0, 255, 0), 5)  # 画绿色对角线
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.2 绘制矩形
	cv2.rectangle() 函数用于绘制矩形,语法格式如下:
cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
	参数说明:
	pt1:矩形的一个顶点。
	pt2:与 pt1 相对的另一个顶点。
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8)  # 创建一幅黑色图像
cv2.rectangle(img, (20, 20), (300, 180), (255, 0, 0), 5)  # 画蓝色矩形边框
cv2.rectangle(img, (70, 70), (250, 130), (0, 255, 0), -1)  # 画绿色填充矩形
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.3 绘制圆
	cv2.circle() 函数用于绘制圆,语法格式如下:
cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]])
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8)  # 创建一幅黑色图像
cv2.circle(img, (160, 100), 80, (255, 0, 0), 5)  # 画蓝色圆
cv2.circle(img, (160, 100), 40, (0, 255, 0), -1)  # 画绿色填充圆
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.4 绘制椭圆
	cv2.ellipse() 函数用于绘制椭圆,语法格式如下:
cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]])
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8) + 255  # 创建一幅白色图像
cv2.ellipse(img, (160, 100), (120, 50), 0, 0, 360, (255, 0, 0), 5)  # 画蓝色椭圆
cv2.ellipse(img, (160, 100), (60, 15), 0, 0, 360, (0, 255, 0), 51)  # 画绿色填充椭圆
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.5 绘制多边形
	cv2.polylines() 函数用于绘制多边形,语法格式如下:
cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8) + 255  # 创建一幅白色图像
pts = np.array([[160, 20], [20, 100], [160, 180], [300, 100]], np.int32)  # 创建顶点
cv2.polylines(img, [pts], True, (255, 0, 0), 5)  # 画蓝色多边形边框
pts = np.array([[160, 60], [60, 100], [160, 140], [260, 100]], np.int32)  # 创建顶点
cv2.polylines(img, [pts], False, (0, 255, 0), 5)  # 画绿色曲线
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.6 绘制文本
	cv2.putText() 函数用于绘制文本,语法格式如下:
cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8) + 255  # 创建一幅白色图像
font = cv2.FONT_HERSHEY_SCRIPT_SIMPLEX
cv2.putText(img, 'Python', (50, 100), font, 2, (255, 0, 0), 2, cv2.LINE_AA)  # 绘制文本
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
3.2.7 绘制箭头
	cv2.arrowedLine() 函数用于绘制箭头,语法格式如下:
cv2.arrowedLine(img, pt1, pt2, color[, thickness[, lineType[, shift[, tipLength]]]])
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8) + 255  # 创建一幅白色图像
cv2.arrowedLine(img, (50, 50), (50, 150), (0, 0, 255), 2)  # 绘制红色垂直箭头
cv2.arrowedLine(img, (50, 50), (300, 50), (0, 0, 255), 2)  # 绘制红色水平箭头
cv2.imshow('draw', img)  # 显示图像
cv2.waitKey(0)
________________________________________
🖱️ 3.3 响应鼠标事件
	OpenCV可在用户触发鼠标事件时调用鼠标回调函数完成事件处理。
鼠标回调函数格式
def mouseCallback(event, x, y, flags, param): ...
	参数说明:
	event:鼠标事件对象。
	x 和 y:鼠标指针在窗口中的坐标。
	flags:触发鼠标事件时的鼠标拖动或键盘按键操作。
	cv2.EVENT_LBUTTONDBLCLK:双击鼠标左键。
	cv2.EVENT_LBUTTONDOWN:按下鼠标左键。
	cv2.EVENT_LBUTTONUP:释放鼠标左键。
	等等。
绑定鼠标回调函数
	cv2.setMouseCallback() 用于为图像窗口绑定鼠标回调函数,格式如下:
cv2.setMouseCallback(wname, mouseCallback)
示例代码
import numpy as np
import cv2
img = np.zeros((200, 320, 3), np.uint8) + 255  # 创建一幅白色图像

def draw(event, x, y, flag, param):  # 定义鼠标回调函数
    if event == cv2.EVENT_LBUTTONDBLCLK:
        cv2.circle(img, (x, y), 20, (255, 0, 0), -1)  # 双击鼠标左键时画圆
    elif event == cv2.EVENT_RBUTTONDBLCLK:
        cv2.rectangle(img, (x, y), (x + 20, y + 20), (0, 0, 255), -1)  # 双击鼠标右键时画矩形

cv2.namedWindow('drawing')  # 命名图像窗口
cv2.setMouseCallback('drawing', draw)  # 为窗口绑定回调函数

while True:
    cv2.imshow('drawing', img)  # 显示图像
    k = cv2.waitKey(1)
    if k == 27:  # 按【Esc】键时结束循环
        break
cv2.destroyAllWindows()
________________________________________
📊 3.4 使用跟踪栏
跟踪栏定义
	跟踪栏(Trackbar)是OpenCV为图像窗口提供的交互工具,用户可以通过滑块获取特定范围内的值。
创建跟踪栏
	cv2.createTrackbar() 函数用于创建跟踪栏,格式如下:
cv2.createTrackbar(trackbarname, wname, value, count, onChange, userdata)
	参数说明:
	trackbarname:跟踪栏名称。
	wname:图像窗口名称。
	value:滑块初始位置。
	count:最大值,最小值为0。
	onChange:滑块位置变化时调用的回调函数名称。
	userdata:传递给回调函数的其他可选数据。
获取当前值
	cv2.getTrackbarPos() 用于返回跟踪栏的当前值,格式如下:
retval = cv2.getTrackbarPos(trackbarname, wname)
示例代码
import numpy as np
import cv2
img = np.zeros((120, 400, 3), np.uint8)  # 创建一幅黑色图像
(后续内容未显示,需进一步补充)
🖼️ 图像处理实验
3.5 实验概述
本节主要内容包括两个实验:
	实验1:使用鼠标指针取点绘图
	实验2:使用跟踪栏选择通道图像
________________________________________
🎨 3.5.1 实验1:使用鼠标指针取点绘图
1. 实验目的
掌握在图像窗口中响应鼠标事件和绘制图形的基本方法。
2. 实验内容
创建一幅图像,在图像中单击鼠标左键时绘制鼠标指针所在的点和坐标,单击鼠标右键时使用前面单击鼠标左键所取的点绘制多边形。
3. 实验过程
	通过编程实现鼠标事件的捕捉。
	左键单击响应:获取鼠标坐标并绘制点。
	右键单击响应:根据已绘制的点绘制多边形。
________________________________________
🎛️ 3.5.2 实验2:使用跟踪栏选择通道图像
1. 实验目的
掌握跟踪栏的基本使用方法。
2. 实验内容
创建一个跟踪栏,将其值设置为0123,根据跟踪栏的值依次显示原图像、B通道图像、G通道图像和R通道图像。
3. 实验过程
	使用 OpenCV 创建跟踪栏的代码示例:
def doChange(x):
    b = cv2.getTrackbarPos('B', 'tracebar')
    g = cv2.getTrackbarPos('G', 'tracebar')
    r = cv2.getTrackbarPos('R', 'tracebar')
    img[:] = [b, g, r]  # 更改图像

cv2.namedWindow('tracebar')
cv2.createTrackbar('B', 'trackbar', 0, 255, doChange)  # 创建 B 通道跟踪栏
cv2.createTrackbar('G', 'trackbar', 0, 255, doChange)  # 创建 G 通道跟踪栏
cv2.createTrackbar('R', 'trackbar', 0, 255, doChange)  # 创建 R 通道跟踪栏

while True:
    cv2.imshow('trackbar', img)  # 显示图像
    k = cv2.waitKey(1)
    if k == 27:  # 按【Esc】键时结束循环
        break

cv2.destroyAllWindows()
	跟踪栏功能:根据用户选择的 B、G、R 值动态更新图像的显示。
________________________________________
🚀 结束语
感谢观看本次实验内容!
📸 OpenCV计算机视觉基础教程
4.1 色彩空间变换
4.1.1 RGB色彩空间
	RGB色彩空间使用红色(R)、绿色(G)和蓝色(B)三种基本颜色表示图像像素。
	在RGB色彩空间中,图像的每个像素用一个三元组表示,分别代表R、G和B通道。
	OpenCV默认采用BGR色彩空间,顺序为B、G、R。
	使用cv2.cvtColor()函数中的cv2.COLOR_BGR2RGB转换码可以将图像从BGR色彩空间转换为RGB色彩空间。
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('BGR', img)  # 显示图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换色彩空间为RGB
cv2.imshow('RGB', img2)  # 显示图像
cv2.waitKey(0)
4.1.2 GRAY色彩空间
	GRAY色彩空间通常指8位灰度图像,颜色取值范围为[0,255],共256个灰度级。
	从RGB色彩空间转换为GRAY色彩空间的计算公式:
"Gray"=0.299R+0.587G+0.114B
	使用cv2.COLOR_BGR2GRAY转换码可以将图像从BGR色彩空间转换为GRAY色彩空间。
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('BGR', img)  # 显示图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换色彩空间为GRAY
cv2.imshow('GRAY', img2)  # 显示图像
cv2.waitKey(0)
4.1.3 YCrCb色彩空间
	YCrCb色彩空间用亮度Y、红色Cr和蓝色Cb表示图像。
	从RGB色彩空间转换为YCrCb色彩空间的计算公式:
Y=0.299R+0.587G+0.114B
Cr=0.713(R-Y)+δ
Cb=0.564(B-Y)+δ
	其中,δ的值取决于图像的位数。
	使用cv2.COLOR_BGR2YCrCb转换码可以将图像从BGR色彩空间转换为YCrCb色彩空间。
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('BGR', img)  # 显示图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)  # 转换色彩空间为YCrCb
cv2.imshow('YCrCb', img2)  # 显示图像
cv2.waitKey(0)
4.1.4 HSV色彩空间
	HSV色彩空间使用色调(Hue)、饱和度(Saturation)和亮度(Value)表示图像。
	色调H表示颜色,取值范围为[0°,360°]。
	饱和度S表示颜色接近光谱色的程度,取值范围为[0,1]。
	亮度V表示颜色明亮的程度,取值范围为[0,1]。
从RGB色彩空间转换为HSV色彩空间的计算公式较为复杂,涉及到多个步骤。
	使用cv2.COLOR_BGR2HSV转换码可以将图像从BGR色彩空间转换为HSV色彩空间。
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('BGR', img)  # 显示图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # 转换色彩空间为HSV
cv2.imshow('HSV', img2)  # 显示图像
cv2.waitKey(0)
4.2 几何变换
4.2.1 缩放
	OpenCV的cv2.resize()函数用于缩放图像,基本格式为:
dst=cv2.resize(src,dsize[,dst[,fx[,fy[,interpolation]]]])
	参数说明:
	dst:转换后的图像。
	src:原图像。
	dsize:转换后的图像大小。
	fx:水平方向的缩放比例。
	fy:垂直方向的缩放比例。
	interpolation:插值方式。
可用的插值方式包括: - cv2.INTER_NEAREST:最近邻插值 - cv2.INTER_LINEAR:双线性插值(默认) - cv2.INTER_CUBIC:3次样条插值等
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
sc = [1, 0.2, 0.5, 1.5, 2]  # 设置缩放比例
cv2.imshow('showimg', img)  # 显示图像
while True:
    key = cv2.waitKey()
    if 48 <= key <= 52:  # 按键【0】【1】【2】【3】或【4】
        x = y = sc[key - 48]  # 获得缩放比例
        img2 = cv2.resize(img, None, fx=x, fy=y)  # 缩放图像
        cv2.imshow('showimg', img2)  # 显示图像
4.2.2 翻转
	OpenCV的cv2.flip()函数用于翻转图像,基本格式为:
dst=cv2.flip(src,flipCode)
	参数说明:
	dst:转换后的图像。
	src:原图像。
	flipCode:翻转类型。
	flipCode = 0:绕x轴翻转(垂直翻转)。
	flipCode > 0:绕y轴翻转(水平翻转)。
	flipCode < 0:同时绕x轴和y轴翻转。
示例代码:
import cv2
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('showimg', img)  # 显示图像
while True:
    key = cv2.waitKey()
    if key == 48:  # 按【0】键时显示原图
        img2 = img
    elif key == 49:  # 按【1】键时垂直翻转
        img2 = cv2.flip(img, 0)
    elif key == 50:  # 按【2】键时水平翻转
        img2 = cv2.flip(img, 1)
    elif key == 51:  # 按【3】键时水平、垂直翻转
        img2 = cv2.flip(img, -1)
    cv2.imshow('showimg', img2)
4.2.3 仿射变换
	仿射变换包含了平移、旋转、缩放等操作,保持原图像中所有平行线在转换后的图像中仍然平行。
	使用cv2.warpAffine()函数进行仿射变换,基本格式为:
dst=cv2.warpAffine(src,M,dsize[,dst[,flags[,borderMode[,borderValue]]]])
	参数说明:
	dst:转换后的图像。
	src:原图像。
	M:大小为2×3的转换矩阵。
	dsize:转换后的图像大小。
示例代码:
import cv2
import numpy as np
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('img', img)  # 显示图像
height = img.shape[0]  # 获得图像高度
width = img.shape[1]  # 获得图像宽度
dsize = (width, height)
M = np.array([[1, 0, 100], [0, 1, 50]])  # 平移矩阵示例
img2 = cv2.warpAffine(img, M, dsize)  # 执行仿射变换
cv2.imshow('imgAffine', img2)  # 显示图像
cv2.waitKey(0)
4.2.4 透视变换
	透视变换将图像转换为任意的四边形,保持原始图像中的所有直线在转换后的图像中仍然是直线。
	使用cv2.warpPerspective()函数进行透视变换,基本格式为:
dst=cv2.warpPerspective(src,M,dsize[,flags[,borderMode[,borderValue]]])
	使用cv2.getPerspectiveTransform()函数计算透视变换的转换矩阵。
示例代码:
import cv2
import numpy as np
img = cv2.imread('bee.jpg')  # 读取图像
cv2.imshow('img', img)  # 显示图像
height = img.shape[0]  # 获得图像高度
width = img.shape[1]  # 获得图像宽度
dsize = (width, height)
src = np.float32([[0, 0], [width-10, 0], [0, height-10], [width-1, height-1]])  # 原图像中的4个点
dst = np.float32([[50, 50], [width-50, 80], [50, height-100], [width-100, height-10]])  # 目标图像中的4个点
M = cv2.getPerspectiveTransform(src, dst)  # 创建转换矩阵
img2 = cv2.warpPerspective(img, M, dsize)  # 执行转换
cv2.imshow('imgPerspective', img2)  # 显示图像
cv2.waitKey(0)
4.3 图像模糊
4.3.1 均值滤波
	均值滤波是指以当前点为中心,用其周围N×N个点像素值的平均值替代当前点的像素值。
	OpenCV的cv2.blur()函数用于实现均值滤波,基本格式为:
dst=cv2.blur(src,ksize[,anchor[,borderType]])
	参数说明:
	dst:滤波结果图像。
	src:原图像。
	ksize:卷积核大小,通常为正数和奇数。
示例代码:
import cv2
img = cv2.imread('lena2.jpg')
cv2.imshow('img', img)
img2 = cv2.blur(img, (20, 20))  # 可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur', img2)
cv2.waitKey(0)
4.3.2 高斯滤波
	高斯滤波按像素点与中心点的不同距离,赋予像素点不同的权重值。
	OpenCV的cv2.GaussianBlur()函数用于实现高斯滤波,基本格式为:
dst=cv2.GaussianBlur(src,ksize,sigmaX[,sigmaY[,borderType]])
	参数说明:
	sigmaX:水平方向上的权重值。
	sigmaY:垂直方向上的权重值。
示例代码:
import cv2
img = cv2.imread('lena2.jpg')
cv2.imshow('img', img)
img2 = cv2.GaussianBlur(img, (5, 5), 0)  # 可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur', img2)
cv2.waitKey(0)
4.3.3 方框滤波
	方框滤波以均值滤波为基础,可选择是否对滤波结果进行归一化。
	OpenCV的cv2.boxFilter()函数用于实现方框滤波。
🖼️ 方框滤波
方框滤波简介
方框滤波是一种简单的图像平滑技术,主要用于去除噪声。其基本格式如下:
dst=cv2.boxFilter(src,ddepth,ksize[,anchor[,normalize[,borderType]]]
参数说明
	ddepth: 目标图像的深度,通常使用 -1 表示与原图像深度一致。
	normalize: 默认值为 True,执行归一化操作;如果为 False,则不执行归一化操作。
	其他参数与 cv2.blur() 函数中的一致。
示例代码
# test4-14.py:方框滤波
import cv2
img = cv2.imread('lena2.jpg')
cv2.imshow('img', img)
img2 = cv2.boxFilter(img, -1, (3, 3), normalize=False)  # 可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur', img2)
cv2.waitKey(0)
📏 中值滤波
中值滤波通过对邻域内所有像素值排序,取中间值作为邻域中心点的像素值。使用 cv2.medianBlur() 函数实现中值滤波,基本格式如下:
dst=cv2.medianBlur(src,ksize)
参数说明
	ksize: 卷积核大小,必须是大于 1 的奇数。
示例代码
# test4-15.py:中值滤波
import cv2
img = cv2.imread('lena2.jpg')
cv2.imshow('img', img)
img2 = cv2.medianBlur(img, 21)  # 可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur', img2)
cv2.waitKey(0)
🔄 双边滤波
双边滤波在计算像素值时考虑距离和色差信息,能够在消除噪声的同时保护边缘信息。使用 cv2.bilateralFilter() 函数实现双边滤波,基本格式如下:
dst=cv2.bilateralFilter(src,d,sigmaColor,sigmaSpace[,borderType])
参数说明
	d: 当前点为中心的邻域直径,通常为 5。
	sigmaColor: 色差范围。
	sigmaSpace: 空间坐标中的 sigma 值,值越大表示更多像素点参与滤波计算。
示例代码
# test4-16.py:双边滤波
import numpy as np
import cv2
img = cv2.imread('lena2.jpg')
cv2.imshow('img', img)
img2 = cv2.bilateralFilter(img, 20, 100, 100)  # 可调整参数以查看不同效果
cv2.imshow('imgBlur', img2)
cv2.waitKey(0)
⚙️ 2D卷积
均值滤波、高斯滤波、方框滤波、中值滤波和双边滤波等可以通过参数确定卷积核,2D卷积可使用自定义卷积核执行滤波操作。使用 cv2.filter2D() 函数实现 2D 卷积,基本格式如下:
dst=cv2.filter2D(src,ddepth,kernel[,anchor[,delta[,borderType]]])
参数说明
	ddepth: 目标图像的深度,通常使用 -1 表示与原图像一致。
	kernel: 单通道卷积核(一维数组)。
	anchor: 图像处理的锚点。
	delta: 修正值,未省略时,将加上该值作为最终滤波结果。
	borderType: 边界值处理方式。
示例代码
# test4-17.py:2D卷积
import numpy as np
import cv2
img = cv2.imread('lena2.jpg')
k1 = np.array([[3, 3, 3, 3, 3], [3, 9, 9, 9, 3], [3, 11, 12, 13, 3], [3, 8, 8, 8, 3], [3, 3, 3, 3, 3]]) / 25  # 自定义卷积核1
k2 = np.ones((5, 5), np.float32) / 25  # 自定义卷积核2
img2 = cv2.filter2D(img, -1, k1)
cv2.imshow('imgK1', img2)
img2 = cv2.filter2D(img, -1, k2)
cv2.imshow('imgK2', img2)
cv2.waitKey(0)
🔲 阈值处理
全局阈值处理
全局阈值处理将大于阈值的像素值设置为 255,将其他像素值设置为 0;或者将大于阈值的像素值设置为 0,将其他像素值设置为 255。使用 cv2.threshold() 函数实现全局阈值处理,基本格式如下:
retval,dst=cv2.threshold(src,thresh,maxval,type)
参数说明
	retval: 返回的阈值。
	dst: 全局阈值处理后的结果图像。
	src: 原图像。
	thresh: 设置的阈值。
	maxval: 当阈值类型为 THRESH_BINARY 和 THRESH_BINARY_INV 时使用的最大值。
	type: 阈值类型。
二值化阈值处理
当 type 参数值为 cv2.THRESH_BINARY 时,执行二值化阈值处理。
示例代码
# test4-18.py:二值化阈值处理
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
ret, img2 = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)  # 二值化阈值处理
cv2.imshow('imgTHRESH_BINARY', img2)
cv2.waitKey(0)
反二值化阈值处理
当 type 参数值为 cv2.THRESH_BINARY_INV 时,执行反二值化阈值处理。
示例代码
# test4-19.py:反二值化阈值处理
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
ret, img2 = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY_INV)  # 反二值化阈值处理
cv2.imshow('imgTHRESH_BINARY_INV', img2)
cv2.waitKey(0)
截断阈值处理
当 type 参数值为 cv2.THRESH_TRUNC 时,执行截断阈值处理。
示例代码
# test4-20.py:截断阈值处理
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
ret, img2 = cv2.threshold(img, 150, 255, cv2.THRESH_TRUNC)  # 截断阈值处理
cv2.imshow('imgTHRESH_TRUNC', img2)
cv2.waitKey(0)
超阈值零处理
当 type 参数值为 cv2.THRESH_TOZERO 时,执行超阈值零处理。
示例代码
# test4-21.py:超阈值零处理
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
ret, img2 = cv2.threshold(img, 150, 255, cv2.THRESH_TOZERO)  # 超阈值零处理
cv2.imshow('imgTHRESH_TOZERO', img2)
cv2.waitKey(0)
低阈值零处理
当 type 参数值为 cv2.THRESH_TOZERO_INV 时,执行低阈值零处理。
示例代码
# test4-22.py:低阈值零处理
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
ret, img2 = cv2.threshold(img, 150, 255, cv2.THRESH_TOZERO_INV)  # 低阈值零处理
cv2.imshow('imgTHRESH_TOZERO_INV', img2)
cv2.waitKey(0)
Otsu算法阈值处理
对于色彩不均衡的图像,Otsu算法阈值处理方式更好。
示例代码
# test4-23.py:Otsu算法阈值处理
import cv2
img = cv2.imread('bee.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图像,将其转换为单通道灰度图像
cv2.imshow('img', img)  # 显示原图
ret, img2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)  # 阈值处理
cv2.imshow('img2', img2)
ret, img3 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow('img3', img3)
ret, img4 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
cv2.imshow('img4', img4)
cv2.waitKey(0)
三角算法阈值处理
使用三角算法阈值处理。
示例代码
# test4-24.py:三角算法阈值处理
import cv2
img = cv2.imread('bee.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图像,将其转换为单通道灰度图像
cv2.imshow('img', img)  # 显示原图
ret, img2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)  # 阈值处理
cv2.imshow('img2', img2)
ret, img3 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE)
cv2.imshow('img3', img3)
ret, img4 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_TRIANGLE)
cv2.imshow('img4', img4)
cv2.waitKey(0)
⚙️ 自适应阈值处理
自适应阈值处理也称局部阈值处理,通过计算每个像素点邻域的加权平均值来确定阈值。使用 cv2.adaptiveThreshold() 函数实现自适应阈值处理,基本格式如下:
dst=cv2.adaptiveThreshold(src,maxValue,adaptiveMethod,thresholdType,blockSize,C)
参数说明
	dst: 阈值处理的结果图像。
	src: 原图像。
	maxValue: 最大值。
	adaptiveMethod: 自适应方法,值为 cv2.ADAPTIVE_THRESH_MEAN_C 或 cv2.ADAPTIVE_THRESH_GAUSSIAN_C。
	thresholdType: 阈值处理方式,值为 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV。
	blockSize: 计算局部阈值的邻域大小。
	C: 常量,自适应阈值为 blockSize 指定邻域的加权平均值减去 C。
示例代码
# test4-25.py:自适应阈值处理
import cv2
img = cv2.imread('bee.jpg', cv2.IMREAD_GRAYSCALE)  # 读取图像,将其转换为单通道灰度图像
cv2.imshow('img', img)
img2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 10)  # 阈值处理
cv2.imshow('img2', img2)
cv2.waitKey(0)
🔍 形态变换
形态操作内核
形态操作会使用一个内核(结构元)遍历图像,根据内核和图像的位置关系决定内核中心对应的图像像素点的输出结果。使用 cv2.getStructuringElement() 函数返回内核,基本格式如下:
retval=cv2.getStructuringElement(shape,ksize)
参数说明
	shape: 内核的形状,包括 cv2.MORPH_RECT(矩形)、cv2.MORPH_CROSS(十字形)和 cv2.MORPH_ELLIPSE(椭圆形)。
	ksize: 内核的大小。
示例代码
# 获取不同形状的内核
import cv2

rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 返回矩形内核
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))  # 返回十字形内核
ellipse_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))  # 返回椭圆形内核
腐蚀
腐蚀操作遍历图像时,根据内核和图像的位置决定内核中心对应的图像像素点的输出结果。使用 cv2.erode() 函数实现腐蚀,基本格式如下:
dst=cv2.erode(src,kernel[,anchor[,iterations[,borderType[,borderValue]]]])
参数说明
	src: 原图像。
	kernel: 内核。
	iterations: 腐蚀操作的迭代次数。
示例代码
# test4-26.py:腐蚀
import cv2
import numpy as np
img = cv2.imread('zh2.jpg')  # 读取图像
cv2.imshow('img', img)  # 显示原图像
kernel = np.ones((5, 5), np.uint8)  # 定义大小为5×5的内核
img2 = cv2.erode(img, kernel, iterations=1)  # 腐蚀,迭代1次
cv2.imshow('img2', img2)  # 显示转换结果图像
cv2.waitKey(0)

# 🖼️ 图像膨胀与形态学操作

## 膨胀操作原理

> 膨胀操作是图像处理中一种重要的形态学操作,其基本原理是通过调整内核位置来改变图像的像素值。

### 内核设置

- 当内核完全处于前景外部时,内核中心对应像素点的值设置为 **0**(背景)。
- 当内核部分在前景内时,内核中心对应像素点的值设置为 **1**(前景)。

### 图示解释

- **4-39(a)**:内核完全在背景中,值为 **0**- **4-39(b)**:内核部分在前景中,值为 **1**- **4-39(c)**:膨胀结果,白色区域增大。

## OpenCV 膨胀操作函数

**函数格式:**
```python
dst = cv2.dilate(src, kernel[, anchor[, iterations[, borderType[, borderValue]]]])
	各参数含义与 cv2.erode() 函数中的一致。
示例代码
# test4-27.py:膨胀
import cv2
import numpy as np

img = cv2.imread('zh.jpg')                          # 读取图像
cv2.imshow('img', img)                               # 显示原图像
kernel = np.ones((5, 5), np.uint8)                   # 定义大小为5×5的内核
img2 = cv2.dilate(img, kernel, iterations=5)        # 膨胀,迭代5次
cv2.imshow('img2', img2)                             # 显示转换结果图像
cv2.waitKey(0)
高级形态操作
基本概念
高级形态操作基于腐蚀(Erosion)和膨胀(Dilation)操作,包括:
	开运算(Opening)
	闭运算(Closing)
	形态学梯度运算(Morphological Gradient)
	黑帽运算(Black Hat)
	礼帽运算(Top Hat)
OpenCV 形态操作函数
函数格式:
dst = cv2.morphologyEx(src, op, kernel[, anchor[, iterations[, borderType[, borderValue]]]])
	参数 op 为形态操作类型。
开运算
	原理:先执行腐蚀操作,再执行膨胀操作。
	示例代码:
# test4-28.py:开运算
import cv2
import numpy as np

img = cv2.imread('zh2.jpg')                              # 读取图像
cv2.imshow('img', img)                                   # 显示原图像
kernel = np.ones((5, 5), np.uint8)                       # 定义大小为5×5的内核
op = cv2.MORPH_OPEN                                       # 设置形态操作类型
img2 = cv2.morphologyEx(img, op, kernel, iterations=5)  # 形态操作,迭代5次
cv2.imshow('img2', img2)                                 # 显示转换结果图像
cv2.waitKey(0)
闭运算
	原理:先执行膨胀操作,再执行腐蚀操作。
	示例代码:
# test4-29.py:闭运算
import cv2
import numpy as np

img = cv2.imread('zh.jpg')                                  # 读取图像
cv2.imshow('img', img)                                       # 显示原图像
kernel = np.ones((5, 5), np.uint8)                            # 定义大小为5×5的内核
op = cv2.MORPH_CLOSE                                          # 设置形态操作类型
img2 = cv2.morphologyEx(img, op, kernel, iterations=5)       # 形态操作,迭代5次
cv2.imshow('img2', img2)                                     # 显示转换结果图像
cv2.waitKey(0)
形态学梯度运算
	原理:用图像的膨胀操作结果减去腐蚀操作结果。
	示例代码:
# test4-30.py:形态学梯度运算
import cv2
import numpy as np

img = cv2.imread('zh.jpg')                                  # 读取图像
cv2.imshow('img', img)                                       # 显示原图像
kernel = np.ones((5, 5), np.uint8)                            # 定义大小为5×5的内核
op = cv2.MORPH_GRADIENT                                       # 设置形态操作类型
img2 = cv2.morphologyEx(img, op, kernel, iterations=1)       # 形态操作,迭代1次
cv2.imshow('img2', img2)                                     # 显示转换结果图像
cv2.waitKey(0)
黑帽运算
	原理:用图像的闭运算结果减去原图像。
	示例代码:
# test4-31.py:形态学黑帽运算
import cv2
import numpy as np

img = cv2.imread('zh.jpg')                                  # 读取图像
cv2.imshow('img', img)                                       # 显示原图像
kernel = np.ones((5, 5), np.uint8)                            # 定义大小为5×5的内核
op = cv2.MORPH_BLACKHAT                                       # 设置形态操作类型
img2 = cv2.morphologyEx(img, op, kernel, iterations=5)       # 形态操作,迭代5次
cv2.imshow('img2', img2)                                     # 显示转换结果图像
cv2.waitKey(0)
礼帽运算
	原理:用原图像减去图像的开运算结果。
	示例代码:
# test4-32.py:形态学礼帽运算
import cv2
import numpy as np

img = cv2.imread('zh.jpg')                                  # 读取图像
cv2.imshow('img', img)                                       # 显示原图像
kernel = np.ones((5, 5), np.uint8)                            # 定义大小为5×5的内核
op = cv2.MORPH_TOPHAT                                       # 设置形态操作类型
img2 = cv2.morphologyEx(img, op, kernel, iterations=5)       # 形态操作,迭代5次
cv2.imshow('img2', img2)                                     # 显示转换结果图像
cv2.waitKey(0)
实验内容
实验1:图像几何变换
	目的:掌握图像几何变换的基本方法。
	内容:编写程序,使图像沿顺时针方向旋转,同时先缩小到10%,再放大到100%。
实验2:图像形态变换
	目的:掌握图像形态变换的基本方法。
	内容:使用画图工具创建黑白图像,通过形态操作去除噪声点和线条。
📸 边缘和轮廓
5.1 边缘检测
5.1.1 Laplacian边缘检测
Laplacian(拉普拉斯)边缘检测使用图像矩阵与拉普拉斯核进行卷积运算,其本质是计算图像中任意一点与其在水平方向和垂直方向上4个相邻点平均值的差值。
cv2.Laplacian() 函数
基本格式如下:
dst = cv2.Laplacian(src, ddepth[, ksize[, scale[, delta[, borderType]]]])
参数	说明
dst	边缘检测结果图像
src	原图像
ddepth	目标图像的深度
ksize	用于计算二阶导数滤波器的系数,必须为正数且为奇数
scale	可选比例因子
delta	添加到边缘检测结果中的可选增量值
borderType	边界值类型
示例代码
import cv2
img = cv2.imread('bee.jpg')              # 读取图像
cv2.imshow('original', img)               # 显示原图像
img2 = cv2.Laplacian(img, cv2.CV_8U)     # 边缘检测
cv2.imshow('Laplacian', img2)             # 显示结果
5.1.2 Sobel边缘检测
Sobel边缘检测将高斯滤波和微分结合起来执行图像卷积运算,其结果具有一定的抗噪性。
cv2.Sobel() 函数
基本格式如下:
dst = cv2.Sobel(src, depth, dx, dy[, ksize[, scale[, delta[, borderType]]]])
参数	说明
dst	边缘检测结果图像
src	原图像
depth	目标图像的深度
dx	导数x的阶数
dy	导数y的阶数
ksize	扩展的Sobel内核的大小,必须是1357
scale	计算导数的可选比例因子
delta	添加到边缘检测结果中的可选增量值
borderType	边界值类型
示例代码
import cv2
img = cv2.imread('bee.jpg')                      # 读取图像
cv2.imshow('original', img)                       # 显示原图像
img2 = cv2.Sobel(img, cv2.CV_8U, 0, 1)           # 边缘检测
cv2.imshow('Sobel', img2)                         # 显示结果
5.1.3 Canny边缘检测
Canny边缘检测的算法更复杂,包含下列5个步骤:
	使用高斯滤波去除图像噪声。
	使用Sobel核进行滤波,计算梯度。
	在边缘使用非最大值抑制。
	对检测出的边缘使用双阈值以去除假阳性。
	分析边缘之间的连接性,保留真正的边缘,消除不明显的边缘。
cv2.Canny() 函数
基本格式如下:
dst = cv2.Canny(src, threshold1, threshold2[, apertureSize[, L2gradient]])
参数	说明
dst	边缘检测结果图像
src	原图像
threshold1	第1阈值
threshold2	第2阈值
apertureSize	计算梯度时使用的Sobel核大小
L2gradient	标志
示例代码
import cv2
img = cv2.imread('bee.jpg')                  # 读取图像
cv2.imshow('original', img)                   # 显示原图像
img2 = cv2.Canny(img, 200, 300)               # 边缘检测
cv2.imshow('Canny', img2)                     # 显示结果
5.2 图像轮廓
5.2.1 查找轮廓
cv2.findContours() 函数用于从二值图像中查找图像轮廓,其基本格式如下:
contours, hierarchy = cv2.findContours(image, mode, method[, offset])
参数	说明
contours	返回的轮廓
hierarchy	返回的轮廓的层次结构
image	原图像
mode	轮廓的检索模式
method	轮廓的近似方法
offset	每个轮廓点移动的可选偏移量
示例代码
import cv2
import numpy as np

img = cv2.imread('shapes.jpg')                # 读取图像
cv2.imshow('original', img)                    # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

print('轮廓:', c)
print('轮廓个数:', len(c))
print('层次:', h)

for n in range(3):
    img3 = np.zeros(img.shape, np.uint8) + 255  # 创建白色图像
    cv2.polylines(img3, [c[n]], True, (255, 0, 0), 2)  # 绘制轮廓
    cv2.imshow('%s' % n, img3)                    # 显示轮廓图像

cv2.waitKey(0)                                    # 按任意键结束等待
cv2.destroyAllWindows()                           # 关闭所有窗口
5.2.2 绘制轮廓
cv2.drawContours() 函数用于绘制轮廓,其基本格式如下:
image = cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
参数	说明
image	在其中绘制轮廓的图像
contours	要绘制的轮廓
contourIdx	要绘制的轮廓的索引
color	轮廓颜色,BGR格式
thickness	可选参数,表示绘制轮廓时画笔的粗细
lineType	可选参数,表示绘制轮廓时使用的线型
hierarchy	对应cv2.findContours()函数返回的轮廓层次
maxLevel	可绘制的最大轮廓层次深度
offset	绘制轮廓的偏移位置
示例代码
import cv2
import numpy as np

img = cv2.imread('shapes.jpg')                            # 读取图像
cv2.imshow('original', img)                                # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)              # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

img3 = np.zeros(img.shape, np.uint8) + 255                 # 创建白色图像
img3 = cv2.drawContours(img3, c, -1, (0, 0, 255), 2)      # 绘制轮廓
cv2.imshow('Contours', img3)                                # 显示轮廓图像
cv2.waitKey(0)                                            # 按任意键结束等待
cv2.destroyAllWindows()                                   # 关闭所有窗口
5.2.3 轮廓特征
1. 轮廓的矩
cv2.moments() 函数用于返回轮廓的矩,其基本格式如下:
ret = cv2.moments(array[, binaryImage])
参数	说明
ret	返回的轮廓矩,是一个字典对象
array	表示轮廓的数组
binaryImage	值为True时,会将array对象中的所有非0值设置为1
示例代码
import cv2
import numpy as np

img = cv2.imread('shape2.jpg')                            # 读取图像
cv2.imshow('original', img)                               # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)             # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

for n in range(len(c)):
    m = cv2.moments(c[n])
    print('轮廓%s的矩:' % n, m)                             # 输出轮廓矩
    print('轮廓%s的面积:' % n, m['m00'])                   # 输出轮廓面积

cv2.waitKey(0)                                            # 按任意键结束等待
cv2.destroyAllWindows()                                   # 关闭所有窗口
2. 轮廓的面积
cv2.contourArea() 函数用于返回轮廓面积,其基本格式如下:
ret = cv2.contourArea(contour[, oriented])
参数	说明
ret	返回的面积
contour	轮廓
oriented	可选参数,True时返回顺时针或逆时针
示例代码
import cv2
import numpy as np

img = cv2.imread('shape2.jpg')                            # 读取图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)             # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

for n in range(len(c)):
    m = cv2.contourArea(c[n])                             # 计算轮廓面积
    print('轮廓%s的面积:' % n, m)                          # 输出轮廓面积
3. 轮廓的长度
cv2.arcLength() 函数用于返回轮廓的长度,其基本格式如下:
ret = cv2.arcLength(contour, closed)
参数	说明
ret	返回的长度
contour	轮廓
closed	布尔值,True时表示轮廓是封闭的
示例代码
import cv2
import numpy as np

img = cv2.imread('shape2.jpg')                            # 读取图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)             # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

for n in range(len(c)):
    m = cv2.arcLength(c[n], True)                          # 计算轮廓长度
    print('轮廓%s的长度:' % n, m)                          # 输出轮廓长度
4. 轮廓的近似多边形
cv2.approxPolyDP() 函数用于返回轮廓的近似多边形,其基本格式如下:
ret = cv2.approxPolyDP(contour, epsilon, closed)
参数	说明
ret	返回的近似多边形
contour	轮廓
epsilon	精度,表示近似多边形接近轮廓的最大距离
closed	布尔值,True时表示轮廓是封闭的
示例代码
import cv2
import numpy as np

img = cv2.imread('shape3.jpg')                            # 读取图像
cv2.imshow('original', img)                                # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)              # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)  # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 查找轮廓

ep = [0.1, 0.05, 0.01]
arcl = cv2.arcLength(c[0], True)                          # 计算轮廓长度
print(arcl)
img3 = np.zeros(img.shape, np.uint8) + 255                 # 创建白色图像
img3 = cv2.drawContours(img3, c, -1, (0, 0, 255), 2)      # 绘制轮廓

for n in range(3):
    eps = ep[n] * arcl
    img4 = img3.copy()
    app = cv2.approxPolyDP(c[0], eps, True)                # 获得近似多边形
    img4 = cv2.drawContours(img4, [app], -1, (255, 0, 0), 2)  # 绘制近似轮廓
    cv2.imshow('appro %.2f' % ep[n], img4)                 # 显示轮廓图像

cv2.waitKey(0)                                            # 按任意键结束等待
cv2.destroyAllWindows()                                   # 关闭所有窗口
5. 轮廓的凸包
cv2.convexHull() 函数用于返回轮廓的凸包,其基本格式如下:
hull = cv2.convexHull(contour[, clockwise[, returnPoints]])
参数	说明
hull	返回的凸包,是一个numpy.ndarray对象
contour	轮廓
clockwise	方向标记,True时,凸包为顺时针方向
returnPoints	True时,返回的hull中包含的关键点
📐 轮廓相关函数
1. 凸包(Convex Hull)
	凸包的定义:凸包是一个最小的凸多边形,能够包围所有的轮廓点。
函数:cv2.convexHull()
	基本格式:
	hull = cv2.convexHull(contour, returnPoints=True)
	参数说明:
	contour:用于计算凸包的轮廓。
	returnPoints:如果为True,返回凸包的关键点坐标;如果为False,返回凸包关键点在轮廓中的索引。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape3.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
img3 = cv2.drawContours(img3, c, -1, (0, 0, 255), 2)        # 绘制轮廓
hull = cv2.convexHull(c[0])                                   # 计算凸包
print('returnPoints=True时返回的凸包:\n ', hull)
hull2 = cv2.convexHull(c[0], returnPoints=False)             # 计算索引
print('returnPoints=False时返回的凸包:\n ', hull2)
cv2.polylines(img3, [hull], True, (255, 0, 0), 2)           # 绘制凸包
cv2.imshow('Convex Hull', img3)                              # 显示轮廓图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
2. 直边界矩形(Bounding Rectangle)
	定义:直边界矩形是能够容纳轮廓的矩形,其两条边必须是水平的。
函数:cv2.boundingRect()
	基本格式:
	ret = cv2.boundingRect(contour)
	参数说明:
	contour:用于计算直边界矩形的轮廓。
	返回值为一个四元组:(矩形左上角x坐标, 矩形左上角y坐标, 矩形的宽度, 矩形的高度)。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
ret = cv2.boundingRect(c[0])                                  # 计算直边界矩形
print('直边界矩形:\n', ret)
pt1 = (ret[0], ret[1])
pt2 = (ret[0] + ret[2], ret[1] + ret[3])
cv2.rectangle(img3, pt1, pt2, (255, 0, 0), 2)               # 绘制直边界矩形
cv2.imshow('Rectangle', img3)                                 # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
3. 旋转矩形(Rotated Rectangle)
	定义:旋转矩形是能够容纳轮廓的面积最小的矩形。
函数:cv2.minAreaRect()
	基本格式:
	box = cv2.minAreaRect(contour)
	参数说明:
	contour:用于计算旋转矩形的轮廓。
	返回的三元组格式为:((矩形中心点x坐标, 矩形中心点y坐标), (矩形的宽度, 矩形的高度), 矩形的旋转角度)。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
ret = cv2.minAreaRect(c[0])                                   # 计算最小矩形
rect = cv2.boxPoints(ret)                                     # 计算矩形顶点
rect = np.int0(rect)                                         # 转换为整数
cv2.drawContours(img3, [rect], 0, (255, 0, 0), 2)            # 绘制旋转矩形
cv2.imshow('Rectangle', img3)                                 # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
4. 最小外包圆(Minimum Enclosing Circle)
函数:cv2.minEnclosingCircle()
	基本格式:
	center, radius = cv2.minEnclosingCircle(contour)
	参数说明:
	contour:用于计算最小外包圆的轮廓。
	返回值为圆心坐标和半径。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
(x, y), radius = cv2.minEnclosingCircle(c[0])                # 计算最小外包圆
center = (int(x), int(y))
radius = int(radius)
cv2.circle(img3, center, radius, (255, 0, 0), 2)            # 绘制最小外包圆
cv2.imshow('Circle', img3)                                    # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
5. 拟合椭圆(Fitting an Ellipse)
函数:cv2.fitEllipse()
	基本格式:
	ellipse = cv2.fitEllipse(contour)
	参数说明:
	contour:用于计算拟合椭圆的轮廓。
	返回值为拟合椭圆的参数。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
ellipse = cv2.fitEllipse(c[0])                                # 计算拟合椭圆
cv2.ellipse(img3, ellipse, (255, 0, 0), 2)                   # 绘制拟合椭圆
cv2.imshow('ellipse', img3)                                   # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
6. 拟合直线(Fitting a Line)
函数:cv2.fitLine()
	基本格式:
	line = cv2.fitLine(contour, distType, param, reps, aeps)
	参数说明:
	contour:用于计算拟合直线的轮廓。
	distType:距离类型参数,决定拟合直线的计算方式。
	cv2.DIST_USER:用户自定义距离。
	cv2.DIST_L1:曼哈顿距离。
	cv2.DIST_L2:欧氏距离。
	其他距离类型见讲义内容。
	param:与距离类型相关的参数。
	reps:径向精度,通常设置为0.01。
	aeps:角度精度,通常设置为0.01。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
rows, cols = img.shape[:2]
[vx, vy, x, y] = cv2.fitLine(c[0], cv2.DIST_L2, 0, 0.01, 0.01) # 计算拟合直线
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)
cv2.line(img3, (cols - 1, righty), (0, lefty), (255, 0, 0), 2) # 绘制拟合直线
cv2.imshow('FitLine', img3)                                   # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
7. 最小外包三角形(Minimum Enclosing Triangle)
函数:cv2.minEnclosingTriangle()
	基本格式:
	retval, triangle = cv2.minEnclosingTriangle(contour)
	参数说明:
	contour:用于计算最小外包三角形的轮廓。
	返回值为最小外包三角形的面积和三角形的顶点。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape4.jpg')                                # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
ret, img2 = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY) # 二值化处理
c, h = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 计算轮廓
img3 = np.zeros(img.shape, np.uint8) + 255                   # 创建白色图像
cv2.drawContours(img3, c, -1, (0, 0, 255), 2)               # 绘制轮廓
retval, triangle = cv2.minEnclosingTriangle(c[0])            # 计算最小外包三角形
triangle = np.int0(triangle)
cv2.polylines(img3, [triangle], True, (255, 0, 0), 2)        # 绘制最小外包三角形
cv2.imshow('Triangle', img3)                                  # 显示结果图像
cv2.waitKey(0)                                                # 按任意键结束等待
cv2.destroyAllWindows()                                       # 关闭窗口
8. 霍夫变换(Hough Transform)
8.1 霍夫直线变换
函数:cv2.HoughLines()
	基本格式:
	lines = cv2.HoughLines(image, rho, theta, threshold)
	参数说明:
	image:必须是8位的单通道二值图像。
	rho:距离的精度,通常为1。
	theta:角度的精度,通常为π/180。
	threshold:阈值,值越小,检测出的直线越多。
示例代码:
import cv2
import numpy as np

img = cv2.imread('shape6.jpg')                               # 读取图像
cv2.imshow('original', img)                                   # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
edges = cv2.Canny(gray, 50, 150, apertureSize=3)            # 执行边缘检测
lines = cv2.HoughLines(edges, 1, np.pi / 180, 150)           # 霍夫直线变换
img3 = img.copy()
for line in lines:                                           # 逐条绘制直线
    rho, theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0, y0 = a * rho, b * rho
    pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))
    pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))
    cv2.line(img3, pt1, pt2, (0, 0, 255), 2)                # 绘制直线
cv2.imshow('Hough Lines', img3)                              # 显示结果图像
cv2.waitKey(0)                                              # 按任意键结束等待
cv2.destroyAllWindows()                                     # 关闭窗口

# 🖼️ 霍夫变换

## 1. 霍夫直线变换

### cv2.HoughLinesP() 函数

> **霍夫直线变换** 是一种通过概率霍夫变换算法来检测图像中的直线的技术,其基本格式如下:

```python
lines = cv2.HoughLinesP(image, rho, theta, threshold[, minLineLength[, maxLineGap]])
参数说明
参数	说明
lines	返回的直线。
image	原图像,必须是8位的单通道二值图像。通常在霍夫变换前需执行阈值处理或Canny边缘检测。
rho	距离的精度(以像素为单位),通常为1。
theta	角度的精度,通常使用 π/180,表示搜索所有可能的角度。
threshold	阈值,值越小,检测出的直线越多。
minLineLength	可接受的直线的最小长度,默认值为0。
maxLineGap	共线线段之间的最大间隔,默认值为0。
示例代码
import cv2
import numpy as np

img = cv2.imread('shape6.jpg')  # 读取图像
cv2.imshow('original', img)      # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
edges = cv2.Canny(gray, 50, 150, apertureSize=3)  # 执行边缘检测
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 1, minLineLength=100, maxLineGap=10)  # 概率霍夫直线变换
img3 = img.copy()

for line in lines:  # 逐条绘制直线
    x1, y1, x2, y2 = line[0]
    cv2.line(img3, (x1, y1), (x2, y2), (0, 0, 255), 2)  # 绘制直线

cv2.imshow('HoughLines', img3)  # 显示结果图像
cv2.waitKey(0)  # 按任意键结束等待
cv2.destroyAllWindows()  # 关闭所有窗口
2. 霍夫圆变换
cv2.HoughCircles() 函数
霍夫圆变换 是一种通过霍夫变换查找图像中的圆的技术,其基本格式如下:
circles = cv2.HoughCircles(image, method, dp, minDist[, param1[, param2[, minRadius[, maxRadius]]]])
参数说明
参数	说明
circles	返回的圆。
image	原图像,必须是8位的单通道二值图像。
method	查找方法,可设置为 cv2.HOUGH_GRADIENT 和 cv2.HOUGH_GRADIENT_ALT。
dp	累加器分辨率,与图像分辨率成反比。
minDist	圆心间的最小距离。
param1	对应Canny边缘检测的高阈值(低阈值是高阈值的一半),默认值为100。
param2	圆心位置必须达到的投票数,值越大,检测出的圆越少,默认值为100。
minRadius	最小圆半径,半径小于该值的圆不会被检测出来,默认值为0。
maxRadius	最大圆半径,半径大于该值的圆不会被检测出来,默认值为0。
示例代码
import cv2
import numpy as np

img = cv2.imread('shape6.jpg')  # 读取图像
cv2.imshow('original', img)      # 显示原图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
edges = cv2.Canny(gray, 50, 150, apertureSize=3)  # 执行边缘检测
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 50, param2=30, minRadius=10, maxRadius=40)  # 检测圆
circles = np.uint16(np.around(circles))
img2 = img.copy()

for i in circles[0, :]:
    cv2.circle(img2, (i[0], i[1]), i[2], (255, 0, 0), 2)  # 画圆
    cv2.circle(img2, (i[0], i[1]), 2, (0, 0, 255), 3)      # 画圆心

cv2.imshow('circles', img2)  # 显示结果图像
cv2.waitKey(0)  # 按任意键结束等待
cv2.destroyAllWindows()  # 关闭所有窗口
3. 实验内容
实验1:执行Canny边缘检测
	实验目的:使用函数 cv2.Canny() 对图像执行 Canny 边缘检测操作。
	实验内容:原图如图 5-18 所示,使用函数 cv2.Canny() 对其执行边缘检测操作。
实验2:查找和绘制轮廓
	实验目的:掌握查找轮廓和绘制轮廓的基本方法。
	实验内容:原图如图 5-18 所示,查找并绘制该图中的轮廓。
🖼️ OpenCV计算机视觉基础教程
📊 6.1 直方图基础
定义直方图
直方图用于统计图像内各个灰度级出现的次数。横坐标表示图像像素的灰度级,纵坐标表示像素灰度级的数量。
关键概念
	RANGE:要统计的灰度级范围,一般为[0, 255]。
	BINS:灰度级的分组数量,例如将[0, 255]16个灰度级分为一组,BINS为16。
	DIMS:绘制直方图时采集的参数数量,通常为1。
主要内容
	使用hist()函数绘制直方图
	使用calcHist()函数查找直方图
	应用掩模的直方图
	NumPy中的直方图
📈 6.1.1 用hist()函数绘制直方图
基本格式
matplotlib.pyplot.hist(src, bins)
	src:用于绘制直方图的图像数据,需为一维数组。
	bins:灰度级分组数量。
示例代码
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('gate.jpg')  # 读取图像
cv2.imshow('original', img)    # 显示原图像
plt.hist(img.ravel(), 256)     # 绘制直方图
plt.show()                     # 显示直方图
📉 6.1.2 用calcHist()函数查找直方图
基本格式
hist = cv2.calcHist(image, channels, mask, histSize, ranges)
参数说明
	hist:返回的直方图,大小为256。
	image:原图像,使用方括号括起来。
	channels:通道编号,灰度图像为[0],BGR图像为[0][1][2]。
	mask:掩模图像,None时统计整个图像。
	histSize:BINS的值,使用方括号括起来,如[256]。
	ranges:像素值范围,8位灰度图像为[0, 255]。
示例代码
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('gate.jpg')  # 读取图像
cv2.imshow('original', img)    # 显示原图像
histb = cv2.calcHist([img], [0], None, [256], [0, 255])  # 计算B通道直方图
histg = cv2.calcHist([img], [1], None, [256], [0, 255])  # 计算G通道直方图
histr = cv2.calcHist([img], [2], None, [256], [0, 255])  # 计算R通道直方图
plt.plot(histb, color='b')  # 绘制B通道直方图
plt.plot(histg, color='g')  # 绘制G通道直方图
plt.plot(histr, color='r')  # 绘制R通道直方图
plt.show()  # 显示直方图
🖤 6.1.3 应用掩模的直方图
掩模的概念
掩模图像为黑底,白色区域为透明区域,覆盖在原图像上,计算掩模结果图像的直方图。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('gate.jpg')  # 读取图像
w, h, d = img.shape
mask = np.zeros((w, h), np.uint8)  # 创建黑色图像
w1 = np.int0(w / 4)
w2 = np.int0(w * 0.75)
h1 = np.int0(h / 4)
h2 = np.int0(h * 0.75)
mask[w1:w2, h1:h2] = 255  # 设置掩模白色区域
cv2.imshow('mask', mask)  # 显示掩模图像
histb = cv2.calcHist([img], [0], mask, [256], [0, 255])  # 计算B通道直方图
histg = cv2.calcHist([img], [1], mask, [256], [0, 255])  # 计算G通道直方图
histr = cv2.calcHist([img], [2], mask, [256], [0, 255])  # 计算R通道直方图
plt.plot(histb, color='b')  # 绘制B通道直方图
plt.plot(histg, color='g')  # 绘制G通道直方图
plt.plot(histr, color='r')  # 绘制R通道直方图
plt.show()  # 显示直方图
📊 6.1.4 NumPy中的直方图
基本格式
hist, bin_edges = np.histogram(src, bins, range)
参数说明
	hist:返回的直方图。
	bin_edges:返回的灰度级分组数量边界值。
	src:原图转换成的一维数组。
	bins:灰度级分组数量。
	range:像素值范围。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('gate.jpg')  # 读取图像
cv2.imshow('original', img)    # 显示原图像
histb, e1 = np.histogram(img[0].ravel(), 256, [0, 256])  # 计算B通道直方图
histg, e2 = np.histogram(img[1].ravel(), 256, [0, 256])  # 计算G通道直方图
histr, e3 = np.histogram(img[2].ravel(), 256, [0, 256])  # 计算R通道直方图
plt.plot(histb, color='b')  # 绘制B通道直方图
plt.plot(histg, color='g')  # 绘制G通道直方图
plt.plot(histr, color='r')  # 绘制R通道直方图
plt.show()  # 显示直方图
🌈 6.2 直方图均衡化
概述
直方图均衡化通过调整图像的灰度来提高图像的对比度。
主要内容
	普通直方图均衡化
	限制对比度自适应直方图均衡化
⚖️ 6.2.1 普通直方图均衡化
基本格式
dst = cv2.equalizeHist(src)
参数说明
	dst:直方图均衡化后的图像。
	src:原图像,必须是8位的单通道图像。
示例代码
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('bee.jpg', 0)  # 打开灰度图像
cv2.imshow('original', img)      # 显示原图像
plt.figure('原图的直方图')
plt.hist(img.ravel(), 256)       # 绘制原直方图
img2 = cv2.equalizeHist(img)
cv2.imshow('equalizeHist', img2) # 显示均衡化后的图像
plt.figure('均衡化后的直方图')
plt.hist(img2.ravel(), 256)       # 绘制均衡化后图像的直方图
plt.show()                        # 显示直方图
🎨 6.2.2 限制对比度自适应直方图均衡化
概念
自适应直方图均衡化用于提高图像的局部对比度,限制对比度自适应直方图均衡化(CLAHE)是其改进版本。
创建CLAHE对象
retval = cv2.createCLAHE([clipLimit[, tileGridSize]])
参数说明
	retval:返回的CLAHE对象。
	clipLimit:对比度受限的阈值,默认值为40.0。
	tileGridSize:直方图均衡化的网格大小,默认值为(8, 8)。
📜 示例代码
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('clahe.jpg', 0)  # 打开图像(单通道灰度图像)
cv2.imshow('original', img)        # 显示原图像
img2 = cv2.equalizeHist(img)
cv2.imshow('equalizeHist', img2)   # 显示均衡化后的图像
clahe = cv2.createCLAHE(clipLimit=5)  # 创建CLAHE对象
img3 = clahe.apply(img)              # 应用CLAHE对象
cv2.imshow('CLAHE', img3)            # 显示应用CLAHE对象后的图像
cv2.waitKey(0)
🌐 6.3 二维直方图
主要内容
	OpenCV中的二维直方图
	NumPy中的二维直方图
📊 6.3.1 OpenCV中的二维直方图
基本格式
使用cv2.calcHist()函数,参数有所不同: - image参数应从BGR色彩空间转换为HSV色彩空间。 - channels参数设置为[0, 1]以处理色相和饱和度。
示例代码
import cv2

img = cv2.imread('building.jpg')                        # 打开图像
cv2.imshow('original', img)                              # 显示原图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)            # 转换色彩空间为HSV
hist = cv2.calcHist([img2], [0, 1], None, [180, 256], [0, 180, 0, 256])  # 计算颜色直方图
cv2.imshow('2Dhist', hist)                              # 显示颜色直方图
cv2.waitKey(0)
🎨 6.3.2 NumPy中的二维直方图
基本格式
hist, xedges, yedges = np.histogram2D(x, y, bins, range)
参数说明
	hist:返回的直方图。
	xedges:返回的x的直方图的BINS边界值。
	yedges:返回的y的直方图的BINS边界值。
	x和y:原图对应通道转换成的一维数组。
	bins:BINS的值,如[180, 256]range:像素值范围。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('building.jpg')                       # 打开图像
cv2.imshow('original', img)                             # 显示原图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)           # 转换色彩空间为HSV
h, s, v = cv2.split(img2)
hist, x, y = np.histogram2d(h.ravel(), s.ravel(), [180, 256], [[0, 180], [0, 256]])  # 计算颜色直方图
cv2.imshow('2Dhist', hist)                              # 显示灰度颜色直方图
plt.imshow(hist, interpolation='nearest')               # 绘制颜色直方图
plt.show()                                             # 显示颜色直方图
cv2.waitKey(0)
🧪 6.4 实验
主要内容
	实验1:使用NumPy函数计算直方图
	实验2:使用OpenCV函数计算直方图
🧪 6.4.1 实验1:使用NumPy函数计算直方图
	实验目的:掌握使用NumPy函数计算一维直方图和二维直方图的基本方法。
	实验内容:使用NumPy的histogram()和histogram2D()函数计算图像的一维直方图和二维直方图。
🧪 6.4.2 实验2:使用OpenCV函数计算直方图
	实验目的:掌握使用OpenCV函数计算一维直方图和二维直方图的基本方法。
	实验内容:使用OpenCV的calcHist()函数计算图像的一维直方图和二维直方图。
🖼️ 模板匹配和图像分割
📌 模板匹配
定义
模板匹配是指在当前图像中查找与目标图像最相近的部分。
主要内容
	单目标匹配
	多目标匹配
📌 7.1.1 单目标匹配
功能
单目标匹配是指输入图像中只存在一个可能匹配结果。
使用方法
OpenCV中的cv2.matchTemplate()函数用于执行匹配操作,基本格式如下:
result = cv2.matchTemplate(image, templ, method)
参数说明
	image: 输入图像,必须是8位或32位浮点类型。
	templ: 模板图像,不能大于输入图像,且数据类型要和输入图像相同。
	method: 匹配方法,返回结果会有所不同。可用的匹配方法如下:
匹配方法	描述
cv2.TM_SQDIFF	方差匹配,完全匹配时结果为0,其他时为较大值。
cv2.TM_SQDIFF_NORMED	标准(归一化)方差匹配。
cv2.TM_CCORR	相关匹配,乘积越大匹配度越高,乘积为0时匹配度最低。
cv2.TM_CCORR_NORMED	标准(归一化)相关匹配。
cv2.TM_CCOEFF	相关系数匹配,结果为1表示完美匹配,-1表示糟糕匹配,0表示无相关性。
cv2.TM_CCOEFF_NORMED	标准(归一化)相关系数匹配。
结果说明
	结果为numpy.ndarray对象,若输入图像大小为W×H,模板图像大小为w×h,则结果大小为(W-w+1)×(H-h+1)。
	匹配结果值越小说明匹配度越高(对于cv2.TM_SQDIFF和cv2.TM_SQDIFF_NORMED),反之则说明匹配度越低。
处理匹配结果
使用cv2.minMaxLoc()函数处理匹配结果:
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(src)
参数说明
	src: cv2.matchTemplate()函数的返回结果。
	minVal: 最小值。
	maxVal: 最大值。
	minLoc: 最小值的位置。
	maxLoc: 最大值的位置。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img1 = cv2.imread('bee.jpg')  # 打开输入图像
temp = cv2.imread('template.jpg')  # 打开模板图像
img1gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
tempgray = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
res = cv2.matchTemplate(img1gray, tempgray, cv2.TM_SQDIFF)  # 执行匹配
plt.imshow(res, cmap='gray')  # 显示匹配结果
plt.show()

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 返回最值和位置
top_left = min_loc  # 最小值为最佳匹配
bottom_right = (top_left[0] + w, top_left[1] + h)  # 匹配范围的右下角位置
cv2.rectangle(img1, top_left, bottom_right, (255, 0, 0), 2)  # 绘制匹配范围
cv2.imshow('Detected Range', img1)
cv2.waitKey(0)
📌 7.1.2 多目标匹配
功能
多目标匹配是指输入图像中存在多个可能的匹配结果。
使用方法
在使用cv2.matchTemplate()函数执行匹配操作后,根据匹配方法设置阈值,符合条件的匹配目标将被识别。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img1 = cv2.imread('bee2.jpg')  # 打开输入图像
temp = cv2.imread('template.jpg')  # 打开模板图像
img1gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
tempgray = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
th, tw = tempgray.shape  # 模板图像的高度和宽度
img1h, img1w = img1gray.shape
res = cv2.matchTemplate(img1gray, tempgray, cv2.TM_SQDIFF_NORMED)  # 执行归一化方差匹配
mloc = []  # 保存符合条件的匹配位置
threshold = 0.24  # 设置匹配度阈值

for i in range(img1h - th):
    for j in range(img1w - tw):
        if res[i][j] <= threshold:  # 保存小于阈值的匹配位置
            mloc.append((j, i))

for pt in mloc:
    cv2.rectangle(img1, pt, (pt[0] + tw, pt[1] + th), (255, 0, 0), 2)  # 标注匹配位置
cv2.imshow('result', img1)  # 显示结果
cv2.waitKey(0)
📌 7.2 图像分割
定义
图像分割是指将前景对象从图像中分割或提取出来。
主要内容
	使用分水岭算法分割图像
	图像金字塔
📌 7.2.1 使用分水岭算法分割图像
原理
分水岭算法将灰度图像视为地形图表面,灰度值高的部分表示山峰,低的部分表示山谷。通过构建水坝以避免水的汇合,完成图像分割。
操作步骤
	将原图像转换为灰度图像。
	应用形态变换中的开运算和膨胀去除图像噪声,获得图像边缘信息。
	进行距离转换,阈值处理,确定图像前景。
	确定未知区域。
	标记背景图像。
	执行分水岭算法进行图像分割。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('qizi.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
ret, imgthresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)  # Otsu算法阈值处理
kernel = np.ones((3, 3), np.uint8)  # 定义形态变换卷积核
imgopen = cv2.morphologyEx(imgthresh, cv2.MORPH_OPEN, kernel, iterations=2)  # 开运算
imgdist = cv2.distanceTransform(imgopen, cv2.DIST_L2, 5)  # 距离转换
ret, imgfg = cv2.threshold(imgdist, 0.7 * imgdist.max(), 255, 2)  # 阈值处理
imgfg = np.uint8(imgfg)  # 转换为整数
ret, markers = cv2.connectedComponents(imgfg)  # 标记阈值处理结果
imgwater = cv2.watershed(img, markers)  # 执行分水岭算法
plt.imshow(imgwater)  # 显示分割结果
plt.title('watershed')
plt.axis('off')
plt.show()
📌 7.2.2 图像金字塔
定义
图像金字塔从分辨率的角度分析处理图像,底部为原始图像,向上采样得到更小的图像。
功能
	高斯金字塔向下采样:使用cv2.pyrDown()函数。
	高斯金字塔向上采样:使用cv2.pyrUp()函数。
	拉普拉斯金字塔:第n层是该层高斯金字塔图像减去第n+1层向上采样结果。
示例代码
高斯金字塔向下采样
import cv2

img0 = cv2.imread('qizi.jpg')
img1 = cv2.pyrDown(img0)  # 第1次采样
img2 = cv2.pyrDown(img1)  # 第2次采样
cv2.imshow('img0', img0)  # 显示第0层
cv2.imshow('img1', img1)  # 显示第1层
cv2.imshow('img2', img2)  # 显示第2层
cv2.waitKey(0)
高斯金字塔向上采样
import cv2

img0 = cv2.imread('qizi2.jpg')
img1 = cv2.pyrUp(img0)  # 第1次采样
img2 = cv2.pyrUp(img1)  # 第2次采样
cv2.imshow('img0', img0)  # 显示第0层
cv2.imshow('img1', img1)  # 显示第1层
cv2.imshow('img2', img2)  # 显示第2层
cv2.waitKey(0)
拉普拉斯金字塔
import cv2

img0 = cv2.imread('qizi.jpg')
img1 = cv2.pyrDown(img0)  # 第1次采样
img2 = cv2.pyrDown(img1)  # 第2次采样
img3 = cv2.pyrDown(img2)  # 第3次采样
imgL0 = cv2.subtract(img0, cv2.pyrUp(img1))  # 拉普拉斯金字塔第0层
imgL1 = cv2.subtract(img1, cv2.pyrUp(img2))  # 拉普拉斯金字塔第1层
imgL2 = cv2.subtract(img2, cv2.pyrUp(img3))  # 拉普拉斯金字塔第2层
cv2.imshow('imgL0', imgL0)  # 显示第0层
cv2.imshow('imgL1', imgL1)  # 显示第1层
cv2.imshow('imgL2', imgL2)  # 显示第2层
cv2.waitKey(0)
📌 7.3 交互式前景提取
原理
交互式前景提取通过矩形指定要提取的前景范围,执行前景提取算法以获得初步结果。
🖼️ 前景提取
“前景提取是计算机视觉中的一项重要技术,旨在从图像中分离出感兴趣的对象或区域。”
前景提取的背景处理
在进行前景提取时,可能面临以下问题:
	前景未提取完整
	背景被错误处理为前景
此时需要进行人工干预,用户需复制原图像作为掩模图像,并用白色标注要提取的前景区域,黑色标注背景区域,标注的精确度要求不高。
掩模图像的使用
使用掩模图像执行前景提取算法,获得理想的提取结果。
OpenCV中的前景提取函数
cv2.grabCut() 函数
cv2.grabCut() 函数用于实现前景提取,其基本格式如下:
mask2, bgdModel, fgdModel = cv2.grabCut(img, mask1, rect, bgdModel, fgdModel, iterCount[, mode])
参数说明
参数	说明
mask1	输入的8位单通道掩模图像,用于指定图像的哪些区域可能是背景或前景。
mask2	输出的掩模图像,其中的0表示确定的背景,1表示确定的前景,2表示可能的背景,3表示可能的前景。
bgdModel	用于内部计算的临时数组,需定义为大小是1×65的np.float64类型的数组,初始值均为0。
fgdModel	用于内部计算的临时数组,需定义为大小是1×65的np.float64类型的数组,初始值均为0。
img	输入的83通道图像。
rect	矩形坐标,格式为“(左上角的横坐标x, 左上角的纵坐标y, 宽度, 高度)”。
iterCount	迭代次数。
mode	前景提取模式,可设置为以下值:
	- cv2.GC_INIT_WITH_RECT:使用矩形模板。
	- cv2.GC_INIT_WITH_MASK:使用自定义模板。
	- cv2.GC_EVAL:使用修复模式。
	- cv2.GC_EVAL_FREEZE_MODEL:使用固定模式。
示例代码
交互式前景提取示例 1
import cv2
import numpy as np

img = cv2.imread('hehua.jpg')
cv2.imshow('original', img)
mask = np.zeros(img.shape[:2], np.uint8)  # 定义与原图大小相同的掩模图像
bg = np.zeros((1, 65), np.float64)
fg = np.zeros((1, 65), np.float64)
rect = (50, 50, 400, 300)  # 根据原图设置包含前景的矩形大小

cv2.grabCut(img, mask, rect, bg, fg, 5, cv2.GC_INIT_WITH_RECT)  # 提取前景

# 将返回的掩模图像中像素值为0或2的像素设置为0(确认为背景)
# 将所有像素值为1或3的像素设置为1(确认为前景)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:, :, np.newaxis]  # 将掩模图像与原图像相乘,获得分割出来的前景图像
cv2.imshow('grabCut', img)  # 显示获得的前景
cv2.waitKey(0)
交互式前景提取示例 2
为了获得更好的提取结果,我们首先在系统中复制原图像,再使用绘图工具标注前景和背景,最后执行两次前景提取,示例代码如下:
import cv2
import numpy as np

img = cv2.imread('hehua.jpg')
mask = np.zeros(img.shape[:2], np.uint8)  # 定义原始掩模图像
bg = np.zeros((1, 65), np.float64)
fg = np.zeros((1, 65), np.float64)
rect = (50, 50, 400, 300)  # 根据原图设置包含前景的矩形大小

cv2.grabCut(img, mask, rect, bg, fg, 5, cv2.GC_INIT_WITH_RECT)  # 第1次提取前景,矩形模式
imgmask = cv2.imread('hehua2.jpg')  # 读取已标注的掩模图像
cv2.imshow('mask image', imgmask)

mask2 = cv2.cvtColor(imgmask, cv2.COLOR_BGR2GRAY, dstCn=1)  # 转换为单通道灰度图像
mask[mask2 == 0] = 0  # 将掩模图像中黑色像素对应的原始掩模像素设置为0
mask[mask2 == 255] = 1  # 将掩模图像中白色像素对应的原始掩模像素设置为1

cv2.grabCut(img, mask, None, bg, fg, 5, cv2.GC_INIT_WITH_MASK)  # 第2次提取前景,掩模模式

# 将返回的掩模图像中像素值为0或2的像素设置为0(确认为背景)
# 将所有像素值为1或3的像素设置为1(确认为前景)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:, :, np.newaxis]  # 将掩模图像与原图像相乘,获得分割出来的前景图像
cv2.imshow('grabCut', img)  # 显示获得的前景
cv2.waitKey(0)
实验内容
实验1:使用模板匹配查找图像
	实验目的:掌握使用cv2.matchTemplate()函数查找图像的基本方法。
	实验内容:选择两幅图像分别作为原图像和模板图像,在原图像中查找出模板图像的位置,并使用红色线条对该位置进行标注。
实验2:使用交互式前景提取方法分割图像
	实验目的:掌握使用交互式前景提取方法分割图像的基本方法。
	实验内容:使用交互式前景提取方法,将图示的杯子作为前景提取出来。
📷 特征检测与描述
📚 第8章 特征检测
“图像的特征是指图像中具有独特性和易于识别性的区域,角、边缘等都属于有意义的特征。OpenCV可以检测并提取图像的特征,并对其进行描述,以便用于图像匹配和搜索。”
主要内容
	角检测
	特征点检测
	对象查找
	实验
🔺 8.1 角检测
“角是两条边的交点,也可称为角点或拐角,它是图像中各个方向上强度变化最大的区域。”
8.1.1 哈里斯角检测
哈里斯角检测是克里斯·哈里斯和迈克·斯蒂芬斯提出的一种角检测方法。
	函数: cv2.cornerHarris()
	格式: dst = cv2.cornerHarris(src, blockSize, ksize, k)
参数	说明
dst	返回结果,类型为 numpy.ndarray,大小与 src 相同。
src	8位单通道或浮点值图像。
blockSize	邻域大小,值越大,检测出的角占的区域越大。
ksize	哈里斯角检测器使用的Sobel算子的中孔参数。
k	哈里斯角检测器的自由参数,影响检测的敏感度。
示例代码:哈里斯角检测
import cv2
import numpy as np

img = cv2.imread('cube.jpg')                                 # 打开输入图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 转换为灰度图像
gray = np.float32(gray)                                      # 转换为浮点类型
dst = cv2.cornerHarris(gray, 8, 7, 0.01)                    # 执行角检测
img[dst > 0.02 * dst.max()] = [0, 0, 255]                   # 将检测结果中值大于“最大值*0.02”对应的像素设置为红色
cv2.imshow('dst', img)                                       # 显示检测结果
cv2.waitKey(0)
8.1.2 优化哈里斯角
使用 cv2.cornerSubPix() 函数对哈里斯角进行优化,找出更准确的角的位置。
	函数: cv2.cornerSubPix()
	格式: dst = cv2.cornerSubPix(src, corners, winSize, zeroZone, criteria)
参数	说明
dst	返回结果,存储优化后的角信息。
src	8位单通道或浮点值图像。
corners	哈里斯角的质心坐标。
winSize	搜索窗口边长的一半。
zeroZone	零值边长的一半。
criteria	优化查找的终止条件。
示例代码:优化哈里斯角
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('cube.jpg')                            # 打开图像,默认为BGR格式
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)          # 转换为灰度图像
gray = np.float32(gray)                                 # 转换为浮点类型
dst = cv2.cornerHarris(gray, 8, 7, 0.04)               # 查找哈里斯角
r, dst = cv2.threshold(dst, 0.01 * dst.max(), 255, 0)  # 二值化阈值处理
dst = np.uint8(dst)                                     # 转换为整型
r, l, s, cxys = cv2.connectedComponentsWithStats(dst)   # 查找质点坐标
cif = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)  # 定义优化查找条件
corners = cv2.cornerSubPix(gray, np.float32(cxys), (5, 5), (-1, -1), cif) # 执行优化查找
res = np.hstack((cxys, corners))                        # 堆叠构造新数组,便于标注角
res = np.int0(res)                                      # 转换为整型
img[res[:, 1], res[:, 0]] = [0, 0, 255]                # 将哈里斯角对应像素设置为红色
img[res[:, 3], res[:, 2]] = [0, 255, 0]                # 将优化结果像素设置为绿色
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)            # 转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()                                              # 显示检测结果
8.1.3 Shi-Tomasi角检测
Shi-Tomasi角检测是基于哈里斯角检测的改进方法。
	函数: cv2.goodFeaturesToTrack()
	格式: dst = cv2.goodFeaturesToTrack(src, maxCorners, qualityLevel, minDistance)
参数	说明
dst	返回结果,保存检测到的角在原图像中的坐标。
src	8位单通道或浮点值图像。
maxCorners	返回的角的最大数量。
qualityLevel	可接受的角的最低质量。
minDistance	返回的角之间的最小欧几里得距离。
示例代码:Shi-Tomasi角检测
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('five.jpg')                            # 打开图像,默认为BGR格式
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)          # 转换为灰度图像
gray = np.float32(gray)                                 # 转换为浮点类型
corners = cv2.goodFeaturesToTrack(gray, 6, 0.1, 100)   # 检测角,最多6个
corners = np.int0(corners)                              # 转换为整型
for i in corners:
    x, y = i.ravel()
    cv2.circle(img, (x, y), 4, (0, 0, 255), -1)        # 用红色圆点标注找到的角
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)            # 转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()                                              # 显示检测结果
🔍 8.2 特征点检测
8.2.1 FAST特征检测
FAST特征检测器主要根据像素周围16个像素的强度和阈值等参数来判断像素是否为关键点。
	函数: cv2.FastFeatureDetector_create()
	方法: detect()
示例代码:FAST关键点检测
import cv2

img = cv2.imread('cube.jpg')                                # 打开图像,默认为BGR格式
fast = cv2.FastFeatureDetector_create()                     # 创建FAST检测器
kp = fast.detect(img, None)                                  # 检测关键点,不使用掩模
img2 = cv2.drawKeypoints(img, kp, None, color=(0, 0, 255)) # 绘制关键点
cv2.imshow('FAST points', img2)                             # 显示绘制了关键点的图像
fast.setThreshold(20)                                       # 设置阈值,默认阈值为10
kp = fast.detect(img, None)                                 # 检测关键点,不使用掩模
n = 0
for p in kp:                                                # 输出关键点信息
    print("第%s个关键点,坐标:" % (n + 1), p.pt, '响应强度:', p.response, '邻域大小:', p.size, '角度:', p.angle)
    n += 1
img3 = cv2.drawKeypoints(img, kp, None, color=(0, 0, 255))
cv2.imshow('Threshold20', img3)                            # 显示绘制了关键点的图像
cv2.waitKey(0)
8.2.2 SIFT特征检测
SIFT(尺度不变特征变换)算法用于查找图像中的尺度不变特征,返回图像中的关键点。
	函数: cv2.SIFT_create()
	方法: detect()
示例代码:SIFT关键点检测
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('five.jpg')                                # 打开图像,默认为BGR格式
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)              # 转换为灰度图像
sift = cv2.SIFT_create()                                    # 创建SIFT检测器
kp = sift.detect(gray, None)                                # 检测关键点
img2 = cv2.drawKeypoints(img, kp, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)  # 绘制关键点
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)              # 转换为RGB图像
plt.imshow(img2)
plt.axis('off')
plt.show()                                                  # 显示绘制了关键点的图像
8.2.3 ORB特征检测
ORB特征检测基于FAST特征检测器和BRIEF描述符进行了改进。
	函数: cv2.ORB_create()
	方法: detect()
示例代码:ORB关键点检测
import cv2
import numpy as np

img = cv2.imread('cube.jpg')                                # 打开图像,默认为BGR格式
orb = cv2.ORB_create()                                      # 创建ORB检测器
kp = orb.detect(img, None)                                   # 检测关键点
img2 = cv2.drawKeypoints(img, kp, None, color=(0, 0, 255)) # 绘制关键点
cv2.imshow('ORB', img2)                                     # 显示绘制了特征点的图像
cv2.waitKey(0)
🔗 8.3 特征匹配
8.3.1 暴力匹配器
暴力匹配器使用描述符进行特征比较,返回最佳匹配结果。
	函数: cv2.BFMatcher_create()
	格式: bf = cv2.BFMatcher_create([normType[, crossCheck]])
参数	说明
bf	返回的暴力匹配器对象。
normType	距离测量类型,默认为 cv2.NORM_L2。
crossCheck	默认为 False,匹配器为每个查询描述符找到k个距离最近的匹配描述符。
示例代码:暴力匹配器、ORB描述符和match()方法匹配
import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread('xhu1.jpg', cv2.IMREAD_GRAYSCALE)            # 打开灰度图像
img2 = cv2.imread('xhu2.jpg', cv2.IMREAD_GRAYSCALE)            # 打开灰度图像
orb = cv2.ORB_create()                                         # 创建ORB检测器
kp1, des1 = orb.detectAndCompute(img1, None)                 # 检测关键点和计算描述符
kp2, des2 = orb.detectAndCompute(img2, None)                 # 检测关键点和计算描述符
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True) # 创建匹配器
ms = bf.match(des1, des2)                                     # 执行特征匹配
ms = sorted(ms, key=lambda x: x.distance)                     # 按距离排序
img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms[:20], None,   # 绘制前20个匹配结果
                       flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)
plt.axis('off')
plt.show()
8.3.2 knnMatch() 方法
暴力匹配器对象的 knnMatch() 方法可返回指定数量的最佳匹配结果。
	格式: ms = bf.knnMatch(des1, des2, k=n)
参数	说明
ms	返回的匹配结果列表,包含由参数k指定个数的DMatch对象。
des1	查询描述符。
des2	训练描述符。
k	返回的最佳匹配个数。
示例代码:暴力匹配器、ORB描述符和knnMatch()方法匹配
import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread('xhu1.jpg', cv2.IMREAD_GRAYSCALE)         # 打开灰度图像
img2 = cv2.imread('xhu2.jpg', cv2.IMREAD_GRAYSCALE)         # 打开灰度图像
orb = cv2.ORB_create()                                       # 创建ORB检测器
kp1, des1 = orb.detectAndCompute(img1, None)                # 检测关键点和计算描述符
kp2, des2 = orb.detectAndCompute(img2, None)                # 检测关键点和计算描述符
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True) # 创建匹配器
ms = bf.knnMatch(des1, des2, k=2)                           # 执行特征匹配

# 📸 特征匹配与对象查找

## 8.3.1 暴力匹配器

### 创建匹配器

通过以下代码创建一个暴力匹配器:

```python
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)  # 创建匹配器
特征匹配
执行特征匹配的代码如下:
ms = bf.knnMatch(des1, des2, k=2)  # 执行特征匹配
比例测试
应用比例测试选择要使用的匹配结果。代码示例如下:
good = []
for m, n in ms:
    if m.distance < 0.75 * n.distance:  # 比较两个匹配结果的距离
        good.append(m)
绘制匹配结果
绘制前20个匹配结果的代码示例如下:
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None,  # 绘制前20个匹配结果
                        flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)
plt.axis('off')
plt.show()
________________________________________
8.3.2 FLANN匹配器
FLANN简介
FLANN(快速近似最近邻库)特征匹配算法比其他的最近邻算法更快。创建FLANN匹配器时,需要传递两个字典参数:index_params 和 search_params。
index_params
用于指定索引树的算法类型和数量。以下是不同算法的设置代码:
	SIFT 和 SURF:
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
	ORB算法:
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)
search_params
用于指定索引树的遍历次数,通常设置为50,如下所示:
search_params = dict(checks=50)
________________________________________
示例代码:FLANN匹配
以下是使用FLANN匹配的示例代码:
import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread('xhu1.jpg', cv2.IMREAD_GRAYSCALE)  # 打开灰度图像
img2 = cv2.imread('xhu2.jpg', cv2.IMREAD_GRAYSCALE)  # 打开灰度图像
orb = cv2.ORB_create()  # 创建ORB检测器
kp1, des1 = orb.detectAndCompute(img1, None)  # 检测关键点和计算描述符
kp2, des2 = orb.detectAndCompute(img2, None)  # 检测关键点和计算描述符

# 定义FLANN参数
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)
search_params = dict(checks=50)

flann = cv2.FlannBasedMatcher(index_params, search_params)  # 创建FLANN匹配器
matches = flann.match(des1, des2)  # 执行匹配操作

draw_params = dict(matchColor=(0, 255, 0),  # 设置关键点和连接线为绿色
                   singlePointColor=(255, 0, 0),  # 设置单个点为红色
                   matchesMask=None,
                   flags=cv2.DrawMatchesFlags_DEFAULT)

img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None, draw_params)  # 绘制匹配结果
plt.imshow(img3)
plt.axis('off')
plt.show()  # 显示结果
________________________________________
8.4 对象查找
查找最佳匹配
经过特征匹配后,可找到查询图像在训练图像中的最佳匹配。获得最佳匹配结果后,调用 cv2.findHomography() 函数执行查询图像和训练图像的透视转换。
cv2.findHomography()
cv2.findHomography() 函数的基本格式如下:
retv, mask = cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold]])
参数说明:
	retv:返回的转换矩阵。
	mask:返回的查询图像在训练图像中的最佳匹配结果掩模。
	srcPoints:查询图像匹配结果的坐标。
	dstPoints:训练图像匹配结果的坐标。
	method:用于计算透视转换矩阵的方法。
	ransacReprojThreshold:可允许的最大重投影误差。
cv2.perspectiveTransform()
cv2.perspectiveTransform() 函数的基本格式如下:
dst = cv2.perspectiveTransform(src, m)
参数说明:
	src:输入的2通道或3通道浮点类型的数组。
	m:3×34×4的浮点类型的转换矩阵,如使用 cv2.findHomography() 函数返回的转换矩阵。
	dst:输出结果数组,大小和类型与 src 相同。
________________________________________
示例代码:对象查找
以下是对象查找的示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt

img1 = cv2.imread('xhu1.jpg', cv2.IMREAD_GRAYSCALE)  # 打开灰度图像
img2 = cv2.imread('xhu2.jpg', cv2.IMREAD_GRAYSCALE)  # 打开灰度图像
orb = cv2.ORB_create()  # 创建ORB检测器
kp1, des1 = orb.detectAndCompute(img1, None)  # 检测关键点和计算描述符
kp2, des2 = orb.detectAndCompute(img2, None)  # 检测关键点和计算描述符
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)  # 创建匹配器
ms = bf.match(des1, des2)  # 执行特征匹配
ms = sorted(ms, key=lambda x: x.distance)  # 按距离排序

matchesMask = None
if len(ms) > 10:  # 在有足够数量的匹配结果后,才计算查询图像在训练图像中的位置
    querypts = np.float32([kp1[m.queryIdx].pt for m in ms]).reshape(-1, 1, 2)  # 计算查询图像匹配结果的坐标
    trainpts = np.float32([kp2[m.trainIdx].pt for m in ms]).reshape(-1, 1, 2)  # 计算训练图像匹配结果的坐标
    retv, mask = cv2.findHomography(querypts, trainpts, cv2.RANSAC)  # 执行查询图像和训练图像的透视转换
    matchesMask = mask.ravel().tolist()  # 计算最佳匹配结果的掩模,用于绘制匹配结果
    h, w = img1.shape
    pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)  # 定义矩形框的四个角点
    dst = cv2.perspectiveTransform(pts, retv)  # 执行向量的透视矩阵转换
    img2 = cv2.polylines(img2, [np.int32(dst)], True, (255, 255, 255), 5)  # 用白色矩形在训练图像中绘制出查询图像的范围
    img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms, None,
                            matchColor=(0, 255, 0),  # 用绿色绘制匹配结果
                            singlePointColor=None,
                            matchesMask=matchesMask,  # 绘制掩模内的匹配结果
                            flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    plt.imshow(img3)
    plt.axis('off')
    plt.show()  # 显示结果
________________________________________
8.5 实验
实验1:应用Shi-Tomasi角检测器
	实验目的:巩固和掌握OpenCV中角检测器的使用方法。
	实验内容:使用指定的图像,应用Shi-Tomasi角检测器找出图像中的角。
实验2:应用特征匹配查找对象
	实验目的:巩固和掌握OpenCV的特征匹配方法,并应用特征匹配完成对象查找。
	实验内容:使用指定的查询图像和训练图像,应用ORB特征检测和暴力匹配器,在训练图像中找出查询图像。
🖼️ 人脸检测和识别
9.1 人脸检测
本节主要内容:
	基于Haar的人脸检测
	基于深度学习的人脸检测
9.1.1 基于Haar的人脸检测
Haar级联分类器
Haar级联分类器是OpenCV提供的一种用于人脸检测的技术,使用训练好的模型进行特征识别。
在OpenCV的“data”文件夹中包含以下训练好的Haar级联分类器文件: - haarcascade_eye.xml:人眼检测 - haarcascade_frontalface_default.xml:人脸检测 - haarcascade_frontalcatface.xml:猫脸检测
cv2.CascadeClassifier() 函数
用于加载分类器,基本格式如下:
faceClassifier = cv2.CascadeClassifier(filename)
	faceClassifier:返回的级联分类器对象
	filename:级联分类器的文件名
detectMultiScale() 方法
用于执行检测,基本格式如下:
objects = faceClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]])
	objects:返回的目标矩形,矩形中为人脸
	image:输入图像,通常为灰度图像
	scaleFactor:图像缩放比例
	minNeighbors:构成目标矩形的最少相邻矩形个数
示例代码:使用Haar级联分类器检测人脸
import cv2
img = cv2.imread('heard.jpg')  # 打开输入图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
face = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')  # 加载人脸检测器
faces = face.detectMultiScale(gray)  # 执行人脸检测
for x, y, w, h in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # 绘制矩形标注人脸
cv2.imshow('face', img)  # 显示检测结果
cv2.waitKey(0)
9.1.2 基于深度学习的人脸检测
DNN模块
OpenCV的深度神经网络(Deep Neural Network,DNN)模块提供了基于深度学习的人脸检测器,使用流行的深度学习框架,如Caffe、TensorFlow等。
预训练模型
	Caffe模型需加载以下两个文件:
	deploy.prototxt:定义模型结构的配置文件
	res10_300x300_ssd_iter_140000_fp16.caffemodel:包含实际层权重的训练模型文件
使用预训练模型的步骤
	调用 cv2.dnn.readNetFromCaffe() 或 cv2.dnn.readNetFromTensorflow() 函数加载模型
	将待检测图像转换为图像块数据
	设置图像块数据为模型的输入数据
	执行计算,获得预测结果
	将可信度高于指定值的预测结果标注在人脸上
示例代码:DNN人脸检测
import cv2
import numpy as np

dnnnet = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel")
img = cv2.imread("heard.jpg")  # 读取图像
h, w = img.shape[:2]  # 获得图像尺寸
blobs = cv2.dnn.blobFromImage(img, 1.0, (300, 300), [104., 117., 123.], False, False)
dnnnet.setInput(blobs)  # 设置输入数据
detections = dnnnet.forward()  # 执行计算
faces = 0
for i in range(0, detections.shape[2]):
    confidence = detections[0, 0, i, 2]  # 获得可信度
    if confidence > 0.8:  # 输出可信度高于80%的结果
        faces += 1
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])  # 获得人脸坐标
        x1, y1, x2, y2 = box.astype("int")
        cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2)  # 标注人脸范围
cv2.imshow('faces', img)
cv2.waitKey(0)
9.2 人脸识别
本节主要内容:
	EigenFaces人脸识别
	FisherFaces人脸识别
	LBPH人脸识别
9.2.1 EigenFaces人脸识别
基本步骤
	调用 cv2.face.EigenFaceRecognizer_create() 方法创建识别器
	使用已知图像训练模型
	使用未知图像进行识别,确认身份
示例代码:EigenFaces人脸识别
import cv2
import numpy as np

img11 = cv2.imread('xl11.jpg', 0)  # 打开图像,灰度图像
train_images = [img11]  # 创建训练图像数组
labels = np.array([0])  # 创建标签数组
recognizer = cv2.face.EigenFaceRecognizer_create()  # 创建识别器
recognizer.train(train_images, labels)  # 执行训练操作
testimg = cv2.imread('test1.jpg', 0)  # 打开测试图像
label, confidence = recognizer.predict(testimg)  # 识别人脸
print('匹配标签:', label)  # 输出识别结果
print('可信度:', confidence)
9.2.2 FisherFaces人脸识别
基本步骤
	调用 cv2.face.FisherFaceRecognizer_create() 函数创建识别器
	使用已知图像训练模型
	使用未知图像进行识别,确认身份
示例代码:FisherFaces人脸识别
import cv2
import numpy as np

img11 = cv2.imread('xl11.jpg', 0)  # 打开图像,灰度图像
train_images = [img11]  # 创建训练图像数组
labels = np.array([0])  # 创建标签数组
recognizer = cv2.face.FisherFaceRecognizer_create()  # 创建识别器
recognizer.train(train_images, labels)  # 执行训练操作
testimg = cv2.imread('test1.jpg', 0)  # 打开测试图像
label, confidence = recognizer.predict(testimg)  # 识别人脸
print('匹配标签:', label)  # 输出识别结果
print('可信程度:', confidence)
9.2.3 LBPH人脸识别
基本原理
LBPH算法通过比较像素x周围的8个像素与其值,生成二进制数表示。
基本步骤
	调用 cv2.face.LBPHFaceRecognizer_create() 函数创建LBPH识别器
	使用已知图像训练模型
	使用未知图像进行识别,确认身份
示例代码
import cv2
import numpy as np

recognizer = cv2.face.LBPHFaceRecognizer_create()  # 创建LBPH识别器
# 训练和识别代码省略

# 🖼️ LBPH人脸识别

## LBPH识别器训练

> "LBPH(局部二值特征直方图)识别器用于人脸识别,能够通过已知图像训练模型并进行识别。"

### 参数说明

- **src**:用于训练的已知图像数组。要求:
  - 所有图像必须为**灰度图像**,且大小一致。
  
- **labels**:标签数组,需与已知图像数组中的人脸一一对应,同一个人的人脸标签应设置为相同值。

### 识别方法

LBPH识别器的`predict()`方法格式如下:

```python
label, confidence = recognizer.predict(testimg)
参数说明
	testimg:未知人脸图像,必须为灰度图像,且与训练图像大小相同。
	label:返回的识别标签值。
	confidence:返回的可信程度,表示未知人脸和模型中已知人脸之间的距离。数值说明:
	0:表示完全匹配
	低于50:可认为是非常可靠的匹配结果
示例代码
以下是使用LBPH人脸识别的示例代码:
import cv2
import numpy as np

# 读入训练图像
img11 = cv2.imread('xl11.jpg', 0)  # 打开图像,灰度图像
img12 = cv2.imread('xl12.jpg', 0)  # 打开图像,灰度图像
img13 = cv2.imread('xl13.jpg', 0)  # 打开图像,灰度图像
img21 = cv2.imread('xl21.jpg', 0)  # 打开图像,灰度图像
img22 = cv2.imread('xl22.jpg', 0)  # 打开图像,灰度图像
img23 = cv2.imread('xl23.jpg', 0)  # 打开图像,灰度图像

# 创建训练图像数组
train_images = [img11, img12, img13, img21, img22, img23]
# 创建标签数组,0和1表示训练图像数组中人脸的身份
labels = np.array([0, 0, 0, 1, 1, 1])

# 创建LBPH识别器
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 执行训练操作
recognizer.train(train_images, labels)

# 打开测试图像
testimg = cv2.imread('test2.jpg', 0)  # 打开测试图像
# 识别人脸
label, confidence = recognizer.predict(testimg)
print('匹配标签:', label)
print('可信程度:', confidence)
实验内容
9.3实验
本节主要内容: - 实验1:使用Haar级联检测器 - 实验2:使用EigenFaces人脸识别器
9.3.1 实验1:使用Haar级联检测器
1. 实验目的
进一步掌握使用Haar级联检测器检测人脸的基本方法。
2. 实验内容
使用Haar级联检测器检测给定图像中的人脸。
3. 实验过程
(具体实验过程未提供)
9.3.2 实验2:使用EigenFaces人脸识别器
1. 实验目的
进一步掌握使用EigenFaces人脸识别器的基本方法。
2. 实验内容
使用EigenFaces人脸识别器对给定图像执行模型训练,并使用其他图像作为测试图像进行人脸识别。
3. 实验过程
(具体实验过程未提供)
🖥️ 机器学习与深度学习
机器学习概述
机器学习(Machine Learning,ML)是人工智能的核心,专门研究如何让计算机模拟和学习人类的行为。深度学习(Deep Learning,DL)是机器学习中的一个热门研究方向,主要研究样本数据的内在规律和表示层次,使计算机能够像人一样具有分析与学习能力。
本章主要内容
	机器学习
	深度学习
________________________________________
10.1 机器学习
OpenCV的机器学习模块(名称为ml)实现了与机器学习有关的类和相关函数。本节主要介绍以下算法:
算法	描述
k最近邻(kNN)	找出k个距离最近的邻居作为目标的同一类别
支持向量机(SVM)	使用决策边界将数据分为两组
k均值聚类(k-Means)	根据数据的密集程度寻找质心并进行分类
________________________________________
10.1.1 kNN算法
kNN算法步骤
	创建kNN分类器
使用 cv2.ml.KNearest_create() 函数创建kNN分类器。
	训练模型
将训练数据和标志作为输入,调用kNN分类器的 train() 方法。
	找出最近邻居
将待分类数据作为输入,调用kNN分类器的 findNearest() 方法找出k个最近邻居。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 随机选择20个点
points = np.random.randint(0, 100, (20, 2))
labels = np.random.randint(0, 2, (20, 1))

# 绘制标志为0的点
label0s = points[labels.ravel() == 0]
plt.scatter(label0s[:, 0], label0s[:, 1], 80, 'b', 's')

# 绘制标志为1的点
label1s = points[labels.ravel() == 1]
plt.scatter(label1s[:, 0], label1s[:, 1], 80, 'r', '^')

# 随机选择一个待分类点
newpoint = np.random.randint(0, 100, (1, 2))
plt.scatter(newpoint[:, 0], newpoint[:, 1], 80, 'g', 'o')
plt.show()

# 创建kNN分类器
knn = cv2.ml.KNearest_create()
knn.train(points.astype(np.float32), cv2.ml.ROW_SAMPLE, labels.astype(np.float32))

# 找出3个最近邻居
ret, results, neighbours, dist = knn.findNearest(newpoint.astype(np.float32), 3)
print("新点标志:  %s" % results)
print("邻居: %s" % neighbours)
print("距离:%s" % dist)
用kNN算法实现手写数字识别
使用OpenCV源代码中的“samples”文件夹下的 digits.png 文件进行手写数字识别。每个数字的大小为20×20,共有5000个数字样本。
________________________________________
10.1.2 SVM算法
SVM算法原理
可使用一条直线将线性可分离的数据分为两组,称为“决策边界”。对于非线性可分离的数据,通过转换为高维数据后可称为线性可分离数据。
示例代码
以下代码将5个点分为两类,进行SVM模型训练。
# test10-3.py:图解SVM算法
……
________________________________________
10.1.3 k均值聚类算法
k均值聚类算法原理
k均值聚类算法通过寻找数据的质心来完成数据分类。
示例代码
# test10-5.py:图解k均值聚类算法
……
________________________________________
10.2 深度学习
深度学习通常分为输入、特征提取与分类和输出3个步骤,相对于机器学习,深度学习需要提供的输入数据量更大,计算量也更大。
OpenCV与深度学习
OpenCV在3.1版本中引入了一个深度神经网络模块(名称为dnn),支持流行的深度学习框架,包括Caffe、TensorFlow、Torch/Pytorch和ONNX。
________________________________________
10.2.1 基于深度学习的图像识别
图像识别步骤
	从配置文件和预训练模型文件中加载模型。
	将图像处理为块数据(blob)。
	将块数据设置为模型的输入。
	执行预测。
	处理预测结果。
示例代码
# test10-7.py:基于AlexNet和Caffe模型的图像识别
……
________________________________________
10.2.2 基于深度学习的对象检测
对象检测步骤
	从配置文件和预训练模型文件中加载模型。
	创建图像文件的块数据。
	将块数据设置为模型的输入。
	执行预测。
	处理预测结果。
示例代码
# test10-9.py:使用基于MobileNet_SSD和Caffe的预训练模型进行对象检测
……
________________________________________
实验内容
实验1:调整图像颜色
	实验目的:掌握机器学习中使用k均值聚类算法调整图像颜色的基本方法。
实验2:检测视频中的对象
	实验目的:掌握基于深度学习的对象检测方法。
	![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/39aa3b3f872847558afa0df9b536937d.png)

	

OpenCV是一个广受欢迎的开源计算机视觉库 是一个广受欢迎的开源计算机视觉库 ,它提供了 很多函数,实现很多计算机视觉法,从最基本的 滤波到高级物体检测 皆有涵盖 。很多 初学者希望快速掌握 OpenCV OpenCV OpenCVOpenCV的使用方法 ,但 往会 遇到 各种样的困难。 其 实仔细分析,造成这些困难的原因 有两类:第一是 C/C++/C++/C++/C++编程基础不过关; 第二类是不了解算法原理。 解决 这些 困难无非提升编程能力,以及理论基 础知识。 提升编程能力需要多练习,理论知识系统学《数字图 像处理》、《计算机视觉和模式识别等课程,所有这些都不 像处理》、《计算机视觉和模式识别等课程,所有这些都不 像处理》、《计算机视觉和模式识别等课程,所有这些都不 能一蹴而就 , 需要耐下心来认真修炼。 同时我们也 需要 认识到 OpenCV OpenCV OpenCVOpenCV只是一个算法库, 只是一个算法库, 能为我们搭建计算机视觉 应用提供“砖头”。我们并不需要完全精通了算法原理 应用提供“砖头”。我们并不需要完全精通了算法原理 之后 才去使用 OpenCV OpenCV OpenCVOpenCV, 只要了解“砖头”的功能,就可以动手。在实践中学习 才是最高效的学习 方式。 本小册子希望为初学者提供引导,使快速了解 OpenCV OpenCV OpenCVOpenCV的基本数 据结构以及用法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值