此系列文章为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 //插值方法
)
插值方法取值如下:
示例:
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()
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的变换矩阵来表示:
例如:
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()
2.3 旋转
图像旋转θ角度,可以通过矩阵
来表示。
可以将缩放、旋转集成到一个变换矩阵中,并设置旋转的中心点:
其中:
α=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()
原图:
旋转图:
这里值得注意的是,旋转得到的结果,超出原始图片大小的部分会被裁剪掉,即使输出尺寸填写的很大,也只会填充黑色。
旋转的过程可以理解为:
- 将坐标系变换至旋转中心点
- 旋转
- 将坐标系变换至原图左上角
- 超出原坐标系的部分裁减掉
要避免这种情况,只需要在第三步,变换坐标系的时候,将原点选在一个合适的位置,并调整输出图像大小即可。此处参考https://blog.csdn.net/u010122972/article/details/78345447
将旋转后的坐标原点由原始图像左上角,移动到旋转后图像视框左上角即可,需要做的工作有旋转后图像大小计算、旋转矩阵修改等。
代码实现如下:
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()
旋转结果:
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()
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')
2.6 边缘模式/值
上面的示例中,可以发现几何变换后,图片边缘可能需要填充一些内容。 填充的方式是通过wrapAffine函数的borderMode、borderValue方式填充的,默认情况下borderMode为BORDER_CONSTANT,borderValue为(0,0,0)
borderMode取值如下:
例如
用绿色填充边缘:
dst = cv2.warpAffine(img, M, (1500,1500),borderValue=(0,255,0))
透明边缘:
dst = cv2.warpAffine(img, M, (1500,1500), borderMode=cv2.BORDER_TRANSPARENT,borderValue=(0,255,0))
此透明并不会增加alpha通道,只是表明边缘填充数据和wrap输入数据的关系,透明时不会修改dst原来位置处,未被覆盖的数据,这里cpp版本的opencv支持传入输出图参数,py版本的并不支持
本文链接:https://www.zoucz.com/blog/2019/02/26/67267d60-39cf-11e9-9e7f-cf581750e42a/