最近在尝试透视变换的实际作用,看上篇paper也是提到了透视变换,但是一直不知道实际操作,到底有什么样的效果:
一整张图,选定四个点,以及将要变换的四个点,之后对整个画面的变换会有多大畸变。
畸变后的图像能不能用来做识别任务?
在github上找了一圈,有一个大佬的做了对图像处理可视化的项目,但是刚好透视变换没做。
我在他的基础上加了一个透视变换的功能,并且加上了鼠标交互的操作。
参考链接:https://github.com/dapsjj/dealAllPicture
python处理图片,包括图片平移、图片旋转、图片缩放、图片翻转、透视变换。选择图片中的四个关键点和将要变换的点,用来生成新的透视图
Use translate,scale,flip,rotation to turn one picture into multiple pictures.
四点必须共面,否则无法提供8值约束!
进行ptc变换也是会错乱!
import os
import numpy as np
import cv2
import tkinter.filedialog
import time
import tkinter.messagebox
import re
import matplotlib.pyplot as plt
'''
上传照片,批量处理并保存,包括图片平移、图片旋转、图片缩放、图片翻转、透视变换。
'''
# dir = r'D:/deal_pics/' + time.strftime('%Y-%m-%d')
dir = time.strftime('%Y-%m-%d')
filenames='right'
def get_files():
global filenames
filenames = tkinter.filedialog.askopenfilenames(title="选择图片", filetypes=[('图片', 'jpg'), ('图片', 'png')])
CN_Pattern = re.compile(u'[\u4E00-\u9FBF]+')
JP_Pattern = re.compile(u'[\u3040-\u31fe]+')
if filenames:
if not os.path.exists(dir):
os.makedirs(dir)
CN_Match = CN_Pattern.search(str(filenames))
JP_Match = JP_Pattern.search(str(filenames))
if CN_Match:
filenames=None
tkinter.messagebox.showinfo('提示','文件路径或文件名不能含有中文,请修改!')
return
elif JP_Match:
filenames = None
tkinter.messagebox.showinfo('提示','文件路径或文件名不能含有日文,请修改!')
return
if not os.path.exists(dir):
os.makedirs(dir)
def translate(image, x, y):
# 定义平移矩阵
M = np.float32([[1, 0, x], [0, 1, y]])
shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# 返回转换后的图像
return shifted
def pic_translate():
if not filenames:
tkinter.messagebox.showinfo('提示', '请先选择图片才能进行图片平移!')
if not os.path.exists(dir):
os.makedirs(dir)
if filenames:
for filename in filenames:
if filename:
img = cv2.imread(filename)
newFile = filename.split('/')[-1]
name = newFile.split('.')[0]
filetype = newFile.split('.')[-1]
# 将原图分别做上、下、左、右平移操作
Downshifted25 = translate(img, 0, 25)
cv2.imwrite(dir + '/' + name + '_Downshifted25.' + filetype, Downshifted25) # 保存
Upshifted25 = translate(img, 0, -25)
cv2.imwrite(dir + '/' + name + '_Upshifted25.' + filetype, Upshifted25) # 保存
Rightshifted25 = translate(img, 25, 0)
cv2.imwrite(dir + '/' + name + '_Rightshifted25.' + filetype, Rightshifted25) # 保存
Leftshifted25 = translate(img, -25, 0)
cv2.imwrite(dir + '/' + name + '_Leftshifted25.' + filetype, Leftshifted25) # 保存
Downshifted50 = translate(img, 0, 50)
cv2.imwrite(dir + '/' + name + '_Downshifted50.' + filetype, Downshifted50) # 保存
Upshifted50 = translate(img, 0, -50)
cv2.imwrite(dir + '/' + name + '_Upshifted50.' + filetype, Upshifted50) # 保存
Rightshifted50 = translate(img, 50, 0)
cv2.imwrite(dir + '/' + name + '_Rightshifted50.' + filetype, Rightshifted50) # 保存
Leftshifted50 = translate(img, -50, 0)
cv2.imwrite(dir + '/' + name + '_Leftshifted50.' + filetype, Leftshifted50) # 保存
Downshifted100 = translate(img, 0, 100)
cv2.imwrite(dir + '/' + name + '_Downshifted100.' + filetype, Downshifted100) # 保存
Upshifted100 = translate(img, 0, -100)
cv2.imwrite(dir + '/' + name + '_Upshifted100.' + filetype, Upshifted100) # 保存
Rightshifted100 = translate(img, 100, 0)
cv2.imwrite(dir + '/' + name + '_Rightshifted100.' + filetype, Rightshifted100) # 保存
Leftshifted100 = translate(img, -100, 0)
cv2.imwrite(dir + '/' + name + '_Leftshifted100.' + filetype, Leftshifted100) # 保存
tkinter.messagebox.showinfo('提示', '平移后的图片已经保存到了'+dir+'中!')
# 定义旋转rotate函数
def rotation(image, angle, center=None, scale=1.0):
# 获取图像尺寸
(h, w) = image.shape[:2]
# 若未指定旋转中心,则将图像中心设为旋转中心
if center is None:
center = (w / 2, h / 2)
# 执行旋转
M = cv2.getRotationMatrix2D(center, angle, scale)
rotated = cv2.warpAffine(image, M, (w, h))
# 返回旋转后的图像
return rotated
def pic_rotation():
if not filenames:
tkinter.messagebox.showinfo('提示', '请先选择图片才能进行图片旋转!')
if not os.path.exists(dir):
os.makedirs(dir)
if filenames:
for filename in filenames:
if filename:
img = cv2.imread(filename)
newFile = filename.split('/')[-1]
name = newFile.split('.')[0]
filetype = newFile.split('.')[-1]
# 将原图分别做旋转操作
Rotated15Degrees = rotation(img, 15)
cv2.imwrite(dir + '/' + name + '_Rotated15Degrees.' + filetype, Rotated15Degrees) # 保存
Rotated30Degrees = rotation(img, 30)
cv2.imwrite(dir + '/' + name + '_Rotated30Degrees.' + filetype, Rotated30Degrees) # 保存
Rotated45Degrees = rotation(img, 45)
cv2.imwrite(dir + '/' + name + '_Rotated45Degrees.' + filetype, Rotated45Degrees) # 保存
Rotated60Degrees = rotation(img, 60)
cv2.imwrite(dir + '/' + name + '_Rotated60Degrees.' + filetype, Rotated60Degrees) # 保存
Rotated75Degrees = rotation(img, 75)
cv2.imwrite(dir + '/' + name + '_Rotated75Degrees.' + filetype, Rotated75Degrees) # 保存
Rotated90Degrees = rotation(img, 90)
cv2.imwrite(dir + '/' + name + '_Rotated90Degrees.' + filetype, Rotated90Degrees) # 保存
# Rotated180Degrees = rotation(img, 180)
# cv2.imwrite(dir + '/' + name + '_Rotated180Degrees.' + filetype, Rotated180Degrees) # 保存
Rotated105Degrees = rotation(img, 105)
cv2.imwrite(dir + '/' + name + '_Rotated105Degrees.' + filetype, Rotated105Degrees) # 保存
Rotated120Degrees = rotation(img, 120)
cv2.imwrite(dir + '/' + name + '_Rotated120Degrees.' + filetype, Rotated120Degrees) # 保存
Rotated135Degrees = rotation(img, 135)
cv2.imwrite(dir + '/' + name + '_Rotated135Degrees.' + filetype, Rotated135Degrees) # 保存
Rotated150Degrees = rotation(img, 150)
cv2.imwrite(dir + '/' + name + '_Rotated150Degrees.' + filetype, Rotated150Degrees) # 保存
Rotated165Degrees = rotation(img, 165)
cv2.imwrite(dir + '/' + name + '_Rotated165Degrees.' + filetype, Rotated165Degrees) # 保存
Rotated180Degrees = rotation(img, 180)
cv2.imwrite(dir + '/' + name + '_Rotated180Degrees.' + filetype, Rotated180Degrees) # 保存
Rotated195Degrees = rotation(img, 195)
cv2.imwrite(dir + '/' + name + '_Rotated195Degrees.' + filetype, Rotated195Degrees) # 保存
Rotated210Degrees = rotation(img, 210)
cv2.imwrite(dir + '/' + name + '_Rotated210Degrees.' + filetype, Rotated210Degrees) # 保存
Rotated225Degrees = rotation(img, 225)
cv2.imwrite(dir + '/' + name + '_Rotated225Degrees.' + filetype, Rotated225Degrees) # 保存
Rotated240Degrees = rotation(img, 240)
cv2.imwrite(dir + '/' + name + '_Rotated240Degrees.' + filetype, Rotated240Degrees) # 保存
Rotated255Degrees = rotation(img, 255)
cv2.imwrite(dir + '/' + name + '_Rotated255Degrees.' + filetype, Rotated255Degrees) # 保存
Rotated270Degrees = rotation(img, 270)
cv2.imwrite(dir + '/' + name + '_Rotated270Degrees.' + filetype, Rotated270Degrees) # 保存
Rotated285Degrees = rotation(img, 285)
cv2.imwrite(dir + '/' + name + '_Rotated285Degrees.' + filetype, Rotated285Degrees) # 保存
Rotated300Degrees = rotation(img, 300)
cv2.imwrite(dir + '/' + name + '_Rotated300Degrees.' + filetype, Rotated300Degrees) # 保存
Rotated315Degrees = rotation(img, 315)
cv2.imwrite(dir + '/' + name + '_Rotated315Degrees.' + filetype, Rotated315Degrees) # 保存
Rotated330Degrees = rotation(img, 330)
cv2.imwrite(dir + '/' + name + '_Rotated330Degrees.' + filetype, Rotated330Degrees) # 保存
Rotated345Degrees = rotation(img, 345)
cv2.imwrite(dir + '/' + name + '_Rotated345Degrees.' + filetype, Rotated345Degrees) # 保存
# RotatedNeg15Degrees = rotation(img, -15)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg15Degrees.' + filetype, RotatedNeg15Degrees) # 保存
# RotatedNeg30Degrees = rotation(img, -30)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg30Degrees.' + filetype, RotatedNeg30Degrees) # 保存
# RotatedNeg45Degrees = rotation(img, -45)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg45Degrees.' + filetype, RotatedNeg45Degrees) # 保存
# RotatedNeg60Degrees = rotation(img, -60)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg60Degrees.' + filetype, RotatedNeg60Degrees) # 保存
# RotatedNeg75Degrees = rotation(img, -75)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg75Degrees.' + filetype, RotatedNeg75Degrees) # 保存
# RotatedNeg90Degrees = rotation(img, -90)
# cv2.imwrite(dir + '/' + name + '_RotatedNeg90Degrees.' + filetype, RotatedNeg90Degrees) # 保存
tkinter.messagebox.showinfo('提示', '旋转后的图片已经保存到了'+dir+'中!')
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
# 初始化缩放比例,并获取图像尺寸
dim = None
(h, w) = image.shape[:2]
# 如果宽度和高度均为0,则返回原图
if width is None and height is None:
return image
# 宽度是空
if width is None:
# 则根据高度计算缩放比例
r = height / float(h)
dim = (int(w * r), height)
# 如果高度为空
else:
# 根据宽度计算缩放比例
r = width / float(w)
dim = (width, int(h * r))
# 缩放图像
resized = cv2.resize(image, dim, interpolation=inter)
# 返回缩放后的图像
return resized
# 创建插值方法数组
methods = [
# ("cv2.INTER_NEAREST", cv2.INTER_NEAREST),
("cv2.INTER_LINEAR", cv2.INTER_LINEAR),
# ("cv2.INTER_AREA", cv2.INTER_AREA),
# ("cv2.INTER_CUBIC", cv2.INTER_CUBIC),
# ("cv2.INTER_LANCZOS4", cv2.INTER_LANCZOS4)
]
def pic_resize():
if not filenames:
tkinter.messagebox.showinfo('提示', '请先选择图片才能进行图片缩放!')
if not os.path.exists(dir):
os.makedirs(dir)
if filenames:
for filename in filenames:
if filename:
img = cv2.imread(filename)
newFile = filename.split('/')[-1]
name = newFile.split('.')[0]
filetype = newFile.split('.')[-1]
# 将原图做缩放操作
for (resizeTpye, method) in methods:
Resized2Times = resize(img, width=img.shape[1] * 2, inter=method)
cv2.imwrite(dir + '/' + name + '_Resized2Times.' + filetype, Resized2Times) # 保存
Resized3Times = resize(img, width=img.shape[1] * 3, inter=method)
cv2.imwrite(dir + '/' + name + '_Resized3Times.' + filetype, Resized3Times) # 保存
ResizedAHalf = resize(img, width=img.shape[1] //2, inter=method)
cv2.imwrite(dir + '/' + name + '_ResizedAHalf.' + filetype, ResizedAHalf) # 保存
ResizedOneThird = resize(img, width=img.shape[1] // 3, inter=method)
cv2.imwrite(dir + '/' + name + '_ResizedOneThird.' + filetype, ResizedOneThird) # 保存
tkinter.messagebox.showinfo('提示', '缩放后的图片已经保存到了'+dir+'中!')
def pic_flip():
if not filenames:
tkinter.messagebox.showinfo('提示', '请先选择图片才能进行图片翻转!')
if not os.path.exists(dir):
os.makedirs(dir)
if filenames:
for filename in filenames:
if filename:
img = cv2.imread(filename)
newFile = filename.split('/')[-1]
name = newFile.split('.')[0]
filetype = newFile.split('.')[-1]
# 将原图分别做翻转操作
Horizontallyflipped = cv2.flip(img, 1)
cv2.imwrite(dir + '/' + name + '_Horizontallyflipped.' + filetype, Horizontallyflipped) # 保存
Verticallyflipped = cv2.flip(img, 0)
cv2.imwrite(dir + '/' + name + '_Verticallyflipped.' + filetype, Verticallyflipped) # 保存
HorizontallyAndVertically = cv2.flip(img, -1)
cv2.imwrite(dir + '/' + name + '_HorizontallyAndVertically.' + filetype, HorizontallyAndVertically) # 保存
tkinter.messagebox.showinfo('提示', '翻转后的图片已经保存到了'+dir+'中!')
def mouse(event, x, y, flags, param):
image = param[0]
pts1 = param[1]
pts2 = param[2]
if event == cv2.EVENT_LBUTTONDOWN:
pts1.append([x, y])
xy = "%d,%d" % (x, y)
cv2.circle(image, (x, y), 4, (0, 255, 255), thickness = -1)
cv2.putText(image, xy, (x, y), cv2.FONT_HERSHEY_PLAIN,
1.0, (0, 255, 255), thickness = 2)
cv2.imshow("image", image)
if event == cv2.EVENT_RBUTTONDOWN:
pts2.append([x, y])
xy = "%d,%d" % (x, y)
cv2.circle(image, (x, y), 4, (255, 0, 255), thickness = -1)
cv2.putText(image, xy, (x, y), cv2.FONT_HERSHEY_PLAIN,
1.0, (255, 0, 255), thickness = 2)
cv2.imshow("image", image)
def pic_perspective():
if filenames:
for filename in filenames:
if filename:
print("file:", filename)
image = cv2.imread(filename)
# 原图中卡片的四个角点
cv2.namedWindow("image")
tips_str = "Left to right, top to bottom\nLeft click the original image\nRight click the target"
y0, dy = 20, 20
for i, line in enumerate(tips_str.split('\n')):
y = y0 + i*dy
cv2.putText(image, line, (2, y), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, 2)
cv2.imshow("image", image)
pts1 = []
pts2 = []
cv2.setMouseCallback("image", mouse, param=(image, pts1, pts2))
cv2.waitKey(0)
cv2.destroyAllWindows()
print("pts1:", pts1)
pts1 = np.float32(pts1[:4])
print("pts2:", pts2)
pts2 = np.float32(pts2[:4])
assert len(pts1)==4, "每个只允许四个点"
# 生成透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)
# 进行透视变换
dst = cv2.warpPerspective(image, M, (image.shape[1], image.shape[0]))
cv2.imwrite('dst.jpg', dst)
# matplotlib默认以RGB通道显示,所以需要用[:, :, ::-1]翻转一下
plt.subplot(121), plt.imshow(image[:, :, ::-1]), plt.title('input')
plt.subplot(122), plt.imshow(dst[:, :, ::-1]), plt.title('output')
plt.show()
# tkinter.messagebox.showinfo('提示', '透视变换的图片处理完毕!')
return dst
root = tkinter.Tk()
root.title('批量处理')
button = tkinter.Button(root, text="上传图片", command=get_files,width=20,height=2)
button.grid(row=0, column=0, padx=180, pady=20)
button1 = tkinter.Button(root, text="图片平移", command=pic_translate,width=20,height=2)
button1.grid(row=1, column=0, padx=1, pady=1)
button2 = tkinter.Button(root, text="图片旋转", command=pic_rotation,width=20,height=2)
button2.grid(row=2, column=0, padx=1, pady=1)
button3 = tkinter.Button(root, text="图片缩放", command=pic_resize,width=20,height=2)
button3.grid(row=3, column=0, padx=1, pady=1)
button4 = tkinter.Button(root, text="图片翻转", command=pic_flip,width=20,height=2)
button4.grid(row=4, column=0, padx=1, pady=1)
button4 = tkinter.Button(root, text="透视变换", command=pic_perspective,width=20,height=2)
button4.grid(row=5, column=0, padx=1, pady=1)
root.geometry('500x400+600+300')
root.mainloop()