Abbyy 入门(一)

Abbyy 图片(PDF) 转word 实践

1. 简介

Abbyy 是一个俄罗斯软件,官网:https://www.abbyy.com (中文: https://www.abbyy.com ) 作为OCR软件拥有自己的客户端通知提供SDK供开发使用,功能强大。这里是一个简单的OCR图片文字识别Demo。

2. 干货

1. 安装

(1)从http://www.abbyychina.com/xiazai.html 下载ABBYY FineReader。

(2)安装该引擎。

(3)激活序列号:在License Manager中激活你的序列号(该序列号会出现在后文的代码中)。如图:

激活.png

到此为止准备工作就OK了。

2.实践(单个转换)

执行流程:

流程.png

(1)自定义一个配置文件,用户设置文件转换路径、用户序列号等

    public class FreConfig
    {
        /// <summary>
        /// 是否64位
        /// </summary>
        /// <returns></returns>
        private static bool Is64Bit
        {
            get { return IntPtr.Size == 8; }
        }

        /// <summary>
        /// 开发者序列号
        /// </summary>
        /// <returns></returns>
        public static string DeveloperSN
        {
            get { return "xxxxxxxxx"; }
        }

        /// <summary>
        /// dll 文件夹路径
        /// </summary>
        public static string DllFolder
        {
            get
            {
                if (Is64Bit)
                {
                    return "C:\\Program Files\\ABBYY SDK\\11\\FineReader Engine\\Bin64";
                }
                else
                {
                    return "32位dll地址";
                }
            }
        }

        /// <summary>
        /// 文件存放路径
        /// </summary>
        public static string FileFolder
        {
            get { return @"C:\Users\帝子降兮\Desktop\AbbyyImage"; }
        }
    }

==注意: 1. DeveloperSN 就是之前在License Manager中激活的序列号 2. 这里是实践64bit机器==

(2) 加载引擎

 /// <summary>
    ///  装载和初始化(卸载)引擎
    /// </summary>
    public class EngineLoader : IDisposable
    {
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
        private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
        [DllImport("kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
        [DllImport("kernel32.dll")]
        private static extern bool FreeLibrary(IntPtr hModule);


        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        private delegate int GetEngineObject(string devSN, ref IEngine engine);
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate int DeinitializeEngine();
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate int DllCanUnloadNow();

        private IEngine engine = null;
        private IntPtr dllHandle = IntPtr.Zero;
        private GetEngineObject getEngineObject = null;
        private DeinitializeEngine deinitializeEngine = null;
        private DllCanUnloadNow dllCanUnloadNow = null;

        /// <summary>
        /// 引擎对象
        /// </summary>
        public IEngine Engine
        {
            get
            {
                return engine;
            }
        }

        /// <summary>
        /// 加载引擎
        /// </summary>
        /// <param name="developerSN">开发者序列号</param>
        public EngineLoader(string developerSN)
        {
            string enginePath = Path.Combine(FreConfig.DllFolder, "FREngine.dll");

            try
            {
                dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
                if (dllHandle == IntPtr.Zero)
                {
                    throw new Exception("无法加载" + enginePath);
                }

                IntPtr getEngineObjectPtr = GetProcAddress(dllHandle, "GetEngineObject");
                if (getEngineObjectPtr == IntPtr.Zero)
                {
                    throw new Exception("无法找到 GetEngineObject 函数");
                }

                IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
                if (deinitializeEnginePtr == IntPtr.Zero)
                {
                    throw new Exception("无法找到 DeinitializeEngine 函数");
                }

                IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
                if (dllCanUnloadNowPtr == IntPtr.Zero)
                {
                    throw new Exception("无法找到 DllCanUnloadNow 函数");
                }

                //将指针转换为委托
                getEngineObject = (GetEngineObject)Marshal.GetDelegateForFunctionPointer(getEngineObjectPtr, typeof(GetEngineObject));
                deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(deinitializeEnginePtr, typeof(DeinitializeEngine));
                dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(dllCanUnloadNowPtr, typeof(DllCanUnloadNow));

                //获取引擎对象
                int hresult = getEngineObject(developerSN, ref engine);
                Marshal.ThrowExceptionForHR(hresult);
            }
            catch
            {
                engine = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                FreeLibrary(dllHandle);
                dllHandle = IntPtr.Zero;
                getEngineObject = null;
                deinitializeEngine = null;
                dllCanUnloadNow = null;
            }
        }

        /// <summary>
        /// 卸载引擎
        /// </summary>
        public void Dispose()
        {
            if (engine == null)
            {
                return;
            }
            engine = null;
            int hresult = deinitializeEngine();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            hresult = dllCanUnloadNow();
            if (hresult == 0)
            {
                FreeLibrary(dllHandle);
            }
            dllHandle = IntPtr.Zero;
            getEngineObject = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            Marshal.ThrowExceptionForHR(hresult);
        }

    }

(3) 执行逻辑

 public void Start()
        {
            try
            {
                using (EngineLoader loade = new EngineLoader(FreConfig.DeveloperSN))
                {
                    //加载配置文件,其他配置文件可见帮助文档
                    loade.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
                   
                    string imagePath = Path.Combine(FreConfig.FileFolder, @"20161121103603.JPG");

                    //设置引擎的消息语言(默认英语)
                    loade.Engine.MessagesLanguage = MessagesLanguageEnum.ML_ChinesePRC;

                    FRDocument document = loade.Engine.CreateFRDocument();
                    try
                    {
                        //将图片作为照片处理
                        var prepareMode = loade.Engine.CreatePrepareImageMode();
                        prepareMode.PhotoProcessingMode = PhotoProcessingModeEnum.PPM_TreatAsPhoto;

                        //设置解析参数
                        DocumentProcessingParams processingParams = loade.Engine.CreateDocumentProcessingParams();

                        //设置解析的语言(这儿选择中英混合,详见帮助文档)
                        RecognizerParams recognizerParams = processingParams.PageProcessingParams.RecognizerParams;
                        recognizerParams.SetPredefinedTextLanguage("ChinesePRC,English");

                        //添加文件到document
                        document.AddImageFile(imagePath, prepareMode, null);

                        // 执行解析
                        document.Process(processingParams);

                        //var x = document.BasicLanguage;

                        //导出指定格式
                        document.Export(Path.Combine(FreConfig.FileFolder, @"Demo.docx"), FileExportFormatEnum.FEF_DOCX, null);
                    }
                    catch (Exception ex)
                    {
                    }
                    finally
                    {
                        document.Close();
                    }
                }

            }
            catch (Exception ex)
            {

            }
        }

直接调用Start() 方法,一个简单的示例就完成了。
注意: 在测试的时候一定要注意SetPredefinedTextLanguage,默认是英文,如果你的图片或pdf里面是中文的话,==会出现乱码==,同时abbyy 支持设置导出后文档的格式、识别图片文字时旋转,背景色控制等等功能,详见帮助文档

3. 实践(批量转换)

执行流程与单个转换的执行流程大致相似,只是我们创建的不是FRDocument 而是BatchProcessor,同时批量执行时 需要实现IImageSource接口,完成图片(PDF)文件的遍历。

public class FileAdapterImpl : IFileAdapter
{
        private string fileName;

        public FileAdapterImpl(string fileName)
        {
            this.fileName = fileName;
        }

        public string GetFileName()
        {
            return fileName;
        }

        public IntsCollection GetPagesToProcess()
        {
            return null;
        }

        public string GetPassword()
        {
            return string.Empty;
        }
    }

    public class ImageSourceImpl : IImageSource
    {
        private bool isEmpty;
        private Queue<string> imagesNames = new Queue<string>();

        public ImageSourceImpl(string sourceDir)
        {
            string extensionsMask = "bmp|dcx|pcx|png|jpg|jpeg|jp2|jpc|jfif|pdf|tif|tiff|gif|djvu|djv|jb2";
            string[] fileNames = Directory.GetFiles(sourceDir, "*.*");

            foreach (string fileName in fileNames)
            {
                if (extensionsMask.Contains(Path.GetExtension(fileName).Remove(0, 1).ToLower()))
                {
                    imagesNames.Enqueue(fileName);
                }
            }

            isEmpty = imagesNames.Count == 0;
        }

        public IFileAdapter GetNextImageFile()
        {
            if (isEmpty)
            {
                return null;
            }
            FileAdapterImpl fileAdapter = new FileAdapterImpl(imagesNames.Dequeue());
            isEmpty = imagesNames.Count == 0;
            return fileAdapter;
        }

        public bool IsEmpty()
        {
            return this.isEmpty;
        }
    }

执行逻辑:

 public void Start()
        {
            try
            {
                using (EngineLoader loade = new EngineLoader(FreConfig.DeveloperSN))
                {
                    loade.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy");

                    if (!Directory.Exists(sourceFolder))
                    {
                        throw new Exception(sourceFolder + "不存在");
                    }

                    if (!Directory.Exists(resultFolder))
                    {
                        DirectoryInfo newDir = Directory.CreateDirectory(resultFolder);
                        if (!newDir.Exists)
                        {
                            throw new Exception("无法创建" + resultFolder);
                        }
                    }

                    ImageSourceImpl imageSource = new ImageSourceImpl(sourceFolder);
                    if (imageSource.IsEmpty())
                    {
                        throw new Exception("转换文件夹中没有文件");
                    }

                    //创建批量处理
                    BatchProcessor batchProcessor = loade.Engine.CreateBatchProcessor();

                    //设置语言
                    PageProcessingParams processingParams = loade.Engine.CreatePageProcessingParams();
                    processingParams.RecognizerParams.SetPredefinedTextLanguage("ChinesePRC,English");

                    //开始解析
                    batchProcessor.Start(imageSource, null, null, processingParams, null);
                    FRPage page = batchProcessor.GetNextProcessedPage();
                    while (page != null)
                    {
                        //序列化
                        page.Synthesize(null);

                        //导出目标文件
                        string resultFilePath = Path.Combine(resultFolder, Path.GetFileName(page.SourceImagePath) + ".docx");
                        page.Export(resultFilePath, FileExportFormatEnum.FEF_DOCX, null);
                        page = batchProcessor.GetNextProcessedPage();
                    }
                }

            }
            catch (Exception ex)
            {

            }
        }

一个简单的demo就完成了,更多高级的功能可以查看帮助文档(其他地方貌似也查不到。。。)

帮助文档路径:C:\Program Files\ABBYY SDK\11\FineReader Engine\Help\FREngine11.chm,我是默认装在C盘的,你可以根据自己的安装路径查找。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,080评论 4 62
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,646评论 18 139
  • 不知道最近是出于什么心态,老是搜一些虐心的影片来虐自己,今天要谈谈的是《穿条纹睡衣的男孩》。看这部影片之前就看过海...
    麻球麻麻阅读 809评论 0 2
  • 世界之大,无奇不有。人也一样。
    柠萌613阅读 160评论 0 0
  • 当内心有放不下,是不是就代表未看透
    顾_1a17阅读 175评论 0 0