OpenCV4【7】-图像几何变换

图像几何变换 分为 缩放、平移、旋转、仿射变换、透视变换 等;

思路大致相同:在 原图像上 找几个点的坐标,然后设定 变换后 这些点对应的 坐标,根据两组坐标 计算出一个 转换矩阵,把原图像所有点 按这个转换矩阵进行转换即可

缩放

缩放只是调整图像的大小;

转换矩阵

def resize(src, dsize, dst=None, fx=None, fy=None, interpolation=None): 放大和缩小图像
    参数:
        src: 输入图像对象
        dsize:输出矩阵/图像的大小,为0时计算方式如下:dsize = Size(round(fx*src.cols),round(fy*src.rows))
        fx: 水平轴的缩放因子,为0时计算方式:  (double)dsize.width/src.cols
        fy: 垂直轴的缩放因子,为0时计算方式:  (double)dsize.heigh/src.rows
        interpolation:插值算法
            cv2.INTER_NEAREST : 最近邻插值法
            cv2.INTER_LINEAR   默认值,双线性插值法
            cv2.INTER_AREA        基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。
            cv2.INTER_CUBIC        基于4x4像素邻域的3次插值法
            cv2.INTER_LANCZOS4     基于8x8像素邻域的Lanczos插值
                     
    cv2.INTER_AREA 适合于图像缩小, cv2.INTER_CUBIC (slow) & cv2.INTER_LINEAR 适合于图像放大

既可指定缩放比例,也可指定缩放后的大小

示例

img = cv.imread('imgs/2.png')
cv.imshow('org', img)

res = cv.resize(img, None, fx=3, fy=2, interpolation=cv.INTER_CUBIC)    # 3倍比例
cv.imshow('bili', res)
# 或者
res = cv.resize(img, (100, 200), interpolation=cv.INTER_CUBIC)      # 指定尺寸
cv.imshow('hw', res)
cv.waitKey(0)

平移

平移只是位置的移动;

假设在 x轴 y轴 上移动距离为 (tx, ty),则转换矩阵

转换后的坐标为 (x+tx, y+ty

示例

img = cv.imread('imgs/2.png', 0)
cv.imshow('org', img)

rows, cols = img.shape
print(rows, cols)   # 408 388
M = np.float32([[1, 0, 100],[0, 1, 50]])        # 平移向量
print(M)
# [[  1.   0. 100.]
#  [  0.   1.  50.]]
dst = cv.warpAffine(img, M, (cols, rows))   # 第三个参数是输出图像的大小,其形式应为(width,height)。记住width =列数,height =行数。
cv.imshow('img', dst)
cv.waitKey(0)
cv.destroyAllWindows()

warpAffine 用法见下文

效果图

旋转

假设图像旋转角度为θ,则转换矩阵

转换后的坐标为 x1 = xcosθ-ysinθ, y1 =xsinθ+ycosθ;

opencv将其进行了扩展,任意点center为中心进行顺时针旋转θ,放大scale倍的,转换矩阵如下:

  

为了找到此转换矩阵,OpenCV提供了一个函数 cv.getRotationMatrix2D。

cv2.getRotationMatrix2D()  返回2*3的转变矩阵(浮点型)
    参数:
        center:旋转的中心点坐标
        angle:旋转角度,单位为度数,证书表示逆时针旋转
        scale:同方向的放大倍数

示例

img = cv.imread('imgs/2.png', 0)
rows, cols = img.shape
# cols-1 和 rows-1 是坐标限制
M = cv.getRotationMatrix2D(((cols - 1) / 2.0, (rows - 1) / 2.0), 90, 1)     # 旋转矩阵
print(M)
# [[ 6.123234e-17  1.000000e+00 -1.000000e+01]
#  [-1.000000e+00  6.123234e-17  3.970000e+02]]
dst = cv.warpAffine(img, M, (cols, rows))
cv.imshow('img', dst)
cv.waitKey(0)
cv.destroyAllWindows()

warpAffine 用法见下文

效果图

 

仿射变换

仿射变换其实是 缩放、平移、旋转 的组合,其 变换矩阵 可 由 上述变换矩阵 相乘得到

opencv提供了函数getAffineTransform()来计算变换矩阵

cv2.getAffineTransform()  返回2*3的转变矩阵
      参数:
          src:原图像中的三组坐标,如np.float32([[50,50],[200,50],[50,200]])
          dst: 转换后的对应三组坐标,如np.float32([[10,100],[200,50],[100,250]])

示例

img = cv.imread('imgs/2.png')
rows, cols, ch = img.shape
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv.getAffineTransform(pts1, pts2)
print(M)    # 2x3
# [[  1.26666667   0.6        -83.33333333]
#  [ -0.33333333   1.          66.66666667]]
dst = cv.warpAffine(img, M, (cols, rows))
plt.subplot(121); plt.imshow(img); plt.title('Input')
plt.subplot(122); plt.imshow(dst); plt.title('Output')
plt.show()

效果图

warpAffine 用法

cv2.warpAffine()   仿射变换(从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切)
    参数:
        img: 图像对象
        M:2*3 transformation matrix (转变矩阵)
        dsize:输出矩阵的大小,注意格式为(cols,rows)  即width对应cols,height对应rows
        flags:可选,插值算法标识符,有默认值INTER_LINEAR,
               如果插值算法为WARP_INVERSE_MAP, warpAffine函数使用如下矩阵进行图像转dst(x,y)=src(M11*x+M12*y+M13,M21*x+M22*y+M23)
        borderMode:可选, 边界像素模式,有默认值BORDER_CONSTANT 
        borderValue:可选,边界取值,有默认值Scalar()即0

常见插值算法

透视变换(投影变换)

仿射变换是在二维空间的变换,透视变换是在三维空间的变换;

故 仿射变换 需要 3个点,2x3 的转换矩阵,透视变换 需要 4个点,3x3 的转换矩阵;

opencv提供了函数getPerspectiveTransform()来计算转变矩阵,cv2.warpPerspective()函数来进行透视变换。其对应参数如下:

cv2.getPerspectiveTransform()   返回3*3的转变矩阵
        参数:    
            src:原图像中的四组坐标,如 np.float32([[56,65],[368,52],[28,387],[389,390]])
            dst: 转换后的对应四组坐标,如np.float32([[0,0],[300,0],[0,300],[300,300]])
            
cv2.warpPerspective()
        参数:    
            src: 图像对象
            M:3*3 transformation matrix (转变矩阵)
            dsize:输出矩阵的大小,注意格式为(cols,rows)  即width对应cols,height对应rows
            flags:可选,插值算法标识符,有默认值INTER_LINEAR,
                   如果插值算法为WARP_INVERSE_MAP, warpAffine函数使用如下矩阵进行图像转dst(x,y)=src(M11*x+M12*y+M13,M21*x+M22*y+M23)
            borderMode:可选, 边界像素模式,有默认值BORDER_CONSTANT 
            borderValue:可选,边界取值,有默认值Scalar()即0

示例

img = cv.imread('imgs/2.png')
rows, cols, ch = img.shape
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [353, 381]])     # 输入图像的四个点
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])         # 输出图像对应的四个点
M = cv.getPerspectiveTransform(pts1, pts2)
print(M)    # 3x3
# [[ 1.00021368e+00  8.69751023e-02 -6.16653475e+01]
#  [ 4.11810169e-02  9.88344406e-01 -6.65485233e+01]
#  [ 7.90614186e-05  1.41513474e-04  1.00000000e+00]]
dst = cv.warpPerspective(img, M, (300, 300))
plt.subplot(121); plt.imshow(img); plt.title('Input')
plt.subplot(122); plt.imshow(dst); plt.title('Output')
plt.show()

效果图

上图找到原图的4个顶点,将其转换到新图的4个顶点,能将 歪斜 的 ROI 转正并放大,

这在 书籍、名片等拍照上传后进行识别时,是个很好的预处理方法; 

opencv中还提供了一个函数 perspctiveTransform() 来对坐标点进行透视变换,对于原图像上的一点,通过 perspctiveTransform() 能计算出透视变换后图片上该点的坐标,其对应参数如下:

cv2.perspectiveTransform(src, matrix)

参数: 
    src:坐标点矩阵,注意其格式. 如src=np.array([[589, 91],[1355, 91],[1355, 219],[589, 219]], np.float32).reshape(-1, 1, 2), 表示四个坐标点,size为(4, -1, 2)
    matrix:getPerspectiveTransform()得到的透视变换矩阵 
返回值:变换后的坐标点,格式和src相同

参考资料:

https://www.cnblogs.com/silence-cho/p/10926248.html  OpenCV-Python学习—基础知识

原文地址:https://www.cnblogs.com/yanshw/p/15393055.html