环境配置
本次项目使用
numpy库
基于C++开发提供python接口的cv2库
python自带的math库进行数学计算
思路
\quad 通过对opencv库中的图形变换函数,自己定义了一个radial_trans类来对图片矩阵实现平移、旋转、缩放、斜切来进行封装,最后通过main函数接口来对每个封装的函数进行模块化测试
仿射变换
1.平移
函数形式
def translation(image = np.full((1,1,3), (255, 255, 0)), dwidth = 0, dheight = 0, change_pic_w = 0, change_pic_h = 0)
该函数具有5个参数
imgae为图像矩阵
dwidth和dheight分别代表图像向右和向下平移的距离
而后续的两个参数表示最终显示的图像大小
由于平移的过程中可能会出现一些没有数据的空白点,这里使用天蓝色对空白点进行填充以区别系统自带的函数。
核心代码
for i in range(width):
if (i + dwidth < 0) or (i + dwidth >= width + change_pic_w):
continue
else:
for j in range(height):
if (j + dheight < 0) or (j + dheight >= height + change_pic_h):
continue
else:
new_mat[j + dheight][i + dwidth] = image[j][i]
本质上就是一个个点的复制
测试效果
自己的效果
库函数效果
2.旋转
函数说明
def rotate(image, angle, change_pic_w, change_pic_h):
对比平移函数,区别只有angle,这里只需要输入角度即可,函数内会转换成弧度制
核心代码
rot_max = np.matrix([[cos(a), sin(a), 0],
[-sin(a), cos(a), 0],
[0, 0, 0]])
for x in range(width+change_pic_w):
for y in range(height+change_pic_h):
point = np.matrix([x , y , 1])
list = np.matmul(point, rot_max).tolist()
new_x = int(floor(list[0][0]))
new_y = int(floor(list[0][1]))
u = list[0][0] - new_x
v = list[0][1] - new_y
#线性插值的变量
if new_x < 0 or new_y < 0\
or new_y >= height \
or new_x >= width :
continue
#判定是否在边界内
if (new_x + 1 >= width
or new_y + 1 >= height):
new_mat[y][x] = image[new_y][new_x]
else:
new_mat[y][x] = \
(1-u)*(1-v)*image[new_y][new_x] + \
(1-u)*v*image[new_y][new_x+1]+ \
u*(1-v)*image[new_y+1][new_x] +\
u*v*image[new_y+1][new_x+1]
return np.uint8(new_mat)
创建好矩阵后先对每一个原图像点进行矩阵的运算,然后通过逆运算进行反向的计算(把要目标矩阵图形一个个点逆映射到原来图像的点)最后通过二分插值运算来对离散映射过程损失的点进行拟合
测试效果
自己的函数
库函数
3.缩放
函数说明
scale(image, x_times, y_times):
x_times,y_times分别代表在宽度和高度的变化比例
核心代码
for y in range(new_y):
for x in range(new_x):
#求目标点在原图的位置
temp_x = (x + 0.5)* (width/new_x)-0.5
temp_y = (y + 0.5)* (height/new_y)-0.5
if temp_x < 0:
temp_x = 0
elif temp_x >= width:
temp_x = width - 1
if temp_y < 0:
temp_y = 0
elif temp_y >= height:
temp_y = height - 1
# 确定最近的四个点
x1 = int(np.floor(temp_x))
y1 = int(np.floor(temp_y))
x2 = int(min(x1+1, width-1)) #防止超出原图像范围
y2 = int(min(y1+1, height-1))
dx = x2 - x1 if(x2 - x1) else 1
dy = y2 - y1 if(y2 - y1) else 1
for i in range(3):
r1 = ((x2 - temp_x) * image[y1][x1][i] + (temp_x - x1)*image[y1][x2][i]) / (dx)
r2 = ((x2 - temp_x) * image[y2][x1][i] + (temp_x - x1)*image[y2][x2][i]) / (dx)
r3 = ((y2 - temp_y) * r1 + (temp_y - y1) * r2) / (dy)
new_mat[y, x, i] = int(r3)
形式上也是类似旋转,最后都是使用了双线性插值法避免图像的失真
测试效果
左上为自己实现,右上为库函数
下方为原图
放大的情况
4.斜切
函数说明
def slant(image, alpha_x, alpha_y):
alpha_x和alpha_y为斜切变化的角度,直接输入角度大小即可
核心代码
sla_mat = np.array([
[1, a_x, 0],
[a_y, 1, 0],
[0, 0, 1]
])
height, width = image.shape[:2]
new_mat = np.full((height, width, 3), (255,255,0))
#创建黑色矩阵
for y in range(height):
for x in range(width):
point = np.matrix([x , y , 1])
list = np.matmul(point, sla_mat).tolist()
new_x = int(floor(list[0][0]))
new_y = int(floor(list[0][1]))
u = list[0][0] - new_x
v = list[0][1] - new_y
#线性插值的变量
if new_x < 0 or new_y < 0\
or new_y >= height \
or new_x >= width :
continue
#判定是否在边界内
if (new_x + 1 >= width
or new_y + 1 >= height):
new_mat[y][x] = image[new_y][new_x]
else:
new_mat[y][x] = \
(1-u)*(1-v)*image[new_y][new_x] + \
(1-u)*v*image[new_y][new_x+1]+ \
u*(1-v)*image[new_y+1][new_x] +\
u*v*image[new_y+1][new_x+1]
内容都是类似的,只是矩阵变化不一样
测试效果
最终代码
import numpy as np
from math import sin, cos, pi, floor
import cv2
class radial_trans:
def translation(image = np.full((1,1,3), (255, 255, 0)), dwidth = 0, dheight = 0, change_pic_w = 0, change_pic_h = 0):
#1图形矩阵 2向右平移 3向下平移 4图形显示宽度的改变 5图形显示高度的改变
shape = image.shape
height = shape[0]
width = shape[1]
print(height,width)
new_mat = np.full((height + change_pic_h, width + change_pic_w, 3), (255,255,0))
#创建一个全为黑的窗口
for i in range(width):
if (i + dwidth < 0) or (i + dwidth >= width + change_pic_w):
continue
else:
for j in range(height):
if (j + dheight < 0) or (j + dheight >= height + change_pic_h):
continue
else:
new_mat[j + dheight][i + dwidth] = image[j][i]
new_mat = np.uint8(new_mat)
return new_mat
def rotate(image, angle, change_pic_w, change_pic_h):
#对比库函数增加了改变显示图像UI的大小操作
height, width = image.shape[:2]
new_mat = np.full((height + change_pic_h, width + change_pic_w, 3), (255,255,0))
#创建黑色矩阵
a = angle/180*pi
# matrix1 = np.array([[1, 0, 0],
# [0, -1, 0],
# [-0.5 * height, 0.5 * width, 1]])
rot_max = np.matrix([[cos(a), sin(a), 0],
[-sin(a), cos(a), 0],
[0, 0, 0]])
# matrix2 = np.array([[1, 0, 0],
# [0, -1, 0],
# [0.5 * height, 0.5 * width, 1]])
for x in range(width+change_pic_w):
for y in range(height+change_pic_h):
point = np.matrix([x , y , 1])
'''网络上找到的其他变化方法'''
# temp1 = np.matmul(point, matrix1)
# temp2 = np.matmul(temp1, rot_max)
# list = np.matmul(temp2, matrix2).tolist()
list = np.matmul(point, rot_max).tolist()
new_x = int(floor(list[0][0]))
new_y = int(floor(list[0][1]))
u = list[0][0] - new_x
v = list[0][1] - new_y
#线性插值的变量
if new_x < 0 or new_y < 0\
or new_y >= height \
or new_x >= width :
continue
#判定是否在边界内
if (new_x + 1 >= width
or new_y + 1 >= height):
new_mat[y][x] = image[new_y][new_x]
else:
new_mat[y][x] = \
(1-u)*(1-v)*image[new_y][new_x] + \
(1-u)*v*image[new_y][new_x+1]+ \
u*(1-v)*image[new_y+1][new_x] +\
u*v*image[new_y+1][new_x+1]
return np.uint8(new_mat)
def scale(image, x_times, y_times):
height,width = image.shape[:2]
new_y = int(height * y_times)
new_x = int(width * x_times)
new_mat = np.full((new_y, new_x, 3), (255,255,0))
#创建黑色矩阵
for y in range(new_y):
for x in range(new_x):
#求目标点在原图的位置
temp_x = (x + 0.5)* (width/new_x)-0.5
temp_y = (y + 0.5)* (height/new_y)-0.5
if temp_x < 0:
temp_x = 0
elif temp_x >= width:
temp_x = width - 1
if temp_y < 0:
temp_y = 0
elif temp_y >= height:
temp_y = height - 1
# 确定最近的四个点
x1 = int(np.floor(temp_x))
y1 = int(np.floor(temp_y))
x2 = int(min(x1+1, width-1)) #防止超出原图像范围
y2 = int(min(y1+1, height-1))
dx = x2 - x1 if(x2 - x1) else 1
dy = y2 - y1 if(y2 - y1) else 1
for i in range(3):
r1 = ((x2 - temp_x) * image[y1][x1][i] + (temp_x - x1)*image[y1][x2][i]) / (dx)
r2 = ((x2 - temp_x) * image[y2][x1][i] + (temp_x - x1)*image[y2][x2][i]) / (dx)
r3 = ((y2 - temp_y) * r1 + (temp_y - y1) * r2) / (dy)
new_mat[y, x, i] = int(r3)
return np.uint8(new_mat)
def slant(image, alpha_x, alpha_y):
a_x = alpha_x / 180 * pi
a_y = alpha_y / 180 * pi
sla_mat = np.array([
[1, a_x, 0],
[a_y, 1, 0],
[0, 0, 1]
])
height, width = image.shape[:2]
new_mat = np.full((height, width, 3), (255,255,0))
#创建黑色矩阵
for y in range(height):
for x in range(width):
point = np.matrix([x , y , 1])
list = np.matmul(point, sla_mat).tolist()
new_x = int(floor(list[0][0]))
new_y = int(floor(list[0][1]))
u = list[0][0] - new_x
v = list[0][1] - new_y
#线性插值的变量
if new_x < 0 or new_y < 0\
or new_y >= height \
or new_x >= width :
continue
#判定是否在边界内
if (new_x + 1 >= width
or new_y + 1 >= height):
new_mat[y][x] = image[new_y][new_x]
else:
new_mat[y][x] = \
(1-u)*(1-v)*image[new_y][new_x] + \
(1-u)*v*image[new_y][new_x+1]+ \
u*(1-v)*image[new_y+1][new_x] +\
u*v*image[new_y+1][new_x+1]
return np.uint8(new_mat)
#平移
def test_trans(image):
translation_matrix = np.float32([[1, 0, 20],
[0,1,50]])
dst=cv2.warpAffine(image, translation_matrix, (width+20,height+50))
my_trans = radial_trans.translation(image, 20, 50, 20,50)
cv2.imshow("",my_trans)
cv2.imshow('dst',dst)
cv2.waitKey(0)
def test_rot(image):
# 旋转
rot_mat = cv2.getRotationMatrix2D( (0,0), 15, 1)
#库函数,旋转点, 角度, 放缩大小
#参数1 旋转中心坐标 参数2 旋转角度 参数3 缩放比例
dst = cv2.warpAffine(image, rot_mat, (width, height))
my_rot = radial_trans.rotate(image, 15, 100, 0)
cv2.imshow('dst',dst)
cv2.imshow("",my_rot)
cv2.waitKey(0)
def test_sca(image):
dstx = int(image.shape[1]*0.5)
dsty = int(image.shape[0]*0.5)
my_sca = radial_trans.scale(image, 1.5, 1.1)
dst = cv2.resize(image, (dstx,dsty))
cv2.imshow('dst',dst)
cv2.imshow("my_sca",my_sca)
cv2.imshow('',image)
cv2.waitKey(0)
def test_sla(image):
dx = -15
dy = -30
height, width = image.shape[:2]
p1 = np.float32([[0,0], [width-1,0], [0,height-1]])
p2 = np.float32([[0,0], [width *0.3,height], [width,height]])
M = cv2.getAffineTransform(p1, p2)
dst = cv2.warpAffine(image, M, (width, height))
my_sla = radial_trans.slant(image, dx, dy)
cv2.imshow("my_sla",my_sla)
cv2.imshow("dst", dst)
cv2.waitKey(0)
if __name__ == '__main__':
image = cv2.imread("pic1.png")
print(type(image))
height, width = image.shape[:2]
#test_trans(image)
#test_rot(image)
#test_sca(image)
test_sla(image)