如何在OpenCV中运行自定义OCR模型

我们首先介绍如何获取自定义OCR模型,然后介绍如何转换自己的OCR模型以便能够被opencv_dnn模块正确运行,最后我们将提供一些预先训练的模型。

训练你自己的 OCR 模型

此存储库是训练您自己的 OCR 模型的良好起点。在存储库中,MJSynth+SynthText 默认设置为训练集。此外,您可以配置所需的模型结构和数据集。

将 OCR 模型转换为 ONNX 格式并在 OpenCV DNN 中使用它

完成模型训练后,请使用transform_to_onnx.py将模型转换为onnx格式。

在网络摄像头中执行

源码:

'''
    Text detection model: https://github.com/argman/EAST
    Download link: https://www.dropbox.com/s/r2ingd0l3zt8hxs/frozen_east_text_detection.tar.gz?dl=1

    CRNN Text recognition model taken from here: https://github.com/meijieru/crnn.pytorch
    How to convert from pb to onnx:
    Using classes from here: https://github.com/meijieru/crnn.pytorch/blob/master/models/crnn.py

    More converted onnx text recognition models can be downloaded directly here:
    Download link: https://drive.google.com/drive/folders/1cTbQ3nuZG-EKWak6emD_s8_hHXWz7lAr?usp=sharing
    And these models taken from here:https://github.com/clovaai/deep-text-recognition-benchmark

    import torch
    from models.crnn import CRNN

    model = CRNN(32, 1, 37, 256)
    model.load_state_dict(torch.load('crnn.pth'))
    dummy_input = torch.randn(1, 1, 32, 100)
    torch.onnx.export(model, dummy_input, "crnn.onnx", verbose=True)
'''


# Import required modules
import numpy as np
import cv2 as cv
import math
import argparse

############ Add argument parser for command line arguments ############
parser = argparse.ArgumentParser(
    description="Use this script to run TensorFlow implementation (https://github.com/argman/EAST) of "
                "EAST: An Efficient and Accurate Scene Text Detector (https://arxiv.org/abs/1704.03155v2)"
                "The OCR model can be obtained from converting the pretrained CRNN model to .onnx format from the github repository https://github.com/meijieru/crnn.pytorch"
                "Or you can download trained OCR model directly from https://drive.google.com/drive/folders/1cTbQ3nuZG-EKWak6emD_s8_hHXWz7lAr?usp=sharing")
parser.add_argument('--input',
                    help='Path to input image or video file. Skip this argument to capture frames from a camera.')
parser.add_argument('--model', '-m', required=True,
                    help='Path to a binary .pb file contains trained detector network.')
parser.add_argument('--ocr', default="crnn.onnx",
                    help="Path to a binary .pb or .onnx file contains trained recognition network", )
parser.add_argument('--width', type=int, default=320,
                    help='Preprocess input image by resizing to a specific width. It should be multiple by 32.')
parser.add_argument('--height', type=int, default=320,
                    help='Preprocess input image by resizing to a specific height. It should be multiple by 32.')
parser.add_argument('--thr', type=float, default=0.5,
                    help='Confidence threshold.')
parser.add_argument('--nms', type=float, default=0.4,
                    help='Non-maximum suppression threshold.')
args = parser.parse_args()


############ Utility functions ############

def fourPointsTransform(frame, vertices):
    vertices = np.asarray(vertices)
    outputSize = (100, 32)
    targetVertices = np.array([
        [0, outputSize[1] - 1],
        [0, 0],
        [outputSize[0] - 1, 0],
        [outputSize[0] - 1, outputSize[1] - 1]], dtype="float32")

    rotationMatrix = cv.getPerspectiveTransform(vertices, targetVertices)
    result = cv.warpPerspective(frame, rotationMatrix, outputSize)
    return result


def decodeText(scores):
    text = ""
    alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"
    for i in range(scores.shape[0]):
        c = np.argmax(scores[i][0])
        if c != 0:
            text += alphabet[c - 1]
        else:
            text += '-'

    # adjacent same letters as well as background text must be removed to get the final output
    char_list = []
    for i in range(len(text)):
        if text[i] != '-' and (not (i > 0 and text[i] == text[i - 1])):
            char_list.append(text[i])
    return ''.join(char_list)


def decodeBoundingBoxes(scores, geometry, scoreThresh):
    detections = []
    confidences = []

    ############ CHECK DIMENSIONS AND SHAPES OF geometry AND scores ############
    assert len(scores.shape) == 4, "Incorrect dimensions of scores"
    assert len(geometry.shape) == 4, "Incorrect dimensions of geometry"
    assert scores.shape[0] == 1, "Invalid dimensions of scores"
    assert geometry.shape[0] == 1, "Invalid dimensions of geometry"
    assert scores.shape[1] == 1, "Invalid dimensions of scores"
    assert geometry.shape[1] == 5, "Invalid dimensions of geometry"
    assert scores.shape[2] == geometry.shape[2], "Invalid dimensions of scores and geometry"
    assert scores.shape[3] == geometry.shape[3], "Invalid dimensions of scores and geometry"
    height = scores.shape[2]
    width = scores.shape[3]
    for y in range(0, height):

        # Extract data from scores
        scoresData = scores[0][0][y]
        x0_data = geometry[0][0][y]
        x1_data = geometry[0][1][y]
        x2_data = geometry[0][2][y]
        x3_data = geometry[0][3][y]
        anglesData = geometry[0][4][y]
        for x in range(0, width):
            score = scoresData[x]

            # If score is lower than threshold score, move to next x
            if (score < scoreThresh):
                continue

            # Calculate offset
            offsetX = x * 4.0
            offsetY = y * 4.0
            angle = anglesData[x]

            # Calculate cos and sin of angle
            cosA = math.cos(angle)
            sinA = math.sin(angle)
            h = x0_data[x] + x2_data[x]
            w = x1_data[x] + x3_data[x]

            # Calculate offset
            offset = ([offsetX + cosA * x1_data[x] + sinA * x2_data[x], offsetY - sinA * x1_data[x] + cosA * x2_data[x]])

            # Find points for rectangle
            p1 = (-sinA * h + offset[0], -cosA * h + offset[1])
            p3 = (-cosA * w + offset[0], sinA * w + offset[1])
            center = (0.5 * (p1[0] + p3[0]), 0.5 * (p1[1] + p3[1]))
            detections.append((center, (w, h), -1 * angle * 180.0 / math.pi))
            confidences.append(float(score))

    # Return detections and confidences
    return [detections, confidences]


def main():
    # Read and store arguments
    confThreshold = args.thr
    nmsThreshold = args.nms
    inpWidth = args.width
    inpHeight = args.height
    modelDetector = args.model
    modelRecognition = args.ocr

    # Load network
    detector = cv.dnn.readNet(modelDetector)
    recognizer = cv.dnn.readNet(modelRecognition)

    # Create a new named window
    kWinName = "EAST: An Efficient and Accurate Scene Text Detector"
    cv.namedWindow(kWinName, cv.WINDOW_NORMAL)
    outNames = []
    outNames.append("feature_fusion/Conv_7/Sigmoid")
    outNames.append("feature_fusion/concat_3")

    # Open a video file or an image file or a camera stream
    cap = cv.VideoCapture(args.input if args.input else 0)

    tickmeter = cv.TickMeter()
    while cv.waitKey(1) < 0:
        # Read frame
        hasFrame, frame = cap.read()
        if not hasFrame:
            cv.waitKey()
            break

        # Get frame height and width
        height_ = frame.shape[0]
        width_ = frame.shape[1]
        rW = width_ / float(inpWidth)
        rH = height_ / float(inpHeight)

        # Create a 4D blob from frame.
        blob = cv.dnn.blobFromImage(frame, 1.0, (inpWidth, inpHeight), (123.68, 116.78, 103.94), True, False)

        # Run the detection model
        detector.setInput(blob)

        tickmeter.start()
        outs = detector.forward(outNames)
        tickmeter.stop()

        # Get scores and geometry
        scores = outs[0]
        geometry = outs[1]
        [boxes, confidences] = decodeBoundingBoxes(scores, geometry, confThreshold)

        # Apply NMS
        indices = cv.dnn.NMSBoxesRotated(boxes, confidences, confThreshold, nmsThreshold)
        for i in indices:
            # get 4 corners of the rotated rect
            vertices = cv.boxPoints(boxes[i])
            # scale the bounding box coordinates based on the respective ratios
            for j in range(4):
                vertices[j][0] *= rW
                vertices[j][1] *= rH


            # get cropped image using perspective transform
            if modelRecognition:
                cropped = fourPointsTransform(frame, vertices)
                cropped = cv.cvtColor(cropped, cv.COLOR_BGR2GRAY)

                # Create a 4D blob from cropped image
                blob = cv.dnn.blobFromImage(cropped, size=(100, 32), mean=127.5, scalefactor=1 / 127.5)
                recognizer.setInput(blob)

                # Run the recognition model
                tickmeter.start()
                result = recognizer.forward()
                tickmeter.stop()

                # decode the result into text
                wordRecognized = decodeText(result)
                cv.putText(frame, wordRecognized, (int(vertices[1][0]), int(vertices[1][1])), cv.FONT_HERSHEY_SIMPLEX,
                           0.5, (255, 0, 0))

            for j in range(4):
                p1 = (int(vertices[j][0]), int(vertices[j][1]))
                p2 = (int(vertices[(j + 1) % 4][0]), int(vertices[(j + 1) % 4][1]))
                cv.line(frame, p1, p2, (0, 255, 0), 1)

        # Put efficiency information
        label = 'Inference time: %.2f ms' % (tickmeter.getTimeMilli())
        cv.putText(frame, label, (0, 15), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0))

        # Display the frame
        cv.imshow(kWinName, frame)
        tickmeter.reset()


if __name__ == "__main__":
    main()
$ text_detection -m=[path_to_text_detect_model] -ocr=[path_to_text_recognition_model]

提供预先训练的 ONNX 模型

一些预先训练的模型可以在https://drive.google.com/drive/folders/1cTbQ3nuZG-EKWak6emD_s8_hHXWz7lAr?usp=sharing找到。

下表显示了它们在不同文本识别数据集上的表现:

文本识别模型的性能是在OpenCV DNN上测试的,不包括文本检测模型。

选型建议

文本识别模型的输入是文本检测模型的输出,这导致文本检测的性能极大地影响着文本识别的性能。

DenseNet_CTC 的参数最小,FPS 最好,适合边缘设备,对计算成本非常敏感。如果你的计算资源有限,又想达到更好的准确率,VGG_CTC 是个不错的选择。

CRNN_VGG_BiLSTM_CTC适用于对识别准确率要求较高的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938253.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

“从零到一:揭秘操作系统的奇妙世界”【操作系统的发展】

1.手工操作阶段 此时没有OS&#xff0c;用户采用人工操作方式进行。 方式&#xff1a;程序员在纸带机上打孔---计算机读取---结果输出到纸袋机上---程序员取走结果 缺点&#xff1a;耗时长&#xff0c;难度大、用户独占全机、人机速度矛盾导致资源利用率低 2.单批道处理系统 引…

二叉树理论基础篇

这里写目录标题 二叉树的种类**满二叉树&#xff08;Full Binary Tree&#xff09;****完全二叉树&#xff08;Complete Binary Tree&#xff09;****二叉搜索树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;**平衡二叉搜索树 二叉树的存储方式二叉树的遍历方式二…

【376.2协议】国网_用电信息采集系统通信协议

【376.2协议】用电信息采集系统通信协议 文章目录 【376.2协议】用电信息采集系统通信协议1、帧格式2、各传输帧2.1 控制域 C (一个字节|8个位)2.2 用户数据区格式2.2.1 信息域2.2.2 地址域2.2.3 应用数据域 3、式例 1、帧格式 帧格式定义规则起始字符固定报文头&#xff08; …

鸿蒙项目云捐助第十一讲鸿蒙App应用的捐助成功自定义对话框组件实现

在生活中&#xff0c;用户做了一个好事后&#xff0c;很多场合都会收到一份感谢。在捐助的行业也是一样的&#xff0c;用户捐出了一片爱心&#xff0c;就会收获一份温情。这里的温情是通过自定义对话框实现的。 一、通过自定义对话框组件实现捐款成功的信息页 这里用户捐款成…

leetcode区间部分笔记

区间部分 1. 汇总区间2. 合并区间3. 插入区间4. 用最少数量的箭引爆气球 1. 汇总区间 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并…

spring学习(spring-bean实例化(静态工厂))

目录 一、spring容器实例化bean的几种方式。 二、spring容器使用静态工厂方式实现bean实例化。 &#xff08;1&#xff09;基本介绍。 1、静态工厂&#xff1f; 2、"factory-method"属性。 3、二种操作方式。 方法一。 方法二。 &#xff08;2&#xff09;demo(案例)…

25年宁德时代社招在职晋升Verify测评SHL题库:语言理解+数字推理考什么?

宁德时代的社招测评采用Verify系统&#xff0c;主要分为两大核心部分&#xff1a;语言理解和数字推理。 1. **语言理解部分**&#xff1a;包括阅读理解、逻辑填空和语句排序等题型。要求应聘者在17分钟内完成30题&#xff0c;旨在考察应聘者的阅读速度、理解准确性和逻辑性。 …

2024数证杯初赛

计算机取证 请根据计算机检材&#xff0c;回答以下问题&#xff1a;(32个小题&#xff0c;共76分 1.[填空题对计算机镜像进行分析&#xff0c;计算该镜像中ESP分区的SM3值后8位为&#xff1f;&#xff08;答案格式&#xff1a;大写字母与数字组合&#xff0c;如&#xff1a;D…

典型案例 | 旧PC新蜕变!东北师范大学依托麒麟信安云“旧物焕新生”

东北师范大学始建于1946年&#xff0c;坐落于吉林省长春市&#xff0c;是中国共产党在东北地区创建的第一所综合性大学。作为国家“双一流”建设高校&#xff0c;学校高度重视教学改革和科技创新&#xff0c;校园信息化建设工作始终走在前列。基于麒麟信安云&#xff0c;东北师…

项目二十三:电阻测量(需要简单的外围检测电路,将电阻转换为电压)测量100,1k,4.7k,10k,20k的电阻阻值,由数码管显示。要求测试误差 <10%

资料查找&#xff1a; 01 方案选择 使用单片机测量电阻有多种方法&#xff0c;以下是一些常见的方法及其原理&#xff1a; 串联分压法&#xff08;ADC&#xff09; 原理&#xff1a;根据串联电路的分压原理&#xff0c;通过测量已知电阻和待测电阻上的电压&#xff0c;计算出…

ST-Linker V2 烧录器详解说明文档

目录 ST-Linker v2烧录器介绍 STM8烧录口 STM32烧录接口 JTAG烧录接口 ​​​​​​​ ​​​​​​​ ​​​​​​​ 编写不易&#xff0c;仅供学习&#xff0c;请勿搬运&#xff0c;感谢理解 ST-Linker v2烧录器介绍 图片中是两种IC芯片的烧录器&#x…

在Centos7上安装MySQL数据库 How to install MySQL on Centos 7

执行以下命令&#xff0c;下载并安装MySQL。 wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm && yum -y install mysql57-community-release-el7-10.noarch.rpm && yum install -y mysql-community-server --nogpgcheck执行以下…

FireFox火狐浏览器企业策略禁止更新

一直在用火狐浏览器&#xff0c;但是经常提示更新&#xff0c;进入浏览器右上角就弹出提示&#xff0c;比较烦。多方寻找&#xff0c;一直没有找到合适的方案&#xff0c;毕竟官方没有给出禁用检查更新的选项&#xff0c;甚至about:config里都没有。 最终找到了通过企业策略控…

【Qt】按钮类控件:QPushButton、QRadioButton、QCheckBox、ToolButton

目录 QPushButton 例子&#xff1a; QRadioButton 例子&#xff1a; 按钮的常见信号函数 单选按钮分组 例子&#xff1a; QCheckButton 例子&#xff1a; QToolButton QWidget的常见属性及其功能对于它的派生类控件都是有效的(也就是Qt中的各种控件)&#xff0c;包括…

SpringBoot3 升级介绍

优质博文&#xff1a;IT-BLOG-CN 一、项目背景 截止2023.05.18&#xff0c;springboot发布了最新版本3.1.0。而在我们开发项目中&#xff0c;springboot一直使用的是1.5.8版本(相差6年的维护更新)。版本差距较大&#xff0c;很多新功能未能得到使用。例如近几年Loom的兴起&am…

运筹说 第130期 | 对策论引言

通过对对策论基础知识进行梳理和总结&#xff0c;小编绘制了《对策论思维导图》&#xff0c;如下图所示&#xff0c;对策论章节一共包含4个小节。 第1小节是对策论引言。介绍了对策论的基本概念&#xff0c;包含对策行为和对策论、对策现象的三要素、对策问题举例及对策的分类…

Windows 与 Linux 下 Ping IPv6 地址 | 常用网络命令

注&#xff1a;本文为网络命令相关文章合辑。 未整理去重。 一、IPv6 概述 IPv6 即 “Internet 协议版本 6”&#xff0c;因 IPv4 地址资源面临耗尽问题而被引入以替代 IPv4。IPv6 则提供了理论上多达 2 128 2^{128} 2128 个地址&#xff0c;有效解决地址不足困境。 IPv6 具…

密码学——密码学概述、分类、加密技术(山东省大数据职称考试)

大数据分析应用-初级 第一部分 基础知识 一、大数据法律法规、政策文件、相关标准 二、计算机基础知识 三、信息化基础知识 四、密码学 五、大数据安全 六、数据库系统 七、数据仓库. 第二部分 专业知识 一、大数据技术与应用 二、大数据分析模型 三、数据科学 密码学 大数据…

Android Studio、JDK、AGP、Gradle、kotlin-gradle-plugin 兼容性问题

文章目录 问题&#xff1a;解决办法&#xff1a;gradle与 java的版本兼容AGP与Gradle的版本兼容kotlin 与 jvm 的版本兼容KGP、Gradle、AGP兼容关系kotlin 与 java 的编译版本配置 问题&#xff1a; 你从githb上clone了一个项目&#xff0c;本地跑的时候&#xff0c;各种报错。…

ChatGPT搜索全新升级,向全体用户开放,近屿智能助力AI行业发展

12月17日&#xff0c;OpenAI在第八天直播中正式宣布ChatGPT搜索功能全面升级&#xff0c;并即日起对所有ChatGPT用户开放。此次更新不仅带来了显著的性能提升&#xff0c;还引入了多项突破性功能&#xff0c;如更快的搜索速度、全新的地图体验以及YouTube视频嵌入&#xff0c;为…