前言

这个是我 的大创项目。当我们拿到一份数据集,首先就是要对整个项目有个较为清晰的认识,整体的思路是什么,难点在哪,怎么部署和实现。

1.整体的思路

①先通过目标检测网络(比如:yolov5等)识别项目中图像中需要识别的字符区域

②其次再使用ocr相关的技术对于字符区域进行识别

③相应的后处理操作,数据库操作以及部署方式

Java 集装箱PaddleOCR识别_数据集

2.首先我想先介绍一下我们需要是别的区域及其字符的意义

Java 集装箱PaddleOCR识别_ide_02


3.遇到的难点

①严重曝光过度

Java 集装箱PaddleOCR识别_ide_03


当然也有出现根本无法挽回的曝光(全屏白色)!!!‘

解决办法:
OpenCV使用直方图均衡,修正曝光过度

②集装箱平面不平整带来的字符弯曲
解决办法:提高ocr字符识别的泛化能力

③出于落地性的考量,模型一定要尽可能轻量化 解决办法:
使用较为轻量化的backbone
剪枝操作
蒸馏操作

1.目标检测网络

目标识别网络我用了当时(v6,v7还没出)还比较流行性和实用性比较高的Yolov5

(1)数据清洗和标注

我们数据集的数量是16000多张,而且整体的标注上不和我心意,所以我就重新搞了一下
16000多张一张一张标注显然不现实,我的想法就是半自动标注感兴趣的可以看一下这个

(2)模型改进

因为16000张,不做任何改进训练都要两天,所以我取了4500张左右作为训练集,500张左右val 做了个demo
下面写一下实际上能使用的:
(1)替换 backbone(MobileNet V3…)
(2)更换激活函数
(3)更换iou
(4)加入注意集中机制

2.ocr

这个部分我一开始是通过yolov5识别到的图像的坐标点直接输入ocr识别网络进行识别。但是我发现现在的ocr架构基本上都是“检测+识别”,因此我就面临三个方向:
①改代码

②使用非端对端的架构
个人不喜欢非端对端,部署上有困难
③推倒重来

2.1 面临的的问题就是:

2.1.1如何识别坐标点给出的roi区域?

a)首先要先把yolov5返回识别框的位置信息(对角两点 or 左上点+矩形长宽)

不知道怎么找到左上角右下角的可以看看这篇找到绘制识别框的左上右下点

b)使用ocr识别字体

一个比较直观的想法就是直接在box_label里面使用ocr,但是在检测的代码中需要多假加入一个参数

法一:EASYOCR

Java 集装箱PaddleOCR识别_调优_04

我这里使用的是easyocr

def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
        # Add one xyxy box to image with label
        if self.pil or not is_ascii(label):
            self.draw.rectangle(box, width=self.lw, outline=color)  # box
            if label:
                w, h = self.font.getsize(label)  # text width, height
                outside = box[1] - h >= 0  # label fits outside box
                self.draw.rectangle((box[0],
                                     box[1] - h if outside else box[1],
                                     box[0] + w + 1,
                                     box[1] + 1 if outside else box[1] + h + 1), fill=color)
                # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0
                self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
        else:  # cv2

            p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
            #ocr操作
            #左上角(int(box[0]), int(box[1])),右下角(int(box[2]), int(box[3])
            width=abs(int(box[2])-int(box[0]))
            height=abs(int(box[1])-int(box[3]))
            import easyocr
            imgCrop = self.im[int(box[1]):int(box[1])+height, int(box[0]):int(box[0])+width].copy()
            #可以把这里写成接口函数,就可以试试不同ocr的效果
            reader = easyocr.Reader(['en'],gpu = False)
            result = reader.readtext(imgCrop)
            print(result)

Java 集装箱PaddleOCR识别_ide_05


可以识别但是会因为字符相似性产生较大误差。

法二:cnocr
def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
        # Add one xyxy box to image with label
        if self.pil or not is_ascii(label):
            self.draw.rectangle(box, width=self.lw, outline=color)  # box
            if label:
                w, h = self.font.getsize(label)  # text width, height
                outside = box[1] - h >= 0  # label fits outside box
                self.draw.rectangle((box[0],
                                     box[1] - h if outside else box[1],
                                     box[0] + w + 1,
                                     box[1] + 1 if outside else box[1] + h + 1), fill=color)
                # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0
                self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
        else:  # cv2

            p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
            #ocr操作
            #左上角(int(box[0]), int(box[1])),右下角(int(box[2]), int(box[3])
            width=abs(int(box[2])-int(box[0]))
            height=abs(int(box[1])-int(box[3]))

            imgCrop = self.im[int(box[1]):int(box[1])+height, int(box[0]):int(box[0])+width].copy()
            #可以把这里写成接口函数,就可以试试不同ocr的效果

            ########easyocr#########
            # import easyocr
            # reader = easyocr.Reader(['en'],gpu = False)
            # result = reader.readtext(imgCrop)
            # print(result)


            #######cnocr
            from cnocr import CnOcr
            ocr = CnOcr(rec_model_name='en_number_mobile_v2.0')
            out = ocr.ocr(imgCrop)
            print(out)

整体效果完全可以!!!

Java 集装箱PaddleOCR识别_数据集_06

法三:数字识别模型

2.1.2 调优或者重新训练

调优的话,当然是包括3个方面:(1)数字区域识别 (2)ocr (3)后处理
首先检查一下当前detect的效果:

2.1.2.1 在数字区域识别上:

(1)覆盖区域不全影响检测效果
(2)竖式排布效果差检测
我的想法是通过opencv仿射变换增加竖直图像排布的图片

Java 集装箱PaddleOCR识别_调优_07


上诉两个问题刚好是同一张图(3)小概率的误识别

Java 集装箱PaddleOCR识别_ide_08


Java 集装箱PaddleOCR识别_ide_09

一个比较直观的想法就是调高阈值以及增加训练集

2.1.2.2数字的识别

对于数字的识别上:
(1)竖直
(2)模糊,重影
(3)破损,拍摄不全导致的字母不全

Java 集装箱PaddleOCR识别_Java 集装箱PaddleOCR识别_10


Java 集装箱PaddleOCR识别_数据集_11

当然对于一些数字的识别效果比较差,既然选定使用cnocr现在的主要方法就是如何进一步优化!!!
训练自己的数据集是必须的了

2.1.2.3后处理

我的目标效果是:

Java 集装箱PaddleOCR识别_调优_12


对于正对的图片确实可以取得比较好的效果,但是由于曝光,数字破损等原因,效果会变差。

因此后处理除了对ocr获得的数字进行分类输出之外,更重要的是进行一些opencv的图像操作以提高图片数字的可识别性能。

(1)对于曝光过度的后处理策略:灰度拉伸
def grey_scale(image):
    img_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    rows, cols = img_gray.shape
    flat_gray = img_gray.reshape((cols * rows)).tolist()
    min1 = min(flat_gray)
    max1 = max(flat_gray)
    print('min = %d,max = %d' % (min1, max1))
    output = np.uint8(255 / (max1 - min1) * (img_gray - min1) + 0.5)
    return output

Java 集装箱PaddleOCR识别_调优_13


img2就是进行了灰度拉伸清晰度upup,因为字体和背景底色不确定因此难以进一步进行腐蚀和膨胀

(2)直方图均衡化
def balance(src):
    (b, g, r) = cv2.split(src)
    # 对三个通道都进行均衡化
    bH = cv2.equalizeHist(b)
    gH = cv2.equalizeHist(g)
    rH = cv2.equalizeHist(r)
    # 最后合并
    result = cv2.merge((bH, gH, rH))
    return result
(3) 通过仿射变换矫正图像
2.1.2.4训练自己的数据集

详细介绍!!!

Java 集装箱PaddleOCR识别_ide_14


他的文件标注格式好像是可以是txt。其中写入放入训练集

①每一张图片的名称

②用\t分割

③对应的文字。但每一个文字要用单个空格分割,含有空格的话就要用代替

Java 集装箱PaddleOCR识别_数据集_15


我的训练集中的数量确实是可以做一个数据集的但是我一个人做确实是有点扛不住,所以我的想法是直接在“en_number_mobile_v2.0”这个模型上面调优以增强其泛化性

3. 最后

做完之后,我们最后是决定整体使用paddle来做(手动难过