Files
PDFPatcher/App/Processor/ModiOcr.cs
2024-10-07 16:17:27 +08:00

273 lines
7.8 KiB
C#

#define DEBUGOCR
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using PDFPatcher.Common;
using PDFPatcher.Model;
namespace PDFPatcher.Processor
{
public class ModiOcr
{
//static string _modiPath;
const string __MachineRegistryPath = @"Installer\Components\61BA386016BD0C340BBEAC273D84FD5F";
const string __UserRegistryPath = @"Software\Microsoft\Installer\Components\61BA386016BD0C340BBEAC273D84FD5F";
static readonly List<int> __InstalledLanguage = DetectInstalledLanguages();
static readonly bool __ModiInstalled = DetectModi();
public int LangID { get; set; }
public bool StretchPage { get; set; }
public bool OrientPage { get; set; }
public WritingDirection WritingDirection { get; set; }
internal static bool ModiInstalled => __ModiInstalled;
#region COMInterop
static readonly object[] EmptyArray = [];
static object Create(string type) {
var t = Type.GetTypeFromProgID(type);
if (t == null) {
return null;
}
return Activator.CreateInstance(t);
}
static object Call(object instance, string method, params object[] parameters) {
try {
return instance.GetType().InvokeMember(method, BindingFlags.InvokeMethod, null, instance, parameters);
}
catch (Exception ex) {
if (ex.InnerException != null) {
throw ex.InnerException;
}
throw;
}
}
static object Get(object instance, string propertyName) {
return instance.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, instance, EmptyArray);
}
static T Get<T>(object instance, string propertyName) {
return (T)Get(instance, propertyName);
}
static object Get(object instance, string propertyName, int index) {
return instance.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, instance, new object[1] { index });
}
static void FinalReleaseComObjects(params object[] objs) {
for (int i = 0; i < objs.Length; i++) {
if (objs[i] == null) {
continue;
}
try {
var r = Marshal.ReleaseComObject(objs[i]);
System.Diagnostics.Debug.Assert(r == 0);
}
catch (Exception ex) {
System.Diagnostics.Debug.WriteLine("释放对象时出现错误:" + ex.Message);
}
}
}
#endregion
static bool DetectModi() {
if (__InstalledLanguage.Count == 0) {
return false;
}
var ocr = Create("MODI.Document");
if (ocr == null) {
return false;
}
FinalReleaseComObjects(ocr);
return true;
}
internal static bool IsLanguageInstalled(int langID) {
return __InstalledLanguage.Contains(langID);
}
static List<int> DetectInstalledLanguages() {
var l = new List<int>();
try {
using (var k = Registry.CurrentUser.OpenSubKey(__UserRegistryPath)) {
DetectInstalledLanguages(k, l);
}
using (var k = Registry.ClassesRoot.OpenSubKey(__MachineRegistryPath)) {
DetectInstalledLanguages(k, l);
}
}
catch (Exception ex) {
Tracker.DebugMessage("OCR registry error: " + ex.Message);
}
return l;
}
private static void DetectInstalledLanguages(RegistryKey k, List<int> list) {
if (k == null) {
return;
}
foreach (var n in k.GetValueNames()) {
var i = n.ToInt32();
if (i == 0) {
continue;
}
if (Constants.Ocr.LangIDs.Contains(i) && list.Contains(i) == false) {
list.Add(i);
}
}
}
internal void Ocr(string path, string saveImagePath, List<TextLine> results) {
object ocr = null, images = null, image = null, layout = null;
object merge = null, mergeImages = null;
IEnumerable words = null, rects = null;
TextLine line = null;
int lineID = -1, regionID = -1;
int cl, cr;
TextInfo cti = null;
try {
#if DEBUGOCR
Tracker.TraceMessage("创建识别引擎对象。");
#endif
ocr = Create("MODI.Document");
var p = Environment.CurrentDirectory;
//Environment.CurrentDirectory = _modiPath;
#if DEBUGOCR
Tracker.TraceMessage("读取识别图像:" + path);
#endif
Call(ocr, "Create", path);
#if DEBUGOCR
Tracker.TraceMessage("执行识别:" + LangID);
#endif
Call(ocr, "OCR", ValueHelper.MapValue(LangID, Constants.Ocr.LangIDs, Constants.Ocr.OcrLangIDs), OrientPage, StretchPage);
Environment.CurrentDirectory = p;
#if DEBUGOCR
Tracker.TraceMessage("读取识别结果。");
#endif
images = Get(ocr, "Images");
image = Get(images, "Item", 0);
layout = Get(image, "Layout");
#if DEBUGOCR && DEBUG
var l = new StreamWriter(@"m:\ocr.txt", true, System.Text.Encoding.Default);
l.WriteLine("path: " + path);
#endif
words = Get<IEnumerable>(layout, "Words");
foreach (var word in words) {
var ti = new TextInfo();
var w = Get<string>(word, "Text");
ti.Text = w;
var pos = new float[4];
rects = Get<IEnumerable>(word, "Rects");
foreach (var rect in rects) {
int r = Get<int>(rect, "Left");
if (r < pos[0] || pos[0] == 0) {
pos[0] = r;
}
r = Get<int>(rect, "Top");
if (r > pos[1]) {
pos[1] = r;
}
r = Get<int>(rect, "Right");
if (r > pos[2]) {
pos[2] = r;
}
r = Get<int>(rect, "Bottom");
if (r < pos[3] || pos[3] == 0) {
pos[3] = r;
}
FinalReleaseComObjects(rect);
}
Marshal.ReleaseComObject(rects);
ti.Region = new Bound(pos[0], pos[3], pos[2], pos[1]);
ti.Font = null;
ti.Size = pos[3] - pos[1];
if (ti.Text.Length == 1) {
switch (ti.Text[0]) {
case '一':
var r = ti.Region;
ti.Size = r.Width > r.Height ? r.Width : r.Height;
var s = (int)Math.Ceiling(ti.Size / 2 - (r.Width > r.Height ? r.Height : r.Width) / 2);
ti.Region = new Bound(r.Left, r.Bottom + s, r.Right, r.Top - s);
break;
case '\u2022':
ti.Text = "·";
break;
}
}
if (Char.IsLetterOrDigit(ti.Text[0]) && ti.Size > 0) {
ti.LetterWidth = ti.Size;
}
cl = Get<int>(word, "LineID");
cr = Get<int>(word, "RegionID");
var sl = (cl == lineID && cr == regionID); // 处于同一行
if (sl && WritingDirection != WritingDirection.Unknown) {
sl = cti?.Region.IsAlignedWith(ti.Region, WritingDirection) == true;
}
if (sl) {
if (cti != null && cti.Region == ti.Region) {
cti.Text += ti.Text;
}
else {
line.AddText(ti);
}
}
else {
if (line != null) {
results.Add(line);
}
line = new TextLine(ti) {
SuppressTextInfoArrangement = true
};
lineID = cl;
regionID = cr;
}
cti = ti;
#if DEBUGOCR && DEBUG
l.WriteLine($"{ti.Size}\t{ti.Text}\t{ti.Region.Top} {ti.Region.Left} {ti.Region.Bottom} {ti.Region.Right}");
#endif
FinalReleaseComObjects(word);
}
Marshal.ReleaseComObject(words);
if (line != null) {
results.Add(line);
}
#if DEBUGOCR && DEBUG
l.Close();
#endif
if (FileHelper.IsPathValid(saveImagePath)) {
#if DEBUGOCR
Tracker.TraceMessage("保存识别后图像路径:" + saveImagePath);
#endif
if (File.Exists(saveImagePath) == false) {
Call(ocr, "SaveAs", saveImagePath, -1);
}
else {
merge = Create("MODI.Document");
Call(merge, "Create", saveImagePath);
mergeImages = Get(merge, "Images");
Call(mergeImages, "Add", image, Get(mergeImages, "Item", 0));
Call(merge, "Save");
}
}
#if DEBUGOCR
Tracker.TraceMessage("完成识别操作。");
#endif
Call(ocr, "Close", false);
}
finally {
#if DEBUGOCR
Tracker.TraceMessage("释放识别引擎对象。");
#endif
FinalReleaseComObjects(rects, words, layout, image, images, ocr);
FinalReleaseComObjects(merge, mergeImages);
words = rects = null;
ocr = images = image = layout = null;
merge = mergeImages = null;
}
}
}
}