// Copyright (c) 2021 raoyutian Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System;
using System.Linq;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
namespace PaddleOCRSharp
{
///
/// PaddleOCR识别引擎对象
///
public class PaddleOCREngine : EngineBase
{
#region PaddleOCR API
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void Initialize(string det_infer, string cls_infer, string rec_infer, string keys, OCRParameter parameter);
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void Initializejson(string det_infer, string cls_infer, string rec_infer, string keys, string parameterjson);
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern IntPtr Detect(string imagefile);
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern IntPtr DetectByte(byte[] imagebytedata, long size);
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern IntPtr DetectBase64(string imagebase64);
[DllImport(PaddleOCRdllName, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern int FreeEngine();
#endregion
#region 文本识别
///
/// PaddleOCR识别引擎对象初始化
///
/// 模型配置对象,如果为空则按默认值
/// 识别参数,为空均按缺省值
public PaddleOCREngine(OCRModelConfig config, OCRParameter parameter = null) : base()
{
#if NET35
#else
if (!Environment.Is64BitProcess) throw new NotSupportedException($"PaddleOCRSharp只支持64位进程。");
#endif
//0:不支持,1:AVX,2:AVX2
if (IsCPUSupport() <= 0) throw new NotSupportedException($"当前CPU指令集不支持PaddleOCR。The CPU instruction set is not surpport PaddleOCR");
if (parameter == null) parameter = new OCRParameter();
if (config == null)
{
string root= GetRootDirectory();
config = new OCRModelConfig();
string modelPathroot = root + @"\inference";
config.det_infer = modelPathroot + @"\ch_PP-OCRv3_det_infer";
config.cls_infer = modelPathroot + @"\ch_ppocr_mobile_v2.0_cls_infer";
config.rec_infer = modelPathroot + @"\ch_PP-OCRv3_rec_infer";
config.keys = modelPathroot + @"\ppocr_keys.txt";
}
if (!Directory.Exists(config.det_infer)) throw new DirectoryNotFoundException(config.det_infer);
if (!Directory.Exists(config.cls_infer)) throw new DirectoryNotFoundException(config.cls_infer);
if (!Directory.Exists(config.rec_infer)) throw new DirectoryNotFoundException(config.rec_infer);
if (!File.Exists(config.keys)) throw new FileNotFoundException(config.keys);
Initialize(config.det_infer, config.cls_infer, config.rec_infer, config.keys, parameter);
}
///
/// PaddleOCR识别引擎对象初始化
///
/// 模型配置对象,如果为空则按默认值
/// 识别参数json字符串
public PaddleOCREngine(OCRModelConfig config, string parameterjson):base()
{
#if NET35
#else
if (!Environment.Is64BitProcess) throw new NotSupportedException($"PaddleOCRSharp只支持64位进程。");
#endif
//0:不支持,1:AVX,2:AVX2
if (IsCPUSupport() <= 0) throw new NotSupportedException($"当前CPU指令集不支持PaddleOCR。The CPU instruction set is not surpport PaddleOCR");
if (config == null)
{
string root = GetRootDirectory();
config = new OCRModelConfig();
string modelPathroot = root + @"\inference";
config.det_infer = modelPathroot + @"\ch_PP-OCRv3_det_infer";
config.cls_infer = modelPathroot + @"\ch_ppocr_mobile_v2.0_cls_infer";
config.rec_infer = modelPathroot + @"\ch_PP-OCRv3_rec_infer";
config.keys = modelPathroot + @"\ppocr_keys.txt";
}
if ( string.IsNullOrEmpty(parameterjson))
{
parameterjson = GetRootDirectory();
parameterjson += @"\inference\PaddleOCR.config.json";
if(!File.Exists(parameterjson)) throw new FileNotFoundException(parameterjson);
parameterjson = File.ReadAllText(parameterjson);
}
if (!Directory.Exists(config.det_infer)) throw new DirectoryNotFoundException(config.det_infer);
if (!Directory.Exists(config.cls_infer)) throw new DirectoryNotFoundException(config.cls_infer);
if (!Directory.Exists(config.rec_infer)) throw new DirectoryNotFoundException(config.rec_infer);
if (!File.Exists(config.keys)) throw new FileNotFoundException(config.keys);
Initializejson(config.det_infer, config.cls_infer, config.rec_infer, config.keys, parameterjson);
}
///
/// 对图像文件进行文本识别
///
/// 图像文件
/// OCR识别结果
public OCRResult DetectText(string imagefile)
{
if (!File.Exists(imagefile)) throw new Exception($"文件{imagefile}不存在");
var imagebyte = File.ReadAllBytes(imagefile);
var result= DetectText(imagebyte);
imagebyte = null;
return result;
}
///
///对图像对象进行文本识别
///
/// 图像
/// OCR识别结果
public OCRResult DetectText(Image image)
{
if (image == null) throw new ArgumentNullException("image");
var imagebyte = ImageToBytes(image);
var result = DetectText(imagebyte);
imagebyte = null;
return result;
}
///
///文本识别
///
/// 图像内存流
/// OCR识别结果
public OCRResult DetectText(byte[] imagebyte)
{
if (imagebyte == null) throw new ArgumentNullException("imagebyte");
var ptrResult = DetectByte(imagebyte, imagebyte.LongLength);
return ConvertResult(ptrResult);
}
///
///文本识别
///
/// 图像base64
/// OCR识别结果
public OCRResult DetectTextBase64(string imagebase64)
{
if (imagebase64 == null || imagebase64 == "") throw new ArgumentNullException("imagebase64");
IntPtr ptrResult = DetectBase64(imagebase64);
return ConvertResult(ptrResult);
}
///
/// 结果解析
///
///
///
private OCRResult ConvertResult(IntPtr ptrResult)
{
OCRResult result = new OCRResult();
try
{
string json = Marshal.PtrToStringUni(ptrResult);
List textBlocks = JsonHelper.DeserializeObject>(json);
result.JsonText = json;
result.TextBlocks = textBlocks;
Marshal.FreeHGlobal(ptrResult);
}
catch (Exception ex)
{
throw new Exception("OCR结果Json反序列化失败。", ex);
}
return result;
}
#endregion
#region 表格识别
///
///结构化文本识别
///
/// 图像
/// 表格识别结果
public OCRStructureResult DetectStructure(Image image)
{
if (image == null) throw new ArgumentNullException("image");
var imagebyte = ImageToBytes(image);
OCRResult result= DetectText(imagebyte);
List blocks = result.TextBlocks;
if (blocks == null || blocks.Count == 0) return new OCRStructureResult();
var listys = getzeroindexs(blocks.OrderBy(x => x.BoxPoints[0].Y).Select(x => x.BoxPoints[0].Y).ToArray(), 10);
var listxs = getzeroindexs(blocks.OrderBy(x => x.BoxPoints[0].X).Select(x => x.BoxPoints[0].X).ToArray(), 10);
int rowcount = listys.Count;
int colcount = listxs.Count;
OCRStructureResult structureResult = new OCRStructureResult();
structureResult.TextBlocks = blocks;
structureResult.RowCount = rowcount;
structureResult.ColCount = colcount;
structureResult.Cells = new List();
for (int i = 0; i < rowcount; i++)
{
int y_min = blocks.OrderBy(x => x.BoxPoints[0].Y).OrderBy(x => x.BoxPoints[0].Y).ToList()[listys[i]].BoxPoints[0].Y;
int y_max = 99999;
if (i < rowcount - 1)
{
y_max = blocks.OrderBy(x => x.BoxPoints[0].Y).ToList()[listys[i + 1]].BoxPoints[0].Y;
}
for (int j = 0; j < colcount; j++)
{
int x_min = blocks.OrderBy(x => x.BoxPoints[0].X).ToList()[listxs[j]].BoxPoints[0].X;
int x_max = 99999;
if (j < colcount - 1)
{
x_max = blocks.OrderBy(x => x.BoxPoints[0].X).ToList()[listxs[j + 1]].BoxPoints[0].X;
}
var textBlocks = blocks.Where(x => x.BoxPoints[0].X < x_max && x.BoxPoints[0].X >= x_min && x.BoxPoints[0].Y < y_max && x.BoxPoints[0].Y >= y_min).OrderBy(u => u.BoxPoints[0].X);
var texts = textBlocks.Select(x => x.Text).ToArray();
StructureCells cell = new StructureCells();
cell.Row = i;
cell.Col = j;
#if NET35
cell.Text = string.Join("", texts);
#else
cell.Text = string.Join("", texts);
#endif
cell.TextBlocks = textBlocks.ToList();
structureResult.Cells.Add(cell);
}
}
return structureResult;
}
///
/// 计算表格分割
///
///
///
///
private List getzeroindexs(int[] pixellist, int thresholdtozero = 10)
{
List zerolist = new List();
zerolist.Add(0);
for (int i = 0; i < pixellist.Length; i++)
{
if ((i < pixellist.Length - 1)
&& (Math.Abs(pixellist[i + 1] - pixellist[i])) > thresholdtozero)
{
//突增点
zerolist.Add(i + 1);
}
}
return zerolist;
}
#endregion
#region Dispose
///
/// 释放对象
///
public override void Dispose()
{
FreeEngine();
}
#endregion
}
}