基于opencv的交互式-透视变换(perspective transformation)附代码


文章目录

  • 基于opencv的交互式-透视变换(perspective transformation)附代码
  • 前言:
  • 参考链接:
  • 简介:
  • 单应性变换必须四点共面!
  • 效果:
  • 代码:


前言:

最近在尝试透视变换的实际作用,看上篇paper也是提到了透视变换,但是一直不知道实际操作,到底有什么样的效果:
一整张图,选定四个点,以及将要变换的四个点,之后对整个画面的变换会有多大畸变。
畸变后的图像能不能用来做识别任务?

在github上找了一圈,有一个大佬的做了对图像处理可视化的项目,但是刚好透视变换没做。
我在他的基础上加了一个透视变换的功能,并且加上了鼠标交互的操作。

参考链接:

参考链接:https://github.com/dapsjj/dealAllPicture

简介:

python处理图片,包括图片平移、图片旋转、图片缩放、图片翻转、透视变换。选择图片中的四个关键点和将要变换的点,用来生成新的透视图

Use translate,scale,flip,rotation to turn one picture into multiple pictures.

单应性变换必须四点共面!

四点必须共面,否则无法提供8值约束!
进行ptc变换也是会错乱!

效果:

基于opencv的疲劳检测 perspectivetransform opencv_计算机视觉


基于opencv的疲劳检测 perspectivetransform opencv_opencv_02


基于opencv的疲劳检测 perspectivetransform opencv_图像识别_03

基于opencv的疲劳检测 perspectivetransform opencv_计算机视觉_04

代码:

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()