paddleocr 实操笔记 (前向后梳理)

news2024/12/27 13:11:09

要点:

参考:

基于PaddleOCR的数字显示器字符识别

工业仪表数值识别


前言

问题分析 要处理电表中的数据,可以分为步骤,拆解为以下问题:

  1. 感兴趣区域定位问题
  2. OCR读数问题

针对问题1,经过实验与探索,也找到两种方案:

方案1,直接利用PaddleOCR默认自带的检测器,筛选掉其他无效的框体和信息,剩下的就是有用的。(未经过训练的,直接使用预训练模型)

方案2,通过Opencv图像处理的方法,根据电表字符区域特征进行相应的轮廓提取和颜色筛选,从而保证其得到有效的定位。

方案3,收集场景下的大量电表字符识别数据,制作数据集并进行标记,分别训练其定位和识别模型

考虑到时间成本和人工成本问题,这里优先选择前两种方案。 下面是使用Opencv来进行ROI区域定位的方法。

一 导包

# 导入依赖库
import os
from tqdm import tqdm
import cv2
import csv
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import warnings
from paddleocr import PaddleOCR, draw_ocr

# 忽略警告
warnings.filterwarnings("ignore")

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"  # 防止报错

最后一行是防止报错,

二 相关函数

2.1 plt画图

# 可视化绘图
def imshow_image(img_path):
    img = Image.open(img_path)
    plt.figure("test_img", figsize=(5, 5))
    plt.imshow(img)
    plt.show()

2.2 画出最大轮廓

def find_biggest_contour(image):
    """获取最大轮廓"""
    image = image.copy()
    contours, hierarchy = cv2.findContours(image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    contour_sizes = [(cv2.contourArea(contour), contour) for contour in contours]
    biggest_contour = max(contour_sizes, key=lambda x: x[0])[1]
    return biggest_contour

2.3 查找ROI轮廓

def get_find_display(input_path, lower=(0, 0, 0), higher=(255, 255, 255), output_path='./'):
    """查找ROI轮廓"""
    img = cv2.imread(input_path)
    # print('input:', input_path)
    filename = input_path.split('/')[-1]
    f_name = filename.split('.')[0]
    # print('filename:', filename, 'f_name:', f_name)

    global img_crop
    lowHue = lower[0]
    lowSat = lower[1]
    lowVal = lower[2]
    highHue = higher[0]
    highSat = higher[1]
    highVal = higher[2]

    # 可选择不同的模糊方法
    frameBGR = cv2.GaussianBlur(img, (7, 7), 0)

    # 转换为HSV颜色空间
    hsv = cv2.cvtColor(frameBGR, cv2.COLOR_BGR2HSV)

    # 定义HSV值颜色范围
    colorLow = np.array([lowHue, lowSat, lowVal])
    colorHigh = np.array([highHue, highSat, highVal])
    mask = cv2.inRange(hsv, colorLow, colorHigh)

    kernal = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)

    biggest_contour = find_biggest_contour(mask)
    # cv2.drawContours(img, biggest_contour, -1, (0, 255, 0), 2)
    print('cnt_len:', len(biggest_contour))

    # 将遮罩放在原始图像的上方。
    result_img = cv2.bitwise_and(img, img, mask=mask)

    if biggest_contour is not None:
        x, y, w, h = cv2.boundingRect(biggest_contour)
        print(x, y, w, h)
        img_crop = img[y:y + h, x:x + w]

        print('wpath:', output_path + filename)
        save_path = output_path + filename
        if not os.path.exists(output_path):
            os.mkdir(output_path)
        cv2.imwrite(save_path, img_crop)
    else:
        img_crop = img

    return result_img, img_crop

三 直接使用OCR算法识别

3.1 提取目标识别区域(找出文字区域)

output_path = 'work/roi/'
# img_roi = 'test/133102_steerPoint5_preset1255_20220917221726_v.jpeg'
img_roi = 'test/number_item.jpg'
lower = (0, 80, 0)
higher = (255, 255, 255)
result_img, img_crop = get_find_display(img_roi, lower, higher, output_path)

3.2 查看识别区域

img_roi_path = 'work/roi/number_item.jpg'
imshow_image(img_roi_path)

3.3 直接识别

def rec_display_roi(img_roi): 
    ocr = PaddleOCR() 
    result = ocr.ocr(img_roi, det=False) 
    return result[0][0], result[0][1]
rec_display_roi(img_roi_path)

3.4 直接使用图片进行识别

获取识别区

output_path = 'work/roi/'
img_roi = 'test/number_use.jpg'
lower = (0, 0, 0)
higher = (255, 255, 255)
result_img, img_crop = get_find_display(img_roi, lower, higher, output_path)
imshow_image(img_roi)

进行识别

img_roi_path = "work/roi/number_use.jpg"
imshow_image(img_roi_path)
def rec_display_roi(img_roi): 
    # ocr = PaddleOCR() 
    ocr = PaddleOCR(use_gpu=True)
    result = ocr.ocr(img_roi, det=False) 
    return result[0][0]
rec_display_roi(img_roi)

识别结果:   ('0598', 0.7145649790763855)

四 直接使用OCR进行检测和识别

方案2,直接使用PaddleOCR将所有可能是OCR的对象进行检测和识别。 再从中筛选要的结果。

4.1 OCR识别读数

# Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
# 例如`ch`, `en`, `fr`, `german`, `korean`, `japan`
ocr = PaddleOCR(use_angle_cls=True, lang="ch")  # need to run only once to download and load model into memory
# 数据可视化
# img_path = 'test/133102_steerPoint5_preset1255_20220917221726_v.jpeg'
img_path = 'test/number_item.jpg'
save_path = 'work/dst/result.jpg'
result = ocr.ocr(img_path, cls=True)
for line in result:
    print(line)

image = Image.open(img_path).convert('RGB')
boxes = [line[0] for line in result]
txts = [line[1][0] for line in result]
scores = [line[1][1] for line in result]
im_show = draw_ocr(image, boxes, txts, scores, font_path='work/font/simfang.ttf')
im_show = Image.fromarray(im_show)
im_show.save(save_path)
"""[[[1936.0, 56.0], [2461.0, 56.0], [2461.0, 109.0], [1936.0, 109.0]], ('2022-11-28 07:38:28', 0.8835511)]
[[[2461.0, 450.0], [2557.0, 450.0], [2557.0, 500.0], [2461.0, 500.0]], ('原水', 0.99717796)]
[[[12.0, 1310.0], [483.0, 1322.0], [481.0, 1404.0], [10.0, 1392.0]], ('水泵房仪表间', 0.93879247)]
im_show <PIL.Image.Image image mode=RGB size=1200x338 at 0x2852F99EB00>"""

五 算法优化

def write_to_csv(log_path, filename='', result=0.00, score=0, mode_head=True):
    file = open(log_path, 'a+', encoding='utf-8', newline='')
    csv_writer = csv.writer(file)
    if mode_head == True:
        csv_writer.writerow([f'filename', f'result', f'score'])
    else:
        csv_writer.writerow([filename, result, score])
    file.close()


def get_bbox_area(box):
    """计算bbox的面积"""
    bbox_area = (max(box[2]) - max(box[0])) * (max(box[3]) - max(box[1]))
    return bbox_area


def quadArea(nodes):
    """计算多边形的面积"""
    # 基于向量积计算不规则多边形的面积, 坐标点需要按顺序(逆时针或顺时针)选取
    i_count = len(nodes)
    area_temp = 0
    for i in range(i_count):
        area_temp += nodes[i][0] * nodes[(i + 1) % i_count][1] - nodes[(i + 1) % i_count][0] * nodes[i][1]
    return abs(area_temp)


def bboxes_choose(boxes, txts, scores):
    """获取最大框体"""
    area_list = []
    for i in range(0, len(boxes)):
        bx = boxes[i]
        # area = get_bbox_area(bx)
        area = quadArea(bx)
        # print('bx:', bx, 'area:',area)
        area_list.append(area)

    if len(area_list) == 0:
        index = 0
    else:
        index = area_list.index(max(area_list))

    if len(boxes) == 0:
        boxes = []
    else:
        boxes = [boxes[index]]
        txts = [txts[index]]
        scores = [scores[index]]
    return boxes, txts, scores


def ocr_roi_det(img_path, font, save_path='./work/save/'):
    """OCR识别"""
    result = ocr.ocr(img_path, cls=True)
    # for line in result:
    # print(line)

    # 显示结果
    from PIL import Image
    image = Image.open(img_path).convert('RGB')

    fileslist = img_path.split('/')
    fname = fileslist[-1].split('.')[0]

    # [[[151.0, 53.0], [277.0, 53.0], [277.0, 111.0], [151.0, 111.0]], ('00.2', 0.9423570036888123)]
    boxes = [line[0] for line in result]
    txts = [line[1][0] for line in result]
    scores = [line[1][1] for line in result]
    boxes, txts, scores = bboxes_choose(boxes, txts, scores)
    # bs = nms(boxes, scores)
    # print('bs:', bs)

    im_show = draw_ocr(image, boxes, txts, scores, font_path=font)
    im_show = Image.fromarray(im_show)
    if not os.path.exists(save_path):
        os.mkdir(save_path)
    im_show.save(save_path + fname + '_result.jpg')

    return txts[0], scores[0]


def all_test_det(path, log_path, font, save_path):
    """执行识别算法,并记录结果到csv"""
    count = 0
    img_list = []
    img_ans_dic = {}
    for filepath, dirnames, filenames in os.walk(path):  # 在多级目录下找文件
        for filename in filenames:
            file_path = filepath + filename
            # print('file_path:', file_path)
            img_list.append(file_path)

    global score
    write_to_csv(log_path)
    for i in tqdm(range(0, len(img_list) - 1)):
        img_roi = img_list[i]
        # result, score = rec_display_roi(img_roi)
        fileslist = img_roi.split('/')
        fname = fileslist[-1].split('.')[0]
        result, score = ocr_roi_det(img_roi, font, save_path)
        print('result:', result, 'score:', score)

        if result != '':
            img_ans_dic[fname] = score
            count += 1
        else:
            score = -1
            img_ans_dic[fname] = score
            continue
        write_to_csv(log_path, fname, result, score, False)

    print('count:', count)
    print('dict_len:', len(img_ans_dic))
    print('ans_dict:', img_ans_dic)
if __name__ == '__main__':
    # Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
    # 例如`ch`, `en`, `fr`, `german`, `korean`, `japan`
    print('查看ocr数据模型')
    ocr = PaddleOCR(use_angle_cls=False, lang="en")  # need to run only once to download and load model into memory
    print('ocr:', ocr)
    # img_path = 'test/number_use.jpg'
    font_path = 'work/font/simfang.ttf'
    a, b = ocr_roi_det(img_path, font_path)
    print('查看识别结果:', a, b)

    log_path = 'work/log/result.csv'
    save_path = 'work/save_result/'
    test_path = 'work/dataset/test/'
    all_test_det(test_path, log_path, font_path, save_path)

    # 结果分析
    # rs_img = 'work/save_result/133102_steerPoint5_preset1255_20220917221726_v_result.jpg'
    # rs_img = 'test/72635_steerPoint12_preset1294_20220919123447_v.jpeg'
    rs_img = 'test/number_use.jpg'
    imshow_image(rs_img)

    print('执行到最后位置》')

优化后的算法

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马---Redis入门到实战【基础篇】

一、初识Redis 认识NoSql redis是键值数据库&#xff0c;没有表、没有约束&#xff0c;存的都是键值对&#xff0c;称为NoSql数据 NoSQL VS SQL 认识Redis Redis诞生于2009年&#xff0c;全称Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存…

开篇:为什么学习 Go 语言

简介 Go 语言又称 Golang&#xff0c;由 Google 公司于 2009 年发布&#xff0c;近几年伴随着云计算、微服务、分布式的发展而迅速崛起&#xff0c;跻身主流编程语言之列&#xff0c;和 Java 类似&#xff0c;它是一门静态的、强类型的、编译型编程语言&#xff0c;为并发而生…

高等数学笔记(上下)

目录 不定积分定积分微分方程线性微分方程解的结构常系数齐次线性微分方程常系数齐次线性微分方程特解的求法 计算机解法 不定积分 第一类换元积分法&#xff1a;灵感来自于复合函数的求导&#xff0c;利用中间变量替换得到复合函数的积分法&#xff1a;设 f ( u ) f(u) f(u)具…

【Redis7】Redis7 事务管道发布订阅

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Redis7 事务、管道和发布订阅。 后续会继续分享Redis7和其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#xff1a;《【Redis7】Redis7 持…

Qt扫盲-QXYSeries理论总结

QXYSeries理论总结 一、概述二、常用函数介绍1. 维护点2. 绘图相关3. 绘制标签 三、信号说明1. Point 增删相关2. Point 鼠标相关3. 图变化相关 一、概述 QXYSeries 类是折线图、曲线图、散点图的基类。这个类其实就是维护的是图线的相关信息&#xff0c;就比如是这个线条的颜…

5、cmake的简单认识及CMakeLists.txt的编写语法

文章目录 1、cmake是什么&#xff08;1&#xff09;cmake的两大功能 2、CMakeLists.txt常用命令&#xff08;即如何编写CMakeLists.txt文件&#xff09;&#xff08;1&#xff09;cmake常见预定义1&#xff09;常见的预定义变量2&#xff09;系统信息预定义变量3&#xff09;开…

开发者笑疯了! LLaMa惊天泄露引爆ChatGPT平替狂潮,开源LLM领域变天

来源: 新智源 微信号&#xff1a;AI-era Meta的LLaMA模型开源&#xff0c;让文本大模型迎来了Stable Diffustion时刻。谁都没想 谁能想到&#xff0c;一次意外的LLaMA泄漏&#xff0c;竟点燃了开源LLM领域最大的创新火花。 一系列表现出色的ChatGPT开源替代品——「羊驼家族」…

Linux系统网络传输之端口详解

Linux系统网络传输之端口详解 1、端口的概念2、安装namp3、查看端口占用情况4、安装net-tools5、查看指定端口占用情况 1、端口的概念 计算机程序之间的通讯&#xff0c;通过IP只能锁定计算机&#xff0c;但是无法锁定具体的程序。通过端口可以锁定计算机上具体的程序&#xf…

《花雕学AI》深度测试ChatGPT国内镜像站:超简单提示词的猫娘角色扮演,真的好神奇啊!

偶然看过一篇讲解如何使用ChatGPT调教猫娘的文章&#xff0c;有六个步骤&#xff0c;许许多多的调教提示语&#xff0c;让我感觉这是一件非常复杂与专业的事情。今天有空&#xff0c;于是就想先从简单的开始尝试一下。我使用了最简单的提示词&#xff1a;”ChatGPT角色扮演猫娘…

分布式事务Seata实践入门

1 前言 现在应用基本上都是分布式部署&#xff0c;那么针对分布式事务问题&#xff0c;也有对应的解决方案。经过简单的调研&#xff0c;最后选择了阿里的 Seata 组件&#xff0c;来实现分布式事务。 Seata是2019年1月份&#xff0c;蚂蚁金服和阿里巴巴共同开源的分布式事务解…

(TinkSystem SR650)安装服务器操作系统(Windows Server 2022)步骤和相关概念

&#xff08;TinkSystem SR650&#xff09;安装服务器操作系统&#xff08;Windows Server 2022&#xff09;步骤和相关概念 服务器操作系统安装步骤 记录一下服务器操作系统安装过程&#xff0c;虽然简单但还是有一些坑需要注意&#xff0c;本次使用的是联想服务器ThinkSyst…

VS2019中Ctrl+左键不起作用和控制台不驻留(cmd窗口闪退)

1、关于Ctrl左键不起作用 解决&#xff1a;在线装个插件就行。 工具>>扩展和更新 联机>>VS库>>所搜插件“GO TO Definition” 安装&#xff0c;重启VS即可。 2、关于控制台不驻留 当然这个问题解决方案有很多&#xff0c;这个是首选吧

[Java·算法·中等]LeetCode105. 从前序与中序遍历序列构造二叉树

每天一题&#xff0c;防止痴呆 前言题目示例分析思路1题解1分析思路2题解2 &#x1f449;️ 力扣原文 前言 二叉树前序遍历的顺序为&#xff1a; 先遍历根节点&#xff1b; 随后递归地遍历左子树&#xff1b; 最后递归地遍历右子树。 二叉树中序遍历的顺序为&#xff1a; 先递…

HCIP-6.9BGP路由反射器原理与配置

路由反射器原理与配置 1、路由反射器概念1.1、路由反射器原理&#xff1a;1.2、多集群路由反射器1.3、备份路由反射器2、路由反射器配置3、路由反射器防环机制 1、路由反射器概念 IBGP的水平分割&#xff0c;IBGP 1只能update一跳&#xff0c;就是说在IBGP 2 设备收到IBGP 1设…

【RocketMQ】事务的实现原理

事务的使用 RocketMQ事务的使用场景 单体架构下的事务 在单体系统的开发过程中&#xff0c;假如某个场景下需要对数据库的多张表进行操作&#xff0c;为了保证数据的一致性&#xff0c;一般会使用事务&#xff0c;将所有的操作全部提交或者在出错的时候全部回滚。以创建订单…

12-RabbitMQ

一 RabbitMQ概念 1 MQ 消息队列 MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于系统之间的异步通信。 同步通信相当于两个人当面对话&#xff0c;你一言我一语。必须及时回复 异步通信相当于通过第三方转述对…

ObjectBox一种基于中心点的无锚点目标检测方法

ObjectBox: From Centers to Boxes for Anchor-Free Object Detection 论文地址&#xff1a;https://arxiv.org/pdf/2207.06985.pdf 官方代码&#xff1a;https://github.com/MohsenZand/ObjectBox 基于中心点的无锚点目标检测方法是一种目标检测方法&#xff0c;其思路是将目…

DJ编曲用什么软件,DJ编曲教需要哪些步骤

随着现在人们的生活水平不断提高&#xff0c;我们的精神生活也越来越丰富&#xff0c;对于现在的年轻人来说&#xff0c;DJ舞曲是一个较受欢迎的领域&#xff0c;有许多年轻人对DJ这个职业感兴趣&#xff0c;想要深入了解DJ编曲这份工作&#xff0c;那么今天我们就来说一说DJ编…

300元买什么蓝牙耳机性价比高?300左右性价比高的蓝牙耳机推荐

TWS耳机已经成了很多人生活的必需品&#xff0c;如今的耳机在设计、功能、体验等方面都非常完善&#xff0c;拥有一副TWS耳机似乎已经成为时尚的标志&#xff0c;尤其是年轻群体&#xff0c;耳机既是听歌、娱乐的主力设备&#xff0c;也是穿搭风格、个性的体现&#xff0c;下面…

JDBC事务 Hibernate事务 EJB事务详解

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;JDBC事务 Hibernate事务 EJB事务详解 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你…