OCR文字识别标注小助手

目录

背景

工具界面

操作

1、选择目录

2、更改为正确的信息

3、保存

4、说明

项目

代码

下载


背景

为什么要写这么一个小工具呢?因为要对文字进行标注。

为什么对文字进行标注呢?因为要重新训练识别。

为什么要重新训练识别呢?因为通用OCR识别识别不了。(通用OCR识别参考 https://lw112190.blog.csdn.net/article/details/133784164)

什么样子的图片通用OCR识别不了呢?它来了,如下图

放大看一个

识别不了的原因主要是特殊字体+横线干扰

打算重新训练之前,做过哪些努力呢?

1、更换精度更高的服务器端模型,效果不好,放弃

2、图片预处理,去掉文字后面的横线(去掉文字后面横线参考:https://lw112190.blog.csdn.net/article/details/134283487),再识别,效果不好,放弃。

所以自己标注,重新训练了。

训练效果如何?暂时还不知道,因为标注工作还没完成。

工具界面

操作

1、选择目录

选择后默认加载第一章图片

文本框里面的字哪里来的呢,是我用通用OCR识别的,虽然效果不好,但是还是能识别一些,标注的时候可以少打几个字。

2、更改为正确的信息

3、保存

点击保存,或者键盘Enter,自动加载下一张图片,效率还不错。

4、说明

默认会在选择的目录下生成两个文件夹,标注不会对原数据进行任何处理。

target存放标注好保存的文件。

cache记录一下标注的文件,方便后续工作(比如今天标不完了,关机下班,明天来了打开工具,可以从昨天标注的图片的下一张图片继续标注)。

项目

代码

代码写的比较糙,因为只是一个临时性的工具,需要的可以根据自己的需要修改、重构。

using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace OpenCvSharp_Demo
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        int index = -1;
        Mat src;
        String name;
        String ImageFolderPath;
        String TargetFolderPath;
        String CacheFolderPath;
        List<String> ImgFile = new List<string>();
        List<String> CacheImgFile = new List<string>();

        //下一张
        private void button2_Click(object sender, EventArgs e)
        {
            index = index + 1;

            if (index > ImgFile.Count - 1)
            {
                index = ImgFile.Count;
                MessageBox.Show("下一张没有了,干完了!");
                return;
            }

            ShowImg(index);
        }

        //上一张
        private void button4_Click(object sender, EventArgs e)
        {
            index = index - 1;

            if (index < 0)
            {
                MessageBox.Show("上一张没有了");
                index = 0;
                return;
            }

            ShowImg(index);
        }

        private void ShowImg(int index)
        {
            name = ImgFile[index];
            txtName.Text = name.Split('.')[0];
            txtInfo.Text = name + " " + index + "/" + ImgFile.Count;

            if (File.Exists(CacheFolderPath + name.Split('.')[0] + ".txt"))
            {
                string info = System.IO.File.ReadAllText(CacheFolderPath + name.Split('.')[0] + ".txt");
                if (info.StartsWith("del_"))
                {
                    //已经标注
                    txtInfo.Text = name + "[标记为删除] " + (index + 1).ToString() + "/" + ImgFile.Count;
                }
                else
                {
                    //已经标注
                    txtInfo.Text = name + "[已经标注] " + (index + 1) + "/" + ImgFile.Count;
                    txtName.Text = info;
                }
            }
            else
            {
                //未标注
                txtInfo.Text = name + " " + (index + 1) + "/" + ImgFile.Count;
            }

            if (src!=null)
            {
                src.Dispose();
            }

            if (pictureBox2.Image != null)
            {
                pictureBox2.Image.Dispose();
            }

            src = new Mat(ImageFolderPath + name);

            pictureBox2.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(src);

        }

        //保存
        private void button1_Click(object sender, EventArgs e)
        {
            //保存图片
            Cv2.ImWrite(TargetFolderPath + txtName.Text + "." + name.Split('.')[1], src);

            //写个缓存
            FileStream fs = new FileStream(CacheFolderPath + name.Split('.')[0] + ".txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
            sw.WriteLine(txtName.Text);
            sw.Close();
            fs.Close();

            if (pictureBox2.Image != null)
            {
                pictureBox2.Image.Dispose();
            }

            if (src != null)
            {
                src.Dispose();
            }

            //下一张
            button2_Click(null, null);
        }

        /// <summary>
        /// 选择目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            if (dialog.ShowDialog(this) != DialogResult.OK)
            {
                return;
            }

            ImageFolderPath = dialog.SelectedPath + "\\";

            TargetFolderPath = ImageFolderPath + "target\\";
            CacheFolderPath = ImageFolderPath + "cache\\";

            if (!Directory.Exists(TargetFolderPath))
            {
                Directory.CreateDirectory(TargetFolderPath);
            }

            DirectoryInfo root = new DirectoryInfo(ImageFolderPath);
            foreach (FileInfo f in root.GetFiles())
            {
                if (f.Extension.Contains(".jpg") || f.Extension.Contains(".jpeg") || f.Extension.Contains(".png"))
                {
                    ImgFile.Add(f.Name);
                }
            }

            if (ImgFile.Count == 0)
            {
                MessageBox.Show("没有图片");
                return;
            }

            txtInfo.Text = "共计[" + ImgFile.Count + "]张图片";

            if (!Directory.Exists(CacheFolderPath))
            {
                Directory.CreateDirectory(CacheFolderPath);
            }
            else
            {
                //定位上一次标注的位置,然后下一张
                root = new DirectoryInfo(CacheFolderPath);
                foreach (FileInfo f in root.GetFiles())
                {
                    if (f.Extension.Contains(".txt"))
                    {
                        CacheImgFile.Add(f.Name);
                    }
                }

                if (CacheImgFile.Count > 0)
                {
                    string lastImg = CacheImgFile.Last();
                    lastImg = lastImg.Split('.')[0];
                    string temp = ImgFile.Find(o => o.StartsWith(lastImg));
                    if (!string.IsNullOrEmpty(temp))
                    {
                        index = ImgFile.IndexOf(temp);
                    }
                }
            }

            //自动下一张
            button2_Click(null, null);

        }

        //删除
        private void button5_Click(object sender, EventArgs e)
        {
            //保存
            Cv2.ImWrite(TargetFolderPath + "del_" + txtName.Text + "." + name.Split('.')[1], src);

            //写个缓存
            FileStream fs = new FileStream(CacheFolderPath + name.Split('.')[0] + ".txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
            sw.WriteLine("del_" + txtName.Text);
            sw.Close();
            fs.Close();

            if (pictureBox2.Image != null)
            {
                pictureBox2.Image.Dispose();
            }

            if (!src.Empty())
            {
                src.Dispose();
            }

            //下一张
            button2_Click(null, null);
        }
    }
}

下载

源码下载

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

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

相关文章

Linux 小程序-进度条

1.进度条准备知识 1.1回车与换行 以前的键盘会有一个这样的按键 &#xff0c;这个键就是回车与换行。 回车&#xff1a;从当前行回退到当前行的起始位置。 换行&#xff1a;从当前行切换到下一行的该位置。 有了以上的认识我们可以写出一个简单的倒计时代码&#xff1a; 注意&a…

【华为HCIP | 华为数通工程师】IPV4与IPV6 高频题(2)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

机器学习的线性回归与非线性回归

一元线性回归 回归分析用来建立方程模拟两个或者多个变量之间如何关联 一元线性回归包括一个自变量和一个因变量 如果包含两个以上的自变量&#xff0c;则称为多元线性回归 代价函数&#xff08;损失函数&#xff09; 损失函数的最终目的是为了使得误差平方和最小 用梯度下…

【装包拆包----泛型】

文章目录 装箱和拆箱泛型创建一个泛型数组泛型的上界泛型方法 装箱和拆箱 装箱&#xff1a; 把基本数据类型给到引用数据类型 public static void main(String[] args) {//自动装包//第一种装包Integer c 12;//第二种装包int a 7;Integer b a;//显示装包Integer aa Intege…

大语言模型|人工智能领域中备受关注的技术

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️其他领域】 文章目录 前言关于大语言模型大语言模型是什么&#xff1f;大语言模型有什么用?文案写作知识库回答文本分类代码生成 AWS 如何通过 LLM 提供帮助&#xff1f;Amazon BedrockAmazon SageM…

QT day3作业

1.思维导图 2、 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密…

【Oracle 客户端连接数据库过程解析】

文章目录 一、开篇二、Oracle客户端简介三、连接过程解析 一、开篇 Oracle数据库以其卓越的性能和稳定性赢得了众多企业的信赖。作为开发者或数据库管理员&#xff0c;理解Oracle客户端如何与数据库建立连接&#xff0c;是确保系统正常运行的关键。 二、Oracle客户端简介 首…

LabVIEW进行MQTT通信及数据解析

需求&#xff1a;一般通过串口的方式进行数据的解析&#xff0c;但有时候硬件的限制&#xff0c;没法预留串口&#xff0c;那么如何通过网络的方式特别是MQTT数据的通信及解析 解决方式&#xff1a; 1.MQTT通信控件&#xff1a; 参考开源的mqtt-LabVIEW https://github.com…

PostGIS学习教程七:关于几何图形的练习

文章目录 一、函数列表二、练习 一、函数列表 以下是我们迄今为止看到的所有函数的汇总&#xff0c;它们应该对练习有用&#xff01; sum(expression) aggregate to return a sum for a set of records count(expression) aggregate to return the size of a set of records …

JAVA G1垃圾收集器介绍

为解决CMS算法产生空间碎片和其它一系列的问题缺陷&#xff0c;HotSpot提供了另外一种垃圾回收策略&#xff0c;G1&#xff08;Garbage First&#xff09;算法&#xff0c;通过参数-XX:UseG1GC来启用&#xff0c;该算法在JDK 7u4版本被正式推出&#xff0c;官网对此描述如下&am…

JS-项目实战-更新水果单价更新小计更新总计

1、fruit.js //当页面加载完成后执行后面的匿名函数 window.onload function () {//get:获取 Element:元素 By:通过...方式//getElementById()根据id值获取某元素let fruitTbl document.getElementById("fruit_tbl");//table.rows:获取这个表格的所有的行&a…

目标检测—Yolo系列(YOLOv1/2/v3/4/5/x/6/7/8)

目标检测概述 什么是目标检测&#xff1f; 滑动窗口&#xff08;Sliding Window&#xff09; 滑动窗口的效率问题和改进 滑动窗口的效率问题&#xff1a;计算成本很大 改进思路 1&#xff1a;使用启发式算法替换暴力遍历 例如 R-CNN&#xff0c;Fast R-CNN 中使用 Selectiv…

S32DS踩坑日记五-bootloader跳转APP时触发DefaultISR

S32DS踩坑日记五-bootloader跳转APP时触发DefaultISR bootloader和APP由另一位同事开发过程中&#xff0c;被导师叫回去写论文了。 由于项目不急&#xff0c;接手后未作任何改动&#xff0c;后面硬件工程师手工焊了几块电路版&#xff0c;需要刷上程序测试电路板。然后就遇到了…

手摸手入门Springboot2.7集成Swagger2.9.2

环境介绍 技术栈 springbootmybatis-plusmysqloracleSwagger 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 REST软件架构风格 REST即表述性状态传递&#xff08;英文&#xff1a;Representational State T…

Linux项目自动化构建工具-make/Makefile

背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了&#xff0c;一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;…

我把微信群聊机器人项目开源

▍PART 序 开源项目地址》InsCode - 让你的灵感立刻落地 目前支持的回复 ["抽签", "天气", "讲笑话", "讲情话", "梦到", "解第", "动漫图", "去水印-", "历史今天", "星座-…

2023年【北京市安全员-B证】试题及解析及北京市安全员-B证证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 北京市安全员-B证试题及解析根据新北京市安全员-B证考试大纲要求&#xff0c;安全生产模拟考试一点通将北京市安全员-B证模拟考试试题进行汇编&#xff0c;组成一套北京市安全员-B证全真模拟考试试题&#xff0c;学员…

word批量图片导出wps office word 图片批量导出

word批量导出图片教程 背景 今天遇到了一个场景&#xff0c;因为word里的图片打开看太模糊了&#xff0c;如果一个一个导出来太麻烦。想批量将word中的图片全部导出 但是&#xff0c;wps导出的时候需要会员 教程开始&#xff1a; 将word保存为 .docx 格式&#xff0c;可以按F1…

JWT登录认证(2认证)

备注说明&#xff1a; 用户登录成功后&#xff0c;系统会自动下发JWT令牌&#xff0c;然后在后续的每次请求中&#xff0c;浏览器都需要在请求头header中携带到服务器&#xff0c;请求头的名称为Authorization&#xff0c;值为登录时下发的JWT令牌。 如果检测到用户未登录&…

mysql之搭建MHA架构实现高可用

1、定义 全称是masterhigh avaliabulity。基于主库的高可用环境下可以实现主从复制及故障切换&#xff08;基于主从复制才能故障切换&#xff09; MHA最少要求一主两从&#xff0c;半同步复制模式 2、作用 解决mysql的单点故障问题。一旦主库崩溃&#xff0c;MHA可以在0-30…