首页 > 代码库 > [转老马的文章]MODI中的OCR模块

[转老马的文章]MODI中的OCR模块

作者:马健
邮箱:stronghorse_mj@hotmail.com
发布:2012.07.02
更新:
2012.07.09
补充非简体中文版内容

自从基于MODI的DjVuToy、FreePic2Pdf、Pdg2Pic发布后,很多人就在问同一个问题:能不能在不装Office 2003/2007或SharePoint Designer 2007的情况下,让基于MODI的软件正常OCR?毕竟对于简体中文来说,就算只装SharePoint Designer 2007中的MODI,也要近650 MB,装Office 2007的MODI则更夸张,要近1 GB。

想解决这个问题,需要从MODI中OCR模块的来历说起。在《用MODI OCR 21种语言》一文中已经说过,MODI其实只是封装了ScanSoft API,而google(注意我说的是google,不是baidu)一下关键词“ScanSoft API”,可以发现两个网页:
http://www.corrupteddatarecovery.com/Products/ScanSoft-API.asp
http://www.corrupteddatarecovery.com/Products/Asian-OCR-for-ScanSoft-API.asp

从这两个帖子及其他一些相关信息推断,ScanSoft API封装了清华文通以支持亚洲语言,而MODI又在ScanSoft API上封装出MODI接口,并在此基础上提供MODI应用(MSPVIEW.EXE),整个层次结构应该如下图所示:

——————           MSPVIEW.EXE
应用层                ┃
——————            MODI接口
接口层                ┃
                 ScanSoft API
             ┏━━━━━┻━━━━━┓
             ┃     ScanSoft API亚洲语言支持(清华文通)
——————       ┃              ┃
数据层      西欧11国、东欧3国、     亚洲语言文件
         俄、希、土语言文件    (简、繁、日、朝)

从上图看,如果只想调用MODI接口,不需要应用层的支持,可以有以下选择:

  1. 直接调用ScanSoft API。这个比较有难度,至少我到目前为止还找不到相关文档。
  2. 还是调用MODI接口,至少这个的文档是公开的。

所以上面的问题就转换成了:能否抽取出支持MODI接口的最小集合,实现OCR接口功能?

身为资深技术人员,对于这种问题的回答当然不能胡言乱语,而应该以实验为基础:

1、搞一个干净的XP SP3虚拟机,再复制一份,姑且命名为“虚拟机A”、“虚拟机B”。
2、在虚拟机A中安装InstallRite 2.5,用它监视安装SharePoint Designer 2007中的MODI。
3、安装完成后,用InstallRite导出安装后新的注册表,命名为aaa.reg。
4、将虚拟机A中的两个文件夹
C:\Program Files\Common Files\Microsoft Shared\MODI
C:\Program Files\Common Files\Microsoft Shared\OFFICE12
复制到虚拟机B,并将aaa.reg导入虚拟机B。毕竟MODI接口是COM接口,与注册表无关的COM接口是不存在的。
5、运行DjVuToy、FreePic2Pdf或Pdg2Pic,可以验证在虚拟机B中能够用第三方软件正常OCR,只不过每OCR一页都要自动安装一次文件,似乎注册表中掺入了垃圾。

以上可行性实验清楚表明:
1、在不安装Office或SharePoint Designer的情况下,可以通过复制相关文件和注册表项,为第三方软件提供OCR支持。
2、对于简体中文来说,单独安装Office 2007中的MODI需要约1 GB硬盘空间,单独安装SharePoint Designer 2007中的MODI则需约650 MB,而上面两个文件夹加起来也只有约76 MB,何况中间还有水分可挤,空间的节省还是很可观的,所以这笔买卖做得过。

无聊但又必不可少的理论扯完了,下面开始进入实战:哪些文件和注册表项才是必不可少的?

先说文件。上面两个URL中的内容,其实已经说明了接口层中ScanSoft API所需的文件,剩下需要解决的就是MODI接口部分的文件。

在第三方软件中调用MODI接口的初始化代码为:
IDocument doc;
doc.CreateDispatch(_T("MODI.Document"));

在注册表中搜索字符串“MODI.Document”,可以知道此COM对象对应的DLL是MODI安装文件夹下的MDIVWCTL.DLL。再看一眼VC++的Debug窗口,可以知道在调用此DLL后,还接着调用了同文件夹下的MSPGIMME.DLL、MSPCORE.DLL,及OFFICE12文件夹下的MSO.DLL等。从文件属性看,这些文件都是微软鼓捣出来的,因此可以认为是接口层中MODI接口部分的东西。

从VC++的Debug窗口输出信息看,除了上述DLL文件外,OCR过程中还加载了MODI安装文件夹下的XOCR3.PSP、THOCR.PSP、XFILE.PSP。这3个文件虽然扩展名是PSP,但其实是DLL文件,从文件属性看属于ScanSoft API的范畴,可以看作是对上面两个URL中内容的补充。

另外VC++的Debug窗口中还记录到OCR过程调用了OFFICE12文件夹下的OGL.DLL、MSORES.DLL、2052\MSOINTL.DLL,但在后来的回归性测试中证明,在对注册表项进行简化后,这几个文件没有也没关系。

结合上面分析,及《用MODI OCR 21种语言》中的相关信息,可知要OCR简体中文、英语,至少需要的文件如下表所示,加一起也就约30 MB。其中“说明”部分的英文是从DLL文件的文件属性中复制过来的,中文是我自己加的;数据层的数据文件是用文件监视器抓取的。

层次文件名说明
接口层MODIMDIVWCTL.DLLMicrosoft Office Document Imaging Viewer Control
MSPCORE.DLLMicrosoft® Office Document Imaging Object Library
MSPGIMME.DLLMicrosoft® Gimme library
OFFICE12\MSO.DLL2007 Microsoft Office component
ScanSpft
API
BINDER.DLLXDoc Binder module for the ScanSoft SDK
PSOM.DLLComponent Management Module for PefectScan API
XIMAGE3B.DLLImage Processing Module for the ScanSoft SDK
XPAGE3C.DLLPage Management Module for ScanSoft SDK
XOCR3.PSPOCR Module for ScanSoft SDK
XFILE.PSPAsian OCR Module for ScanSoft SDK
THOCR.PSPAsian OCR Module for ScanSoft SDK
ScanSoft
API亚洲
语言
FORM.DLLTable Recognition for Asian OCR
REVERSE.DLLReverse Video Detection for Asian OCR
THOCRAPI.DLLAsian OCR API
TWCUTCHR.DLLCharacter Segmentation for Asian OCR
TWCUTLIN.DLLLine Segmentation for Asian OCR
TWLAY32.DLLLayout Analysis for Asian OCR
TWORIENT.DLLOrientataion Detection for Asian OCR
TWRECC.DLLChinese Recognition for Asian OCR
TWRECE.DLLEnglish Recognition for Asian OCR
TWRECS.DLLPunctuation Recognition for Asian OCR
TWSTRUCT.DLLDocument Structure Processing for Asian OCR
数据层英文LATIN1.SHP西欧11国(含英文)通用特征库
CharSetTable.chr字符编码转换表,文本文件
ENGLISH.LNG英文语言文件
简体中文ENGDIC.DAT清华文通的英文字典文件,貌似它也支持中、英文
ENGIDX.DAT清华文通的英文索引
JFONT.DAT 
LOOKUP.DAT 
OCRHC.DAT 
OCRVC.DAT 
TWGB32.DLLSimplified Chinese code Conversion
SCCODE.UNI 
SCPRINT.DAT 
SCPRINT2.DAT 
SCSERHT.DAT 
SCTREE.DAT 
TW_GU.DAT 
TW_UG.DAT 

如果还想增加对其他语言的OCR能力,可以参阅《用MODI OCR 21种语言》,增加相关语言对应的文件。

另外在上表中,在PSOM.DLL文件描述中出现了一个新的名字:PefectScan API。google了一下,找到其官网http://perfectscan.com/,从介绍上看是做图像处理的:
PerfectScan is an image processing program that automatically analyzes an image, then makes adjustments to that image rendering a black and white image that contains all viewable data as if it were a gray scale scan, only at 15% of the size of a gray scale image.
看来MODI还真是一个大杂烩。不过PefectScan官网上的一句话,感觉道尽了程序员的悲凉:
We built the software after 10 years of hard work, now all we have to do is build the website...OOPS!!

言归正传。搞定文件后,还需要搞定注册表项才行。与MODI相关的注册表项包括两个部分:COM相关与Office相关。

COM相关就是与MODI的COM组件相关的注册表项,这个直接用regsvr32导入即可:启动命令行,进入MODI安装文件夹,执行下面的命令:
regsvr32 MDIVWCTL.DLL
regsvr32 MSPCORE.DLL
即可完成MODI COM组件的注册。

但与Office相关的注册表项就没那么好搞定了。我曾经试过用注册表监视器对OCR过程进行监视,结果发现真相淹没在了细节的海洋里。最终不得不采用了一个笨办法:专门写了一套测试软件,在前面的可行性实验中搭建的虚拟机B里猛跑,逐一尝试删除从aaa.reg中导入的注册表项,每删除一项就检查一下对OCR会不会造成影响。最终试出来约20个注册表项是必不可少的,其中近一半与前面用regsvr32注册COM组件时自动插入的注册表项重复。

最终经过手工调整后,确认在上面表格所列文件及COM注册基础上,再增加下列注册表项即可正常用第三方软件在简体中文环境下OCR简体中文、英文:

[HKEY_CLASSES_ROOT\Installer\Components\61BA386016BD0C340BBEAC273D84FD5F]
"2052"=hex(7):76,00,55,00,70,00,41,00,56,00,53,00,2e,00,7d,00,58,00,25,00,21,\
00,21,00,21,00,21,00,21,00,4d,00,4b,00,4b,00,53,00,6b,00,4f,00,43,00,52,00,\
5f,00,32,00,30,00,35,00,32,00,3c,00,00,00,00,00

[HKEY_CLASSES_ROOT\Installer\Features\00002109F10040800000000000F01FEC]
"OCR_2052"=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109F10040800000000000F01FEC\Features]
"OCR_2052"="%mEMae,7q9*DXdU@EPi="

[HKEY_CLASSES_ROOT\Installer\Products\00002109710000000000000000F01FEC]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\3F745FF6A76FF2F4797DB74FC7B3FD8B]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\XPAGE3C.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\4080B9FA1A0BBF34FB7813E87159FC64]
"00002109F10040800000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\SCCODE.UNI"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\48AD0082D02B3D24C9A56FA50728CCAB]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\MSPCORE.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\D94C8360B8BB1DC41B1950E0F8237563]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109710000000000000000F01FEC\InstallProperties]
"WindowsInstaller"=dword:00000001

前三项的语言编码2052对应简体中文,但只要文件不缺,OCR中文、英文或其他语言都没有问题。不过在面对其他国家的用户时,解释采用简体中文的语言编码还是有点费劲,所以仍然需要继续尝试其他语言编码。

把两个虚拟机复原,重复上面可行性实验步骤:在虚拟机A中监视安装英文版SharePoint Designer 2007(安装后支持英、法、西班牙语),导出安装后的注册表和文件到虚拟机B,用同一套测试软件进行检查,出来的是不是与英文语言编码1033相关的注册表项呢?错,大错特错,跑出来不能删的是与法语(语言编码1036)相关的注册表项:

[HKEY_CLASSES_ROOT\Installer\Components\61BA386016BD0C340BBEAC273D84FD5F]
"1036"=hex(7):76,00,55,00,70,00,41,00,56,00,57,00,3f,00,57,00,41,00,24,00,21,\
00,21,00,21,00,21,00,21,00,4d,00,4b,00,4b,00,53,00,6b,00,4f,00,43,00,52,00,\
5f,00,31,00,30,00,33,00,36,00,3c,00,00,00,00,00

[HKEY_CLASSES_ROOT\Installer\Features\00002109F100C0400000000000F01FEC]
"OCR_1036"=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109F100C0400000000000F01FEC\Features]
"OCR_1036"=")aEMae,7q9*DXdU@EPi="

[HKEY_CLASSES_ROOT\Installer\Products\00002109710000000000000000F01FEC]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\3F745FF6A76FF2F4797DB74FC7B3FD8B]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\XPAGE3C.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\48AD0082D02B3D24C9A56FA50728CCAB]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\MSPCORE.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\C040B9FA1A0BBF34FB7813E87159FC64]
"00002109F100C0400000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\FRENCH.LNG"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\D94C8360B8BB1DC41B1950E0F8237563]
"00002109710000000000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109710000000000000000F01FEC\InstallProperties]
"WindowsInstaller"=dword:00000001

对比两个结果,可以看出:

  1. 与语言相关的注册表项共4项,其他注册表项都是相同的。
  2. 与语言相关的注册表项前3项直接用语言编码标识,第4项用语言文件标识,简体中文的文件是SCCODE.UNI,法语是FRENCH.LNG。
  3. 与语言相关的注册表项键值不是随便乱起的,是在产品基本键值(SharePoint Designer的基本键值是00002109710000000000000000F01FEC)的基础上,叠加相关语言编码(简体中文语言编码2052,十六进制0804,Intel表示为0408;法语语言编码1036,Intel表示为0C04)。敢这么玩GUID的我就见过这么一个,别人似乎都没这胆子。
  4. 不管采用什么语言,都可以只有一种语言,但这种语言不能是英语(语言编码1033)。

为了验证第4点,我从虚拟机A中手工导出英语相关注册表项,在虚拟机B中把与法语相关的注册表项全部换成英语的,结果OCR失败。与英语相关的4个注册表项为:

[HKEY_CLASSES_ROOT\Installer\Components\61BA386016BD0C340BBEAC273D84FD5F]
"1033"=hex(7):76,00,55,00,70,00,41,00,56,00,54,00,28,00,38,00,41,00,24,00,21,\
00,21,00,21,00,21,00,21,00,4d,00,4b,00,4b,00,53,00,6b,00,4f,00,43,00,52,00,\
5f,00,31,00,30,00,33,00,33,00,3e,00,26,00,61,00,45,00,4d,00,61,00,65,00,2c,\
00,37,00,71,00,39,00,2a,00,44,00,58,00,64,00,55,00,40,00,45,00,50,00,69,00,\
3d,00,00,00,00,00

[HKEY_CLASSES_ROOT\Installer\Features\00002109F10090400000000000F01FEC]
"OCR_1033"=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109F10090400000000000F01FEC\Features]
"OCR_1033"="&aEMae,7q9*DXdU@EPi=OFu[`t.WO9zoh+x^{BHE"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\9040B9FA1A0BBF34FB7813E87159FC64]
"00002109F10090400000000000F01FEC"="C:\\Program Files\\Common Files\\Microsoft Shared\\MODI\\12.0\\ENGLISH.LNG"

因此对于国内的用户,直接上简体中文的注册表项就好;对于国外的用户,如果对简体中文解释起来比较麻烦,就上法语或其他语言的注册表项吧 。但注意一次只能上一种语言,而且不能是英语。如果在上了一种语言的注册表项的基础上,再画蛇添足上英语的注册表项,会有问题:由于前面对文件和注册表项进行了精简,在OCR时MODI组件如果发现有英文的注册表项, 就会尝试恢复被精简的文件,造成OCR速度缓慢。

当然不论是哪一种语言,在导入x64 Windows时,都要注意x64下32位软件对应的Program Files文件夹多了一个后缀,改叫Program Files (x86)了。另外在理论上Program Files文件夹也是可以不在C盘上的,所以最保险的方法是通过环境变量CommonProgramFiles、CommonProgramFiles(x86),或SHGetFolderPath等SDK函数获取实际文件夹名称。

另外一个容易被问到的问题是:貌似导入的注册表项中包含了文件路径,那么能不能通过改注册表项的方法,改变MODI的安装路径,不装到CommonProgramFiles文件夹下?答案是:不行。事实上,InstallRite 2.5导出的aaa.reg文件中的原始注册表项里,路径名中是含有非法字符(问号)的,但并不影响使用,所以我怀疑文件夹是被写死在MODI代码里的。

总之,上面说的都是在没有安装Office 2003/2007的情况下的不得已方法,如果已经装了,不仅节省不了多少空间,而且可能会出现文件或注册表项的冲突。

(完)

[转老马的文章]MODI中的OCR模块