此系列文章为Opencv官方文档中图像处理部分的学习,文章集合

1. 简介

Opencv提供了一系列对图像进行几何变换的API,如位移,旋转,缩放,仿射变换,透视变换等。

2. API及示例

2.1 缩放

void cv::resize    (    
InputArray     src, //输入图
OutputArray     dst, //输出图
Size     dsize,       //输出图尺寸,不填则根据原图和xy方向的缩放因子计算
double     fx = 0,      //x方向缩放因子,不填则根据输入输出图尺寸计算
double     fy = 0,      //y方向缩放因子
int     interpolation = INTER_LINEAR //插值方法
)    

插值方法取值如下:
image.png
示例:

import cv2
import matplotlib.pyplot as plt

def testScale():
    img = cv2.imread("test.jpg")
    img = cv2.resize(img,None, fx=0.5, fy=0.1, interpolation = cv2.INTER_CUBIC)
    cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    plt.imshow(img)
    plt.show()

testScale()

image.png

2.2 位移

opencv提供了warpAffine函数来对图像执行更多的几何变换操作

void cv::warpAffine    (    
InputArray     src,  //输入
OutputArray     dst,  //输出
InputArray     M,    //2x3的变换矩阵
Size     dsize,        //输出图尺寸  
int     flags = INTER_LINEAR, //插值方法
int     borderMode = BORDER_CONSTANT,//边缘模式
const Scalar &     borderValue = Scalar() //边框参数,不填为(0,0,0),即填充黑色
)    

如果让一个图片在x,y方向上分别位移Tx,Ty,这个位移可以用一个2*3的变换矩阵来表示:
image.png
例如:

img = cv2.imread('test.jpg', 0)
rows, cols = img.shape
M = np.float32([[1, 0, 250], [0, 1, 200]])
dst = cv2.warpAffine(img, M, (cols, rows))
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
plt.imshow(dst)
plt.show()

image.png

2.3 旋转

图像旋转θ角度,可以通过矩阵
image.png
来表示。
可以将缩放、旋转集成到一个变换矩阵中,并设置旋转的中心点:
image.png
其中:

α=scale⋅cosθ
β=scale⋅sinθ

当然,我们并不需要自己去计算这个复杂的矩阵,opencv提供了函数来计算这个矩阵:

Mat cv::getRotationMatrix2D    (    
Point2f     center, //旋转中心点
double     angle,          //旋转角度
double     scale           //缩放倍数
)    

可以用这个函数来生成旋转矩阵试一下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def testRotate():
    img = cv2.imread('test.jpg')
    rows, cols, chanels = img.shape
    print("rows, cols, chanels :",rows, cols, chanels )
    M = cv2.getRotationMatrix2D((cols / 2,rows / 2), 90, 1)
    dst = cv2.warpAffine(img, M, (900,900))

    cv2.cvtColor(dst, cv2.COLOR_BGR2RGB, dst)
    plt.imshow(dst)
    plt.show()

testRotate()

原图:
image.png
旋转图:
image.png
这里值得注意的是,旋转得到的结果,超出原始图片大小的部分会被裁剪掉,即使输出尺寸填写的很大,也只会填充黑色。
旋转的过程可以理解为:

  1. 将坐标系变换至旋转中心点
  2. 旋转
  3. 将坐标系变换至原图左上角
  4. 超出原坐标系的部分裁减掉

要避免这种情况,只需要在第三步,变换坐标系的时候,将原点选在一个合适的位置,并调整输出图像大小即可。此处参考https://blog.csdn.net/u010122972/article/details/78345447
image.png
将旋转后的坐标原点由原始图像左上角,移动到旋转后图像视框左上角即可,需要做的工作有旋转后图像大小计算、旋转矩阵修改等。
代码实现如下:

img = cv2.imread('test.jpg')
degree = 30
heightOrigin, widthOrigin = img.shape[:2]
heightFinal = int(widthOrigin * math.fabs(math.sin(math.radians(degree))) +
                  heightOrigin * math.fabs(math.cos(math.radians(degree))))
widthFinal = int(heightOrigin * math.fabs(math.sin(math.radians(degree))) +
                 widthOrigin * math.fabs(math.cos(math.radians(degree))))
matRotation = cv2.getRotationMatrix2D((widthOrigin / 2, heightOrigin / 2), degree, 1)
matRotation[0, 2] += (widthFinal - widthOrigin) / 2
matRotation[1, 2] += (heightFinal - heightOrigin) / 2
dst = cv2.warpAffine(img, matRotation, (widthFinal, heightFinal), borderValue=(255, 255, 255))
cv2.cvtColor(dst, cv2.COLOR_BGR2RGB, dst)
plt.imshow(dst)
plt.show()

旋转结果:
image.png

2.4 仿射变换

和2.3小节相似,仿射变换也是由opecv提供的函数生成一个2*3矩阵,然后由wrapAffine函数应用到图像。
仿射变换通过3个点的变换前后位置,来定义变换矩阵:

Mat cv::getAffineTransform    (    
const Point2f     src[], //变换前的点(个)
const Point2f     dst[]  //变换后的点(3个)
)    

示例代码:

img = cv2.imread('test.jpg')
    rows, cols, chanels = img.shape
    pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
    pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
    M = cv2.getAffineTransform(pts1, pts2)
    dst = cv2.warpAffine(img, M, (cols,rows))

    cv2.cvtColor(dst, cv2.COLOR_BGR2RGB, dst)
    plt.imshow(dst)
    plt.show()

image.png

2.5 透视变换

透视变换与仿射变换类似,仿射变换是透视变换的一种特殊情况。对应的矩阵计算函数和变换函数不同,示例如下:

img = cv2.imread('test.jpg')
rows, cols, ch = img.shape
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2 = np.float32([[10, 90], [200, 150], [50, 200], [200, 250]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (300, 300),borderValue=(0,255,0))
cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img)
cv2.cvtColor(dst, cv2.COLOR_BGR2RGB, dst)
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')

image.png

2.6 边缘模式/值

上面的示例中,可以发现几何变换后,图片边缘可能需要填充一些内容。 填充的方式是通过wrapAffine函数的borderMode、borderValue方式填充的,默认情况下borderMode为BORDER_CONSTANT,borderValue为(0,0,0)
borderMode取值如下:
image.png
例如
用绿色填充边缘:

dst = cv2.warpAffine(img, M, (1500,1500),borderValue=(0,255,0))

image.png

透明边缘:

dst = cv2.warpAffine(img, M, (1500,1500), borderMode=cv2.BORDER_TRANSPARENT,borderValue=(0,255,0))

此透明并不会增加alpha通道,只是表明边缘填充数据和wrap输入数据的关系,透明时不会修改dst原来位置处,未被覆盖的数据,这里cpp版本的opencv支持传入输出图参数,py版本的并不支持

☞ 参与评论