由于之前使用easyocr识别图片的时候发现旋转的图片或者倒置的图片效果很差,来利用 cv2.minAreaRect()获取旋转角度,只能解决0-90,对于倒置的图片不能很好解决,因此使用paddleocr中方向分类检测(只能返回0,180)刚好可以用于倒置的图片判断。(当前问题仅仅考虑的是横向文本,不考虑纵向文本,由于paddleocr中方向分类检测0,180也不是100%准确。旋转矫正效果不是100%)
案例演示
第一步:cv2.minAreaRect()获取旋转角度
cv2.minAreaRect()在opencv中老版本返回的是-90到0,新版本0到90,当前使用的是新版处理方式
#获取旋转角度
def get_angle(result):
longs = [] # 记录文本框的长度
weights = [] # 记录文本框的长度*angle
angles = []
for line in result:
points = line.get('box')
# angles.append(int(line.get('angle')[0]))
# print("文本框的坐标:", points, line.get('text'), line.get('angle'))
# 计算角度(示例中为简单的两点计算)
# 假设 points 是四个点的坐标
x1, y1 = points[0]
x2, y2 = points[1]
x3, y3 = points[2]
x4, y4 = points[3]
# print(x1, y1, x2, y2, x3, y3, x4, y4)
# 根据4个点坐标求最小外接矩阵
cnt = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], dtype=np.float32) # 必须是array数组的形式
rect = cv.minAreaRect(cnt) # 得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
box = cv.boxPoints(rect) # cv2.boxPoints(rect) for OpenCV 3.x 获取最小外接矩形的4个顶点坐标
# box = np.int0(box)
box = np.intp(box)
# 旋转角度
width, height = cv.minAreaRect(cnt)[1]
# print(width, height)
# 示例矩形点
# rect_points = [[2240.0, 1214.0], [3120.0, 1214.0], [3120.0, 1338.0], [2240.0, 1338.0]]
is_skewed = is_rectangle_skewed(points)
if is_skewed:
# 矩形倾斜
pass
else:
x_w = x2 - x1
y_h = y3 - y2
if x_w > y_h:
width, height = y_h, x_w
else:
width, height = y_h, x_w
theta = cv.minAreaRect(cnt)[2] # (0,90)
if width < height: # 切换新版本
if theta == 90 or theta == 0: # 长方形正常的时候返回的角度,0获取90,默认就是没有转
angle = 0
else:
angle = theta - 90
# print("angle1", angle)
longs.append(height)
angles.append(angle)
else:
if theta == 90 or theta == 0: # 长方形垂直的时候返回的角度,0获取90,默认就是转90
angle = 90
else:
angle = theta
# print("angle2", angle)
angles.append(angle)
longs.append(width)
# weights.append(angle * width)
weights = [i * abs(j) for i, j in zip(longs, angles)]
angle_s = (sum(weights) / sum(longs))
# 判断正负
is_numbers = len([num for num in angles if num > 0])
ne_numbers = len([num for num in angles if num < 0])
if is_numbers < ne_numbers:
angle_s = -angle_s
# print("angle_s", angle_s)
all_angles=[int(i.get("angle")[0]) for i in result] #paddleocr给的整体旋转角度(0,180)
median_angle = np.median(all_angles) # 中位数抗噪性更好
# print(median_angle)
median_angle_0 = all_angles.count(0)
if median_angle_0 / len(all_angles) >= 0.4:
median_angle = 0
res_angle=median_angle+angle_s
# print(median_angle)
# print(res_angle)
return res_angle
第二步:使用paddleocr进行判断角度是0还是180
因为安装paddleocr太麻烦了,当前使用是imgocr调用的是paddleocr的模型
import sys,cv2,os
import numpy as np
from imgocr import ImgOcr
from imgocr import draw_ocr_boxes
import shutil
from get_paddleocr_angle import get_angle
## 图片旋转
def rotate_image(image, angle):
# 获取图像的高度和宽度
(h, w) = image.shape[:2]
# 计算图像的中心
(cX, cY) = (w // 2, h // 2)
# 获取旋转矩阵,参数为旋转中心、旋转角度和缩放因子
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
# 计算旋转后图像的新宽度和高度
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# 计算旋转后的图像尺寸
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# 调整旋转矩阵,确保旋转后图像不被裁切
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# 使用 warpAffine 进行旋转
rotated_image = cv2.warpAffine(image, M, (nW, nH), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
return rotated_image
if __name__ == "__main__":
m = ImgOcr(use_gpu=True, is_efficiency_mode=True) #is_efficiency_mode=True表示使用移动端的模型
img_path = r"Snipaste_2025-02-21_180.png"
img_save_path = "temp.jpg"
# first_set_right
result = m.ocr(img_path, cls=True)
pic_angle = get_angle(result)
if abs(pic_angle) < 10:
shutil.copy(img_path, img_save_path)
else:
cv_img = cv2.imread(img_path)
rotate_img = rotate_image(cv_img, -pic_angle)
cv2.imwrite(img_save_path, rotate_img)
# second_set_right
img_path = img_save_path
result = m.ocr(img_path, cls=True)
pic_angle = get_angle(result)
if abs(pic_angle) < 10:
pass
else:
cv_img = cv2.imread(img_path)
rotate_img = rotate_image(cv_img, -pic_angle)
cv2.imwrite(img_save_path, rotate_img)
result_cls = m.ocr(img_path, cls=True)
draw_ocr_boxes(img_path, result_cls, 'temp_custom.jpg')
根据上面矫正之后的图片进行识别效果会更友好,其次大家也可以尝试使用easyocr和paddleocr的检测、识别功能混合使用进行测试。
代码、模型已经打包放入资源里了
当前个人部署服务器,体验地址:输入图片识别