1. 旋转与缩放的仿射变换
在 OpenCV 中,cv2.getRotationMatrix2D()
函数可以生成旋转矩阵,该矩阵用于对图像进行旋转和缩放变换。旋转矩阵的主要参数是:
-
Center:旋转中心点的坐标 (x, y)。
-
Angle:旋转角度,单位是度。
-
Scale:缩放因子,调整图像的大小。
import cv2 img=cv2.imread("./src/tu.png") h,w,_=img.shape M=cv2.getRotationMatrix2D((w/2,h/2),45,1) m=cv2.warpAffine(img, M, (w, h)) cv2.imshow("img",img) cv2.imshow("m",m) cv2.waitKey(0)
2. 旋转中的像素信息丢失
旋转图像时,由于三角函数计算出的坐标通常是浮点数,因此旋转后的像素点可能不会落在原始像素的整数坐标上。这导致以下问题:
-
像素信息丢失:旋转过程中,某些像素点的坐标可能会落在图像边界之外,或多个像素点的旋转后坐标重合。
-
插值问题:旋转后的坐标可能不是整数,这会导致需要计算这些非整数坐标对应的像素值。
3. 插值法的作用
为了处理这些问题,我们使用插值法来计算旋转后图像中每个像素点的值。常见的插值方法有:
-
最近邻插值:cv2.INTER_NEAREST
:简单且速度快,适合对图像进行小幅度的变换,但可能会导致锯齿效应。 -
双线性插值:cv2.INTER_LINEAR
:适用于大多数缩放操作,提供平滑的结果,尤其是在放大时。 -
像素区域插值:cv2.INTER_AREA
:用于图像缩小时表现良好,能避免模糊,并保留更多细节。 -
双三次插值:cv2.INTER_CUBIC
:提供比双线性更平滑的结果,适用于高质量的图像放大。 -
Lanczos插值:cv2.INTER_LANCZOS4
:高质量插值方法,适合对图像进行大幅度变换,但计算开销较大。
import cv2 img = cv2.imread("./src/tu.png") h, w, _ = img.shape # 最近邻插值:CV2.INTER_NEAREST img1 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST) # 双线性插值:CV2.INTER_LINEAR img2 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR) # 像素区域插值:cv2.INTER_AREA img3 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA) # 双三次插值:cv2.INTER_CUBIC img4 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC) # Lanczos插值:cv2.INTER_LANCZOS4 img5 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LANCZOS4) cv2.imshow("img", img) cv2.imshow("img1", img1) cv2.imshow("img2", img2) cv2.imshow("img3", img3) cv2.imshow("img4", img4) cv2.imshow("img5", img5) cv2.waitKey(0) cv2.destroyAllWindows()
4.边缘填充方式
边缘填充方式用于处理图像在变换(如旋转、缩放)时的边界问题。常见的填充方式包括:
-
边界复制:cv2.BORDER_REPLICATE
:复制边缘像素填充,适用于保持图像的一致性。 -
边界反射:cv2.BORDER_REFLECT
:用边缘的镜像反射部分填充,使得填充区域与边缘平滑过渡。 -
边界反射101:cv2.BORDER_REFLECT_101
:类似于BORDER_REFLECT
,但不包含边缘的第一个像素,填充效果更加平滑。 -
边界常数:cv2.BORDER_CONSTANT
:用常量值填充边缘,常用于保持边缘的整洁。 -
边界包裹:cv2.BORDER_WRAP
:用图像的对面部分进行填充,适合周期性图像。
import cv2 import numpy as np # 读取图像 img = cv2.imread("./src/face.png") h, w, _ = img.shape # 定义仿射变换矩阵 M = np.array([[0, 1, 50], [-1, 0, 100]], dtype=np.float32) # 设置目标图像尺寸 dsize = (w * 2, h * 2) # 使用不同的边界填充模式 border_modes = [cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_REFLECT_101,cv2.BORDER_CONSTANT,cv2.BORDER_WRAP] for mode in border_modes: img2 = cv2.warpAffine(img, M,dsize=dsize, flags=cv2.INTER_LINEAR, borderMode=mode, borderValue=[213,102,201]) cv2.imshow(f"Border Mode: {mode}", img2) cv2.waitKey(0) cv2.destroyAllWindows()
图片镜像旋转
镜像旋转的翻转方式在 OpenCV 中通过 cv2.flip()
函数实现,flipCode
参数定义了翻转方式:
-
0
:垂直翻转,沿 x 轴对称。 -
大于
0
:水平翻转,沿 y 轴对称。 -
小于
0
:水平和垂直翻转,沿 x 轴和 y 轴对称。import cv2 img = cv2.imread('./src/e.png') img1=cv2.flip(img,0) img2=img[::-1,::-1] #水平垂直翻转 cv2.imshow("img",img) cv2.imshow("img1",img1) cv2.imshow("img2",img2) cv2.waitKey(0)
图像缩放
在图像缩放实验中,你可以独立调整图像在 x 轴和 y 轴的缩放比例,类似于图像旋转中的缩放,但允许不同方向上的不同缩放比例。使用 cv2.resize()
函数时,你可以设置 fx
和 fy
进行缩放,插值方法如最近邻、双线性、像素区域、立方和 Lanczos 等,用于计算新像素值。双线性插值(cv2.INTER_LINEAR
)是一种常用的方法,能够平滑地处理图像细节。
import cv2 img=cv2.imread('./src/c.png') img1 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR) cv2.imshow("img",img) cv2.imshow("img1",img1) cv2.waitKey(0)
图像矫正
图像矫正的原理是透视变换
透视变换的核心在于将图像从一个视角投影到另一个视角,从而修正图像中的透视畸变。相比于仿射变换,透视变换更复杂,它通过透视变换矩阵来处理三维到二维的投影过程。具体来说,透视变换矩阵是一个3x3的矩阵,其中每个元素影响着图像中点的变换关系。OpenCV中的getPerspectiveTransform(src, dst)
函数用于计算这个矩阵,warpPerspective(src, M, dsize)
函数则用来应用这个矩阵进行实际的透视变换。
import cv2 import numpy as np # 读取图像 img = cv2.imread('./src/3.png') # 定义源点 src1 = np.float32([[178, 102], [518, 154], [113, 300], [503, 346]]) # 绘制线条 img_line = img.copy() cv2.line(img_line, tuple(src1[0].astype(np.int64)), tuple(src1[1].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA) cv2.line(img_line, tuple(src1[0].astype(np.int64)), tuple(src1[2].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA) cv2.line(img_line, tuple(src1[1].astype(np.int64)), tuple(src1[3].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA) cv2.line(img_line, tuple(src1[2].astype(np.int64)), tuple(src1[3].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA) # 定义目标点 h, w = img.shape[:2] src2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]]) # 计算透视变换矩阵 src = cv2.getPerspectiveTransform(src1, src2) # 应用透视变换 img1 = cv2.warpPerspective(img, src, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101) cv2.imshow("img", img_line) cv2.imshow("img1", img1) cv2.waitKey(0) cv2.destroyAllWindows()
图像添加水印
添加水印的过程可以理解为将一张图片中的图案叠加到另一张图片上。具体步骤如下:
1. 模板输入
模板输入用于获取水印图案。首先,将水印图像转换为灰度图,然后进行二值化处理。这一步的目的是创建一个掩膜图像,其中水印图案是白色(255),背景是黑色(0)。
2. 与运算
得到掩膜图像后,在目标图像中定义一个区域(ROI,感兴趣区域)作为水印的添加区域。使用掩膜图像与该ROI区域进行“与”运算。通过这种运算,只有掩膜图像中白色部分的区域会在目标图像的ROI区域中保留下来,其余部分将被遮盖。
3. 图像融合
最后,将经过与运算得到的目标区域与原始水印图像进行融合。图像融合步骤是将水印图像与目标图像中的对应区域进行逐像素的相加操作,注意两者的尺寸必须一致。这样,水印图案就会被成功叠加到目标图像中,从而实现水印的效果。
import cv2 import numpy as np img=cv2.imread("./src/cat.png") logo=cv2.imread("./src/logohq.png") # 获取 logo 的尺寸 h, w, _ = logo.shape # 将 logo 转换到 HSV 颜色空间 hsvlogo = cv2.cvtColor(logo, cv2.COLOR_BGR2HSV) #提取红色范围 lower1 = np.array([0, 43, 46]) upper1 = np.array([10, 255, 255]) #提取灰色范围 lower2 = np.array([0, 0, 46]) upper2= np.array([180, 43, 220]) # 创建掩膜 mask1 = cv2.inRange(hsvlogo, lower1, upper1) mask2 = cv2.inRange(hsvlogo, lower2, upper2) mask=mask1+mask2 # 显示掩膜 cv2.imshow("mask", mask) # 提取与 logo 大小相同的背景区域 img2 = img[:h, :w] # 使用掩膜提取文字区域 text_bool = (mask == 255) # 将文字部分融合到背景图像中 img2[text_bool] = logo[text_bool] cv2.imshow("img", img) cv2.imshow("logo", logo) cv2.waitKey(0) cv2.destroyAllWindows()
总结
整个过程包括:
-
从模板图像中创建掩膜。
-
在目标图像中定位水印区域,并用掩膜图像来提取水印形状。
-
将提取出的水印图像与目标图像的相应区域融合