c# 本地离线OCR读取图片上文字(PaddleOCR),通过鼠标点击获取对应位置文字

      c#可用的ocr库比较多,但持续更新且对中文友好的库就不多了。PaddlePaddle(飞桨)是百度开发的开源深度学习平台,但源码是phython,不过有大神饶玉田(明月心)封装了PaddleOCRSharp,还在持续优化更新,可离线使用,最主要的是免费,中英文识别成功率较高。PaddleOCRSharp还支持识别表格,后面有时间再写说明。

    程序原理是首先读取图片,然后转换为bmp格式,再由PaddleOCRSharp处理bmp图片,产生一组包含位置信息的字符串,字符串内的文字即是由图片上转换而来,位置信息可用于在图片上画框标识,方便快速选取各处文字,也可扩展后续排版。

      首先设置一个pictureBox,SizeMode 为Zoom,可通过鼠标滚轮缩放图片大小,实际是缩放pictureBox控件大小;按下鼠标左键拖动图片,再设置控件到边距离,防止图片被拖出视窗;记录鼠标单击位置坐标,转换为图片上坐标,用于提取图片上该点文字。

      接下来是一些主要步骤

1.安装paddleocrsharp包,图示两个都要安装。添加引用,using PaddleOCRSharp;平台设成X64,不支持32位cpu。

关键就一句 ocrResult = engine.DetectText(bmp);读取pictureBox控件内的图片(bmp)。

private void btnRecognise_Click(object sender, EventArgs e)
{
    if (!(pictureBox1.Image == null))
    {
        OCRModelConfig config = null;
        OCRParameter oCRParameter = new OCRParameter();
        OCRResult ocrResult = new OCRResult();
        //建议程序全局初始化一次即可,不必每次识别都初始化,容易报错。
        PaddleOCREngine engine = new PaddleOCREngine(config, oCRParameter);
        {
            ocrResult = engine.DetectText(bmp);
        }
        System.Threading.Thread.Sleep(200);
        Application.DoEvents();
        richTextBox1.Clear();
        richTextBox1.Show();
        if (ocrResult != null)
        {
            foreach (var item in ocrResult.TextBlocks)
            {
                richTextBox1.AppendText(item.Text + "\n");
            }
            polygons.Clear();
            Bitmap bmphuaxian = new Bitmap(bmp.Width, bmp.Height);
            using (Graphics g = Graphics.FromImage(bmphuaxian))
            {
                g.DrawImage(bmp, 0, 0,bmp.Width, bmp.Height);
                // 设置文本的颜色为红色
                SolidBrush blackBrush = new SolidBrush(Color.Red);
                // 设置文本的格式,如字体、大小、样式等
                Font font = new Font("Arial", 18, FontStyle.Bold);
                int i = 1;
                foreach (var item in ocrResult.TextBlocks)
                {
                    // 在图像上画多边形
                    g.DrawPolygon(new Pen(Brushes.Red, 2), item.BoxPoints.Select(x => new PointF() { X = x.X, Y = x.Y }).ToArray());
                    // 记录多边形
                    polygons.Add((item.BoxPoints.Select(x => new Point() { X = x.X, Y = x.Y }).ToArray()));
                    // 在图像上写数字
                    g.DrawString(i.ToString().PadLeft(2, '0'), font, blackBrush, new PointF(item.BoxPoints.First().X, item.BoxPoints.First().Y));
                    i++;
                }
            }
            pictureBox1.Image = bmphuaxian;
        }
    }
    else
    {
        MessageBox.Show("请选择图像!");
    }
}

2.以上已经在图片上画标识框及编号,可以通过编号获取该区域的文字信息;当鼠标左击图片时可以通过获取鼠标在图片上的位置坐标,进而判断在哪个标识框内,获取该区域的文字信息,并放入粘贴板内。

#region 获取图片像素坐标
/// <summary>
/// 获取图片像素坐标
/// </summary>
/// <param name="pictureBoxSize">pictureBox框体的尺寸</param>
/// <param name="imageSize">图片的尺寸</param>
/// <param name="pictureBoxPoint">鼠标在pictureBox上面的坐标</param>  
/// <param name="imagePoint">鼠标在图片中的像素坐标</param>
private void GetImagePixLocation(Size pictureBoxSize, Size imageSize, Point pictureBoxPoint, out Point imagePoint)

{

    imagePoint = new Point(0, 0);

    double scale;

    int detalInHeight = 0;

    int detalInWidth = 0;

    if (Convert.ToDouble(pictureBoxSize.Width) / pictureBoxSize.Height > Convert.ToDouble(imageSize.Width) / imageSize.Height)

    {

        scale = 1.0 * imageSize.Height / pictureBoxSize.Height;

        detalInWidth = Convert.ToInt32((pictureBoxSize.Width * scale - imageSize.Width) / 2.0);

    }

    else

    {

        scale = 1.0 * imageSize.Width / pictureBoxSize.Width;

        detalInHeight = Convert.ToInt32((pictureBoxSize.Height * scale - imageSize.Height) / 2.0);

    }

    imagePoint.X = Convert.ToInt32(pictureBoxPoint.X * scale - detalInWidth);

    imagePoint.Y = Convert.ToInt32(pictureBoxPoint.Y * scale - detalInHeight);

}
#endregion
#region 判断坐标点是否在多边形区域内
/// <summary>
/// 射线法,判断一个坐标点是否在多边形区域内
/// </summary>
/// <param name="p"></param>
/// <param name="poly"></param>
/// <returns></returns>
public string rayCasting(PointF p, Point[] poly)
{
    var px = p.X;
    var py = p.Y;
    var flag = false;

    int l = poly.Length;
    int j = l - 1;

    for (var i = 0; i < l; i++)
    {
        var sx = poly[i].X;
        var sy = poly[i].Y;
        var tx = poly[j].X;
        var ty = poly[j].Y;

        // 点与多边形顶点重合
        if ((sx == px && sy == py) || (tx == px && ty == py))
        {
            return "on";
        }

        // 判断线段两端点是否在射线两侧
        if ((sy < py && ty >= py) || (sy >= py && ty < py))
        {
            // 线段上与射线 Y 坐标相同的点的 X 坐标
            var x = sx + (py - sy) * (tx - sx) / (ty - sy);

            // 点在多边形的边上
            if (x == px)
            {
                return "on";
            }

            // 射线穿过多边形的边界
            if (x > px)
            {
                flag = !flag;
            }

        }

        j = i;
    }

    // 射线穿过多边形边界的次数为奇数时点在多边形内
    return flag ? "in" : "out";
}
#endregion
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)

{
    _moveFlag = false;
    Point mousePos = pictureBox1.PointToClient(Cursor.Position);
    if (pictureBox1.Image != null)
    {
        Point mousePosInPic;
        GetImagePixLocation(pictureBox1.Size, pictureBox1.Image.Size, mousePos, out mousePosInPic);
        for (int i = 0; i < polygons.Count; i++)
        {
            if ((rayCasting(mousePosInPic, polygons[i]) != "out"))
            {
                textBox1.Text = (i + 1).ToString();
                break; // 找到点击的多边形后退出循环
            }
        }

    }
}
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            int number;
            bool isValid = int.TryParse(textBox1.Text, out number);
            if (isValid & number > 0 & number < richTextBox1.Lines.Length)
            {
                textBox2.Text = richTextBox1.Lines[number - 1];
            }
            else { textBox2.Text = ""; }
            Clipboard.SetDataObject(textBox2.Text);
        } 

3.鼠标滚动放大缩小图片,程序初始化时注册鼠标监听,注意richTextBox1要额外加一个监听。                   this.MouseWheel += new MouseEventHandler(MainForm_MouseWheel);
            richTextBox1.MouseWheel += new MouseEventHandler(MainForm_MouseWheel);

//鼠标滚轮滚动事件,实现在panel或pictureBox中滚动滚轮可实现放大和缩小图片,每次缩放步长为1.2倍。
private void MainForm_MouseWheel(object sender, MouseEventArgs e)

{

    if (_isCanDealPicture)

    {

        double step = 1.2;

        int px = Cursor.Position.X - pictureBox1.Location.X;

        int py = Cursor.Position.Y - pictureBox1.Location.Y;

        if (e.Delta > 0)

        {

            if (pictureBox1.Height >= Screen.PrimaryScreen.Bounds.Height * 20)
            { return; }
            pictureBox1.Height = (int)(pictureBox1.Height * step);

            pictureBox1.Width = (int)(pictureBox1.Width * step);

            int px_add = (int)(px * (step - 1.0));

            int py_add = (int)(py * (step - 1.0));

            pictureBox1.Location = new Point(pictureBox1.Location.X - px_add, pictureBox1.Location.Y - py_add);

            Application.DoEvents();

        }

        else

        {

            if (pictureBox1.Width <= Screen.PrimaryScreen.Bounds.Width / 10)

            { return; }
            pictureBox1.Height = (int)(pictureBox1.Height / step);

            pictureBox1.Width = (int)(pictureBox1.Width / step);

            int px_add = (int)(px * (1.0 - 1.0 / step));

            int py_add = (int)(py * (1.0 - 1.0 / step));

            pictureBox1.Location = new Point(pictureBox1.Location.X + px_add, pictureBox1.Location.Y + py_add);

            Application.DoEvents();

        }

    }

}

软件操作效果:

软件下载地址:https://download.csdn.net/download/mybluedesky/89651933

源程序下载地址:https://download.csdn.net/download/mybluedesky/89651943

再设计一个小型的便捷软件,通过快捷键截屏选取要转换的文字图片直接将文字内容复制到粘贴板,

截屏软件下载地址:https://download.csdn.net/download/mybluedesky/89658619

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值