版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
#!/usr/bin/env python
# ---------ymd---------
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from PIL import Image
#获取旋转角度
def get_rotate_angle(image):
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray=cv2.bitwise_not(gray)
thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
coords=np.column_stack(np.where(thresh>0))
angle=cv2.minAreaRect(coords)[2]
return angle
#用Image模块旋转,字体会变模糊
def get_rotated_image(image_path,angle):
img=Image.open(image_path)
rotate=img.rotate(-angle,expand=1)
alpha=Image.new('RGBA',rotate.size,(255,)*4)
no_white_broder=Image.composite(rotate,alpha,rotate)
rotated=np.array(no_white_broder)
return rotated
#用矩阵旋转,字体不会变模糊。注意:warpAffine第三个参数(先列后行)。为防止信息丢失,W、H设置较大
def get_get_rotated_image1(image,angle):
(h,w)=image.shape[:2]
M=cv2.getRotationMatrix2D((h//2,w//2),-angle,1.0)
sin=np.abs(M[0,1])
cos=np.abs(M[0,0])
a=np.maximum(h,w)
W=int(a*sin+a*cos)
H=int(a*cos+a*sin)
M[0,2]=W/2-w//2
M[1,2]=H/2-h//2
rotated=cv2.warpAffine(image,M,(W,H),flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
return rotated
if __name__ == '__main__':
path='D:/10.png'
image=cv2.imread(path)
angle=get_rotate_angle(image)
#rotated_image=get_rotated_image(path,angle)
rotated_image1=get_get_rotated_image1(image,angle)
cv2.putText(rotated_image1,'angle:%.3f'%angle,(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
print('rotate angle=%d'%angle)
cv2.imshow('imput',image)
#cv2.imshow('output',rotated_image)
cv2.imshow('matrix_output',rotated_image1)
cv2.waitKey(20000)
详细注释:
获取旋转角度部分
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #转成单通道图片
gray = cv2.bitwise_not(gray) #将上图颠倒黑白
thresh = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
#二值化,cv2.THRESH_BINARY(黑白二值),cv2.THRESH_OTSU自动判断最佳阈值(适合灰度图片有双峰的情况),使用cv2.THRESH_OTSU时阈值设置必须为0
#cv2.threshold函数是有两个返回值的,(常用)第二个返回值:阈值处理后的图像,第一个返回值:得到图像的阈值
coords = np.column_stack(np.where(thresh > 0))
#print(coords),所有黑色点的点集(数组型)
cv2.minAreaRect(coords)
#返回一个rect:(最小外接矩形的中心(x,y),(宽度,高度),旋转角度)
#rect[0]:中心坐标 rect[1][0]:矩形宽 rect[1][1]:矩形高 rect[2]:旋转角度θ
#旋转角度θ是水平轴(x轴)逆时针旋转,与碰到的矩形的第一条边的夹角。并且这个边的边长是width,另一条边边长是height。
#矩形的4个顶点坐标box, 通过函数 cv2.cv.BoxPoints() 获得
矩阵法变换部分
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
#获得图像绕着某一点的旋转矩阵,为InputArray类型的2×3的变换矩阵。参数:(旋转中心,旋转角度,放缩因子)
#2×3变换矩阵的构成:[[cosθ -sinθ dx][sinθ cosθ dy]] θ为逆时针旋转的角度,dx,dy为中心沿x,y方向的平移量
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# 提取旋转矩阵 sin cos
a=np.maximum(h,w)
W=int(a*sin+a*cos)
H=int(a*cos+a*sin)
#定义输出图片大小,为防止图片信息缺失,输出图片比理论旋转后的图片大,理论旋转后的图片尺寸W = int((h * sin) + (w * cos)),H = int((h * cos) + (w * sin))。
M[0,2]+=W/2-w//2
M[1,2]+=H/2-h//2
#计算原图与输出图片的中心偏移量即2×3变换矩阵中的dx,dy
cv2.warpAffine(image, M, (nW, nH),flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
#cv2.warpAffine旋转图片
#参数(输入图像,变换矩阵(接收2*3的矩阵), 输出图像的大小(先列后行))
#flages表示插值方式,默认为 flags=cv2.INTER_LINEAR,表示线性插值,此外还有:cv2.INTER_NEAREST(最近邻插值) cv2.INTER_AREA (区域插值) cv2.INTER_CUBIC(三次样条插值) cv2.INTER_LANCZOS4(Lanczos插值)
#borderMode - 边界像素模式,cv2.BORDER_REPLICATE复制边界点扩充无像素区 borderValue -边界填充值; 默认情况下,它为None
主程序部分:
cv2.putText(rotated, 'angle:%.3f'%angle,
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
#putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None)
#img图像 text要输出的文本 org文字的起点坐标 fontFace字体 fontScale字体大小 color字体颜色 thickness字图加粗
其他说明:
1.本文算法主要参考来源:(https://blog.csdn.net/u010379996/article/details/83088946)
2.最小外接矩形生成原理:(https://blog.csdn.net/lanyuelvyun/article/details/76614872)
3.旋转矩阵原理:(https://baike.baidu.com/item/旋转矩阵)