使用cv2将图片转正

要点:

参考:如何用Python-OpenCV实现图片倾斜调整?


一 图片转正

比如一张纸的照片是倾斜的,用OpenCV如何实现自动检测出纸的轮廓并调整倾斜角度,让照片变“正”。

import cv2
import numpy as np

def click(event,x,y,flags,param):
    if event==cv2.EVENT_LBUTTONDOWN:
        if len(pts)<4:
            pts.append([x,y])# 只记录前四次鼠标左击的位置
            cv2.circle(img,(x,y),1,(0,0,0))
            cv2.imshow('img1',img)
        else:
            cv2.destroyWindow('img1')# 第五次鼠标左击直接关闭图片窗口

img=cv2.imread('files.jpg')
pts=[]
cv2.namedWindow('img1')
cv2.setMouseCallback('img1',click)
cv2.imshow('img1',img)
cv2.waitKey(0)
pts.sort(reverse=False)
print(pts)
width,height=250,350
pts1=np.float32(pts)
pts2=np.float32([[0,0],[width,0],[0,height],[width,height]])
matrix=cv2.getPerspectiveTransform(pts1,pts2)
img2=cv2.warpPerspective(img,matrix,(width,height))
cv2.imshow('img2',img2)
cv2.waitKey(0)

二 透视变换demo

参考:python opencv实现图像矫正功能_python_脚本之家

import cv2
import numpy as np
 
img = cv2.imread('/home/pzs/图片/1.jpeg')
 
result3 = img.copy()
 
img = cv2.GaussianBlur(img,(3,3),0)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
 
edges = cv2.Canny(gray,50,150,apertureSize = 3)
cv2.imwrite("canny.jpg", edges)
 
src = np.float32([[207, 151], [517, 285], [17, 601], [343, 731]])
dst = np.float32([[0, 0], [337, 0], [0, 488], [337, 488]])
m = cv2.getPerspectiveTransform(src, dst)
result = cv2.warpPerspective(result3, m, (337, 488))
cv2.imshow("result", result)
cv2.waitKey(0)

2.1 如何找到目标的4个顶点

如何找到这4个顶点:
方法有很多种,如:直线检测,轮廓检测,最小外接矩形等。

使用轮廓检测方式:

import cv2
import imutils
 
img = cv2.imread('1.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
dilate = cv2.dilate(blurred, cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)))
edged = cv2.Canny(dilate, 30, 120, 3)            # 边缘检测
 
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测
cnts = cnts[0] if imutils.is_cv2() else cnts[1]  # 判断是opencv2还是opencv3
docCnt = None
 
if len(cnts) > 0:
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True) # 根据轮廓面积从大到小排序
    for c in cnts:
        peri = cv2.arcLength(c, True)                                       # 计算轮廓周长
        approx = cv2.approxPolyDP(c, 0.02*peri, True)           # 轮廓多边形拟合
        # 轮廓为4个点表示找到纸张
        if len(approx) == 4:
            docCnt = approx
            break
 
for peak in docCnt:
    peak = peak[0]
    cv2.circle(img, tuple(peak), 10, (255, 0, 0))
 
cv2.imshow('img', img)
cv2.waitKey(0)

2.2 cv2.approxPolyDP() 多边形逼近

重点讲解这个函数

作用:

对目标图像进行近似多边形拟合,使用一个较少顶点的多边形去拟合一个曲线轮廓,要求拟合曲线与实际轮廓曲线的距离小于某一阀值。

函数原形:

扫描二维码关注公众号,回复: 15146712 查看本文章

cv2.approxPolyDP(curve, epsilon, closed) -> approxCurve

参数:

curve : 图像轮廓点集,一般由轮廓检测得到
epsilon : 原始曲线与近似曲线的最大距离,参数越小,两直线越接近
closed : 得到的近似曲线是否封闭,一般为True

返回值:

approxCurve :返回的拟合后的多边形顶点集。

透视变换

之前都是通过仿射变换方式,转正后截取。

但是参考了 Cropping Rotated Rectangles from Image with OpenCV
这篇文章,主要就是运用了透视变换,非常巧妙

# -*- coding: utf-8 -*-
"""
   File Name:     rotation_v3
   Description :
   date:          2019/5/10
"""
import cv2
import numpy as np
import matplotlib.pylab as plt
import time

img=cv2.imread('3_75.jpg')
# img=img[14:-15,13:-14]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("num of contours: {}".format(len(contours)))


rect = cv2.minAreaRect(contours[1])  #获取蓝色矩形的中心点、宽高、角度


'''
retc=((202.82777404785156, 94.020751953125),
 (38.13406753540039, 276.02105712890625),
 -75.0685806274414)


width = int(rect[1][0])
height = int(rect[1][1])
angle = rect[2]
print(angle)

if width < height:  #计算角度,为后续做准备
  angle = angle - 90
print(angle)
'''
if rect[-1] < -45 or rect[-1] > 45:
	rect = (rect[0], (rect[1][1], rect[1][0]), rect[2] - 90)
angle = rect[2]
width = int(rect[1][0])
height = int(rect[1][1])
# if  angle < -45:
#     angle += 90.0
#        #保证旋转为水平
# width,height = height,width
src_pts = cv2.boxPoints(rect)

# box = cv2.boxPoints(rect)
# box = np.int0(box)
# cv2.drawContours(img_box, [box], 0, (0,255,0), 2) 

dst_pts = np.array([[0, height],
                    [0, 0],
                    [width, 0],
                    [width, height]], dtype="float32")
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(img, M, (width, height))

if angle<=-90:  #对-90度以上图片的竖直结果转正
    warped = cv2.transpose(warped)
    warped = cv2.flip(warped, 0)  # 逆时针转90度,如果想顺时针,则0改为1
    # warped=warped.transpose
cv2.imshow('wr1',warped)
cv2.waitKey(0)

仿射变换是二维变换,就是在一个平面上,可以通过三个定点,随意的拉拽到想要的形状,但是不能拿起来,所以他的投影还是他拉拽的本身。

透视变换 是三维变换,就是可以拿起来,随意平行、竖直、翻转一定角度等,就是可以拉拽4个定点,所以它的投影会变成不规则的形状,更高级一点。


四 根据任意矩形(四点坐标)截取指定区域图像

参考资料: 根据任意矩形(四点坐标)截取指定区域图像

def CutImgeByBox(output,box):
    """
    根据任意内接矩形,四点(顺时针)box[[x1,y2],[x2,y2],[x3,y3],[x4,y4]],从输入图像中,截取图像
    """
    if type(box)==type([]):    
        pts1 = np.float32(box)
        if pts1.shape==(4,2):
            # 变换后的矩形四个顶点坐标    
            dy=int(np.sqrt((box[0][0]-box[1][0])**2+(box[0][1]-box[1][1])**2))
            dx=int(np.sqrt((box[1][0]-box[2][0])**2+(box[1][1]-box[2][1])**2))   
            pts2 = np.float32([[1, dy+1],
                          [1, 1],
                          [1+dx, 1],
                          [1+dx, 1+dy]]) 
            M = cv2.getPerspectiveTransform(pts1,pts2)
            dst = cv2.warpPerspective(output, M, (output.shape[1],output.shape[0]))   
            target = dst[int(pts2[1][1]):int(pts2[0][1]),int(pts2[1][0]):int(pts2[2][0]),:]
            return True,target
        else:
            print("box shape is wrong ,must be list as (4,2)")
            return False,output
    else:
        print("box type is wrong,must be list as [[x1,y2],[x2,y2],[x3,y3],[x4,y4]]")
        return False,output

猜你喜欢

转载自blog.csdn.net/March_A/article/details/130714888