基於opencv的交互式-透視變換(perspective transformation)附代碼
前言:
最近在嘗試透視變換的實際作用,看上篇paper也是提到了透視變換,但是一直不知道實際操作,到底有什麼樣的效果:
一整張圖,選定四個點,以及將要變換的四個點,之後對整個畫面的變換會有多大畸變。
畸變後的圖像能不能用來做識別任務?
在github上找了一圈,有一個大佬的做了對圖像處理可視化的項目,但是剛好透視變換沒做。
我在他的基礎上加了一個透視變換的功能,並且加上了鼠標交互的操作。
參考鏈接:
參考鏈接:https://github.com/dapsjj/dealAllPicture
簡介:
python處理圖片,包括圖片平移、圖片旋轉、圖片縮放、圖片翻轉、透視變換。選擇圖片中的四個關鍵點和將要變換的點,用來生成新的透視圖
Use translate,scale,flip,rotation to turn one picture into multiple pictures.
效果:
代碼:
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()