这是我研究生的计算机视觉课程的一项作业,写完之后整理一下思路发出来,希望能对大家有所帮助。
整个项目所实现的功能是完成对ISBN图片印刷字符的识别,其功能模块可细分为字符分割与字符识别两个核心子功能。在该项目中,由于老师要求(使用传统方法,不能用现有分割网络一步到位),所以在分割模块使用的是传统的数字图像处理方法,识别模块是由三个“异质”的学习器集成学习系统。
在本次的项目实验中,共有训练集100张、测试集100张图片,经测试,最终识别的效果准确率为99.68%, 查全率为95%。具体实验代码将会在文章末尾中给出。
如果文中内容能帮对你有所帮助,请在文章和github给个赞!谢谢!
首先看下图数据集,该数据集是在课程中老师(在此感谢窦燕老师,老师非常好,也跟着学习了很多知识)所搜集的数据集。
可以明显发现,每张图片的名字就是图片条形码上方区域ISBN印刷数字的值,也即是我们训练所需要的Label。且不同图片之间存在图片倾斜、明暗度差异较大、图片模糊、存在干扰等情况。所以在实现图片的分割与识别之前,先要对图片进行预处理操作。
取数据集中一张图片为例:
这张图片相较于其他图片发生了明显的倾斜,这会对我们后面的处理造成不便,因此预处理的第一步要对原数据集中的所有图片进行水平矫正,矫正方法使用统计霍夫曼线性变换(不明白的同学可以查看推荐文章),其能够检测出一张图片中的所有直线部分,我们可以锁定一条基准直线并按照其在图片中的角度特点来对整张图片进行旋转,最终完成水平校正。
那么如何具体的实现这部分的功能呢,从代码实现角度来说,首先读取数据集中图片,将图片从BGR(Opencv默认读取方式为BGR模式)转换为灰度图。考虑到阴影、模糊等问题所带来的干扰,我们使用高斯滤波器进一步处理图像。然后通过Canny边缘检测算子提取图像的边缘轮廓,再通过统计霍夫曼线性变换来找到图片中存在的所有直线。最后在所有直线中找到我们所想要的基准直线,计算其角度,根据该基准直线与图片的关系来完成对图片的水平倾斜校正。水平校正后图像与具体代码如下所示:
def rotate(image, threshold, minLineLength, maxLineGap, flag=False):
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
blur_image = cv2.GaussianBlur(gray_image, (7, 7), 0)
edges = cv2.Canny(blur_image, 50, 150, apertureSize=3)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold, minLineLength=minLineLength, maxLineGap=maxLineGap)
height, width = image.shape[:2]
# 寻找目标线
target_x1, target_y1, target_x2, target_y2 = 0,0,0,0
for line in lines:
x1, y1, x2, y2 = line[0]
# cv2.line(image, (x1, y1), (x2, y2), (255, 0, 0), 1)
if abs(y1 - y2) < abs(x1 - x2) and y2 < height // 3 and y1 < height // 3 and abs(x2 - x1) >= width // 3 and abs(y1-y2)<20:
if y1 > target_y1:
target_x1, target_y1, target_x2, target_y2 = x1, y1, x2, y2
# 获取旋转角度
angle = cv2.fastAtan2(float((target_y2 - target_y1)), float((target_x2 - target_x1)))
angle = angle % 90
if angle > 45:
angle = angle - 90
rotate_mat = cv2.getRotationMatrix2D((width/2,height/2), angle, 1.0) # 获取旋转矩阵
background_color = abstract_background_color(gray_image)
rotate_image = cv2.warpAffine(image, rotate_mat, (width, height), borderValue=background_color)
# cv2.line(image, (target_x1, target_y1), (target_x2, target_y2), (0, 0, 255), 2)
# cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
return rotate_image
注:基准线的选取策略如下所示:红色是本文项目所实现的选取策略,其与上方ISBN印刷字有较好的水平关系。当然,选取绿线作为基准也是可行的。不过由于图片会因为拍摄角度的问题,可能会发生一定的图像畸变,所以还是推荐使用红线更准确一些!
本次先写到这里!到了午饭时间了!等有空了再把后面部分补上,完整代码已经放到github上,地址会在后面文章中给出,有问题的同学也可以在文章下面联系我。
由于刚开始学习,水平也有限,也欢迎大家的指正!