Sub.1 图像翻转
🧠 图像翻转(图像镜像旋转)
一、核心概念⚡
- 关键词:中心为原点;镜像翻转;
1. 定义:
在OpenCV中,图片的镜像旋转是以图像的中心为原点进行镜像翻转的。
2. 函数:
cv2.flip(img, flipcode)
- 参数:
img
:目标图像flipcode
:翻转类型标志- flipcode = 0:垂直翻转,像素点沿x轴翻转(上下翻转)
- flipcode > 0:水平翻转,像素点沿y轴翻转(左右翻转)
- flipcode < 0:中心对称翻转
- 返回值:新图像
Sub.2 图像仿射变换
🧠 1. 仿射变换
一、核心概念⚡
- 关键词:线性变换;线保持平行;点保持相对距离;
1. 定义:
仿射变换(Affine Transformation)是一种线性变换,保持了点之间的相对距离不变。
2. 基本性质
- 保持直线
- 保持平行
- 比例不变性
- 不保持角度和长度
3. 常见仿射变换类型:
- 旋转:绕着某个点或轴旋转一定角度。
- 平移:仅改变物体的位置,不改变其形状和大小。
- 缩放:改变物体的大小。
- 剪切:使物体发生倾斜变形。
4. 原理:线性变换
- 二维空间,图像 (x,y) →映射到→ (x', y')
- 需要:仿射变换矩阵(2X3),使用矩阵乘法
5. 仿射变换函数:
cv2.warpAffine(img, M, dsize)
- 参数
img
:输入图像M
:2X3的变换矩阵,类型:np.float32dsize
:输出图像的尺寸,(width,height)
- 返回值:新图像
⚠️ 图像存储和仿射变换时参数顺序不同
- 图像存储是
(H,W,C)
(高,宽,通道) - 仿射变换因为要分x轴y轴,是
(W,H)
🧠 2. 图像旋转
一、核心概念⚡
- 关键词:旋转中心;旋转矩阵;正逆负顺;
1. 获取旋转矩阵的API:
cv2.getRotationMatrix2D(center, angle, scale)
- 参数:
center
:旋转中心点的坐标,格式为(x,y)
。angle
:旋转角度,单位为度,正值逆时针旋转;负值顺时针旋转。scale
:缩放比例,默认为1
- 返回值:M,2X3的旋转矩阵
🧠 3. 图像平移
一、核心概念⚡
- 关键词:平移矩阵;自己创建;txty
1. 平移矩阵
用NumPy自己创建M,使用 np.float32() 隐式指定数据类型
或:显式指定 dtype 参数
- 📝 碎碎念疑问:平移方向和规则
- OpenCV的图像坐标系:
- 原点在左上角,x轴向右,y轴向下
- 在这个方向上,数学上:左加右减上加下减
- 总结: tx为正向右, ty为正向下(坐标轴方向)
- OpenCV的图像坐标系:
🧠 4. 图像缩放
一、核心概念⚡
- 关键词:缩放因子;自己创建;sxsy
缩放矩阵
也是自己创建 [[sx, 0, 0],[0, sy, 0]]
→ 如果只缩放不平移
🧠 5. 图像剪切
一、核心概念⚡
- 关键词:面积不变;剪切因子shx shy;
1. 定义:
剪切操作可以改变图形的形状,以便其在某个方向上倾斜,它将对象的形状改变为斜边平行四边形,而不改变其面积。
2. 剪切矩阵
自己创建,[[1, shy, 0],[shx, 1, 0]]
→ 两个方向
- 📝 碎碎念疑问
- 如何理解沿x轴剪切是x' = x + shy*y
-
y不变,x' 的意思是加上了y对它的影响,shy其实是x方向的剪切因子
-
同理:沿y轴剪切,是在拉y
x' = x 不变,y'是看x对它的影响,所以y' = y + shx * x,shx是x对y影响的剪切因子
-
- 如何理解沿x轴剪切是x' = x + shy*y
🪹 小结
1. 知识图谱
(1)核心概念
概念 | 解释 |
---|---|
仿射变换 | 线性变换(保持直线和平行性),包含旋转、平移、缩放、剪切等,不改变点的相对距离。 |
仿射变换矩阵 | 2×3的变换矩阵M,通过矩阵乘法完成坐标映射。 |
图像坐标系 | OpenCV的坐标系以左上角为原点,x轴向右,y轴向下(与数学坐标系不同) |
(2)四种仿射变换类型
类型 | 变换矩阵(np.float32) | 注意事项 |
---|---|---|
旋转 | cv2.getRotationMatrix2D(center, angle, scale) | angle旋转角度:正逆负顺 |
平移 | 平移矩阵 M = [[1, 0, tx], [0, 1, ty]] | tx向右为正,ty 向下为正。 |
缩放 | 缩放矩阵 M= [[sx, 0, 0], [0, sy, 0]] | sx、sy为宽高缩放因子。 |
剪切 | 剪切矩阵 M=[[1, shy, 0],[shx, 1, 0]] | shx、shy为剪切因子(控制倾斜程度),需注意 x轴y轴剪切是“相互影响” |
(3)统一接口与流程
- 流程:明确变换类型 → 定义参数,构建对应的变换矩阵M → 调用cv2.warpAffine
- 统一接口:仿射变换操作均通过
cv2.warpAffine(img, M, dsize)
实现,注意:- img:输入图像 (H,W,C)
- disze:输出尺寸(W,H),与图像存储顺序相反
- flags:可选参数,指定插值方法(默认cv2.INTER_LINEAR)
Sub.3 插值方法
- 定义:插值(Interpolation)是一种通过已知数据点之间的推断或估计来获取新数据点的方法。(已知像素点 → 未知像素点)
- 应用:图像处理/计算机图形学,它在图像处理中常用于处理图像的放大、缩小、旋转、变形等操作,以及处理图像中的像素值。
- 为什么要用插值算法:为了解决图像缩放或者旋转等操作时,由于像素之间的间隔不一致而导致的信息丢失和图像质量下降的问题。
功能 | 插值方法 | 边缘填充 |
---|---|---|
作用 | 计算新像素颜色值(非整数坐标) | 填充变换后图像边缘的空白区域 |
应用场景 | 旋转、缩放等导致坐标非整数的操作 | 防止旋转/平移后图像边缘出现黑边 |
OpenCV参数 | flags=INTER_NEAREST/LINEAR | borderMode=BORDER_CONSTANT |
🎐 1. 最近邻插值
一、核心概念⚡
- 关键词:最近邻;简单快速;锯齿现象
1. 定义:
最近邻插值(Nearest Neighbor Interpolation)是一种非自适应插值方法(固定使用一种规则),它通过直接取目标像素在原图像中几何位置最近的像素值来填充新像素点。
其核心思想是“就近取材”,不涉及复杂的数学计算。
2. 原理:
-
坐标映射公式
假设原图像尺寸为
W×H
,目标图像尺寸为W'×H'
则目标图像中某像素点
(x', y')
对应原图中的坐标(x, y)
计算公式为: -
公式目的:将目标图像的像素位置映射回原图坐标系,找到对应的采样点(可能为小数坐标)。
-
最近邻插值原则:
- 整数坐标:若计算出的
(x, y)
是整数,直接取原图该位置的像素值。 - 小数坐标:
(round(x), round(y))
- 四舍五入取整(非向下取整!并且图像处理中的round()是普通的四舍五入,并非银行家舍入),得到最近的整数坐标,再取该位置的像素值。
- 边界截断机制(clamp):越界时将坐标限制在
[0, W-1]
范围内。
- 整数坐标:若计算出的
3. 特性:
- 优点:计算速度快,无复杂运算,适合实时处理。
- 缺点:产生锯齿(Aliasing)和块状效应,图像质量较低
- 适用场景:对实时性要求高、图像质量要求低的场景(如硬件加速渲染)。
4. 参数:
CV2.INTER_NEAREST
- 参数作用:指定使用最近邻插值算法
- 底层逻辑:OpenCV 自动完成坐标映射、四舍五入、边界截断
二、代码骨架💐
2.1 基础案例复现☘️
案例 1:最近邻的体现
- 用数值数组体现最近邻
- 代码实现:
import numpy as np
import cv2 as cv
# 原图 (2x2)
src = np.array([[100, 150],
[200, 50]], dtype=np.uint8)
# OpenCV 最近邻插值放大
dst = cv.resize(src, (4,4), interpolation=cv.INTER_NEAREST)
# resize设置插值参数的关键字是 interpolation
print("目标图像:\n", dst)
- 输出分析:cv.resize()方法中,设置插值的关键字参数是interpolation
目标图像:
[[100 100 150 150]
[100 100 150 150]
[200 200 50 50]
[200 200 50 50]]
案例 2:参数应用
- e.g. 在仿射变换的旋转中,使用最近邻插值
- 代码复现:
import cv2 as cv
# 最近邻插值
org = cv.imread("CuteCat.jpg")
cat = cv.resize(org, (300, 300))
h,w = cat.shape[:2]
M = cv.getRotationMatrix2D((w//2,h//2),-30,0.7)
# 在旋转中使用最近邻插值
dst = cv.warpAffine(cat,M,(w,h),flags=cv.INTER_NEAREST)
# 在warpAffine中,设置插值的关键字参数是flags
# 创建窗口并显示图像
cv.namedWindow("CuteCat", cv.WINDOW_NORMAL)
cv.imshow("CuteCat", dst)
# 设置窗口位置,确保窗口在屏幕内可见
cv.moveWindow("CuteCat", 100, 100)
cv.waitKey(0)
cv.destroyAllWindows()
-
输出分析:cv.warpAffine()方法中,设置插值的关键字参数是flags(也可以作为位置参数设置)
🎐 2. 双线性插值
一、核心概念⚡
- 关键词:四邻域矩阵;加权平均;平滑过渡
1. 定义:
双线性插值(Bilinear Interpolation)是一种基于二维空间线性插值的方法,通过目标像素在原图中的四个最近邻像素(2×2邻域矩阵)的加权平均计算像素值。能有效缓解锯齿现象。
2. 参数:
cv2.INTER_LINEAR
- 应用函数
cv2.resize(src, dsize, interpolation=cv2.INTER_LINEAR)
cv2.warpAffine(src, M, dsize, flags=cv2.INTER_LINEAR)
- 在OpenCV中,关于插值方法默认选择的都是双线性插值,且一般情况下双线性插值已经能满足大部分需求(平衡图像平滑度与计算效率)
🎐 3. 像素区域插值
一、核心概念⚡
- 关键词:最佳缩小算法
1. 定义:
像素区域插值(Pixel Area Relation)是一种基于像素区域重采样的插值方法,主要用于图像缩小。
2. 原理:
(1)缩小图像(核心场景):均值滤波器
核心思想:
将目标图像的每个像素视为原图中的一个区域,通过计算该区域内原图像素的加权平均值来确定目标像素值,从而避免缩小图像时产生的锯齿(Aliasing)和摩尔纹(Moire Patterns)
(2)放大图像
- 整数倍放大:退化为最近邻插值
- 非整数倍放大:退化为双线性插值
3. 参数:
cv2.INTER_AREA
- 最适用图像缩小✅
- 图像放大会退化,效果下降❌
- 旋转/变形不适用❌
🎐 4. 双三次插值
一、核心概念⚡
- 关键词:16邻域加权;BiCubic基函数
1. 定义:
双三次插值(Bicubic Interpolation)是一种高阶图像插值算法,通过目标像素周围4x4 共 16 个相邻像素的加权平均计算目标值,权重由双三次基函数确定(基于距离的非线性函数)。
2. 原理:
- 与双线性插值法相同,也是通过映射,在映射点的领域内通过加权来得到放大图像中的像素值。
- 但是用的是近邻的16个点来加权(4X4)
- 权重函数:BiCubic基函数(根据距离,即W(d) )
- 过程
-
坐标映射:
- 得到小数坐标
(x+u, y+v)
(例如(3.7, 2.4)
→ 基准点(3,2)
,偏移量u=0.7, v=0.4
)
- 得到小数坐标
-
确定16邻域:
- 4x4网格(行范围:
x-1, x, x+1, x+2
;列同理)
- 4x4网格(行范围:
-
给出BiCubic函数:
⇒ 所以,需要计算16个点相较于p点的位置距离,然后用BiCubic函数计算权重W(d)
⇒注意:BiCubic函数是一维的,所以需要分别计算行列
-
如何计算距离?
e.g. 目标点(3.7, 2.4)
⇒ 参考点(3, 2),偏移量 b_h=0.4,b_w=0.7
⇒参考点作为原点,(u,v)是偏移量
- 此时,P点在行上的四列像素(ii = -1,0,1,2),对应的距离d_x = | ii - u |
- P点在列上的四行像素(jj = -1,0,1,2),d_y = | jj - v |
-
最后利用加权求和,算出像素值
3. 参数:
CV2.INTER_CUBIC
🎐 5. Lanczos插值
一、核心概念⚡
- 关键词:8X8网格;加权平均;
1. 定义:
Lanczos插值方法与双三次插值的思想是一样的,不同的就是其需要的原图像周围的像素点的范围变成了8*8,并且不再使用BiCubic函数来计算权重,而是换了一个公式计算权重。
2. 参数:
cv2.INTER_LANCZOS4
🪹 小结与案例
(1)知识图谱
方法 | 原理 | 优缺点 | 适用场景 | OpenCV参数 |
---|---|---|---|---|
最近邻插值 | 取最近整数坐标像素值(四舍五入) | 速度快,但锯齿明显。 | 实时性要求高(如硬件渲染) | cv2.INTER_NEAREST |
双线性插值 | 用4邻域像素加权平均(权重与距离成反比) | 平滑过渡,计算量低 | 大多数缩放/旋转场景(默认方法) | cv2.INTER_LINEAR |
区域插值 | 缩小时取区域均值,放大时退化 | 适合缩小,避免摩尔纹 | 图像缩小时的高质量处理 | cv2.INTER_AREA |
双三次插值 | 用16邻域像素加权平均(双三次基函数计算权重) | 平滑度高,计算复杂 | 放大或需要高精度的场景 | cv2.INTER_CUBIC |
Lanczos插值 | 用8×8邻域像素加权平均(Lanczos函数计算权重) | 平滑度最优,但计算量最大 | 高质量图像处理(如专业摄影) | cv2.INTER_LANCZOS4 |
(2)插值方法使用案例☘️
- 语法使用
import cv2 as cv
# 读图
org = cv.imread("images/CuteCat.jpg")
cat = cv.resize(org, (300,300))
h,w = cat.shape[:2]
center=(w//2,h//2)
# 获取旋转矩阵center,angle,scale
M=cv.getRotationMatrix2D(center,0,0.5)
# 仿射变换
img1=cv.warpAffine(cat,M,(w,h), cv.INTER_NEAREST)# 最近邻插值
img2=cv.warpAffine(cat,M,(w,h), cv.INTER_LINEAR)# 双线性插值
img3=cv.warpAffine(cat,M,(w,h), cv.INTER_AREA)# 像素区域插值:这里是缩小,相当于用均值滤波器
img4=cv.warpAffine(cat,M,(w,h), cv.INTER_CUBIC)# 双三次插值,16个点
img5=cv.warpAffine(cat,M,(w,h), cv.INTER_LANCZOS4)# Lanczons插值,64个点
# 显示图像
cv.imshow("cat",cat)
cv.imshow("NEAREST",img1)
cv.imshow("LINEAR",img2)
cv.imshow("AREA",img3)
cv.imshow("CUBIC",img4)
cv.imshow("LANCZOS4",img5)
cv.waitKey(0)
cv.destroyAllWindows()
Sub.4 边缘填充
intro:为什么要边缘填充?🔍
- 目的:解决图像变换(如仿射变换)后,产生的空白区域问题
- 作用:
- 保持图像完整尺寸,便于后续处理(如图像拼接、神经网络输入)
- 避免信息丢失,提升视觉连贯性
- 应用:作为参数,如
borderMode = cv2.BORDER_REPLICATE
🏖️ 1. 边界复制
一、核心概念⚡
cv2.BORDER_REPLICATE
1. 定义与原理
-
机制:将最边缘像素向外无限延伸复制
2. 应用与效果
- 适用场景:自然渐变背景(如天空、水面)
- 优点:计算快、保留边缘连续性。
- 缺点:在纹理复杂区域产生“拉丝”伪影。
🏖️ 2. 边界反射
一、核心概念⚡
- 边界反射:
cv2.BORDER_REFLECT
- 边界反射101:
cv2.BORDER_REFLECT_101
1. 定义与原理
-
机制:根据图像边缘进行反射,边界反射101不反射边缘像素点
-
镜像原理:
ABCD|DCBA
式对称复制 (边界反射101:ABCD|CBA
)
2. 进阶理解
- 适合处理有重复图案的场景(如瓷砖、布料纹理)
- 比BORDER_REPLICATE更自然,但计算量稍大
- 注意与BORDER_REFLECT_101的区别(后者不重复边缘像素)
🏖️ 3. 边界常数
一、核心概念⚡
cv2.BORDER_CONSTANT
1. 用法:
- 选择边界常数时,除了声明
borderMode=cv2.BORDER_CONSTANT
,还需要设置borderValue
- 函数示例:borderValue = (B,G,R) 选择颜色(默认全0,即黑色)
cv2.warpAffine(img,M,(w,h),cv.BORDER_CONSTANT,borderValue=(255,255,255))
⚠️ 使用陷阱
- 填充色可能干扰后续算法(如目标检测)
- 解决方案:使用mask标记填充区域
🏖️ 4. 边界包裹
一、核心概念⚡
cv2.BORDER_WRAP
1. 定义与原理:
-
机制:循环平铺模式:
ABCD|ABCD|ABC
2. 场景:
- 适用场景:周期性纹理(如心电图波形)
- 慎用场景:自然图像中会产生明显重复痕迹
🪹 小结与案例
(1)知识图谱
方法 | OpenCV 参数 | 核心原理 | 特点与适用场景 |
---|---|---|---|
边界复制 | BORDER_REPLICATE | 复制边缘像素值填充边界 | 简单快速,适合边缘像素具有代表性的场景(如纯色边缘),但可能导致边缘重复纹理。 |
边界反射 | BORDER_REFLECT | 以边缘为对称轴,镜像反射填充 | 避免边界重复,保持边缘连续性,适合自然图像(如风景、人物),但边缘像素不参与反射。 |
边界反射 101 | BORDER_REFLECT_101 | 与反射类似,但边缘像素自身不参与镜像 | 减少边缘像素的过度影响,边缘更平滑,是BORDER_REFLECT 的优化版。 |
边界常数 | BORDER_CONSTANT | 用指定常数填充边界(BGR) | 灵活控制填充颜色,适合突出主体(如黑色背景突出前景),需指定borderValue 参数。 |
边界包裹 | BORDER_WRAP | 循环利用图像自身像素填充(类似平铺),边缘与对侧像素相连。 | 保持图像纹理连续性,适合无缝拼接场景,但可能引入非边缘区域的像素干扰。 |
(2)边缘填充使用案例
- 语法使用
import cv2 as cv
# 准备工作
org = cv.imread("images/CuteCat.jpg")
cat = cv.resize(org, (300,300))
h,w = cat.shape[:2]
center=(w//2,h//2)
M=cv.getRotationMatrix2D(center,45,0.5)
img1=cv.warpAffine(cat,M,(w,h))
# 边缘填充效果示例
# 1.边界复制: 利用图像像素边缘,直接复制填充
img2 = cv.warpAffine(cat,M,(w,h), borderMode=cv.BORDER_REPLICATE)
# 2.边界反射: 以图像边缘为轴,镜像反射填充
img3 = cv.warpAffine(cat,M,(w,h), borderMode=cv.BORDER_REFLECT)
# 3.边界反射101: 边缘像素不参与反射,其余像素以图像边缘为轴,镜像反射填充
img4 = cv.warpAffine(cat,M,(w,h), borderMode=cv.BORDER_REFLECT_101)
# 4. 边界常数: 用指定常数填充
img5 = cv.warpAffine(cat,M,(w,h), borderMode=cv.BORDER_CONSTANT, borderValue=(255,255,255))
# 5. 边界包裹: 用自身像素循环平铺填充
img6 = cv.warpAffine(cat,M,(w,h), borderMode=cv.BORDER_WRAP)
# 显示图像
cv.imshow("cat",cat)
cv.imshow("Replicate",img2)
cv.imshow("Reflect",img3)
cv.imshow("Reflect_101",img4)
cv.imshow("Constant",img5)
cv.imshow("Wrap",img6)
cv.waitKey(0)
cv.destroyAllWindows()
Sub.5图像校正(透视变换)
特性 | 仿射变换 | 透视变换 |
---|---|---|
矩阵维度 | 2×3 | 3×3 |
保持性质 | 直线平行性、相对距离 | 无(允许非线性形变) |
典型应用 | 旋转、平移、缩放 | 矫正透视畸变(如倾斜文档转正) |
OpenCV函数 | cv.warpAffine() | cv.warpPerspective() |
一、核心概念⚡
1. 定义
- 把一个图像投影到一个新的视平面的过程
-
现实世界:近大远小 →透视投影到二维平面,出现形变和透视畸变
-
透视变换通过数学模型矫正透视畸变
-
通俗的讲:透视变换就是改变了图像里目标物体的被观测视角
平视 → 俯视
-
-
2. 原理:透视变换
- 通过 3×3 透视变换矩阵,将三维空间点投影到二维平面,实现视角转换。
- 与仿射变换的区别:
- 仿射变换(2×3 矩阵):保持线性关系,适合平移、旋转、缩放
- 透视变换(3×3 矩阵):处理非线性畸变,允许视角变化(如近大远小)
3. 关键步骤与函数🎨
(1)选取特征点
-
在原图和目标图中选取 4 个对应点(如矩形的四个角点)
-
通过鼠标回调或角点检测获取对应点
-
画图软件可以显示像素
-
(2)生成透视矩阵
- M = cv2.getPerspectiveTransform(src, dst)
- 参数:
src
:二维数组,原图中 4 个点的坐标dst
:二维数组,目标图中对应 4 个点的坐标,需要和src的顺序一致
src_pts = np.float32([[x1,y1], [x2,y2], [x3,y3], [x4,y4]])
dst_pts = np.float32([[0,0], [w,0], [0,h], [w,h]])
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
(3)应用透视变换
- dst = cv2.warpPerspective(img, M, dsize)
- 参数:
dsize
:输出图像尺寸(width, height)
;- 支持插值方法(如
flags=cv.INTER_LINEAR
)和边缘填充(如borderMode=cv.BORDER_CONSTANT
)
📝 Tips
-
点坐标顺序:
src
和dst
点需按顺时针或逆时针顺序排列,确保映射关系正确(如左上角→左上角,右上角→右上角)。 -
如何动态获取特征点?
→ 结合鼠标交互函数(如
cv.setMouseCallback
)手动选取,或通过边缘检测、轮廓提取自动获取。
二、实践案例💐
2.1 基础案例复现☘️
案例 1:语法应用
- 用画图软件,选点,记录下特征点
- 特征点选取后需转化为ndarray
- 代码实现:
import cv2 as cv
import numpy as np
img = cv.imread("./images/card.png")
# 获取宽高
h, w = img.shape[:2]
# 用画图软件选择特征点,注意必须顺时针/逆时针 连续选点
# (174,134) (489,170) (479,312) (138,270)
# 定义特征点
src_pts = np.float32([[174, 134], [489, 170], [479, 312],[138, 270]])
dst_pts = np.float32([[0, 0], [w, 0], [w,h], [0, h]]) # 两个数组需要一一对应
# 生成透视矩阵, 用函数生成,是一个3*3的矩阵
M = cv.getPerspectiveTransform(src_pts, dst_pts)
# 应用透视变换
dst = cv.warpPerspective(img, M, (w, h))
# 在原图上绘制特征点所选区域
cv.polylines(img, [src_pts.astype(np.int32)], True, (0, 0, 255), 2)
cv.imshow("img", img)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
-
输出分析: