OpenCV学习day2
一、图像翻转
在OpenCV中,图像的翻转是以图像的中心为原点进行镜像翻转
cv2.flip(img,flipcode)
- 参数
-
- img:图像地址
-
- flipcode:指定翻转类型
-
-
- flipcode = 0 ,垂直翻转。图片像素点沿x轴翻转
-
-
-
- flipcode > 0,水平翻转。图片像素沿y轴翻转
-
-
-
- flipcode < 0,水平垂直翻转,水平垂直翻转相结合
-
代码实现:
import cv2 as cv#读图
cat = cv.imread("../../images/cat1.png")
#调整大小 (w,h)
cat = cv.resize(cat, (360, 420))
cv.imshow("cat", cat)
#图片的翻转 cv.flip(img,flipcode)
#flipcode > 0 水平反转 沿着Y轴
flip1 = cv.flip(cat,flipCode = 3)
cv.imshow("flip1", flip1)
#flipcode = 0 垂直翻转 沿着x轴
flip0 = cv.flip(cat,0)
cv.imshow("flip0", flip0)
#flipcode < 0 水平加垂直反转
flip = cv.flip(cat,flipCode = -1)
cv.imshow("flip", flip)
cv.waitKey(0)
cv.destroyAllWindows()
二、图像仿射变换
仿射变换(Affine Transformation)是一种线性变换,保持了点之间的相对距离不变。
仿射变换的基本性质
- 保持直线
- 保持平行
- 比例不变性
- 不保持角度和长度
常见的仿射变换类型
- 旋转:绕着某个点或轴旋转一定角度。
- 平移:仅改变图像的位置,不改变其形状和大小。
- 缩放:改变图像大小。
- 剪切:使图像发生倾斜变形。
仿射变换的基本原理
- 线性变换
- 二维空间仿射变换的目的是将原先的点坐标映射到新的位置
- 为了实现这种映射,通常会使用一个矩阵乘法的形式
[x′y′]=[abtxcdty]∗[xy1]\begin{bmatrix} x^{'}\\y^{'} \end{bmatrix} =\begin{bmatrix} a &b &t_{x} \\ c&d &t_{y} \end{bmatrix} *\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} [x′y′]=[acbdtxty]∗xy1
- a,b,c,d是线性变换部分的系数,控制旋转、缩放和剪切
- txt_{x}tx,tyt_{y}ty是平移部分的系数,控制图像在平面上的移动。
仿射变换函数
cv2.warpAffine()函数
cv2.warpAffine(img,M,dsize)
- img:图像地址
- M:2*3的变换矩阵,类型为np.float32
- dsize:输出图像的尺寸,形式为(width,height)
1、图像旋转
旋转操作时将图像按照某一个点旋转一定的角度
cv2.getRotationMatrix2D()函数
作用:获取旋转矩阵
cv2.getRotationMatrix2D(center,angle,scale)
- center:旋转中心点的坐标,格式为(x,y)。
- angle:旋转角度,单位为度,正值表示逆时针旋转负值表示顺时针旋转。
- scale:缩放比例,大于0;若设为1,则不缩放。
返回值:M,2x3的旋转矩阵。
代码实现:
import cv2 as cv#读图
pig = cv.imread("../../images/pig.png")
#获取旋转矩阵
M = cv.getRotationMatrix2D((362,438), 90, 1)
#方式变换函数,实现旋转
pig1 = cv.warpAffine(pig, M, (1080,650))# 显示效果 释放资源
cv.imshow("pig1", pig1)
cv.waitKey(0)
cv.destroyAllWindows()
2、图像平移
平移操作是将图像每个点沿着某个方向平移一定的距离
- 假设现在有一个点p(x,y),按照需求我们需要将点p沿着x轴方向平移txt_{x}tx个单位,沿着y轴平移tyt_{y}ty个单位到新的p′p^{'}p′(x′(x^{'}(x′,y′)y^{'})y′),那么我们可以得到
x′=x+tx,y′=y+tyx^{'} = x +t_{x},y^{'} = y +t_{y}x′=x+tx,y′=y+ty
txt_{x}tx,tyt_{y}ty分别表示为在x轴和y轴上的平移量、平移的距离
在矩阵形式下可表达为
[x′y′]=[10tx01ty]∗[xy1]\begin{bmatrix} x^{'}\\y^{'} \end{bmatrix} =\begin{bmatrix} 1 &0 &t_{x} \\ 0&1 &t_{y} \end{bmatrix} *\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}[x′y′]=[1001txty]∗xy1
代码实现:
import cv2 as cv
import numpy as np#读图
pig = cv.imread("../../images/pig.png")
pig = cv.resize(pig, (500,300))# (w,h)
cv.imshow("pig", pig)
#获取整个图像的宽高
h,w=pig.shape[:2]
#定义平移矩阵
tx = 100
ty = 100
#定义平移矩阵 np.float32
M = np.float32([[1,0,tx],[0,1,ty]])
#放射变换_平移
pig1 = cv.warpAffine(pig,M,(500,300))
cv.imshow("pig1", pig1)
cv.waitKey(0)
cv.destroyAllWindows()
三、图像缩放
缩放操作是将图像的宽和高按照比例改变,从而改变图像的大小
- 假设把图像的宽和高缩放为0.4和0.7,那么对应的缩放因子sxs_{x}sx=0.4,sys_{y}sy=0.7
- 图像中的点p(x,y)对应到变换后图像中的位置p′p^{'}p′(x′(x^{'}(x′,y′)y^{'})y′),缩放公式为:
x′=x∗sx,y′=y∗syx^{'} = x*s_{x},y^{'} = y*s_{y}x′=x∗sx,y′=y∗sy
在矩阵的形势下可表示为
[x′y′]=[sx000sy0]∗[xy1]\begin{bmatrix} x^{'}\\y^{'} \end{bmatrix} =\begin{bmatrix} s_{x} &0 &0 \\ 0&s_{y} &0 \end{bmatrix} *\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}[x′y′]=[sx00sy00]∗xy1
相较于图像旋转中只能等比例的缩放,图像缩放更加灵活,可以在指定方向上进行缩放。
代码实现:
import cv2 as cv
import numpy as np#读图
pig = cv.imread("../../images/pig.png")
pig = cv.resize(pig, (500,300))# (w,h)
cv.imshow("pig", pig)
#获取整个图像的宽高
h,w=pig.shape[:2]
#定义缩放矩阵
sx=0.5
sy=0.5
M = np.float32([[sx,0,0],[0,sy,0]])
pig1 = cv.warpAffine(pig, M, (500,300))
cv.imshow("pig1", pig1)
cv.waitKey(0)
cv.destroyAllWindows()
4、图像剪切
剪切操作是将图像在某个方向上倾斜,将图像的形状改编为斜边平行四边形,但不改变其面积
- 假设图像中的某一个点p(x,y),对他进行剪切操作
-
- 沿x轴剪切:x′=x+shy∗yx^{'} = x+sh_{y}*yx′=x+shy∗y , y′=yy^{'} = yy′=y
-
- 沿y轴剪切:x′=xx^{'} = xx′=x,y′=y+shx∗xy^{'} = y+sh_{x}*xy′=y+shx∗x
-
- 两个方向需要同时剪切:x′=x+shy∗yx^{'} = x+sh_{y}*yx′=x+shy∗y,y′=y+shx∗xy^{'} = y+sh_{x}*xy′=y+shx∗x
-
- shxsh_{x}shx和shysh_{y}shy分别对应x轴和y轴上的剪切因子
在矩阵形势下可表示为:
[x′y′]=[1shy0shx10]∗[xy1]\begin{bmatrix} x^{'}\\y^{'} \end{bmatrix} =\begin{bmatrix} 1 &sh_{y} &0 \\ sh_{x}&1 &0 \end{bmatrix} *\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}[x′y′]=[1shxshy100]∗xy1
代码实现:
import cv2 as cv
import numpy as np#读图
pig = cv.imread("../../images/pig.png")
pig = cv.resize(pig, (500,300))# (w,h)
cv.imshow("pig", pig)
#获取整个图像的宽高
h,w=pig.shape[:2]
#定义剪切矩阵
shx=0.2
shy=0.2
M = np.float32([[1,shy,0],[shy,1,0]])
pig1 = cv.warpAffine(pig, M, (w,h))
cv.imshow("pig1", pig1)
cv.waitKey(0)
cv.destroyAllWindows()
三、插值方法
在图像处理和几何变换中,插值方法(Interpolation) 用于估算新像素点的值,特别是在 图像缩放、旋转、仿射变换 等操作时。OpenCV 和 NumPy 提供了多种插值算法,适用于不同场景。
1、常见的插值方法
OpenCV 中常用的插值方法(通过 cv2.resize() 或几何变换函数指定):
插值方法 | OpenCV常量 | 描述 | 适用场景 | 计算速度 |
---|---|---|---|---|
最紧邻插值 | cv2.INTER_NEAREST | 直接取最近的像素值 | 速度快,但边缘锯齿明显 | ⚡⚡⚡⚡⚡(最快) |
双线性插值 | cv2.INTER_LINEAR | 用周围 4 个像素加权平均 | 平衡速度与质量(最常用) | ⚡⚡⚡⚡ |
双三次插值 | cv2.INTER_CUBIC | 用周围 16 个像素计算 | 高质量,但较慢 | ⚡⚡⚡ |
Lanczos插值 | cv2.INTER_LANCZOS4 | 基于 Lanczos 核函数 | 高质量,抗锯齿 | ⚡⚡(最慢) |
像素区域插值 | cv2.INTER_AREA | 像素区域关系重采样 | 缩小图像 时效果好 | ⚡⚡⚡ |
2、代码实现(仿射旋转变换为例)
import cv2 as cv
# 插值方法在哪里都可以用 是在图像改变后可添加的一个参数
#读图
pig = cv.imread("../../images/pig.png")
pig = cv.resize(pig, (500,300))
cv.imshow("pig", pig)
#获取整个图像的宽高
h,w = pig.shape[:2]
#获取旋转矩阵
M = cv.getRotationMatrix2D((w//2,h//2), 90, 1)
#仿射变换函数,实现旋转
pig1 = cv.warpAffine(pig, M, (500,300),flags=cv.INTER_NEAREST)#最近邻 速度快 精度相较低
pig2 = cv.warpAffine(pig, M, (500,300),flags=cv.INTER_AREA)#像素区域 放大(整数 非整数) 缩小
pig3 = cv.warpAffine(pig, M, (500,300),flags=cv.INTER_LINEAR)#双线性 4个 2x2 2次线性插值 水平+垂直
pig4 = cv.warpAffine(pig, M, (500,300),flags=cv.INTER_CUBIC)#双三次 16个 4x4
pig5 = cv.warpAffine(pig,M,(500,300),flags=cv.INTER_LANCZOS4)# lanczos 64个 8x8# 显示效果 释放资源
cv.imshow("pig1", pig1)
cv.imshow("pig2", pig2)
cv.imshow("pig3", pig3)
cv.imshow("pig4", pig4)
cv.imshow("pig5", pig5)
cv.waitKey(0)
cv.destroyAllWindows()
3、插值方法对比
插值方法 | 放大效果(锯齿/平滑度) | 缩小效果(抗摩尔纹) |
---|---|---|
最近邻 | 明显锯齿 | 容易失真 |
双线性 | 较平滑 | 一般 |
双三次 | 更平滑 | 较好 |
Lanczos | 最优平滑 | 优秀 |
像素区域 | 不推荐放大 | 最佳缩小效果 |
直观对比:放大时用 双三次插值 或 Lanczos插值,缩小时用 区域插值
4、数学原理简介
(1) 最近邻插值
- 公式:p_new = p_round(x), round(y))
- 直接取最近像素,不计算加权值
(2) 双线性插值
- 用周围 4 个像素的加权平均值:
(3) 双三次插值
- 基于 16 个周围像素的三次多项式拟合,计算更复杂但更平滑。
5、如何选择插值方法
场景 | 推荐方法 | 原因 |
---|---|---|
实时处理(如摄像头) | 最近邻(INTER_NEAREST) | 速度最快 |
普通放大图片 | 双线性(INTER_LINEAR) | 平衡质量与速度 |
高质量放大(如打印) | 双三次或Lanczos(INTER_CUBIC 或 INTER_LANCZOS4) | 边缘更平滑 |
缩小图片 | 像素区域(INTER_AREA) | 避免波纹和失真 |
四、边缘填充
1、OpenCV边缘填充的作用
✅ 保持输出图像尺寸不变
✅ 避免边缘信息丢失
✅ 减少黑边或伪影
2、OpenCV的边缘填充方法
OpenCV 通过 cv2.copyMakeBorder() 或卷积操作的 borderType 参数(如 cv2.filter2D)指定填充方式。以下是常见的填充方法:
填充类型 | OpenCV常量 | 效果描述 | 示意图 |
---|---|---|---|
边界复制 | cv2.BORDER_REPLICATE | 复制边缘像素值 | [A B C] → [A A A B C C C] |
边界反射 | cv2.BORDER_REFLECT | 镜像反射边缘像素 | [A B C] → [C B A B C B A] |
反射101 | cv2.BORDER_REFLECT_101 | 反射边缘(不含边缘像素) | [A B C] → [B A B C B A] |
边界常数 | cv2.BORDER_CONSTANT | 用固定值(如黑色或白色)填充 | [A B C] → [0 0 A B C 0 0] |
边界包裹 | cv2.BORDER_WRAP | 重复图像内容 | [A B C] → [B C A B C A B] |
3、代码实现(仿射旋转变换为例)
import cv2 as cv#读图
cat = cv.imread("../../images/cat1.png")
cat = cv.resize(cat, (500, 300))
cv.imshow("cat", cat)
h,w=cat.shape[:2]
#获取旋转矩阵
M = cv.getRotationMatrix2D((w//2,h//2), 90, 0.5)
#方式变换函数,实现旋转
cat0 = cv.warpAffine(cat, M, (500,300))
#边界复制
cat1 = cv.warpAffine(cat, M, (500, 300),borderMode=cv.BORDER_REPLICATE)
#边界反射
cat2 = cv.warpAffine(cat, M, (500, 300),borderMode=cv.BORDER_REFLECT)
#边界反射101
cat3 = cv.warpAffine(cat, M, (500, 300),borderMode=cv.BORDER_REFLECT_101)
#边界常数
cat4 = cv.warpAffine(cat, M, (500, 300),borderMode=cv.BORDER_CONSTANT,borderValue=(255,0,0))
#边界包裹
cat5 = cv.warpAffine(cat, M, (500, 300),borderMode=cv.BORDER_WRAP)# 显示效果 释放资源
cv.imshow("cat0", cat0)
cv.imshow("cat1", cat1)
cv.imshow("cat2", cat2)
cv.imshow("cat3", cat3)
cv.imshow("cat4", cat4)
cv.imshow("cat5", cat5)cv.waitKey(0)
cv.destroyAllWindows()
4、不同填充方式的效果对比
填充类型 | 效果 | 适用场景 |
---|---|---|
边界复制(BORDER_REPLICATE) | 直接复制边缘像素 | 简单快速,适合大多数滤波 |
边界反射(BORDER_REFLECT) | 镜像反射边缘 | 避免边缘突变,适合自然图像 |
反射101(BORDER_REFLECT_101) | 反射(不含边缘像素) | OpenCV默认方式,更平滑 |
边界常数(BORDER_CONSTANT) | 填充固定颜色(默认黑色) | 需要明确背景色时 |
边界包裹(BORDER_WRAP) | 重复图像内容 | 周期性纹理(如花纹) |
5、如何选择合适的填充方式
- 默认推荐:BORDER_REFLECT_101(OpenCV 许多函数的默认选项)。
- 滤波/边缘检测:用 BORDER_REPLICATE 或 BORDER_REFLECT。
- 深度学习(CNN):通常用 BORDER_CONSTANT(填充0)。
- 避免黑边:用 BORDER_REFLECT。
五、图像矫正(透视变换)
图像矫正的原理是透视变换
透视变换是把一个图像投影到一个新的视平面的过程,在现实世界中,我们观察到的物体在视觉上会受到透视效果的影响,即远处的物体看起来会比近处的物体小。透视投影是指将三维空间中的物体投影到二维平面上的过程,这个过程会导致物体在图像中出现形变和透视畸变。透视变换可以通过数学模型来校正这种透视畸变,使得图像中的物体看起来更符合我们的直观感受。
通俗的讲,透视变换的作用其实就是改变一下图像里的目标物体的被观察的视角。
1、透视变换能解决什么问题?
- 典型应用场景
-
- 文档扫描:把手机斜拍的文档变成规整的A4纸效果
-
- 车牌识别:矫正倾斜的车牌
-
- AR标记跟踪:将倾斜的平面广告牌变成正视图
-
- 图像拼接:统一不同视角的照片
2. 透视变换原理
透视变换的本质是 通过一个3×3的变换矩阵,把图像从歪斜的四边形映射到规整的矩形。
关键概念
-
1.源点(src points):原始图像上四边形的4个角点
-
2.目标点(dst points):你想让这4个点变换后的位置(通常是矩形)
-
3.变换矩阵(M):通过解方程计算得到的3×3矩阵
3、代码实现
import cv2 as cv
import numpy as np
#读图
card = cv.imread("../../images/3.png")
shape = card.shape
#定义点坐标 [174,89],[527,135],[505,348],[110,295]
pts1 = np.float32([[174,89],[527,135],[505,348],[110,295]])
pts2=np.float32([[0,0],[shape[1],0],[shape[1],shape[0]],[0,shape[0]]])
#透视变换矩阵
M = cv.getPerspectiveTransform(pts1,pts2)
#透视变换
dst = cv.warpPerspective(card, M, (shape[1], shape[0]),cv.INTER_LINEAR,cv.BORDER_REPLICATE)
#显示效果
cv.imshow("card", card)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
六、图像色彩空间转换
1、RGB颜色空间
在图像处理中,最常见的就是RGB颜色空间。RGB颜色空间是我们接触最多的颜色空间,是一种用于表示和显示彩色图像的一种颜色模型。RGB代表红色(Red)、绿色(Green)和蓝色(Blue),这三种颜色通过不同强度的光的组合来创建其他颜色,广泛应用于我们的生活中,比如电视、电脑显示屏以及上面实验中所介绍的RGB彩色图。
RGB颜色空间可以产生大约1600万种颜色,几乎包括了世界上的所有颜色,也就是说可以使用RGB颜色空间来生成任意一种颜色。
注:在OpenCV中,颜色是以BGR的方式进行存储的,而不是RGB,这也是上面红色的像素值是(0,0,255)而不是(255,0,0)的原因。
2、颜色加法与颜色加权加法
(1)颜色加法
可以使用OpenCV的cv.add()函数把两幅图像相加,或者可以简单地通过numpy操作添加两个图像,如res = img1 + img2。两个图像应该具有相同的大小和类型。
注:OpenCV加法和Numpy加法之间存在差异。OpenCV的加法是饱和操作,而Numpy添加是模运算。
(2)颜色加权加法
cv2.addWeighted(src1,alpha,src2,deta,gamma)
- src1、src2:输入图像。
- alpha、beta:两张图象权重。
- gamma:亮度调整值。
-
- gamma > 0,图像会变亮。
-
- gamma < 0,图像会变暗。
-
- gamma = 0,则没有额外的亮度调整。
(3)两者代码实现:
import cv2 as cv
import numpy as npx = np.uint8([[250]])
y = np.uint8([[10]])
#读图
pig = cv.imread("../../images/pig.png")
cao = cv.imread("../../images/cao.png")#饱和操作 cv.add()
add = cv.add(x, y)
print(add)
#cv.add颜色加法
img_add=cv.add(pig,cao)
cv.imshow("add", img_add)#np 直接相加 取模运算,对256取模,取余
dst=x+y
print(dst)
img_dst=pig+cao
cv.imshow("dst", img_dst)#颜色加权加法
img_weight = cv.addWeighted(cao,0.3,pig,0.8,gamma = -100)
cv.imshow("weight", img_weight)cv.waitKey(0)
cv.destroyAllWindows()
(4)颜色转换
BGR转Gray(灰度)、HSV、RGB
cv2.cvtColor(img,code)
- img:输入图像,可以是一个Numpy数组绘着一个OpenCV的Mat对象
-
- Mat 是一个核心的数据结构,主要用于存储图像和矩阵数据。在 Python 中使用 OpenCV 时,通常直接处理的是 NumPy 数组,cv2 模块自动将 Mat 对象转换为 NumPy 数组。二者之间的转换是透明且自动完成的。例如,当你使用 cv2.imread() 函数读取图像时,返回的是一个 NumPy 数组,但在C++中则是 Mat 对象。
- code:指定转换的类型,可以使用预定义的转换代码
-
- 例如cv2.COLOR_RGB2GRAY表示从rgb到灰度图像的转换
代码实现:
import cv2 as cv
#读图
cat = cv.imread('../../images/cat1.png')
#转灰度
gray = cv.cvtColor(cat, cv.COLOR_BGR2GRAY)
print(gray.shape)
cv.imshow('gray', gray)
#转HSV
hsv = cv.cvtColor(cat, cv.COLOR_BGR2HSV)
cv.imshow('hsv', hsv)
#转RGB
rgb = cv.cvtColor(cat, cv.COLOR_BGR2RGB)
cv.imshow('rgb', rgb)
cv.waitKey(0)
cv.destroyAllWindows()