Python OpenCV实现任意角度二维码矫正
本文介绍利用Python和OpenCV库实现任意角度二维码图像矫正的方法。通过识别二维码角点计算倾斜角度,对原图进行旋转处理,从而校正文字方向。文章包含具体思路、斜率计算公式及基于pyzbar和imutils的代码实现示例,适用于需要批量处理倾斜图片的场景。
前言
近日听到领导团队讨论图片矫正处理方案,并邀请算法部门专家参与研讨,讨论过程十分热烈。
好奇心驱使我关注到了这一话题。
观察后我认为,作为Java开发人员,我也能尝试处理此类问题。
通过观察图片,我发现了一个关键特征点:
待矫正的图片中均包含二维码图案,且需要矫正的文字与二维码图案处于同一水平线上。
如下面这个

要矫正图片中的“水中加点糖”四个字,只需对二维码进行矫正即可。
具体思路是:求出二维码矫正的角度a,然后对原图整体按照角度a进行旋转。
有了想法后,便趁热打铁,利用周末时间在家进行尝试。
一般图片矫正方式
对于一般的图片矫正,最常见的做法有这么两种:
对图片进行预处理获取出轮廓,并求得轮廓的近似矩形,通过矩形的定位点来进行透视变换
对图片进行预处理后,进行霍夫变换进行直线检测,再根据直线的倾斜角进行旋转
但是对于图片中有二维码的图片进行矫正就可以更加简单了,因为二维码中有定位点并且成熟框架很多,实现起来也更加方便且识别率非常高。
二维码图片矫正
思路
识别出二维码的角点,通过相邻两个角点的坐标计算出夹角度数,再次用此度数对图片进行旋转。
以下面图为例:

先获取出二维码正方向时底部的两点坐标,并求得两点的倾斜角。
斜率计算用初中数学中求两点坐标斜率的公式算一下即可,忘了就搜搜回忆一下:
两点的斜率公式:k=(y1-y2)/(x1-x2),x1≠x2。其中(x1,y1),(x2,y2)是已知两点的坐标,x1≠x2。
斜率是表示一条直线(或曲线的切线)关于(横)坐标轴倾斜程度的量。它通常用直线(或曲线的切线)与(横)坐标轴夹角的正切,或两点的纵坐标之差与横坐标之差的比来表示。记作k,k=tgα。
一条直线与某平面直角坐标系横坐标轴正半轴方向所成的角的正切值即该直线相对于该坐标系的斜率。如果直线与x轴互相垂直,直角的正切值为tan90°,故此直线不存在斜率(也可以说直线的斜率为无穷大)。当直线L的斜率存在时,对于一次函数y=kx+b(斜截式),k即该函数图像的斜率。
编码实现
实现时对于二维码的识别用到了pyzbar库,对图片处理用的opencv包
"""
author: puhaiyang
blog: https://blog.csdn.net/puhaiyang
github: https://github.com/puhaiyang
"""
import math
import cv2
from pyzbar import pyzbar
import imutils
def azimuthangle(x1, y1, x2, y2):
""" 已知两点坐标计算角度 -
:param x1: 原点横坐标值
:param y1: 原点纵坐标值
:param x2: 目标点横坐标值
:param y2: 目标纵坐标值
"""
dx = x2 - x1
dy = y2 - y1
# 求斜率
k = dy / dx
# 结果是弧度值
angle = math.atan(k)
# 弧度值转为角度
return angle * 180 / math.pi
def get_angle(qr_item):
"""
获取出进行矫正所需要的角度
"""
# 将坐标从下到上,从左到右进行排序
locs = {qr_item.polygon[0], qr_item.polygon[1], qr_item.polygon[2], qr_item.polygon[3]}
locs = sorted(locs, key=lambda x: x.y * 100000 + x.x * 1000)
return azimuthangle(locs[2].x, locs[2].y, locs[3].x, locs[3].y)
def to_up_angle(qr_item):
"""
获取出使二维码朝上的角度
"""
if qr_item.orientation == 'UP':
angle_ext = 0
elif qr_item.orientation == 'RIGHT':
angle_ext = 270
elif qr_item.orientation == 'DOWN':
angle_ext = 180
else:
angle_ext = 90
return angle_ext
def resize_img(ori_img):
"""
图片压缩
"""
height = ori_img.shape[0]
width = ori_img.shape[1]
# 执行压缩,按照500的宽度为标准
if width > 500:
scale_percent = int(500 / width * 100)
s_width = int(width * scale_percent / 100)
s_height = int(height * scale_percent / 100)
# 新的宽度和高度
dim = (s_width, s_height)
return cv2.resize(ori_img, dim, interpolation=cv2.INTER_AREA)
else:
return ori_img
def adjust_rotae_angle(img):
angle = 0
# 对图片进行压缩
img = resize_img(img)
# symbol为64代表二维码
qr_result = pyzbar.decode(img, symbols=[64])
if len(qr_result) == 1:
# 识别到了一个二维码,将二维码朝上旋转
first_adjust_angle = to_up_angle(qr_result[0])
# 进行旋转
img_rotae_to_up = imutils.rotate_bound(img, first_adjust_angle)
# 再次识别
qr_result2 = pyzbar.decode(img_rotae_to_up, symbols=[64])
if len(qr_result2) == 1:
last_adjust_angle = -get_angle(qr_result2[0])
angle = first_adjust_angle + last_adjust_angle
print("first angle:%d last angle:%d angle:%d" % (first_adjust_angle, last_adjust_angle, angle))
else:
print('last 未识别到二维码')
else:
print('first 未识别到二维码')
return angle
if __name__ == '__main__':
# 加载图片
img = cv2.imread('123.jpg')
adjust_angle = adjust_rotae_angle(img.copy())
if adjust_angle != 0:
img_rotae = imutils.rotate_bound(img, adjust_angle)
cv2.imwrite('img_rotae.jpg', img_rotae)
最终输出的图片结果:

矫正成功!
需要说明的是,上面之所以要进行对图片的resize,是因为图片太大的话pyzbar可能会识别不出来二维码
到此这篇关于Python OpenCV实现任意角度二维码矫正的文章就介绍到这了,更多相关Python OpenCV二维码矫正内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













