This may already have been posted, but a search turned nothing up here in the CodeBank.
If you have an appropriate version of Microsoft Office you can use MODI to do OCR on images. The obvious candidates are 32-bit Office 2003 and 2007, but supposedly this can be made to work in 32-bit Office 2010 as well.
As far as I can tell there is no way to feed images to MODI.Document except by having it load them from disk. But you could always "dump" images to a temporary folder as required, so that isn't a nasty restriction.
Requirements
VB6 development tools. Of course the logic can be trivially ported to Office VBA or even a WSH or MSHTA script written in VBScript.
A version of 32-bit Office supporting MODI.
Notes
This program uses early binding against Microsoft Office Document Imaging 11.0 Type Library (Office 2003). This is used to give us easy access to MODI.Document's OnOCRProgress event and the predefined constant miLANG_ENGLISH in Enum MiLANGUAGES.
To use this code with Office 2007 you'd need to change the reference to Microsoft Office Document Imaging 12.0 Type Library and recompile.
You could also use late binding, but then you would either have to give up using WithEvents (not valid for As Object) and the OnOCRProgress event entirely... or else use additional code or a C++ helper DLL to bind to the event.
MODI was removed in Office 2010, but you might look at:
The attachment is large because of included image files.
The program just grabs the file names from a hard-coded folder, then loads and OCRs them one by one and displays the resulting text in a RichTextBox. A status line reports progress on each image as it works.
A Timer control is used to work through the queue of images, primarily to help avoid the program being marked unresponsive by Windows.
The demo helps illustrate the "garbage in, garbage out" nature of OCR: the quality of the results depends on what you feed into it.
Last edited by dilettante; Nov 10th, 2014 at 11:54 AM.
Reason: typos
Microsoft Office Document Imaging 2003 (MODI) adds programmability features to the document scanning and viewing tools that Microsoft Office 2002 (XP) included for the first time. Programmers can take advantage of a simple object model built around the Document and Image (page) objects to display and read a scanned document as easily as a paper document, perform optical character recognition (OCR), search for text within scanned documents, copy and export scanned text and images, combine multiple pages into a single compressed file, and reorganize scanned document pages as easily as rearranging papers in a folder.
In order to install MODI and the support files required for MODI programmability, including the MODI Viewer ActiveX control, select "Microsoft Office Document Imaging" in the "Office Tools" group when installing Microsoft Office 2003. The default installation location is C:\Program Files\Common Files\Microsoft Shared\MODI\11.0. Installing MODI adds 2 applications, Microsoft Office Document Scanning and Microsoft Office Document Imaging, to the Microsoft Office Tools submenu on the Start menu.
You can program the MODI object model from any development tool that supports the Component Object Model (COM) by setting a reference to the Microsoft Office Document Imaging 11.0 Type Library. You can use the MODI Viewer control from any development tool that supports ActiveX controls by adding Microsoft Office Document Imaging Viewer Control 11.0 to the toolbox.
I am using MS Office 2007 . It is giving me message Missing-Microsoft Office Document Imaging 11.0 Type Library.
Thanks
The Office team at Microsoft has never cared a whit for standards, conventions, or courtesy. As a result one of the crosses we bear as supplicants on our knees at their altar is the fact that they break binary compatibility on every release.
The real answer is to boycott MS Office and use alternatives at every turn. Since this isn't always practical, our alternative is to either recompile for every Office version using a different reference for each version... or use late binding.
Office 11 is Office 2003, and you don't have Office 2003. Thus you must use an Office 12 library reference to early bind to the Office 2007 MODI library.
This may already have been posted, but a search turned nothing up here in the CodeBank.
If you have an appropriate version of Microsoft Office you can use MODI to do OCR on images. The obvious candidates are 32-bit Office 2003 and 2007, but supposedly this can be made to work in 32-bit Office 2010 as well.
As far as I can tell there is no way to feed images to MODI.Document except by having it load them from disk. But you could always "dump" images to a temporary folder as required, so that isn't a nasty restriction.
Requirements
VB6 development tools. Of course the logic can be trivially ported to Office VBA or even a WSH or MSHTA script written in VBScript.
A version of 32-bit Office supporting MODI.
Notes
This program uses early binding against Microsoft Office Document Imaging 11.0 Type Library (Office 2003). This is used to give us easy access to MODI.Document's OnOCRProgress event and the predefined constant miLANG_ENGLISH in Enum MiLANGUAGES.
To use this code with Office 2007 you'd need to change the reference to Microsoft Office Document Imaging 12.0 Type Library and recompile.
You could also use late binding, but then you would either have to give up using WithEvents (not valid for As Object) and the OnOCRProgress event entirely... or else use additional code or a C++ helper DLL to bind to the event.
MODI was removed in Office 2010, but you might look at:
The attachment is large because of included image files.
The program just grabs the file names from a hard-coded folder, then loads and OCRs them one by one and displays the resulting text in a RichTextBox. A status line reports progress on each image as it works.
A Timer control is used to work through the queue of images, primarily to help avoid the program being marked unresponsive by Windows.
The demo helps illustrate the "garbage in, garbage out" nature of OCR: the quality of the results depends on what you feed into it.
Hi,
I found the attached demo helpful !
I would have an other question: it is possible to implement also the Printer "Microsoft Office Document Image Writer" ?
I am using Tesseract OCR engine for our inhouse software OCR needs - mainly from scanned PDF's or PDF files printed as bitmaps. Developed VB6 frontend for that purpose, which extracts raster image from pdf pages and calls tesseract engine.
Tesseract is quite good OCR engine, but fails miserably (as many other OCR engines), when text orientation is vertical, fex. landscape pages scanned in portrait mode. There are several page segmentation settings, but it is quite difficult to automate text orientation detection in raster images.
I would have an other question: it is possible to implement also the Printer "Microsoft Office Document Image Writer" ?
I'm not sure what you mean. Do you want to print to a specific printer instead of displaying in the RichTextBox?
That ought to be easy enough. You can print text using Printer.Print and images using the Render method of the IPictureDisp/StdPicture or Printer.PaintPicture.
Oops! Just noticed this is a year old question. Sorry I missed you!
Last edited by dilettante; Jan 26th, 2019 at 03:08 AM.
Windows 10 has inbuilt OCR engine, but not tested this yet.
That appears to only work for UWP ("Metro") Store Apps, not normal programs. Supposedly with enough gyrations you can use some UWP APIs in a normal desktop program but good luck with that!
I hadn't installed MODI on this computer since my last clean Windows install. When I went back and ran my demo program in the elevated IDE it popped up a dialog "Installing VB6.exe feature" and appears to have successfully installed it.
Now, it might do this for a compiled EXE too but I would not recommend this unless your first run of the compiled EXE is elevated.
When you add an image to OneNote it automatically OCR's the images and stores the text as xml with the page. So you reference the app, create a page, insert the image, and then get the text. Code example and binaries here: https://www.journeyofcode.com/free-e...using-onenote/
(this is obviously not recommended for production use)
I took a llok last week, and not usefull in my case.
I wrote a complete OCR part in my soft that recognize invoices & document, according the position of each element in the document.
So based on templates, I can reach arround 85% of recognition, and users can define their templates.
It is combined with NAP2 (to do the scan), and Popller to extract the text. All combined is quite good
I can't make work the google (tried 3 min)
And the azure is giving interesting result.
It is nearly the same as MODI, except we have the result directyle in JSON.
But it is slower, and needs to upload the picture.
Maybe in complement of MODI to have a better result, I'll do some more tests
Attribute VB_Name = "ModOneNoteOcr"
' COM API uses image recognition feature of OneNote 2013/2016
'If the picture is too small, it will lead to recognition. The reason is unknown. It is better to be larger than 60*50
'win10 refer lib
'OneNote 15
'xml 6.0
Private Sub Command2_Click()
'MsgBox VarType(Picture1)
MsgBox GetTextFromSinglePicture(stdPictureToArraybyPropertyBag(Picture1), ScaleX(Picture1.Image.Width, vbHimetric, vbPixels), ScaleY(Picture1.Image.Height, vbHimetric, vbPixels)) '"C:\Users\xx\Desktop\test.JPG")
End Sub
Public Type imageBase64
base64Text As String
imageWidth As Long
imageHeight As Long
End Type
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function CreateStreamOnHGlobal Lib "ole32" (ByVal hGlobal As Long, ByVal fDeleteOnRelease As Long, pPStm As Any) As Long
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal uFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GetHGlobalFromStream Lib "ole32" (ByVal pPStm As Long, hGlobal As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Function OleLoadPicture Lib "oleaut32.dll" (ByVal lpstream As IUnknown, ByVal lSize As Long, ByVal fRunmode As Long, ByRef riid As Any, ByRef lplpvObj As IPicture) As Long
Private Declare Function SHCreateMemStream Lib "shlwapi.dll" Alias "#12" (ByRef pInit As Byte, ByVal cbInit As Long) As IUnknown
Private Function PictureToByteArray(nPic As IPicture) As Byte()
Dim IFauxStream As IUnknown ' hack that works with some apis that expect an IStream object
Dim pData() As Byte
Dim lSize As Long
Dim o_hMem As Long, o_lpMem As Long
Dim o_lngByteCount As Long
Call CreateStreamOnHGlobal(0, 1, IFauxStream) ' create IStream object
If nPic.KeepOriginalFormat Then ' for some reason; cannot use boolean vartype...
nPic.SaveAsFile ByVal ObjPtr(IFauxStream), False, lSize ' one for false
Else
nPic.SaveAsFile ByVal ObjPtr(IFauxStream), True, lSize ' one for true
End If
If lSize Then
If Not IFauxStream Is Nothing Then
If GetHGlobalFromStream(ByVal ObjPtr(IFauxStream), o_hMem) = 0 Then
o_lngByteCount = GlobalSize(o_hMem)
If o_lngByteCount > 0 Then
o_lpMem = GlobalLock(o_hMem)
If o_lpMem <> 0 Then
ReDim pData(0 To o_lngByteCount - 1)
CopyMemory pData(0), ByVal o_lpMem, o_lngByteCount
GlobalUnlock o_hMem
PictureToByteArray = pData
End If
End If
End If
End If
End If
End Function
Private Function ByteArrayToPicture(ByRef Data() As Byte) As IPicture
Dim lSize As Long, IID_IPicture(0 To 1) As Currency, oStream As IUnknown
lSize = UBound(Data) - LBound(Data) + 1&
Set oStream = SHCreateMemStream(Data(0&), lSize)
If Not oStream Is Nothing Then
IID_IPicture(0&) = 116045007755044.6976@ '{7BF80980-BF32-101A-8BBB-00AA00300CAB}
IID_IPicture(1&) = -612146501409303.8709@
lSize = OleLoadPicture(oStream, lSize, 0&, IID_IPicture(0&), ByteArrayToPicture) ': Debug.Assert lSize = 0& 'S_OK
End If
End Function
'Identify pictures
'The parameter can be the picture address, stdpicture object, picture array, and the array object needs to manually specify the width and height
Public Function GetTextFromSinglePicture(inPicPathOrArrayPic As Variant, _
Optional mWpix As Long, _
Optional mHpix As Long) As String
'
Dim xmlDoc As New MSXML2.DOMDocument60
Dim xmlNode As MSXML2.IXMLDOMNode
Dim xmlEle As MSXML2.IXMLDOMElement
Dim inPicPath As String
If VarType(inPicPathOrArrayPic) = vbString Then
inPicPath = inPicPathOrArrayPic
End If
Dim picBase64 As imageBase64
'Create a temporary notebook
Dim onenoteFullName As String
With New Scripting.FileSystemObject
onenoteFullName = .GetSpecialFolder(TemporaryFolder) & "\" & .GetBaseName(.GetTempName) & ".one"
'判断函数值是否正常
If Len(inPicPath) > 0 Then
If .FileExists(inPicPath) = False Then
GetTextFromPicture = "! Error File Path !"
Exit Function
End If
End If
End With
Dim onenoteApp As New OneNote.Application ' OneNote12.Application
If onenoteApp Is Nothing Then
GetTextFromPicture = "! Error in Openning OneNote !"
GoTo clear_variable_before_exit
End If
Dim sectionID As String
Dim pageID As String
Select Case VarType(inPicPathOrArrayPic) 'TypeName(inPicPathOrArrayPic)
Case vbArray Or vbByte '"Byte()"
Set xmlEle = CreateNotePageContentElement(2, inPicPathOrArrayPic, mWpix, mHpix)
Case Else
Set xmlEle = CreateNotePageContentElement(2, inPicPathOrArrayPic)
End Select
Set xmlEle = AddNodeInfo(xmlEle)
'getsectionID
onenoteApp.OpenHierarchy onenoteFullName, "", sectionID, cftSection
'getpageID
onenoteApp.CreateNewPage sectionID, pageID, npsBlankPageNoTitle
'get XML
Dim pageXmlText As String
onenoteApp.GetPageContent pageID, pageXmlText, , xs2013
'
If xmlDoc.loadXML(pageXmlText) = False Then
GetTextFromPicture = "! Error in Loading Xml !"
GoTo clear_variable_before_exit
End If
With xmlDoc.getElementsByTagName("one:Page").Item(0)
.appendChild xmlEle
End With
'
onenoteApp.UpdatePageContent xmlDoc.documentElement.xml, , xs2013
',
Sleep 1000
Dim iCNT As Integer
iCNT = 10
re_getPageContent:
onenoteApp.GetPageContent pageID, pageXmlText, , xs2013
xmlDoc.loadXML pageXmlText
Set xmlEle = xmlDoc.documentElement.getElementsByTagName("one:OCRText").Item(0)
If xmlEle Is Nothing Then
If iCNT > 0 Then
Sleep 1000
iCNT = iCNT - 1
GoTo re_getPageContent
Else
GetTextFromSinglePicture = "! Waiting OneNote Time Expired !"
End If
Else
GetTextFromSinglePicture = xmlEle.Text
End If
clear_variable_before_exit:
If Not onenoteApp Is Nothing Then
If Len(pageID) > 0 Then
onenoteApp.DeleteHierarchy pageID, , True
End If
Set onenoteApp = Nothing
End If
Kill onenoteFullName
End Function
'When paraContent is an image array, you need to manually add the image size, W is width, H is height. Pixel unit
Private Function CreateNotePageContentElement(contentType As Integer, _
paraContent As Variant, _
Optional mWpix As Long, _
Optional mHpix As Long) As MSXML2.IXMLDOMElement
Dim xmlEle As MSXML2.IXMLDOMElement
Dim xmlNode As MSXML2.IXMLDOMElement
Dim ns As String
ns = "one:"
With New MSXML2.DOMDocument60
Select Case contentType
Case 1 '文本
Set xmlNode = .createElement(ns & "T")
xmlNode.Text = paraContent
Case 2 '图片
Dim picBase64 As imageBase64
Select Case VarType(paraContent)
Case vbArray Or vbByte '"Byte()"
picBase64 = getBase64(paraContent, mWpix, mHpix)
Case Else
picBase64 = getBase64(paraContent)
End Select
'创建一个图片XML信息
Set xmlNode = .createElement(ns & "Image")
xmlNode.setAttribute "format", "jpg"
xmlNode.setAttribute "originalPageNumber", 0
Set xmlEle = .createElement(ns & "Position")
xmlEle.setAttribute "x", 0
xmlEle.setAttribute "y", 0
xmlEle.setAttribute "z", 0
xmlNode.appendChild xmlEle
Set xmlEle = .createElement(ns & "Size")
xmlEle.setAttribute "width", picBase64.imageWidth
xmlEle.setAttribute "height", picBase64.imageHeight
xmlNode.appendChild xmlEle
Set xmlEle = .createElement(ns & "Data")
xmlEle.Text = picBase64.base64Text
xmlNode.appendChild xmlEle
End Select
End With
Set CreateNotePageContentElement = xmlNode
End Function
'XML处理的函数
Private Function AddNodeInfo(ContentElement As MSXML2.IXMLDOMElement) As MSXML2.IXMLDOMElement
Dim xmlEle As MSXML2.IXMLDOMElement
Dim xmlNode As MSXML2.IXMLDOMElement
Dim ns As String
ns = "one:"
Set xmlNode = ContentElement
With New MSXML2.DOMDocument60
Set xmlEle = .createElement(ns & "OE")
xmlEle.appendChild xmlNode
Set xmlNode = xmlEle
Set xmlEle = .createElement(ns & "OEChildren")
xmlEle.appendChild xmlNode
Set xmlNode = xmlEle
Set xmlEle = .createElement(ns & "Outline")
xmlEle.appendChild xmlNode
Set xmlNode = xmlEle
End With
Set AddNodeInfo = xmlNode
End Function
'图片处理为Base64编码的函数
Public Function getBase64(inBmpFile As Variant, _
Optional mWpix As Long, _
Optional mHpix As Long) As imageBase64
Dim xmlEle As MSXML2.IXMLDOMElement
With New MSXML2.DOMDocument60
Set xmlEle = .createElement("Base64Data")
End With
xmlEle.dataType = "bin.base64"
Select Case VarType(inBmpFile) 'typename(inBmpFile)
Case vbString '"String"
Debug.Print "图片路径"
With CreateObject("ADODB.Stream")
.Type = adTypeBinary
.Open
.LoadFromFile inBmpFile
xmlEle.nodeTypedValue = .Read()
.Close
End With
With CreateObject("WIA.ImageFile")
.loadfile inBmpFile
getBase64.imageHeight = .Height
getBase64.imageWidth = .Width
End With
' Case vbLong '"Picture"
' Debug.Print "图片对象 stdpicture对象句柄"
' xmlEle.nodeTypedValue =
' getBase64.imageHeight = Form1.ScaleY(inBmpFile.Height, vbHimetric, vbPixels)
' getBase64.imageWidth = Form1.ScaleX(inBmpFile.Width, vbHimetric, vbPixels)
Case vbArray Or vbByte '"Byte()"
xmlEle.nodeTypedValue = inBmpFile
Debug.Print "图片数组"
getBase64.imageHeight = mHpix
getBase64.imageWidth = mWpix
End Select
getBase64.base64Text = xmlEle.Text
End Function
Public Function stdPictureToArraybyPropertyBag(nPic As PictureBox) As Byte()
Dim pb As New PropertyBag
pb.WriteProperty "test", nPic.Picture
' Dim b() As Byte
'Dim a() As Byte
' SavePicture Picture1.Image, App.Path & "\test.jpg"
' a = PictureToByteArray(Picture1.Image)
' MsgBox TypeName(a)
stdPictureToArraybyPropertyBag = MidB(pb.Contents, 49, LenB(pb.Contents) - 50) 'propertybag比正常读取的多50字节
'TestBase64 b
End Function
Last edited by xxdoc123; Sep 27th, 2022 at 03:44 AM.