Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Screengrab --> OCR -- text, GUI for options/results


  • Please log in to reply
46 replies to this topic
pajenn
  • Members
  • 391 posts
  • Last active: Jan 28 2014 03:01 PM
  • Joined: 07 Feb 2009
Interesting project. I just downloaded the files. If they work I'll test them against ABBYY Screenshot Reader, which is a commercial alternative. I've also tried ScanSoft's Omnipage 16 for regular OCR, but didn't like it.

Zendarpc
  • Members
  • 1 posts
  • Last active: May 14 2009 12:12 AM
  • Joined: 13 May 2009
Just download the latest version from http://jocr.sourcefo...t/download.html.

To fix the script, open ShowOCRUnderMouse.ahk, go to line 77, and replace "gocr.exe" with "gocr047.exe" (the name of the newly downloaded gocr binary). Good to go!

MetaStable
  • Guests
  • Last active:
  • Joined: --
I've been trying to rewrite n-l-i-d's OCR demo script into a function so that I may call it only when needed, though I've run into a wall with an error that I can't seem to get rid of.

#Include GDIplusWrapper.ahk


GetOcr(thisX, thisY)
{
  fileNameDestJ = ResultImage.jpg

  If (GDIplus_Start() != 0)
  	Goto GDIplusError
  
  If (GDIplus_CaptureScreenRectangle(bitmap, thisX, thisY, 

100, 20) != 0)
  	Goto GDIplusError

  If (GDIplus_GetEncoderCLSID(jpgEncoder, 

#GDIplus_mimeType_JPG) != 0)
  	Goto GDIplusError
  
  GDIplus_InitEncoderParameters(jpegEncoderParams, 1)
  jpegQuality = 100	

  If (GDIplus_AddEncoderParameter(jpegEncoderParams, 

#EncoderQuality, jpegQuality) != 0)
  	Goto GDIplusError

  If (GDIplus_SaveImage(bitmap, fileNameDestJ, jpgEncoder, 

jpegEncoderParams) != 0)
  	Goto GDIplusError
  
  ; Wait for jpg
  Loop,
  {
    IfExist, %fileNameDestJ%
      Break
  }

  RunWait, djpeg.exe -pnm -grayscale %fileNameDestJ% in.pnm
  RunWait, gocr.exe -l 0 -s 1 -i in.pnm -o output.txt

  FileRead, result, output.txt

GDIplusError:
	If (#GDIplus_lastError != "")
		MsgBox 16, GDIplus Test, (Error in %

#GDIplus_lastError% (at %step%))
GDIplusEnd:
	GDIplus_FreeImage(bitmap)
	GDIplus_Stop()

  ; Cleanup
  ;FileDelete, in.pnm
  ;FileDelete, %fileNameDestJ%

Return, result
}

As you can tell I've gotten rid of the need to use cmdstub.exe because I have the 32bit gocr.exe. After some investigation the error seems to be coming from GDIplus_ScreenCaptureRectangle, apparently its #GpStatus@13 = UnknownImageFormat, though I've left n-l-i-d's portion of that code almost untouched =/

Anyone able to help me out? Would be greatly appreciated =)

pajenn
  • Members
  • 391 posts
  • Last active: Jan 28 2014 03:01 PM
  • Joined: 07 Feb 2009

Interesting project. I just downloaded the files. If they work I'll test them against ABBYY Screenshot Reader, which is a commercial alternative. I've also tried ScanSoft's Omnipage 16 for regular OCR, but didn't like it.



UPDATE: I had problems getting the scripts to work so I wasn't able to test them out yet, until now. Today I finally got around to setting them up:

1. For the ShowOCRUnderMouse.ahk I had to replace gocr.exe with the newer gocr047.exe (but renamed to gocr.exe for obvious reasons).

It works ok: Accuracy is poor, but the conversion is instantaneous, whereas other OCR scripts need a few seconds to process the input. Examples:

Posted Image

2. I had lots of trouble setting up m2's Screengrab script - maybe he was using an older version of AHK with slighly different syntax. In any case, I had to remove every instance of "cmd /c" from the RunWait commands to get his Screengrab script to work. I've only tried it on notepad text, but so far I'm impressed (the result is in a text box in the lower right corner).

Selection:

Posted Image

Result:

Posted Image

m2's script nailed that one (grabbed the cursor too). ABBYY's Screenshot Reader can get that too. Both use a draw-box-with-mouse-to-select-input interface. I only did one comparison shot so far, and they both did about the same. Judge for yourself (both with default settings):

Posted Image

So yes, I'm impressed. That said, I have no idea how m2's script will perform with different backgrounds, fonts, text colors, etc. Also, I think the ABBYY program (full version, not Screenshot Reader alone) may be able to learn. Then again, the tesseract engine can probably do the same

Here's here the ABBYY app looks when called out of the system tray:

Posted Image

Insignificant details for completeness (don't read):
-I used the following settings for paths:

; Default variables
TmpDir =Z:\temp
TmpFile =image_out
ImageFormat =pgm
TmpImageFormat =ppm ; This is the only format supported

; Program paths for dependencies
convert_path =Z:\My Programs\ImageMagick
mkbitmap_path =Z:\My Programs\Potrace
potrace_path =Z:\My Programs\Potrace
gocr_path =Z:\My Programs\gOCR
tesseract_path =C:\WINDOWS

I downloaded the full portable version of ImageMagick (ImageMagick-6.5.2-Q16-windows.zip), potrace with mkbitmap package (potrace-1.8.win32-i386.tar.gz package, and finally Windows-binary gocr047.exe (but renamed to gocr.exe). I also installed the super stable version of NetPBM from the previous link - just in case, but in hindsight I don't think it's needed or used by m2's version of gOCR. Finally, the version of tesseract I have came with Softi FreeOCR. It put tesseract.exe and tessdata folder into my Windows directory; the rest of the program resides in various Program Files and Documents and Settings folders, but I suppose m2's script doesn't need those.


pajenn
  • Members
  • 391 posts
  • Last active: Jan 28 2014 03:01 PM
  • Joined: 07 Feb 2009
Couple more comparison pics...

1. Both m2's ScreenGrab and ABBYY pick up AHK forum heading and subtitle fine:

Posted Image

Blue web text is not a problem:

Posted Image

However, m2's script gets tripped up by non-uniform background while ABBYY handled the AHK forum logo pic fine:

Posted Image

Suggestions:
-I would (and will) change the hotkey from Ctrl+LeftClick -- that gets triggered too easily because people (I at least) use it instinctively to select multiple files in a folder. I'd go with:
(i) a similar hotkey setup to the screenshot script presented in this thread: http://www.autohotke...opic.php?t=4086 (i.e. WINKEY+S to trigger script (launches screen mask) and pics up the next LeftClick down and LeftClick up to determine the box to screengrab.
(ii) leftclick tray icon option to launch the screen mask. (I'd change the tray icon too while at it)
-please keep the project alive; it's quite impressive imo.

pajenn
  • Members
  • 391 posts
  • Last active: Jan 28 2014 03:01 PM
  • Joined: 07 Feb 2009
I ended up implementing the suggestions I made in the previous post. i.e. I changed the tray icon, added the option to launch screengrab by left-clicking on the tray icon (single click), and changed the hotkey. Previously: CTRL+LeftClick to start selection and 2nd LeftClick to end selection. Now: WINKEY+Q to enable selection, LeftButton Down -> drag box -> LeftClick Up to select area for OCR.

However, the selection method is a bit slower than the original one (but smoother).

I left in my own paths, so if anyone tries to use this version, you'll need to change the icon path on line 4, and dependencies paths on line 17, and 23-27.

;#Include GDIPlusHelper.ahk
#Persistent
#SingleInstance force
Menu, Tray, Icon, Z:\My Programs\AutoHotkey\icons\smiley.ico
Menu, Tray, Add ; separator
Menu, Tray, Add, About and Options, AboutOptions
Menu, Tray, Add, Grab screen, GrabIt
Menu, Tray, Default, Grab screen
Menu, Tray, Click, 1


; Default GUI options
FileSave = 0
InfoWindow = 1

; Default variables
TmpDir =Z:\temp
TmpFile =image_out
ImageFormat =pgm
TmpImageFormat =ppm ; This is the only format supported

; Program paths for dependencies
convert_path =Z:\My Programs\ImageMagick
mkbitmap_path =Z:\My Programs\Potrace
potrace_path =Z:\My Programs\Potrace
gocr_path =Z:\My Programs\gOCR
tesseract_path =C:\WINDOWS

; Default processing options

;   Image preprocessing
   
   ; mkbitmap
   mkb_s =3 ; scale and interpolate
   mkb_s1 =0 ;-2 linear scale
   mkb_s2 =1 ;-3 cubic scale
   mkb_i =0 ; -i invert
   mkb_f =4 ; highpassfilter
   mkb_t =0.45 ; threshold
   
   ; potrace
   pre_potrace = 1 ; Run bitmap thru potrace
   pot_z =minority ;how to resolve ambiguities in path decomposition
         ;black, white, right,  left,  minority|,  majority,  or  random
   pot_t =2 ;suppress speckles of up to this size (default 2)
   pot_a =4 ;corner threshold parameter (default 1)
   pot_n =0 ;-n turn off curve optimization
   pot_O =0.2 ;curve optimization tolerance (default 0.2)
   pot_u =10 ;Quantize output to 1/unit pixel
   pot_k =0.5 ; Black/white cutof
   pot_i =0 ;-i ; -i invert
   pot_r =0 ; Rotate clock wise degree
   
;   OCR
   ocr_d =-1 ; -d, Dust size
   ocr_s =0 ; -d, Spacewidth
   ocr_m =32 ; -m, operational modes
   ocr_n =0 ; -n, numbers only

;   OCR/TEXT postprocessing
   NoLineReturn = 1
Return


#q::Goto, GrabIt ;press win + q (for "start")

GrabIt:
Sleep, 100

   stop=0
   
   ; First method for positioning of screengrab
  ;Mask Screen
  Gui, Color, FFFFFF
  Gui +LastFound
  WinSet, Transparent, 50
  Gui, -Caption 
  Gui, +AlwaysOnTop
  Gui, Show, x0 y0 h%A_ScreenHeight% w%A_ScreenWidth%,"AutoHotkeySnapshotApp"     

  ;Drag Mouse
  CoordMode, Mouse, Screen
  CoordMode, Tooltip, Screen
  WinGet, hw_frame_m,ID,"AutoHotkeySnapshotApp"
hdc_frame_m := DllCall( "GetDC", "uint", hw_frame_m)
  KeyWait, LButton, D 
  MouseGetPos, scan_x_start, scan_y_start 
  Loop
  {
    Sleep, 10   
    KeyIsDown := GetKeyState("LButton")
    if (KeyIsDown = 1)
    {
      MouseGetPos, scan_x, scan_y 
      DllCall( "gdi32.dll\Rectangle", "uint", hdc_frame_m, "int", 0,"int",0,"int", A_ScreenWidth,"int",A_ScreenWidth)
      DllCall( "gdi32.dll\Rectangle", "uint", hdc_frame_m, "int", scan_x_start,"int",scan_y_start,"int", scan_x,"int",scan_y)
    }
    else
      break
  }

  ;KeyWait, LButton, U
  MouseGetPos, scan_x_end, scan_y_end
  if (scan_x_end < scan_x_start)
  {
    tmp := scan_x_start    
    scan_x_start := scan_x_end
    scan_x_end := tmp
  }
  if (scan_y_end < scan_y_start)
  {
    tmp := scan_y_start    
    scan_y_start := scan_y_end
    scan_y_end := tmp
  }

   ; Main scanning function
   TrayTip, , Scanning...., , 1
   
   CoordMode, Pixel, Screen
   CoordMode, Mouse, Screen
   MouseGetPos, scan_x_end, scan_y_end
   
   scan_current_y=%scan_y_start%
   scan_current_x=%scan_x_start%
   scan_current_line=
   scan_current_line_source=
   Loop
   {
      scan_current_x := scan_current_x + 1
      if scan_current_x > %scan_x_end%
      {
         scan_current_line =%scan_current_line%`n
         scan_current_line_source =%scan_current_line_source%`n
       
         scan_current_y := scan_current_y + 1
         if scan_current_y > %scan_y_end%
            break
         scan_current_x = %scan_x_start%
         continue
      }
      PixelGetColor, found_color, %scan_current_x%, %scan_current_y%
      
      StringMid, scan_rgb_r, found_color, 3, 2
      StringMid, scan_rgb_g, found_color, 5, 2
      StringMid, scan_rgb_b, found_color, 7, 2
      
      scan_current_line_source =%scan_current_line_source% %found_color%
   
      scan_rgb_r =0x%scan_rgb_r%
      scan_rgb_g =0x%scan_rgb_g%
      scan_rgb_b =0x%scan_rgb_b%
   
      SetFormat, integer, d
      scan_rgb_r -= 0
      scan_rgb_g -= 0
      scan_rgb_b -= 0
   
      scan_rgb_r := "   " . scan_rgb_r
      scan_rgb_g := "   " . scan_rgb_g
      scan_rgb_b := "   " . scan_rgb_b
   
      StringRight, scan_rgb_r, scan_rgb_r, 3
      StringRight, scan_rgb_g, scan_rgb_g, 3
      StringRight, scan_rgb_b, scan_rgb_b, 3
      
      found_color =%scan_rgb_r% %scan_rgb_g% %scan_rgb_b%
      
      ;scan_current_line=%scan_current_line% %found_color%
      ;/*
      if scan_current_x > %scan_x_start%
      {
        scan_current_line=%scan_current_line% %found_color%
      }
      else
      {
        scan_current_line=%scan_current_line%%found_color%
      }
      ;*/
   }
   
   ; Add Header for image file
   format :="P3"
   comment :="#File made in Autohotkey"
   hight :=scan_y_end - scan_y_start
   width :=scan_x_end - scan_x_start
   colors :="255"
      
   file_data =
   (
      %format%
      %comment%
      %width%
      %hight%
      %colors%
      %scan_current_line%
   )
   
   TrayTip, , Scan complete, , 1
   sleep, 1000
   TrayTip

   GoSub, MainProcess
   ;MsgBox, The MainProcess subroutine has returned (it is finished).
   return

   MainProcess:
   
   Gui Destroy

   ; File Save, function to allow save a file of the screengrab image
   if FileSave = 1
   {
      FileSelectFile, SelectedFile, 16, , Save image, (*.ppm)
      if SelectedFile =
      {
      MsgBox,,Save canceled, No image saved.
      }
      else
      {
      IfExist %SelectedFile%
      {
         FileDelete %SelectedFile%
         if ErrorLevel <> 0
         {   
            MsgBox The attempt to overwrite "%SelectedFile%" failed.
            return
         }
         else
         {
            FileAppend, %file_data%, *%SelectedFile%
         }
      }
      }
   }
   ; End function, File Save

   ; Cleaning, Functions for cleaning up temporary files from previus grabs
       IfExist %TmpDir%\%TmpFile%.ppm
        {
          FileDelete %TmpDir%\%TmpFile%.ppm
          if ErrorLevel <> 0
          {   
            MsgBox The attempt to remove "%TmpDir%\%TmpFile%.ppm" failed.
            return
          }
        }
   
        IfExist %TmpDir%\%TmpFile%.%ImageFormat%
        {
          FileDelete %TmpDir%\%TmpFile%.%ImageFormat%
          if ErrorLevel <> 0
          {   
            MsgBox The attempt to remove "%TmpDir%\%TmpFile%.%ImageFormat%" failed.
            return
          }
        }
       
        IfExist %TmpDir%\%TmpFile%.txt
        {
          FileDelete %TmpDir%\%TmpFile%.txt
          if ErrorLevel <> 0
          {   
            MsgBox The attempt to remove "%TmpDir%\%TmpFile%.txt" failed.
            return
          }
        }
   
        IfExist %TmpDir%\%TmpFile%.png
        {
          FileDelete %TmpDir%\%TmpFile%.png
          if ErrorLevel <> 0
          {   
            MsgBox The attempt to remove "%TmpDir%\%TmpFile%.png" failed.
            return
          }
        } 
   ; End Cleaning funtions
   
   ; Write, function to write the screengrab image to file
   FileAppend, %file_data%, *%TmpDir%\%TmpFile%.ppm
   
   ; Start Preprocessing image
   
   if mkb_i = 1
   {
      mkb_ii :="-i "
   }
   else
   {
      mkb_ii =
   }
   
   RunWait, %mkbitmap_path%\mkbitmap %mkb_ii% -f %mkb_f% -s %mkb_s% -t %mkb_t% -o %TmpFile%.pbm %TmpFile%.ppm, %TmpDir%, hide,
   
   IfNotExist %TmpDir%\%TmpFile%.pbm
   {
      MsgBox,
      (
      Running mkbitmap "%TmpDir%\%TmpFile%.pbm" failed.
      %mkbitmap_path%\mkbitmap %mkb_ii% -f %mkb_f% -s %mkb_s% -t %mkb_t% -o %TmpFile%.pbm %TmpFile%.ppm
        )
      return
   }
   
   If pre_potrace = 1
   {
      if pot_i = 1
      {
         pot_ii =-i
      }
      else
      {
         pot_ii =
      }
      if pot_n = 1
      {
         pot_nn :="-n "
      }
      else
      {
         pot_nn =
      }
      RunWait %potrace_path%\potrace %pot_ii%%pot_nn% -O %pot_o% -k %pot_k% -r %pot_r% -t %pot_t% -g -a %pot_a% -o %TmpFile%.pgm %TmpFile%.pbm, %TmpDir%, hide,
      IfNotExist %TmpDir%\%TmpFile%.pgm
      {
         MsgBox,
         (
         Running potrace "%TmpDir%\%TmpFile%.pgm" failed.
         %potrace_path%\potrace %pot_ii%%pot_nn% -O %pot_o% -k %pot_k% -r %pot_r% -t %pot_t% -g -a %pot_a% -o %TmpFile%.pgm %TmpFile%.pbm
         )
         return
      }
      RunWait %convert_path%\convert %TmpFile%.pgm %TmpFile%.pbm, %TmpDir%, hide,
   }
   ; End Preprocessing
   
   ; Start OCR processing
   
   ; Need to run gocr thru cmd, ???
   RunWait %gocr_path%\gocr -i %TmpDir%\%TmpFile%.pbm -s %ocr_s% -d %ocr_d% -m %ocr_m% -n %ocr_n% -o %TmpFile%.txt, %TmpDir%, hide,
   IfNotExist %TmpDir%\%TmpFile%.txt
   {
      MsgBox,
      (
      Running gocr "%TmpDir%\%TmpFile%.txt" failed.
      cmd /c %gocr_path%\gocr %TmpDir%\%TmpFile%.pbm -s %ocr_s% -d %ocr_d% -m %ocr_m% -n %ocr_n% -o %TmpFile%.txt
      )
      return
   }

   FileRead, ocr_text, %TmpDir%\%TmpFile%.txt
   
   ; Test Tesseract, a other OCR

   ; Tesseract needs a BMP image
   RunWait %convert_path%\convert %TmpFile%.pbm %TmpFile%.t.bmp, %TmpDir%, hide,

   ; Start OCR processing
   RunWait %tesseract_path%\tesseract %TmpFile%.t.bmp %TmpFile%.t, %TmpDir%, hide,
   IfNotExist %TmpDir%\%TmpFile%.t.txt
   {
      MsgBox,
      (
      Running gocr "%TmpDir%\%TmpFile%.t.txt" failed.
      cmd /c %tesseract_path%\tesseract %TmpFile%.t.bmp output %TmpFile%.t.txt
      )
      return
   }
   
   FileRead, ocr_t_text, %TmpDir%\%TmpFile%.t.txt

   ; Save the raw OCR result into variable, could be useful
   ocr_raw =%ocr_text%
   ocr_t_raw =%ocr_t_text%

   ; End OCR processing
   
   ; Start OCR postprocessing
   
   
   ; Remove all CR+LF's from the contents
   If NoLineReturn = 1
   {
      StringReplace, ocr_text, ocr_text, `r`n, , All
   }
   
   ; Remove all underscors from the contents:
   StringReplace, ocr_text, ocr_text, _, " ", All
   
   ; Remove all spaces from the contents:
   StringReplace, ocr_text, ocr_text, %A_SPACE%, , All

   ; Remove all " from the contents:
   StringReplace, ocr_text, ocr_text, """, "", All

   /*
   ; Only allow characters in CharOK, not finished
   CharOk := "abc"
   Loop
   {
      IfInString, CharOK, `r`n
   }
   */

   ; End OCR postprocessing
   
   ; Copy postprocessed text to clipboard
   Clipboard =%ocr_text%
   

   /*
   ; Functions to post the result into a search
   #g::
     Send, ^c
     Run, http://www.google.com/search?q=%Clipboard%
   Return
   
   #w::
     Send, ^c
     Run, http://en.wikipedia.org/wiki/Special:Search?search=%Clipboard%
   Return
   */
   
   
   ; Start GUI info Windows, function to show what we got, and how. For debugging mainly
   If InfoWindow = 1
   {

   ; Print size data of screengrab
   Gui, +owner
   Gui, font, s10, Verdana  ; Set 10-point Verdana.
   Gui, Add, Text,, The hight is %hight%, %scan_y_start% - %scan_y_end%.
   Gui, Add, Text,, The width is %width%, %scan_x_start% - %scan_x_end%.
   
   ; Show asci of raw screengrab in hex and preprocessed to rgb
   
   ; Start Fix, Edit field seems to krasch if large
   ImageArea := hight * width
   
   hex =Grab to large to show all hex, show first 10000 chars ony
   rgb =Grab to large to show all rgb, show first 10000 chars ony


   
   If ImageArea < 1000
   {
      hex =%scan_current_line_source%
      rgb =%scan_current_line%
      gui, font,s2, Terminal   
      Gui, Add, Edit, w600 h100 -wrap +HScroll +VScroll, %hex%
      Gui, Add, Edit, w600 h100 -wrap +HScroll +VScroll, %rgb%
   }
   else
   {
      StringLeft, hex_short, scan_current_line_source, 10000
      StringLeft, rgb_short, scan_current_line, 10000

      Gui, font, s10, Verdana  ; Set 10-point Verdana.
      Gui, Add, Text,, %hex%
      gui, font,s2, Terminal   
      Gui, Add, Edit, w600 h100 -wrap +HScroll +VScroll, %hex_short%

      Gui, font, s10, Verdana  ; Set 10-point Verdana.
      Gui, Add, Text,, %rgb%
      gui, font,s2, Terminal   
      Gui, Add, Edit, w600 h100 -wrap +HScroll +VScroll, %rgb_short%
   }
   ; End fix

   
   Gui, font, s10, Verdana  ; Set 10-point Verdana.

   ; Convert images to compatible format for GUI, adjust size if large screengrab
   
   If ImageArea > 5000
   {
      ; convert options to resize image
      con_options =-resize 300x200
   }
   RunWait %convert_path%\convert %con_options% %TmpFile%.ppm %TmpFile%.bmp, %TmpDir%, hide,
   RunWait %convert_path%\convert %con_options% %TmpFile%.pbm %TmpFile%_ocr.bmp, %TmpDir%, hide,

   Gui, Add, Picture,, %TmpDir%\%TmpFile%.bmp
   Gui, Add, Picture,, %TmpDir%\%TmpFile%_ocr.bmp
   Gui, Add, Text,,
   (
      Text from OCR:
   )
   Gui, Add, Edit, xp+100, %Clipboard%
   Gui, Add, Edit, xp+150, %ocr_t_text%
   Gui, Add, Button, default, OK  ; The label ButtonOK (if it exists) will be run when the button is pressed.
   Gui, Add, Button, Default xp+60, Rerun
   Gui, Show, AutoSize,
   Return
   
   ButtonOK:
      GuiClose:
      GuiEscape:
      Gui Destroy
   Return
   
   ButtonRerun: 
      Gui, Submit
      Gosub, MainProcess
   Return

   }
   ; End GUI info Window
   
   /*
   ; Alternate method for positioning of screengrab
   ; press ctrl-alt-b (for "begin")
   ^!b::
      CoordMode, Mouse, Screen
      MouseGetPos, scan_x_start, scan_y_start
   return
   */
   
   ; Clean up tmp
   FileDelete %TmpDir%\%TmpFile%.ppm ; Color image, the raw screengrab picture
   FileDelete %TmpDir%\%TmpFile%.pgm ; Gray
   FileDelete %TmpDir%\%TmpFile%.pbm ; BW
   FileDelete %TmpDir%\%TmpFile%.txt
   
   Return

Return

AboutOptions:
Gui, 2:+owner  ; Make the main window (Gui #1) the owner of the "about box" (Gui #2).
Gui +Disabled  ; Disable main window.
Gui, font, s12, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Text,, Tool for Text extraction from Screen grabs
Gui, 2:Add, Text,, Options:
Gui, 2:Add, Text, xs+10, Image Preprocessing

Gui, 2:Add, Text, xs+20, mkbitmap:
Gui, font, s10, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Checkbox, vmkb_i checked%mkb_i% xs+25, Invert image
Gui, 2:Add, Edit, w35 vmkb_f xs+25, %mkb_f%
Gui, 2:Add, Text, xp+40, Highpassfilter
Gui, 2:Add, Edit, w35 vmkb_t xs+25, %mkb_t%
Gui, 2:Add, Text, xp+40, Threshold
Gui, 2:Add, Edit, w35 vmkb_s xs+25, %mkb_s%
Gui, 2:Add, Text, xp+40, Scale by integer factor
Gui, 2:Add, Radio, Group vmkb_s1 checked%mkb_s1% xs+25, Liner interpolation
Gui, 2:Add, radio, vmkb_s2 checked%mkb_s2% xs+25, Cubic interpolation

Gui, font, s12, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Text, xs+20, potrace:
Gui, font, s10, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Checkbox, vprepotrace checked%prepotrace% xs+25, Run thrue potrace
Gui, 2:Add, DropDownList, w100 vpot_z checked%pot_z% xs+25, black|white|right|left|minority||majority|random
Gui, 2:Add, Text, xp+110, Path decomposition
Gui, 2:Add, Edit, w35 vpot_t xs+25, %pot_t%
Gui, 2:Add, Text, xp+40, Speckles size to remove
Gui, 2:Add, Edit, w35 vpot_a xs+25, %pot_a%
Gui, 2:Add, Text, xp+40, Corner threshold
Gui, 2:Add, Checkbox, vpot_n checked%pot_n% xs+25, No curv optimization
Gui, 2:Add, Edit, w35 vpot_o xs+25, %pot_o%
Gui, 2:Add, Text, xp+40, Curve optimizion tolerance
Gui, 2:Add, Edit, w35 vpot_u xs+25, %pot_u%
Gui, 2:Add, Text, xp+40, Quantize output to 1/unit pixel
Gui, 2:Add, Edit, w35 vpot_k xs+25, %pot_k%
Gui, 2:Add, Text, xp+40, Black/white cutof
Gui, 2:Add, Checkbox, vpot_i checked%pot_i% xs+25, Invert image
Gui, 2:Add, Edit, w35 vpot_r xs+25, %pot_r%
Gui, 2:Add, Text, xp+40, Rotate image clockwise degree

Gui, 2:Add, Text,,

Gui, font, s12, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Text, xs+10, OCR
Gui, font, s10, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Text, xs+25, Operational Mode:
Gui, 2:Add, Edit, w35 vocr_m xp+100, %ocr_m%
Gui, 2:Add, Text, xp+40,
(
Operation mode:
4   Barcode
16   divide overlapping chars
32   context correction
64   char packing
)
Gui, 2:Add, Checkbox, vocr_n checked%ocr_n% xs+25, Only numbers
Gui, 2:Add, Text,,
Gui, 2:Add, Text, xs+10, OCR Postprocessing
Gui, 2:Add, Checkbox, vNoLineReturn checked%NoLineReturn% xs+25, Remove line returns.
Gui, 2:Add, Text,,

Gui, font, s12, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Text, xs+10, GUI
Gui, font, s10, Verdana  ; Set 10-point Verdana.
Gui, 2:Add, Checkbox, vFileSave checked%FileSave%, Allow FileSave
Gui, 2:Add, Checkbox, vInfoWindow checked%InfoWindow%, Show Info Window.
Gui, 2:Add, Text,,
Gui, 2:Add, Button, Default, OK
Gui, 2:Add, Button, Default xp+50, Save
Gui, 2:Add, Button, Default xp+50, Cancel
Gui, 2:Show, r x50
Return

2ButtonOK:
Gui, 1:-Disabled  ; Re-enable the main window (must be done prior to the next step).
Gui, Submit
Gui Destroy  ; Destroy the about box.
Return

2ButtonCancel:
Gui, 1:-Disabled  ; Re-enable the main window (must be done prior to the next step).
Gui Destroy  ; Destroy the about box.
Return

2ButtonSave:
Gui, Submit, NoHide
Gui, 1:-Disabled  ; Re-enable the main window (must be done prior to the next step).
Return

#^r:: ; Reload this script
   Reload
Return


lrh9
  • Members
  • 102 posts
  • Last active: Mar 14 2011 08:33 PM
  • Joined: 10 Jun 2009
Here's a rewrite of the original script. (Minus selection functionality. This way you could pass coordinates to it as a function.) I couldn't get my tesseract executable to work. Kept getting system error 1.

; Requires gocr or tesseract, mkbitmap, potrace, and imagemagick.

; Default variables

; Temporary file options
TmpDir = C:\Program Files\gocr\tmp
TmpFile = image_out

; mkbitmap options
mkbitmap = 1 ; 1, Preprocess screen grab using mkbitmap. 0, false.
mkbitmap_path = C:\Program Files\gocr
mkb_i = ; -i, inversion. Blank by default.
mkb_f = 4
mkb_n = ; -n, nofilter. Turns off highpass filtering. Blank by default.
mkb_s = 2
mkb_interpolation = "-1" ; "-3" or "-1". Cubic or linear interpolation. "-1" yields better results for text.
mkb_t = 0.45
mkb_g = ; -g, grey. Only scales and greymaps the image. Blank by default.

; potrace options
potrace = 1 ; 1, Preprocess screen grab using potrace. 0, false.
potrace_path = C:\Program Files\gocr
pot_z = minority ; black, white, right, left, minority, majority, random
pot_t = 0
pot_a = 1
pot_n = ; Blank by default. "-n" turns off curve optimization.
pot_O = 0.2
pot_u = 10

app = "gocr"

; gocr options
gocr_path = C:\Program Files\gocr
gocr_s = 0
gocr_d = -1
gocr_m = 32
gocr_n = 0

; tesseract options
convert_path = C:\Program Files\gocr
tesseract_path = C:\Program Files\gocr

; Needs four variables representing the points needed for the scan.
; A set of coordinates for the upper left corner of the scan area (the origin), and a set for the lower right corner of the scan area.
; Represented by scan_x_start, scan_y_start, scan_x_end, scan_y_end.

CoordMode, Pixel, Screen    ; Sets pixel actions relative to coordinates of the full desktop window or the active window. Use the mode that you used to obtain the starting and ending coordinates of the image area.

scan_current_x = %scan_x_start%   ; Sets the starting x point of the scan. The origin of the window is the upper left corner.
scan_current_y = %scan_y_start%   ; Sets the starting y point of the scan.

BlockInput, On

Loop
{
   scan_current_y++
   if scan_current_y > %scan_y_end%
   {
      Break
   }
   Loop
   {
      scan_current_x++
      if scan_current_x > %scan_x_end%
      {
         Break
      }
   
      PixelGetColor, current_pixel_color, %scan_current_x%, %scan_current_y%, RGB
      
      StringMid, current_pixel_r, current_pixel_color, 3, 2
      StringMid, current_pixel_g, current_pixel_color, 5, 2
      StringMid, current_pixel_b, current_pixel_color, 7, 2
      
      ; Need to turn the value returned into a hexadecimal value.
      
      current_pixel_r = 0x%current_pixel_r%
      current_pixel_g = 0x%current_pixel_g%
      current_pixel_b = 0x%current_pixel_b%
      
      SetFormat, integer, d
      ; Need to perform a math operation using the variables in order to get them in integer decimal format.
      current_pixel_r -= 0
      current_pixel_g -= 0
      current_pixel_b -= 0
      
      current_pixel_r := " " . current_pixel_r
      current_pixel_g := " " . current_pixel_g
      current_pixel_b := " " . current_pixel_b
      
      image_data = %image_data% %current_pixel_r% %current_pixel_g% %current_pixel_b%
   }
   
   image_data = %image_data% `n
   
   scan_current_x = %scan_x_start%
}

BlockInput, Off

format := "P3`n", comment := "# File made using AutoHotkey.`n", width := scan_x_end - scan_x_start, height := scan_y_end - scan_y_start, max_colors := 255

file_data = %format% %comment% %width% %height% `n %max_colors% `n %image_data%

; Clean old temporary files.
IfExist %TmpDir%\%TmpFile%.ppm
{
   FileDelete %TmpDir%\%TmpFile%.ppm
}
; End of cleaning.

FileAppend, %file_data%, *%TmpDir%\%TmpFile%.ppm
ImageFormat = "ppm"

If mkbitmap = 1
{
   RunWait, %mkbitmap_path%\mkbitmap.exe %mkb_i% -f %mkb_f% %mkb_n% -s %mkb_s% %mkb_interpolation% -t %mkb_t% %mkb_g% "%TmpDir%\%TmpFile%.ppm" -o "%TmpDir%\%TmpFile%.pbm", %mkbitmap_path%, hide,
   ImageFormat = "pbm"
}

If potrace = 1
{
   RunWait, %potrace_path%\potrace.exe -z %pot_z% -t %pot_t% -a %pot_a% %pot_n% -O %pot_O% -u %pot_u% -g -o "%TmpDir%\%TmpFile%.pgm" "%TmpDir%\%TmpFile%.%ImageFormat%", %potrace_path%, hide,
   ImageFormat = "pgm"
}

If app = "gocr"
{
   RunWait, %gocr_path%\gocr.exe -i "%TmpDir%\%TmpFile%.%ImageFormat%" -s %gocr_s% -d %gocr_d% -m %gocr_m% -n %gocr_n% -o "%TmpDir%\%TmpFile%.txt", %gocr_path%, Hide,
}
/* Code not functional.
Else If app = "tesseract"
{
   RunWait, %convert_path%\convert.exe "%TmpDir%\%TmpFile%.%ImageFormat%" "%TmpDir%\%TmpFile%.bmp", %convert_path%, hide,
   ImageFormat = "bmp"
   RunWait, %tesseract_path%\tesseract.exe "%TmpDir%\%TmpFile%.%ImageFormat%" "%TmpDir%\%TmpFile%.txt", %tesseract_path%, UseErrorLevel,
   MsgBox, %errorlevel%
}
*/


pajenn
  • Members
  • 391 posts
  • Last active: Jan 28 2014 03:01 PM
  • Joined: 07 Feb 2009

Here's a rewrite of the original script. (Minus selection functionality. This way you could pass coordinates to it as a function.) I couldn't get my tesseract executable to work. Kept getting system error 1.


fwiw, the version of tesseract I have came with Softi FreeOCR. It put tesseract.exe and tessdata folder into my Windows directory; the rest of the program resides in various Program Files and Documents and Settings folders, but I suppose m2's script doesn't need those.

not sure if this works anymore, but I downloaded FreeOCR from here: http://www.brotherso...eocr-59672.html

I wrote a small print text at the bottom of one of my earlier posts with details because I had trouble finding all software needed to go with this project.

trackingCR
  • Members
  • 1 posts
  • Last active: Nov 27 2009 07:33 PM
  • Joined: 26 Nov 2009

fwiw, the version of tesseract I have came with Softi FreeOCR. It put tesseract.exe and tessdata folder into my Windows directory; the rest of the program resides in various Program Files and Documents and Settings folders, but I suppose m2's script doesn't need those.

not sure if this works anymore, but I downloaded FreeOCR from here: http://www.brotherso...eocr-59672.html

I wrote a small print text at the bottom of one of my earlier posts with details because I had trouble finding all software needed to go with this project.

Your documentation has been quite thorough and awesome !

Your work on this has lead me to a workable version that I am content with. Kudos to you and M2 and others that paved the way -- bigtime ! :mrgreen:

Basically re-used Pajenn's last code and set Clipboard =%ocr_t_text%, remove gocr lines [optional]; thats it.

http://www.viddler.c...ngCR/videos/60/ [Full Screen recommended]

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Apr 30 2013 09:57 PM
  • Joined: 04 Dec 2006
just curious,

you've not published your code/package in zip, etc.,

because you don't want the Finns to get their hands on it ??

m1m1k
  • Members
  • 2 posts
  • Last active: Sep 23 2010 11:16 PM
  • Joined: 03 Mar 2010
I've compiled all the bits together into one zip file,
hope you all don't mind.
Most of the relative paths *Should* work out of the box... with the exception of the TempDir variable (see below)


screenreader_ocr.zip (17 MB) !!

one small note,
the gocr.exe file needs a dos-passable path.

this means that /Documents and Settings/ doesn't work,
you have to write it /Documen~1/

So for the TempDir Variable (at the top), make sure you write out the whole absolute path, because otherwise the %A_workingDir% won't cut it.
The symptom is that no error messages are displayed, and pictures are popping up, but the final ocr text fields are both blank.

Hopefully this is helpful for someone!

-M1m1K

samfiller
  • Guests
  • Last active:
  • Joined: --
I downloaded - the compiled version-

Thanks for that,

but I can not seem to get it to work - no matter what i select it does not copy the image even to perform the OCR.

Can anyone tell me what I am doing wrong?

Just for info i unzipped it to C:\ocr\ and updated the one line for the Temp file to c:\ocr\temp .

any help would be really appreciated!!!!!

samfiller
  • Guests
  • Last active:
  • Joined: --
Ok I got it to work, silly mistake but may be important pointer for others.


After you press win+q and the screen grays out to take the snapshot, you left click drag the selection box and unleft click BUT it only works on a drag from left to right not right to left.

I was dragging the box right to left......

Hope this helps others

Maestr0
  • Members
  • 650 posts
  • Last active: Mar 16 2014 09:27 PM
  • Joined: 18 Oct 2008
I took some ideas and scripts and made the following, works pretty well.

It needs a bit of work on the GUI side, but the capturing/OCR-ing works pretty solidly.

Links to all programs needed are in the script.

Please let me know what you think, this has been a lot of trial and error and I'd love other peoples' opinion as to the usage, settings and my code.

/*
requirements:
	nconvert.exe (place in .\app subfolder)
		http://www.xnview.com/en/download.html
	MKBitmap and POTrace (place in app subfolder)
		http://potrace.sourceforge.net/#downloading
	Tesseract (place in .\app\tesseract subfolder)
		http://code.google.com/p/tesseract-ocr/downloads/list
	GOCR.exe (place in .\app subfolder)
		http://jocr.sourceforge.net/download.html
	
code borrowed from:
	http://www.autohotkey.com/forum/viewtopic.php?t=49950 (the grabbing GUI)
	http://www.autohotkey.com/forum/viewtopic.php?t=35600 (the cursor changes)

description:
	This script allows a user to select a rectangle to OCR to clipboard.
	What it basically does is :
	1. copy the rectangle to the clipboard using the capture subroutine (called by ctrl-leftclick)
	2. using NConvert the clipboard is converted to a BMP file
	3. using MKBitmap the file is optimised with scaling and filtering
	4. using potrace the file is then transformed into vector graphics
	if Tesseract is set as OCR:
		5. using NConvert the clipboard is converted to a TIFF file
	else if GOCR is set as OCR:
		5. using NConvert the clipboard is converted to a BMP file
	6. using either Tesseract or GOCR the file is "read"
	7. the result is read and copied to the clipboard (if it contains text)

to do:
	- make a GUI to set the parameters of each app
	- allow user to get rid of line endings in the OCR_Text variable
*/
	#persistent
	#SingleInstance Force
	SetBatchLines -1 		; maximum speed for loops
	#NoEnv
	#MaxThreadsPerHotkey 1	; enable correction on accidental press of several hotkeys (only last pressed hotkey will be fired)

	app_name	= ahk-ocr
	app_version = 0.0.008
	app_author	= Maestr0
	
	ini_file	= %A_ScriptDir%\ahk-ocr.ini
	app_icon	= %A_ScriptDir%\ahk-ocr.ico
	
	gosub menu
	gosub read_variables
	
	hotkey Lbutton , capture , off
	hotkey escape , capture_off, off
	
	gosub capture_on
return
menu:
	ifexist %app_icon%
		Menu, Tray, Icon, %app_icon%
	Menu, Tray, Tip, %app_name% %app_version%
	Menu, tray, NoStandard
	Menu, tray, add, %app_name% %app_version%, menu_reload
	Menu, tray, add
	Menu, tray, add, Capture, capture_toggle
	Menu, tray, default, Capture
	Menu, tray, add
	if A_IsCompiled <> 1
	{
		Menu, StandardMenu, Standard
		Menu, tray, add, Standard menu options, :StandardMenu
		Menu, tray, add
	}
	Menu, tray, add, Einde, menu_exit
return
menu_reload:
	reload
menu_exit:
	exitapp
capture_toggle:
	if capture_mode = 1
		gosub capture_off
	else
		gosub capture_on
return
capture_on:
	capture_mode = 1
	hotkey Lbutton , capture, on
	hotkey escape , capture_off, on
	Menu, tray, Check, Capture
	settimer , tooltip_start , 50
	SetSystemCursor("IDC_CROSS") ; sets the mouse cursor to a big +
return
capture_off:
	ToolTip ; clears any tooltip that may be showing
	capture_mode = 0
	hotkey Lbutton , capture, off
	hotkey escape , capture_off, off
	Menu, tray, Uncheck, Capture
	gosub RestoreCursors	; restores the mouse cursor
	settimer , tooltip_start , off
return
read_variables:
	IniRead, test, %ini_file%, general, test, 0
	IniRead, tmpfolder, %ini_file%, general, tmpfolder, %A_Temp%
	IniRead, ocr_engine, %ini_file%, general, ocr_engine, Tesseract
	
	IniRead, app_nconvert, %ini_file%, general, app_nconvert, %A_ScriptDir%\app\nconvert.exe
	IniRead, app_mkbitmap, %ini_file%, general, app_mkbitmap, %A_ScriptDir%\app\mkbitmap.exe
	IniRead, app_potrace, %ini_file%, general, app_potrace, %A_ScriptDir%\app\potrace.exe
	IniRead, app_tesseract, %ini_file%, general, app_tesseract, %A_ScriptDir%\app\tesseract\tesseract.exe
	IniRead, app_gocr, %ini_file%, general, app_gocr, %A_ScriptDir%\app\gocr.exe

	IniRead, url_nconvert, %ini_file%, general, url_nconvert, http://www.xnview.com/en/download.html
	IniRead, url_mkbitmap, %ini_file%, general, url_mkbitmap, http://potrace.sourceforge.net/#downloading
	IniRead, url_potrace, %ini_file%, general, url_potrace, http://potrace.sourceforge.net/#downloading
	IniRead, url_tesseract, %ini_file%, general, url_tesseract, http://code.google.com/p/tesseract-ocr/downloads/list
	IniRead, url_gocr, %ini_file%, general, url_gocr, http://jocr.sourceforge.net/download.html

	check_instance(app_nconvert,url_nconvert)
	check_instance(app_mkbitmap,url_mkbitmap)
	check_instance(app_potrace,url_potrace)
	if ocr_engine = gocr
		check_instance(app_gocr,url_gocr)
	else
		check_instance(app_tesseract,url_tesseract)
return
check_instance(location,url)	; checks the existence for a needed app, if it's not found, a message appears and the script goes dormant again
{
	ifnotexist %location%
	{
		msgbox % "A required file does not exist:`n" location "`n`nDownload it using the link below and try again.`n`n" url
		gosub capture_off
		return
	}
}
capture:
;=== step 0 ========================================================================
; Before we do anything, read the variables, maybe they changed since the start of the script
	gosub read_variables
;=== step 1 ========================================================================
; Allow the user to select an area for the OCR and save selected area without cursor in clipboard
	if ( clipboard <> "" ) OR ( test <> 1 )
		gosub get_area
	gosub capture_off	; to turn the active mode to off
;=== step 2 ========================================================================
; Clean up the temporary files that may have been left behind
	FileDelete %tmpfolder%\1_nconvert_output*.*
	FileDelete %tmpfolder%\2_mkbitmap_output*.*
	FileDelete %tmpfolder%\3_potrace_output*.*
	FileDelete %tmpfolder%\4_nconvert_output*.*
	FileDelete %tmpfolder%\tesseract_output*.*
	FileDelete %tmpfolder%\gocr_output*.*
;=== step 3 ========================================================================
	; 3a: Load parameters for command line tools
	IniRead, parameters_nconvert, %ini_file%, parameters, nconvert, %A_Space%
	IniRead, parameters_mkbitmap, %ini_file%, parameters, mkbitmap, -f 4 -s 3 -t 0.48
	IniRead, parameters_potrace, %ini_file%, parameters, potrace, -O 0.2 -k 0.5 -r 0 -t 2 -g -a 4
	IniRead, parameters_tesseract, %ini_file%, parameters, tesseract, %A_Space%
	; 3b: NConvert: Convert the clipboard to an image file
	RunWait , "%app_nconvert%" %parameters_nconvert% -overwrite -out bmp -o 1_nconvert_output.bmp -clipboard, %tmpfolder%, hide
	; 3c: MKBitmap: transform images into bitmaps with scaling and filtering
	RunWait , "%app_mkbitmap%" %parameters_mkbitmap% -o 2_mkbitmap_output.pbm 1_nconvert_output.bmp, %tmpfolder%, hide
	; 3d: Potrace: transform bitmaps into vector graphics.
	RunWait , "%app_potrace%" %parameters_potrace% -o 3_potrace_output.pgm 2_mkbitmap_output.pbm, %tmpfolder%, hide
	if ocr_engine = gocr
	{
		; 3e: NConvert: Convert the clipboard to an image file
		RunWait , "%app_nconvert%" -overwrite -out pnm -o 4_nconvert_output.pnm 3_potrace_output.pgm, %tmpfolder%, hide
		; 3f: GOCR: The actual OCR
		RunWait , "%app_gocr%" %parameters_gocr% 4_nconvert_output.pnm gocr_output, %tmpfolder%, hide
	}
	else	; this is the default: Tesseract
	{
		; 3e: NConvert: Convert the clipboard to an image file
		RunWait , "%app_nconvert%" -overwrite -out tiff -o 4_nconvert_output.tif 3_potrace_output.pgm, %tmpfolder%, hide
		; 3f: Tesseract: The actual OCR
		RunWait , "%app_tesseract%" %parameters_tesseract% 4_nconvert_output.tif tesseract_output, %tmpfolder%, hide
	}

;=== step 4 ========================================================================
; Read the OCR contents to the clipboard
	FileRead OCR_text, %tmpfolder%\tesseract_output.txt
	if OCR_Text =
		outputdebug Error`, no text was detected.
	else
	{
		n := 5000
		SetTimer, tooltip_finish, 50
		SetTimer, RemoveToolTip, 5000
		clipboard := OCR_text
	}

	if test = 1
		run %tmpfolder%\tesseract_output.txt
return
tooltip_start:
	Tooltip, Maak een kader om de te herkennen tekst.`nDruk op Escape om te annuleren.,%tx%,%ty%
return
tooltip_finish:
	n -= 100
	sec := ceil(n/1000)
	MouseGetPos, xpos, ypos
	xpos += 16
	ypos += 16
	if sec > 1
		Tooltip , De tekst is herkend en op je klembord gezet.`nDit bericht sluit automatisch na %sec% seconden., %xpos%, %ypos%,
	else
		Tooltip
return
RemoveToolTip:
    SetTimer, RemoveToolTip, Off
	SetTimer, tooltip_finish, Off
    ToolTip
return

;===Description========================================================================
; Source: Screen clipping script. Saves selected area to clipboard. http://www.autohotkey.com/forum/viewtopic.php?t=49950
;===Functions==========================================================================
CaptureScreen(aRect)
{
   StringSplit, rt, aRect, `,, %A_Space%%A_Tab%
   nL := rt1
   nT := rt2
   nW := rt3 - rt1
   nH := rt4 - rt2
   znW := rt5
   znH := rt6

   mDC := DllCall("CreateCompatibleDC", "Uint", 0)
   hBM := CreateDIBSection(mDC, nW, nH)
   oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)
   hDC := DllCall("GetDC", "Uint", 0)
   DllCall("BitBlt", "Uint", mDC, "int", 0, "int", 0, "int", nW, "int", nH, "Uint", hDC, "int", nL, "int", nT, "Uint", 0x40000000 | 0x00CC0020)
   DllCall("ReleaseDC", "Uint", 0, "Uint", hDC)
   DllCall("SelectObject", "Uint", mDC, "Uint", oBM)
   DllCall("DeleteDC", "Uint", mDC)
   SetClipboardData(hBM)
}

CreateDIBSection(hDC, nW, nH, bpp = 32, ByRef pBits = "")
{
   NumPut(VarSetCapacity(bi, 40, 0), bi)
   NumPut(nW, bi, 4)
   NumPut(nH, bi, 8)
   NumPut(bpp, NumPut(1, bi, 12, "UShort"), 0, "Ushort")
   NumPut(0,  bi,16)
   Return   DllCall("gdi32\CreateDIBSection", "Uint", hDC, "Uint", &bi, "Uint", 0, "UintP", pBits, "Uint", 0, "Uint", 0)
}

SetClipboardData(hBitmap)
{
   DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
   hDIB :=   DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+NumGet(oi,44))
   pDIB :=   DllCall("GlobalLock", "Uint", hDIB)
   DllCall("RtlMoveMemory", "Uint", pDIB, "Uint", &oi+24, "Uint", 40)
   DllCall("RtlMoveMemory", "Uint", pDIB+40, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44))
   DllCall("GlobalUnlock", "Uint", hDIB)
   DllCall("DeleteObject", "Uint", hBitmap)
   DllCall("OpenClipboard", "Uint", 0)
   DllCall("EmptyClipboard")
   DllCall("SetClipboardData", "Uint", 8, "Uint", hDIB)
   DllCall("CloseClipboard")
}

get_area:
	CoordMode, Mouse ,Screen
	CoordMode, Tooltip ,Screen
	MouseGetPos, MX, MY
	Gui, +AlwaysOnTop -caption +Border +ToolWindow +LastFound
	WinSet, Transparent, 80
	Gui, Color, lime
	
	; shut tooltip_start off
	settimer , tooltip_start , off
	tooltip
	While, (GetKeyState("LButton", "p"))
	{
	   MouseGetPos, MXend, MYend
	   Send {control up}
	   w := abs(MX - MXend)
	   h := abs(MY - MYend)
	   If ( MX < MXend )
	   {
		X := MX
		tx := MX + 2
		}
	   Else
	   {
		X := MXend
		tx := MX + 16
		}
	   If ( MY < MYend )
	   {
		Y := MY
		tY := MY - 20
		}
	   Else
	   {
		Y := MYend
		tY := MY + 16
		}
	   Gui, Show, x%X% y%Y% w%w% h%h%

		MouseGetPos, xpos, ypos
		Tooltip, Maak een kader om de te herkennen tekst.,%tx%,%ty%
	   Sleep, 10
	}
	ToolTip
	gosub RestoreCursors	; restores the mouse cursor

	MouseGetPos, MXend, MYend
	Gui, Destroy
	If ( MX > MXend )
	{
	   temp := MX
	   MX := MXend
	   MXend := temp
	}
	If ( MY > MYend )
	{
	   temp := MY
	   MY := MYend
	   MYend := temp
	}
	Area = %MX%, %MY%, %MXend%, %MYend%
	Sleep, 100   ; if omitted, GUI sometimes stays in picture
	CaptureScreen(Area)   ; Saves selected area without cursor in Clipboard.
return

OnExit:
	ToolTip
	Suspend
	gosub RestoreCursors	; restores the mouse cursor
ExitApp

; --------------
; below is just to change the mouse cursor so it's clear when the capture mode is active
; source: http://www.autohotkey.com/forum/viewtopic.php?t=35600
SetSystemCursor( Cursor = "", cx = 0, cy = 0 )
{
   BlankCursor := 0, SystemCursor := 0, FileCursor := 0 ; init
   
   SystemCursors = 32512IDC_ARROW,32513IDC_IBEAM,32514IDC_WAIT,32515IDC_CROSS
   ,32516IDC_UPARROW,32640IDC_SIZE,32641IDC_ICON,32642IDC_SIZENWSE
   ,32643IDC_SIZENESW,32644IDC_SIZEWE,32645IDC_SIZENS,32646IDC_SIZEALL
   ,32648IDC_NO,32649IDC_HAND,32650IDC_APPSTARTING,32651IDC_HELP
   
   If Cursor = ; empty, so create blank cursor 
   {
      VarSetCapacity( AndMask, 32*4, 0xFF ), VarSetCapacity( XorMask, 32*4, 0 )
      BlankCursor = 1 ; flag for later
   }
   Else If SubStr( Cursor,1,4 ) = "IDC_" ; load system cursor
   {
      Loop, Parse, SystemCursors, `,
      {
         CursorName := SubStr( A_Loopfield, 6, 15 ) ; get the cursor name, no trailing space with substr
         CursorID := SubStr( A_Loopfield, 1, 5 ) ; get the cursor id
         SystemCursor = 1
         If ( CursorName = Cursor )
         {
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            Break               
         }
      }   
      If CursorHandle = ; invalid cursor name given
      {
         Msgbox,, SetCursor, Error: Invalid cursor name
         CursorHandle = Error
      }
   }   
   Else If FileExist( Cursor )
   {
      SplitPath, Cursor,,, Ext ; auto-detect type
      If Ext = ico 
         uType := 0x1   
      Else If Ext in cur,ani
         uType := 0x2      
      Else ; invalid file ext
      {
         Msgbox,, SetCursor, Error: Invalid file type
         CursorHandle = Error
      }      
      FileCursor = 1
   }
   Else
   {   
      Msgbox,, SetCursor, Error: Invalid file path or cursor name
      CursorHandle = Error ; raise for later
   }
   If CursorHandle != Error 
   {
      Loop, Parse, SystemCursors, `,
      {
         If BlankCursor = 1 
         {
            Type = BlankCursor
            %Type%%A_Index% := DllCall( "CreateCursor"
            , Uint,0, Int,0, Int,0, Int,32, Int,32, Uint,&AndMask, Uint,&XorMask )
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }         
         Else If SystemCursor = 1
         {
            Type = SystemCursor
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            %Type%%A_Index% := DllCall( "CopyImage"
            , Uint,CursorHandle, Uint,0x2, Int,cx, Int,cy, Uint,0 )      
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }
         Else If FileCursor = 1
         {
            Type = FileCursor
            %Type%%A_Index% := DllCall( "LoadImageA"
            , UInt,0, Str,Cursor, UInt,uType, Int,cx, Int,cy, UInt,0x10 ) 
            DllCall( "SetSystemCursor", Uint,%Type%%A_Index%, Int,SubStr( A_Loopfield, 1, 5 ) )         
         }          
      }
   }   
}
return
RestoreCursors:
   SPI_SETCURSORS := 0x57
   DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
return

/*
comparison between Tesseract and GOCR with the same image files:
---------------
Tesseract

For two years I spent my early mornings walking the
streets of West Oakland in the San Francisco Bay Area.
lt is a neighborhood steeped in history and diversity,
from ship workers to blues musicians. Yet, as freeways
were built and factories moved in, the neighborhood
became cut off from the rest of city and left isolated and
forgotten by many.
As I spent time photographing in West Oakland, I began to hnd beauty
in the unique and improvised envimnment that was born from the
neighborhoods very isolation. Sometimes it was a sad and hard beauty,
but often it was hopeful and timeless. I also found intimate moments of ritual
that stem from the neighborhood's mixed rural Southem and Latino heritage.
These photographs describe the stories and spaces that I found on my
walks through West Oakland.

---------------
GOCR

for mo years t spent my early mornings wa lking the
streets ot West Oakland in the San francisco 8ay Area.
lt is a neighborhood steeped in hi_oN and diversiN,
trom ship workers to blues m_sicians. Yet, as freeways
were buitt and tactories moved in, the neighborhood
became cut off trom the rest of ciN and le_ isolated and
torgottenbymany.

As l sp$nt _m_ photogr8phing in We_ 08_l8nd, I beg8n to F_nd be8uN
in _8 uniqu8 8nd impro_sed 8nvi_nme___w8s born trom _8
neighbomood's _N isol8_on. Same_mes _ w8s 8 s8d 8nd h8rd b$8u_,
but o_en it w8s hop$ful 8nd tim$lest l 8lso tound i_m8te mome_ _ r0'  8l
th8t stem trom lh_ neighbomood's mixed _r8l Southem 8nd __no hent8g8.
mes_ photogr8phs descnb_ lh_ _ones 8nd sp8ces __ I tound on my
w8lks _rough Wen 08tI8nd.
*/


Maestr0
  • Members
  • 650 posts
  • Last active: Mar 16 2014 09:27 PM
  • Joined: 18 Oct 2008
updated version. Making steady progression with the GUI.

Comments still welcomed :D
/*
requirements:
	NConvert (place in .\app subfolder)
		http://www.xnview.com/en/download_nc.html
	MKBitmap and POTrace (place both in .\app subfolder)
		http://potrace.sourceforge.net/#downloading
	Tesseract (place in .\app\tesseract subfolder)
		http://code.google.com/p/tesseract-ocr/downloads/list
	GOCR (optional app (default is Tesseract) place in .\app\gocr subfolder)
		http://jocr.sourceforge.net/download.html
	
code borrowed from:
	http://www.autohotkey.com/forum/viewtopic.php?t=49950 (the grabbing GUI)
	http://www.autohotkey.com/forum/viewtopic.php?t=35600 (the cursor changes)

description:
	This script allows a user to select a rectangle to OCR to clipboard.
	What it basically does is :
	1. copy the rectangle to the clipboard using the capture subroutine (called by ctrl-leftclick)
	2. using NConvert the clipboard is converted to a BMP file (using parameters)
	3. using MKBitmap the file is optimised with scaling and filtering
	4. using potrace the file is then transformed into vector graphics
	if Tesseract is set as OCR:
		5. using NConvert the clipboard is converted to a TIFF file (without parameters)
	else if GOCR is set as OCR:
		5. using NConvert the clipboard is converted to a BMP file (without parameters)
	6. using either Tesseract or GOCR the file is "read"
	7. the result is read and copied to the clipboard (if it contains text)

to do:
	- make a GUI to set the parameters of each app
	- decide what backend is the best for potrace
	- allow user to get rid of line endings in the OCR_Text variable
	- allow a hotkey to enable/disable capture mode
	- allow the hotkey to enable/disable capture mode to be changed through the GUI
	- scan for tesseract available languages and allow a choice in the GUI
	- language problem being entered in the GUI on load
*/
	#persistent
	#SingleInstance Force
	SetBatchLines -1 		; maximum speed for loops
	#NoEnv
	#MaxThreadsPerHotkey 1	; enable correction on accidental press of several hotkeys (only last pressed hotkey will be fired)

	app_name	= ahk-ocr
	app_version = 0.0.011
	app_author	= Maestr0
	
	ini_file	= %A_ScriptDir%\ahk-ocr.ini
	app_icon	= %A_ScriptDir%\ahk-ocr.ico
	
	gosub menu
	gosub read_variables
	
	hotkey Lbutton , capture , off
	hotkey escape , capture_off, off
	
	if Capture_on_startup = 1
		gosub capture_on
	
	gosub gui	; just for testing
return
read_variables:
	IniRead, test, %ini_file%, general, test, 0
	IniRead, tmpfolder, %ini_file%, general, tmpfolder, %A_Temp%
	IniRead, ocr_engine, %ini_file%, general, ocr_engine, Tesseract
	
	IniRead, app_nconvert, %ini_file%, general, app_nconvert, %A_ScriptDir%\app\nconvert.exe
	IniRead, app_mkbitmap, %ini_file%, general, app_mkbitmap, %A_ScriptDir%\app\mkbitmap.exe
	IniRead, app_potrace, %ini_file%, general, app_potrace, %A_ScriptDir%\app\potrace.exe
	IniRead, app_tesseract, %ini_file%, general, app_tesseract, %A_ScriptDir%\app\tesseract\tesseract.exe
	IniRead, app_gocr, %ini_file%, general, app_gocr, %A_ScriptDir%\app\gocr\gocr.exe

	IniRead, url_nconvert, %ini_file%, general, url_nconvert, http://www.xnview.com/en/download.html
	IniRead, url_mkbitmap, %ini_file%, general, url_mkbitmap, http://potrace.sourceforge.net/#downloading
	IniRead, url_potrace, %ini_file%, general, url_potrace, http://potrace.sourceforge.net/#downloading
	IniRead, url_tesseract, %ini_file%, general, url_tesseract, http://code.google.com/p/tesseract-ocr/downloads/list
	IniRead, url_gocr, %ini_file%, general, url_gocr, http://jocr.sourceforge.net/download.html

	SplitPath, app_gocr,, path_gocr
	IniRead, db_gocr, %ini_file%, general, db_gocr, %path_gocr%\db\
	StringReplace, db_gocr, db_gocr, %A_ScriptDir%, .
	
	; the ...._n_params are the parameters which have additional values
	nconvert_n_params = contrast,deinterlace,eedge
	mkbitmap_n_params = f,s,t
	potrace_n_params = z,t,a,n,o,u,r
	tesseract_n_params = l
	gocr_n_params = p,l,d,s,m,a
	
	gosub collect_tesseract_languages
	
	check_instance(app_nconvert,url_nconvert)
	check_instance(app_mkbitmap,url_mkbitmap)
	check_instance(app_potrace,url_potrace)
	if ocr_engine = gocr
		check_instance(app_gocr,url_gocr)
	else
		check_instance(app_tesseract,url_tesseract)
		
	IniRead, NoLineReturn, %ini_file%, general, NoLineReturn, 0
	IniRead, Capture_on_startup, %ini_file%, general, Capture_on_startup, 1
	IniRead, CleanTemporaryOutput, %ini_file%, general, CleanTemporaryOutput, 1
	
	IniRead, parameters_nconvert, %ini_file%, parameters, nconvert, %A_Space%
	IniRead, parameters_mkbitmap, %ini_file%, parameters, mkbitmap, -f 4 -s 3 -t 0.48
	IniRead, parameters_potrace, %ini_file%, parameters, potrace, -O 0.2 -r 0 -t 2 -g -a 4
	IniRead, parameters_tesseract, %ini_file%, parameters, tesseract, %A_Space%
	IniRead, parameters_gocr, %ini_file%, parameters, gocr, %A_Space%
	
	; the parameters below are meant for settings that cannot be changed in the GUI, the parameters above in the ini file will be overwritten by selections in the GUI
	IniRead, parameters_nconvert_override, %ini_file%, parameters, nconvert, %A_Space%
	IniRead, parameters_mkbitmap_override, %ini_file%, parameters, mkbitmap, %A_Space%
	IniRead, parameters_potrace_override, %ini_file%, parameters, potrace, %A_Space%
	IniRead, parameters_tesseract_override, %ini_file%, parameters, tesseract, %A_Space%
	IniRead, parameters_gocr_override, %ini_file%, parameters, gocr, %A_Space%
return
check_instance(location,url)	; checks the existence for a needed app, if it's not found, a message appears and the script goes dormant again
{
	ifnotexist %location%
	{
		msgbox % "A required file does not exist:`n" location "`n`nDownload it using the link below and try again.`n`n" url
		gosub capture_off
		return
	}
}
gui:
	GUI, destroy
	gosub read_variables	; make sure the last settings are loaded
	
	gui_width 	= 600
	tab_left	= 22
	tab_right	= 22
	tab_width 	:= gui_width - tab_left - tab_right
	
	gui_height 	:= gui_width + 50
	tab_top		= 32
	tab_bottom	= 10
	tab_height 	:= gui_height - tab_top - tab_bottom

	; ---- you shouldn't have to change anything in the GUI below, that's what the variables directly above are for
	ifexist %app_gocr%
		Gui, Add, Tab2, % "-wrap h" tab_height " w" gui_width - tab_left, General|NConvert|MKBitmap|Potrace|Tesseract|GOCR|About
	else
		Gui, Add, Tab2, % "-wrap h" tab_height " w" gui_width - tab_left, General|NConvert|MKBitmap|Potrace|Tesseract|About
	
	GUI, Tab, General
	GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, General settings
	GUI, add, checkbox, xs w%tab_width% vNoLineReturn checked%NoLineReturn%, Remove Line returns after OCR
	GUI, add, checkbox, xs w%tab_width% vCapture_on_startup checked%Capture_on_startup%, Start a capture immediately afters loading the script
	GUI, add, checkbox, xs w%tab_width% vCleanTemporaryOutput checked%CleanTemporaryOutput%, Delete temporary files after OCR
	ifexist %app_gocr%
	{
		GUI, add, Text, xs+15 section, Choose OCR program: 
		GUI, add, DropDownList, ys-3 w150 gchoose_engine vocr_engine, Tesseract|GOCR
		GUIControl, ChooseString, ocr_engine, %ocr_engine%
	}
	
/*
   parse the parameters to figure out which boxes to check and which values to enter
		mkbitmap, -f 4 -s 3 -t 0.48
		potrace, -O 0.2 -k 0.5 -r 0 -t 2 -g -a 4
*/
	GUI, Tab, mkbitmap
	GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, mkbitmap processing options
	GUI, add, checkbox, xs w%tab_width% vmkb_i section, %A_Space%-i : Invert the input image.
	GUI, add, checkbox, xs w%tab_width% vmkb_f gcheckedit section, %A_Space%-f : Apply a highpass filter to the image.
	GUI, add, edit, xs20 w50 right number limit100 vmkb_f_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vmkb_n section, %A_Space%-n : Turn off highpass filtering.
	GUI, add, checkbox, xs w%tab_width% vmkb_b gcheckedit section, %A_Space%-b : Blur the image to reduce visual noise.
	GUI, add, edit, xs20 w50 right number limit100 vmkb_b_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vmkb_s gcheckedit section, %A_Space%-s : Scale the image by an integer factor great than 0.
	GUI, add, edit, xs20 w50 right number limit100 vmkb_s_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vmkb_l section, %A_Space%-l : Use linear interpolation when scaling to a higher resolution.
	GUI, add, checkbox, xs w%tab_width% vmkb_3 section, %A_Space%-3 : Use cubic interpolation when scaling to a higher resolution. (default)
	GUI, add, checkbox, xs w%tab_width% vmkb_t gcheckedit section, %A_Space%-t : Set the threshold grey value for bilevel conversion. (0 = black, 1 = white)
	GUI, add, edit, xs20 w50 right number limit100 vmkb_t_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vmkb_g section, %A_Space%-g : Disable bilevel conversion, processing stops after the scaling step and a greymap is output.
	
	GUI, add, Text, xs w%tab_width%, `nDescription:`n`nmkbitmap reads an image and applies one or more of the following operations to it`nin this order: inversion, highpass filtering, scaling, and thresholding. Each operation can be individually controlled and turned on or off. 
/*
potrace
-b name, --backend name
Select backend by name, where name is one of eps, postscript, ps, pdf, pdfpage, svg, pgm, gimppath, xfig. Backend names can be abbreviated by an unambiguous prefix.
-e, --eps, -b eps, --backend eps
Encapsulated PostScript backend (default). The input is a single bitmap; the output is a stand-alone, encapsulated PostScript file that can be included in other documents.
*/
	GUI, Tab, potrace
	GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, potrace processing options
	GUI, add, checkbox, xs w%tab_width% vpot_z gcheckedit section, %A_Space%-z : Specify how to resolve ambiguities in path decomposition.`nMust be one of black, white, right, left, minority, majority, or random. Default is minority.
	GUI, add, edit, xs20 w50 right number limit100 vpot_z_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_t gcheckedit section, %A_Space%-t : Suppress speckles of up to this many pixels.
	GUI, add, edit, xs20 w50 right number limit100 vpot_t_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_a gcheckedit section, %A_Space%-a : Set the corner threshold parameter. The smaller this value, the more sharp corners will be produced. If this parameter is negative, then no smoothing will be performed and the output is a polygon.
	GUI, add, edit, xs20 w50 right number limit100 vpot_a_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_n gcheckedit section, %A_Space%-n : Turn off curve optimization. Larger values allow more consecutive Bezier curve segments to be joined together in a single segment, at the expense of accuracy.
	GUI, add, edit, xs20 w50 right number limit100 vpot_n_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_o gcheckedit section, %A_Space%-o : Set the curve optimization tolerance. Larger values allow more consecutive Bezier curve segments at the expense of accuracy.
	GUI, add, edit, xs20 w50 right number limit100 vpot_o_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_u gcheckedit section, %A_Space%-u : Set output quantization, output is rounded to 1/unit pixels. The default of 10 usually gives good results. For some of the debug modes, a value of 100 gives more accurate output.
	GUI, add, edit, xs20 w50 right number limit100 vpot_u_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vpot_i section, %A_Space%-i : Invert
	GUI, add, checkbox, xs w%tab_width% vpot_r gcheckedit section, %A_Space%-r : set the resolution (in dpi). Note that it follows that a larger value results in a smaller output image. It is possible to specify different resolutions in the x and y directions by giving an argument of the form nxn.
	GUI, add, edit, xs20 w50 right number limit100 vpot_r_edit disabled, 
	
	GUI, add, Text, xs w%tab_width%, `nDescription:`n`npotrace is a utility for tracing a bitmap, which means, transforming a bitmap into a smooth, scalable image. The input is a bitmap, which means, a pixel-based image composed of the two colors black and white only. The default output is an encapsulated PostScript file (EPS). A typical use is to create EPS files from scanned data, such as company or university logos, handwritten notes, etc. The resulting image is not "jaggy" like a bitmap, but smooth. It can then be rendered at any resolution.

	GUI, Tab, NConvert
	; contrast,deinterlace,eedge
	GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, NConvert converting options
	GUI, add, checkbox, xs w%tab_width% vncon_autocontrast section, %A_Space%-autocontrast : Auto Contrast
	GUI, add, checkbox, xs w%tab_width% vncon_autolevels section, %A_Space%-autolevels : Auto Levels
	GUI, add, checkbox, xs w%tab_width% vncon_contrast gcheckedit section, %A_Space%-contrast :  Modify contrast (-100...100)
	GUI, add, edit, xs20 w50 right number limit100 vncon_contrast_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vncon_dither section, %A_Space%-dither : Use Bayer dithering for conversion (Colors and Grey only)
	GUI, add, checkbox, xs w%tab_width% vncon_deinter gcheckedit section, %A_Space%-deinter : De-interlace (k = even or odd, n = dup or int)
	GUI, add, edit, xs20 w50 right number limit100 vncon_deinter_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vncon_edetail section, %A_Space%-edetail : Enhance detail
	GUI, add, checkbox, xs w%tab_width% vncon_efocus section, %A_Space%-efocus : Enhance focus
	GUI, add, checkbox, xs w%tab_width% vncon_eedge gcheckedit section, %A_Space%-eedge : Enhance edges (1...100)
	GUI, add, edit, xs20 w50 right number limit100 vncon_eedge_edit disabled, 
	GUI, add, checkbox, xs w%tab_width% vncon_equalize section, %A_Space%-equalize : Equalize
	GUI, add, checkbox, xs w%tab_width% vncon_floyd section, %A_Space%-floyd : Use floydSteinberg dithering for conversion (Colors and Grey only)

	GUI, add, Text, xs w%tab_width%, `nDescription:`n`nNConvert is a batch utility for converting graphic files. `nNConvert is provided as FREEWARE for private non-commercial or educational use (including non-profit organization)
/*
	;	nconvert
	  -frestore         : Focus restoration
	  -gamma value      : Modify gamma (0.01<->5.0
	  -gammasat value   : Modify gamma (0.01<->5.0
	  -log              : Apply logarithmic correction
	  -negate           : Negate
	  -new bpp w h      : Create new bitmap
	  -noise reduce     : Reduce noise
	  -normalize        : Normalize
	  -rotate_flag      : Rotate flags
		  smooth : Use smooth rotate
	  -rotate degrees   : Rotate
	  -sepia percent    : Sepia
	  -sharpen percent  : Sharpen (1...100)
	  -shear            : Shear
	  -slice            : Slice
	  -soften percent   : Soften (1...100)
	  -solarize value   : Solarize (1...255)
	  -text_font name size  : Font name and size
	  -text_color r g b : Text color
	  -text_back r g b  : Text background color
	  -text_flag pos    : Position of text
		  top-left, top-center, top-right
		  center-left, center, center-right
		  bottom-left, bottom-center, bottom-right
	  -text_pos x y     : Position or offset
	  -text_rotation degrees : Rotation
	  -truecolours      : Convert in True Colors
*/
	GUI, Tab, Tesseract
	GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, Tesseract processing options
	GUI, add, checkbox, xs w%tab_width% vtess_l gcheckedit section, %A_Space%-l : Language of scanned text.
	GUI, add, DropDownList, xs20 w100 limit100 vtess_l_edit disabled, %lang%

	GUI, add, Text, xs w%tab_width%, `nDescription:`n`nTesseract is a free software optical character recognition engine for various operating systems.`nOriginally developed as proprietary software at Hewlett-Packard between 1985 and 1995, it had very little work done on it in the following decade. It was then released as open source in 2005 by Hewlett Packard and UNLV. Tesseract development is currently sponsored by Google. It is released under the Apache License, Version 2.0.`n`nTesseract is considered one of the most accurate free software OCR engines currently available
	
	ifexist %app_gocr%
	{
		GUI, Tab, GOCR
		GUI, add, Text, x%tab_left% y%tab_top% w%tab_width% section, GOCR processing options
		GUI, add, checkbox, xs w%tab_width% vgocr_p gcheckedit section, %A_Space%-p : database path including final slash (default is ./db/)
		GUI, add, edit, % "xs20 w" tab_width - 60 " vgocr_p_edit section disabled", %db_gocr%
		GUI, add, button, ys w30 gfunction_browse, ...
		GUI, add, Text, x%tab_left% ys+5 h0 section,
		GUI, add, checkbox, xs w%tab_width% vgocr_l gcheckedit section, %A_Space%-l : threshold grey level 0<160<=255 (0 = autodetect)
		GUI, add, edit, xs20 w50 right number limit100 vgocr_l_edit disabled, 
		GUI, add, checkbox, xs w%tab_width% vgocr_d gcheckedit section, %A_Space%-d : dust_size (remove small clusters, -1 = autodetect)
		GUI, add, edit, xs20 w50 right number limit100 vgocr_d_edit number disabled, 
		GUI, add, checkbox, xs w%tab_width% vgocr_s gcheckedit section, %A_Space%-s : spacewidth/dots (0 = autodetect)
		GUI, add, edit, xs20 w50 right number limit100 vgocr_s_edit disabled, 
		GUI, add, checkbox, xs w%tab_width% vgocr_m gcheckedit section, %A_Space%-m : operation modes (bitpattern, see manual)
		GUI, add, edit, xs20 w50 right number limit100 vgocr_m_edit disabled, 
		GUI, add, checkbox, xs w%tab_width% vgocr_a gcheckedit section, %A_Space%-a : value of certainty (in percent, 0..100, default=95)
		GUI, add, edit, xs20 w50 right number limit100 vgocr_a_edit disabled, 
		GUI, add, Text, xs w%tab_width%, `nDescription:`n`nGOCR is an OCR (Optical Character Recognition) program, developed under the GNU Public License. It converts scanned images of text back to text files. 
	}
	
	GUI, Tab, About

	GUI, Tab  ; Future controls are not part of any tab control.
	GUI, Add, Button, % "x" gui_width - 125 " y" gui_height - tab_bottom - 18 " w50 gbutton_cancel section", Cancel
	GUI, Add, Button, ys w50 gbutton_ok default, OK
	
	gosub parse_parameters	; to make sure the set parameters are reflected in the GUI 
	GUI, show, h%gui_height% w%gui_width%, %app_name% %app_version% - preferences and settings
return
choose_engine:
	GUI, submit, nohide
	IniWrite, %ocr_engine%, %ini_file%, general, ocr_engine
return
checkedit:	; this subroutine disables and enables the edit gui control accompanying a checkbox
	gui, submit, nohide	; needed so we can tell if the checkbox is checked
	if %A_GuiControl% = 1
		GUIControl, enable, %A_GuiControl%_edit
	else
		GUIControl, disable, %A_GuiControl%_edit
return
function_browse:
	gui, submit, nohide	; needed so we can tell if the checkbox is checked
	if gocr_p <> 1
		return
	GUI, submit, nohide
	FileSelectFolder, db_gocr , *%db_gocr%, , %App_Name% : Select GOCR database folder
	if db_gocr <>
	{
		db_gocr = %db_gocr%\
		GuiControl,, gocr_p_edit, %db_gocr%
	}
	outputdebug > %gocr_p_edit%
return
collect_tesseract_languages:	; collects the available language for Tesseract
	lang :=
	SplitPath, app_tesseract , , OutDir
	Loop, %OutDir%\tessdata\*.traineddata
	{
		if lang =
			lang = %A_LoopFileName%
		else
			lang = %lang%|%A_LoopFileName%
	}
	StringReplace, lang, lang, .traineddata,,ALL
return
about:
	msgbox %App_name% - %App_version%`nby %App_author%`n`nabout text.
return
button_cancel:
	GUI, destroy
return
parse_parameters:	; parse_parameters is the subroutine that turns parameters_mkbitmap is turned into : vmkb_x and more
	function_parse("ncon",parameters_nconvert,nconvert_n_params)
	function_parse("mkb",parameters_mkbitmap,mkbitmap_n_params)
	function_parse("pot",parameters_potrace,potrace_n_params)
	function_parse("tess",parameters_tesseract,tesseract_n_params)
	ifexist %app_gocr%
		function_parse("gocr",parameters_gocr,gocr_n_params)
return
; function_parse will parse the parameters found in the ini, compare them to the ..._n_params above and fill the GUI with them
function_parse(name,list,n_params)
{
	loop, parse, list, %A_Space%
	{
		if A_LoopField <> 
		{
			if n <> 1	; n = 1 means that previously, a parameter has been found that has a value after it
			{
				; not a value, so check if this parameter will have a value
				stringreplace , param, A_LoopField, -,,ALL	; first get rid of the - in the parameter
				if param in %n_params%
					n = 1	; has no value
				else
					n = 0	; has a value
				GUIControl,, %name%_%param%, 1
				;outputdebug gui %name%_%param% = 1
			}
			else
			{
				if param in %n_params%
				{
					GUIControl, enable, %name%_%param%_edit
					GUIControl,, %name%_%param%_edit, %A_LoopField%
					;outputdebug gui %name%_%param%_edit = enable
					;outputdebug gui %name%_%param%_edit = %A_LoopField%
					param :=
					n = 0
				}
			}
		}
	}
}
collect_parameters: ; this is where the parameters are turned from vmkb_x into : parameters_mkbitmap
	total := function_collect("tess","l","1")
	parameters_tesseract := total
	total := function_collect("gocr","p","1")
	total := % total function_collect("gocr","l","1")
	total := % total function_collect("gocr","d","1")
	total := % total function_collect("gocr","s","1")
	total := % total function_collect("gocr","m","1")
	total := % total function_collect("gocr","a","1")
	parameters_gocr := total
	total := function_collect("mkb","i")
	total := % total function_collect("mkb","f","1")
	total := % total function_collect("mkb","s","1")
	total := % total function_collect("mkb","t","1")
	total := % total function_collect("mkb","n","1")
	total := % total function_collect("mkb","b","1")
	total := % total function_collect("mkb","l")
	total := % total function_collect("mkb","3")
	total := % total function_collect("mkb","g")
	parameters_mkbitmap := total
	total := function_collect("pot","z")
	total := % total function_collect("pot","o","1")
	total := % total function_collect("pot","r","1")
	total := % total function_collect("pot","t","1")
	total := % total function_collect("pot","a","1")
	total := % total function_collect("pot","n","1")
	total := % total function_collect("pot","u","1")
	total := % total function_collect("pot","i")
	parameters_potrace := total
	total := function_collect("ncon","autocontrast")
	total := % total function_collect("ncon","autolevels")
	total := % total function_collect("ncon","contrast","1")
	total := % total function_collect("ncon","dither")
	total := % total function_collect("ncon","deinter","1")
	total := % total function_collect("ncon","edetail")
	total := % total function_collect("ncon","efocus")
	total := % total function_collect("ncon","eedge","1")
	total := % total function_collect("ncon","equalize")
	total := % total function_collect("ncon","floyd")
	parameters_nconvert := total
return
function_collect(name,switch,edit=0)
{
	param := %name%_%switch%
	if param = 1
	{
		if edit = 0
			variables = % " -" switch " "
		else
			variables = % " -" switch " " %name%_%switch%_edit
		return variables
	}
}
button_ok:
	GUI, submit, nohide
	gosub collect_parameters
	GUI, destroy
	IniWrite, %parameters_nconvert%, %ini_file%, parameters, nconvert
	IniWrite, %parameters_mkbitmap%, %ini_file%, parameters, mkbitmap
	IniWrite, %parameters_potrace%, %ini_file%, parameters, potrace
	IniWrite, %parameters_tesseract%, %ini_file%, parameters, tesseract
	IniWrite, %parameters_gocr%, %ini_file%, parameters, gocr
return
menu:
	ifexist %app_icon%
		Menu, Tray, Icon, %app_icon%
	Menu, Tray, Tip, %app_name% %app_version%
	Menu, tray, NoStandard
	Menu, tray, add, %app_name% %app_version%, menu_reload
	Menu, tray, add
	Menu, tray, add, Capture, capture_toggle
	Menu, tray, default, Capture
	Menu, tray, add
	Menu, tray, add, Open Ini file, open_ini
	Menu, tray, add, Preferences and settings, gui
	Menu, tray, add
	if A_IsCompiled <> 1
	{
		Menu, StandardMenu, Standard
		Menu, tray, add, Standard menu options, :StandardMenu
		Menu, tray, add
	}
	Menu, tray, add, Over, about
	Menu, tray, add, Einde, menu_exit
return
open_ini:
	ifexist %ini_file%
		run %ini_file%
return
menu_reload:
	reload
menu_exit:
	gosub OnExit
capture_toggle:
	if capture_mode = 1
		gosub capture_off
	else
		gosub capture_on
return
capture_on:
	capture_mode = 1
	hotkey Lbutton , capture, on
	hotkey escape , capture_off, on
	Menu, tray, Check, Capture
	settimer , tooltip_start , 50
	SetSystemCursor("IDC_CROSS") ; sets the mouse cursor to a big +
return
capture_off:
	ToolTip ; clears any tooltip that may be showing
	capture_mode = 0
	hotkey Lbutton , capture, off
	hotkey escape , capture_off, off
	Menu, tray, Uncheck, Capture
	gosub RestoreCursors	; restores the mouse cursor
	settimer , tooltip_start , off
return
capture:
;=== step 0 ========================================================================
; Before we do anything, read the variables, maybe they changed since the start of the script
	gosub read_variables
;=== step 1 ========================================================================
; Allow the user to select an area for the OCR and save selected area without cursor in clipboard
	if ( clipboard <> "" ) OR ( test <> 1 )
		gosub get_area
	gosub capture_off	; to turn the active mode to off
;=== step 2 ========================================================================
; Clean up the temporary files that may have been left behind
	FileDelete %tmpfolder%\output_*.*
;=== step 3 ========================================================================
	; 3a: NConvert: Convert the clipboard to an image file
	RunWait , "%app_nconvert%" %parameters_nconvert% %parameters_nconvert_override% -overwrite -out bmp -o output_1_nconvert.bmp -clipboard, %tmpfolder%, hide
	; 3b: MKBitmap: transform images into bitmaps with scaling and filtering
	RunWait , "%app_mkbitmap%" %parameters_mkbitmap% %parameters_mkbitmap_override% -o output_2_mkbitmap.pbm output_1_nconvert.bmp, %tmpfolder%, hide
	; 3c: Potrace: transform bitmaps into vector graphics.
	RunWait , "%app_potrace%" %parameters_potrace% %parameters_potrace_override% -g -o output_3_potrace.pgm output_2_mkbitmap.pbm, %tmpfolder%, hide
	if ocr_engine = gocr
	{
		; 3d: NConvert: Convert the clipboard to an image file
		RunWait , "%app_nconvert%" -overwrite -out pnm -o output_4_nconvert.pnm output_3_potrace.pgm, %tmpfolder%, hide
		; 3e: GOCR: The actual OCR
		RunWait , "%app_gocr%" %parameters_gocr% %parameters_gocr_override% -i output_4_nconvert.pnm -o output_gocr.txt, %tmpfolder%, hide
	}
	else	; this is the default: Tesseract
	{
		; 3d: NConvert: Convert the clipboard to an image file
		RunWait , "%app_nconvert%" -overwrite -out tiff -o output_4_nconvert.tif output_3_potrace.pgm, %tmpfolder%, hide
		; 3e: Tesseract: The actual OCR
		RunWait , "%app_tesseract%" %parameters_tesseract% %parameters_tesseract_override% output_4_nconvert.tif output_tesseract, %tmpfolder%, hide
	}

;=== step 4 ========================================================================
; Read the OCR contents to the clipboard
	if ocr_engine = gocr
		FileRead OCR_text, %tmpfolder%\output_gocr.txt
	else
		FileRead OCR_text, %tmpfolder%\output_tesseract.txt
		
	if NoLineReturn = 1
		StringReplace, OCR_text, OCR_text, `r`n, , All

	if OCR_text =
		finish_status = Error`, no text was detected.
	else
	{
		finish_status = De tekst is herkend en op je klembord gezet.`nDit bericht sluit automatisch na %sec% seconden.`n`n%OCR_text%
		clipboard := OCR_text
	}
	
	n := 5000
	SetTimer, tooltip_finish, 50
	SetTimer, RemoveToolTip, 5000

	if test = 1
	{
		if ocr_engine = gocr
			run %tmpfolder%\output_gocr.txt
		else
			run %tmpfolder%\output_tesseract.txt
	}
	if CleanTemporaryOutput = 1
		FileDelete %tmpfolder%\output_*.*
return
tooltip_start:
	Tooltip, Maak een kader om de te herkennen tekst.`nDruk op Escape om te annuleren.,%tx%,%ty%
return
tooltip_finish:
	n -= 100
	sec := ceil(n/1000)
	MouseGetPos, xpos, ypos
	xpos += 16
	ypos += 16
	if sec > 1
		Tooltip , %finish_status%, %xpos%, %ypos%,
	else
		Tooltip
return
RemoveToolTip:
    SetTimer, RemoveToolTip, Off
	SetTimer, tooltip_finish, Off
    ToolTip
return

;===Description========================================================================
; Source: Screen clipping script. Saves selected area to clipboard. http://www.autohotkey.com/forum/viewtopic.php?t=49950
;===Functions==========================================================================
CaptureScreen(aRect)
{
   StringSplit, rt, aRect, `,, %A_Space%%A_Tab%
   nL := rt1
   nT := rt2
   nW := rt3 - rt1
   nH := rt4 - rt2
   znW := rt5
   znH := rt6

   mDC := DllCall("CreateCompatibleDC", "Uint", 0)
   hBM := CreateDIBSection(mDC, nW, nH)
   oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)
   hDC := DllCall("GetDC", "Uint", 0)
   DllCall("BitBlt", "Uint", mDC, "int", 0, "int", 0, "int", nW, "int", nH, "Uint", hDC, "int", nL, "int", nT, "Uint", 0x40000000 | 0x00CC0020)
   DllCall("ReleaseDC", "Uint", 0, "Uint", hDC)
   DllCall("SelectObject", "Uint", mDC, "Uint", oBM)
   DllCall("DeleteDC", "Uint", mDC)
   SetClipboardData(hBM)
}

CreateDIBSection(hDC, nW, nH, bpp = 32, ByRef pBits = "")
{
   NumPut(VarSetCapacity(bi, 40, 0), bi)
   NumPut(nW, bi, 4)
   NumPut(nH, bi, 8)
   NumPut(bpp, NumPut(1, bi, 12, "UShort"), 0, "Ushort")
   NumPut(0,  bi,16)
   Return   DllCall("gdi32\CreateDIBSection", "Uint", hDC, "Uint", &bi, "Uint", 0, "UintP", pBits, "Uint", 0, "Uint", 0)
}

SetClipboardData(hBitmap)
{
   DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
   hDIB :=   DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+NumGet(oi,44))
   pDIB :=   DllCall("GlobalLock", "Uint", hDIB)
   DllCall("RtlMoveMemory", "Uint", pDIB, "Uint", &oi+24, "Uint", 40)
   DllCall("RtlMoveMemory", "Uint", pDIB+40, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44))
   DllCall("GlobalUnlock", "Uint", hDIB)
   DllCall("DeleteObject", "Uint", hBitmap)
   DllCall("OpenClipboard", "Uint", 0)
   DllCall("EmptyClipboard")
   DllCall("SetClipboardData", "Uint", 8, "Uint", hDIB)
   DllCall("CloseClipboard")
}

get_area:
	CoordMode, Mouse ,Screen
	CoordMode, Tooltip ,Screen
	MouseGetPos, MX, MY
	Gui, 2: +AlwaysOnTop -caption +Border +ToolWindow +LastFound
	WinSet, Transparent, 80
	Gui, 2:Color, lime
	
	; shut tooltip_start off
	settimer , tooltip_start , off
	tooltip
	While, (GetKeyState("LButton", "p"))
	{
	   MouseGetPos, MXend, MYend
	   Send {control up}
	   w := abs(MX - MXend)
	   h := abs(MY - MYend)
	   If ( MX < MXend )
		X := MX
	   Else
		X := MXend
		tx := MX + 16
	   If ( MY < MYend )
	   {
		Y := MY
		tY := MY - 36
		}
	   Else
	   {
		Y := MYend
		tY := MY + 16
		}
	   Gui, 2:Show, x%X% y%Y% w%w% h%h%

		MouseGetPos, xpos, ypos
		Tooltip, Maak een kader om de te herkennen tekst.,%tx%,%ty%
	   Sleep, 10
	}
	ToolTip
	gosub RestoreCursors	; restores the mouse cursor

	MouseGetPos, MXend, MYend
	Gui, 2:Destroy
	If ( MX > MXend )
	{
	   temp := MX
	   MX := MXend
	   MXend := temp
	}
	If ( MY > MYend )
	{
	   temp := MY
	   MY := MYend
	   MYend := temp
	}
	Area = %MX%, %MY%, %MXend%, %MYend%
	Sleep, 100   ; if omitted, GUI sometimes stays in picture
	CaptureScreen(Area)   ; Saves selected area without cursor in Clipboard.
return

OnExit:
	ToolTip
	Suspend
	gosub RestoreCursors	; restores the mouse cursor
ExitApp

; --------------
; below is just to change the mouse cursor so it's clear when the capture mode is active
; source: http://www.autohotkey.com/forum/viewtopic.php?t=35600
SetSystemCursor( Cursor = "", cx = 0, cy = 0 )
{
   BlankCursor := 0, SystemCursor := 0, FileCursor := 0 ; init
   
   SystemCursors = 32512IDC_ARROW,32513IDC_IBEAM,32514IDC_WAIT,32515IDC_CROSS
   ,32516IDC_UPARROW,32640IDC_SIZE,32641IDC_ICON,32642IDC_SIZENWSE
   ,32643IDC_SIZENESW,32644IDC_SIZEWE,32645IDC_SIZENS,32646IDC_SIZEALL
   ,32648IDC_NO,32649IDC_HAND,32650IDC_APPSTARTING,32651IDC_HELP
   
   If Cursor = ; empty, so create blank cursor 
   {
      VarSetCapacity( AndMask, 32*4, 0xFF ), VarSetCapacity( XorMask, 32*4, 0 )
      BlankCursor = 1 ; flag for later
   }
   Else If SubStr( Cursor,1,4 ) = "IDC_" ; load system cursor
   {
      Loop, Parse, SystemCursors, `,
      {
         CursorName := SubStr( A_Loopfield, 6, 15 ) ; get the cursor name, no trailing space with substr
         CursorID := SubStr( A_Loopfield, 1, 5 ) ; get the cursor id
         SystemCursor = 1
         If ( CursorName = Cursor )
         {
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            Break               
         }
      }   
      If CursorHandle = ; invalid cursor name given
      {
         Msgbox,, SetCursor, Error: Invalid cursor name
         CursorHandle = Error
      }
   }   
   Else If FileExist( Cursor )
   {
      SplitPath, Cursor,,, Ext ; auto-detect type
      If Ext = ico 
         uType := 0x1   
      Else If Ext in cur,ani
         uType := 0x2      
      Else ; invalid file ext
      {
         Msgbox,, SetCursor, Error: Invalid file type
         CursorHandle = Error
      }      
      FileCursor = 1
   }
   Else
   {   
      Msgbox,, SetCursor, Error: Invalid file path or cursor name
      CursorHandle = Error ; raise for later
   }
   If CursorHandle != Error 
   {
      Loop, Parse, SystemCursors, `,
      {
         If BlankCursor = 1 
         {
            Type = BlankCursor
            %Type%%A_Index% := DllCall( "CreateCursor"
            , Uint,0, Int,0, Int,0, Int,32, Int,32, Uint,&AndMask, Uint,&XorMask )
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }         
         Else If SystemCursor = 1
         {
            Type = SystemCursor
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            %Type%%A_Index% := DllCall( "CopyImage"
            , Uint,CursorHandle, Uint,0x2, Int,cx, Int,cy, Uint,0 )      
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }
         Else If FileCursor = 1
         {
            Type = FileCursor
            %Type%%A_Index% := DllCall( "LoadImageA"
            , UInt,0, Str,Cursor, UInt,uType, Int,cx, Int,cy, UInt,0x10 ) 
            DllCall( "SetSystemCursor", Uint,%Type%%A_Index%, Int,SubStr( A_Loopfield, 1, 5 ) )         
         }          
      }
   }   
}
return
RestoreCursors:
   SPI_SETCURSORS := 0x57
   DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
return

/*
comparison between Tesseract and GOCR with the same image files:
---------------
Tesseract

For two years I spent my early mornings walking the
streets of West Oakland in the San Francisco Bay Area.
lt is a neighborhood steeped in history and diversity,
from ship workers to blues musicians. Yet, as freeways
were built and factories moved in, the neighborhood
became cut off from the rest of city and left isolated and
forgotten by many.
As I spent time photographing in West Oakland, I began to hnd beauty
in the unique and improvised envimnment that was born from the
neighborhoods very isolation. Sometimes it was a sad and hard beauty,
but often it was hopeful and timeless. I also found intimate moments of ritual
that stem from the neighborhood's mixed rural Southem and Latino heritage.
These photographs describe the stories and spaces that I found on my
walks through West Oakland.

---------------
GOCR

for mo years t spent my early mornings wa lking the
streets ot West Oakland in the San francisco 8ay Area.
lt is a neighborhood steeped in hi_oN and diversiN,
trom ship workers to blues m_sicians. Yet, as freeways
were buitt and tactories moved in, the neighborhood
became cut off trom the rest of ciN and le_ isolated and
torgottenbymany.

As l sp$nt _m_ photogr8phing in We_ 08_l8nd, I beg8n to F_nd be8uN
in _8 uniqu8 8nd impro_sed 8nvi_nme___w8s born trom _8
neighbomood's _N isol8_on. Same_mes _ w8s 8 s8d 8nd h8rd b$8u_,
but o_en it w8s hop$ful 8nd tim$lest l 8lso tound i_m8te mome_ _ r0'  8l
th8t stem trom lh_ neighbomood's mixed _r8l Southem 8nd __no hent8g8.
mes_ photogr8phs descnb_ lh_ _ones 8nd sp8ces __ I tound on my
w8lks _rough Wen 08tI8nd.
*/