Jump to content

Loop over 1M numbers fails on 9964th iteration


Recommended Posts

4 minutes ago, ioa747 said:

from: 103.986 seconds
to: 102.365 seconds

...
    ; Generate patterns for each found number
    For $i = 1 To $aNum[0][0]
        Local $sPattern = ""
        For $y = $firstRow To $lastRow
            For $x = $aNum[$i][0] To $aNum[$i][1] - 1
                $sPattern &= ($aColorMap[$x][$y] = $hColor ? "1" : ".") ; shave a nanosecond ?
            Next
            $sPattern &= @CRLF
        Next
        $sNumber &= _GetPattern($sPattern)
    Next
...

 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

6 minutes ago, argumentum said:

The next problem the OP presented, was not the speed, but the crash.

@Nine fixed that earlier, now we are just playing around, OP has gotten above 15k, and is probably having his computer running for days now with no time to try out our new examples, we may probably come up with a solution that would finish faster than the script he has running now. :D

Just like Voyager 1 will be surpassed by spaceships we build later. :)

 

Edited by Werty

Some guy's script + some other guy's script = my script!

Link to comment
Share on other sites

Wouldn't it be easier to see if it can be read by uiautomation or other accessibility tools. try inspect.exe or simplespy (see faq in signature)

It seems to be a certain Delphi version  that the tool.exe was build in (https://www.embarcadero.com/products/delphi/starter)

This imagerecognition is probably much faster in Delphi or C#

I remembered I build this 15 years ago 😉 for a similar purpose. 

And for fun some reference on analog digits on screen for having fun to recognize with pixel checking 😉 

#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

Dim $ws[12] = [0, 0, 10, -7, 50, -7, 60, 0, 50, 7, 10, 7]  ;Koordinaten Polygonzug: von links nach rechts WAAGRECHTER Balken, von rechts nach links SENKRECHTER Balken
Dim $ziffer[10] = [239, 10, 118, 94, 154, 220, 253, 14, 254, 222] ;alle gesetzten bits des Indexes in der 7-segmentanzeige ergeben die Ziffer
Dim $balkenpos[8] = [0, "60.0", "0.0", "60.60", "0.60", "0.60", "0.120", "0.0"] ;position der Leuchtbalken der 7-Segmentanzeige innerhalb der Ziffer
Dim $p[7][2]                                               ;nimmt die polygonzug-koordinaten zum Zeichnen auf
$p[0][0] = 6                                               ;6 Punkte im Polygonzug

_GDIPlus_Startup()

Global $hgui = GUICreate('Uhr', 620, 175, -1, -1, $WS_POPUP) ;GUI erstellen ohne Rahmen
Global $hWnd = WinGetHandle($hgui)                         ;Handle holen
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd)  ;"Leinwand" erstellen, auf der gezeichnet werden kann
GUICtrlCreateLabel("", 0, 0, 620, 175, Default, $GUI_WS_EX_PARENTDRAG) ;verschieben des Fensters möglich machen durch 2. Fenster
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)            ;das 2. Fenster transparent machen
GUISetBkColor(0x000000)                                    ;Hintergrund der GUI schwarz
GUISetState()                                              ;GUI anzeigen
AdlibRegister("_time", 1000)                               ;jede Zehntelsekunde ein Refresh der Zeitanzeige

Do                                                         ;Endlosschleife, solange bis..
Until GUIGetMsg() = -3                                     ;..ESC gedrückt wird
_GDIPlus_GraphicsDispose($hGraphic)                        ;freigeben
_GDIPlus_Shutdown()

Func _time()                                               ;Uhrzeit anzeigen :o)
    For $k = 1 To 6                                        ;die 6 Ziffern der Uhrzeit
        $setbits = $ziffer[StringMid(String(@HOUR & @MIN & @SEC), $k, 1)] ;gesetzte Bits in der siebensegmentanzeige anhand der Ziffer in der Uhrzeit holen
        For $bitnr = 7 To 1 Step -1                        ;alle Bits durchlaufen
            _drawpolygon(BitAND($setbits, 128), $k * 100 - 80 + ($k = 1 Or $k = 3 Or $k = 5) * 20, $bitnr) ;parameter: bit gesetzt ja/nein, position der gesamten ziffer,nummer des bits(gerade=waagrechter balken, ungerade=senkrechter balken)
            $setbits = BitShift($setbits, -1)              ;nächstes Bit holen
        Next
    Next
    $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + (@SEC / 2 = Int(@SEC / 2)) * 0xBB0000) ;Pinsel erstellen, wenn Ziffer gerade, dann farbig, ansonsten schwarz
    _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 55, 15, 15, $brush) ;Punkte zeichnen
    _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 55, 15, 15, $brush)
    _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 105, 15, 15, $brush)
    _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 105, 15, 15, $brush)
    _GDIPlus_BrushDispose($brush)                          ;Pinsel auflösen
EndFunc                                                    ;==>_time

Func _drawpolygon($bit, $xpos, $bitnr)                     ;zeichnet einen polygonzug ("Balken") an die entsprechende Position
    $split = StringSplit($balkenpos[$bitnr], ".", 3)       ;x- und y-koordinaten des Balkens innerhalb der Ziffer holen
    $b = (($bitnr / 2) = Int($bitnr / 2))                  ;$bit gerade => $b = true => Balken waagrecht, ansonsten Balken senkrecht
    $step = -1 + 2 * $b                                    ;schrittweite durch das $WS-Array
    For $i = 11 - 11 * $b To 11 * $b Step 2 * $step        ;array mit waagrechten (bit=gerade) oder (Bit=ungerade) senkrechten Balken füllen
        $r = Int(Abs((11 * (Not ($b))) - $i) / 2) + 1      ;abhängig von der Reihenfolge, egal ob $i von 0 bis 11 oder von 11 bis 0, $r muss immer 1,2,3,4,5,6
        $p[$r][0] = $ws[$i] + $split[0] + $xpos            ;x- und
        $p[$r][1] = $ws[$i + $step] + $split[1] + 30       ;y-position in das polygonarray schreiben
    Next
    $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + ($bit <> 0) * 0xBB0000) ;wenn bit gesetzt, dann farbig, ansonsten schwarz
    _GDIPlus_GraphicsFillPolygon($hGraphic, $p, $brush)    ;Balken zeichnen
    _GDIPlus_BrushDispose($brush)
EndFunc                                                    ;==>_drawpolygon

 

Edited by junkew
Clock reference analog digits
Link to comment
Share on other sites

7 hours ago, junkew said:

Clock reference analog digits

image.png.5d184f2a9916288f5d4e592890b0504d.png
 

Spoiler
#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

;~ Global Const $iColorBright = 0xFF000099, $iColorDimmed = 0xFF000044 ; blue
Global Const $iColorBright = 0xFF006600, $iColorDimmed = 0xFF002200 ; green
;~ Global Const $iColorBright = 0xFF880000, $iColorDimmed = 0xFF330000 ; red

Dim $ws[12] = [0, 0, 10, -7, 50, -7, 60, 0, 50, 7, 10, 7] ;Coordinates of polyline: from left to right HORIZONTAL bar, from right to left VERTICAL beam
Dim $digit[10] = [239, 10, 118, 94, 154, 220, 253, 14, 254, 222] ;all set bits of the index in the 7-segment display result in the digit
Dim $barkenpos[8] = [0, "60.0", "0.0", "60.60", "0.60", "0.60", "0.120", "0.0"] ;position of the light bars of the 7-segment display within the number
Dim $p[7][2] ;takes the polygon coordinates for drawing
$p[0][0] = 6 ;6 points in the polyline

_GDIPlus_Startup()

Global $hgui = GUICreate('Clock', 620, 175, -1, -1, $WS_POPUP) ;Create GUI without frame
Global $hWnd = WinGetHandle($hgui) ;Get handle
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd) ;Create a "canvas" to draw on
GUICtrlCreateLabel("", 0, 0, 620, 175, Default, $GUI_WS_EX_PARENTDRAG) ;move the window made possible by 2nd window
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT) ;make the 2nd window transparent
GUISetBkColor(0x000000) ;Background of the GUI black
GUISetState() ;Show GUI
AdlibRegister("_time", 1000) ;a refresh of the time display every tenth of a second

Do ;infinite loop until...
Until GUIGetMsg() = -3 ;..ESC is pressed
_GDIPlus_GraphicsDispose($hGraphic) ;release
_GDIPlus_Shutdown()

Func _time() ;Show time :o)
    For $k = 1 To 6 ;the 6 digits of the time
        $setbits = $digit[StringMid(String(@HOUR & @MIN & @SEC), $k, 1)] ;get set bits in the seven-segment display based on the digit in the time
        For $bitnr = 7 To 1 Step -1 ;go through all bits
            _drawpolygon(BitAND($setbits, 128), $k * 100 - 80 + ($k = 1 Or $k = 3 Or $k = 5) * 20, $bitnr) ;parameter: bit set yes/no, position of the entire digit, number of the bit (even=horizontal bar, odd=vertical bar)
            $setbits = BitShift($setbits, -1) ;get next bit
        Next
    Next
;~     $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + (@SEC / 2 = Int(@SEC / 2)) * 0xBB0000) ;Create a brush if the number is even, then colored, otherwise black
    $brush = _GDIPlus_BrushCreateSolid( (@SEC / 2 = Int(@SEC / 2)) ? $iColorBright : $iColorDimmed) ;Create a brush if the number is even, then colored, otherwise black
    _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 55, 15, 15, $brush) ;Draw points
    _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 55, 15, 15, $brush)
    _GDIPlus_GraphicsFillEllipse($hGraphic, 202, 105, 15, 15, $brush)
    _GDIPlus_GraphicsFillEllipse($hGraphic, 402, 105, 15, 15, $brush)
    _GDIPlus_BrushDispose($brush) ;Dispose brush
EndFunc ;==>_time

Func _drawpolygon($bit, $xpos, $bitnr) ;draws a polygon ("bar") at the corresponding position
    $split = StringSplit($barkenpos[$bitnr], ".", 3) ;get x and y coordinates of the bar within the digit
    $b = (($bitnr / 2) = Int($bitnr / 2)) ;$bit even => $b = true => Bar horizontal, otherwise bar vertical
    $step = -1 + 2 * $b ;step through the $WS array
    For $i = 11 - 11 * $b To 11 * $b Step 2 * $step ;fill array with horizontal (bit=even) or (bit=odd) vertical bars
        $r = Int(Abs((11 * (Not ($b))) - $i) / 2) + 1 ;depending on the order, whether $i is from 0 to 11 or from 11 to 0, $r must always 1,2,3,4,5,6
        $p[$r][0] = $ws[$i] + $split[0] + $xpos ;x- and
        $p[$r][1] = $ws[$i + $step] + $split[1] + 30 ;write y-position into the polygon array
    Next
;~     $brush = _GDIPlus_BrushCreateSolid(0xFF440000 + ($bit <> 0) * 0xBB0000) ;if bit set, then colored, otherwise black
    $brush = _GDIPlus_BrushCreateSolid(($bit <> 0) ? $iColorBright : $iColorDimmed) ;if bit set, then colored, otherwise black
    _GDIPlus_GraphicsFillPolygon($hGraphic, $p, $brush) ;Draw bars
    _GDIPlus_BrushDispose($brush)
EndFunc ;==>_drawpolygon

Greenfied
Thank you ( and AndyG ) for the code :)

 

Edited by argumentum
forgot to mention AndyG

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

On 11/7/2024 at 5:20 AM, ioa747 said:

I modified the script so that it calls _GDIPlus_Startup() once at the beginning,
and _GDIPlus_Shutdown() once at the end
instead of calling her for every number
I replaced the _ScreenCapture  function (although I believe I fixed it after  @Nine 's comments),
with _ScreenCapture_Capture  (since you didn't need the extras staff of _ScreenCapture)

Great minds! I came up with the same ideas last night, I also modified your script to expect a bitmap so I can just pass the bitmap directly instead of capturing a bitmap then saving that to a file then converting that to a bitmap and then creating a bitmap from the file. That said it's actually not as fast an improvement as I thought it would be considering I'm not doing all that or saving a million files to the filesystem anymore.

20 hours ago, Werty said:

You dont need to check the whole pattern, the first column in each digit is unique...

 

Image49.png

11 hours ago, ioa747 said:

Here it is the edition with a single column patern
 

; https://www.autoitscript.com/forum/topic/211521-ocr-from-a-small-area/?do=findComment&comment=1538057
; Version: 5.0.2

#include <GDIPlus.au3>
#include <Array.au3>
#include <File.au3>
#include <WinAPIProc.au3>

;~ Local $Result = _GetNumber(@ScriptDir & "\100num\040.png")
;~ MsgBox($MB_SYSTEMMODAL, "_GetNumber", $Result)

Test("M:\TEMP\100num")

;--------------------------------------------------------------------------------------------------------------------------------
Func _GetNumber($sFileName, $hColor = 0x00FF00)   ; Main Program
    ; Initialize GDI+ to work with bitmaps
    _GDIPlus_Startup()

    ; Capture the color map for the specified area
    Local $aColorMap = CaptureAreaColorMap($sFileName)
    If @error = 1 Then
        MsgBox($MB_SYSTEMMODAL, "LineNumber:" & @ScriptLineNumber, "Path was invalid.")
        Exit
    EndIf

;~  _ArrayDisplay($aColorMap)

    Local $Result = FindNumberUsingColorMap($aColorMap, $hColor)
;~  MsgBox($MB_SYSTEMMODAL, "$Result", $Result)

    ; Cleanup resources
    _GDIPlus_Shutdown()

    Return $Result
EndFunc   ;==>_GetNumber
;--------------------------------------------------------------------------------------------------------------------------------
Func CaptureAreaColorMap($sFileName)   ; create color map array
    ; Initialize GDI+ to work with bitmaps
    ;_GDIPlus_Startup()

    ; Capture the screen area as a bitmap
    Local $hBitmap = _GDIPlus_BitmapCreateFromFile($sFileName)
    If @error Then Return SetError(1, 0, "") ; because the user ( me ) might do the unexpected  ;)

    ; Get the width and height of the captured area
    Local $width = _GDIPlus_ImageGetWidth($hBitmap)
    Local $height = _GDIPlus_ImageGetHeight($hBitmap)
;~  ConsoleWrite("Image: $width=" & $width & ", $height=" & $height & @CRLF)

    ; Create an array to store color values
    Local $aColorMap[$width][$height]

    ; Loop through each pixel in the bitmap and retrieve its color
    For $y = 0 To $height - 1
        For $x = 0 To $width - 1
            ; Get the pixel color from the bitmap in ARGB format
            Local $argbColor = _GDIPlus_BitmapGetPixel($hBitmap, $x, $y)
            ; Convert ARGB to BGR for comparison (ignore the alpha channel)
            Local $bgrColor = BitAND($argbColor, 0x00FFFFFF)
            $aColorMap[$x][$y] = $bgrColor
        Next
    Next

    ; Cleanup resources
    _GDIPlus_BitmapDispose($hBitmap)
    ;_GDIPlus_Shutdown()

    Return $aColorMap
EndFunc   ;==>CaptureAreaColorMap
;--------------------------------------------------------------------------------------------------------------------------------
Func FindNumberUsingColorMap($aColorMap, $hColor = 0x00FF00)   ; find number in color map array
    Local $width = UBound($aColorMap, 1)
    Local $height = UBound($aColorMap, 2)

    Local $firstRow = -1, $lastRow = -1, $firstCol = -1, $lastCol = -1

    ; Scan for the first and last rows and columns with $hColor pixels
    For $y = 0 To $height - 1
        For $x = 0 To $width - 1
            If $aColorMap[$x][$y] = $hColor Then
                If $firstRow = -1 Then $firstRow = $y
                $lastRow = $y
                If $firstCol = -1 Or $x < $firstCol Then $firstCol = $x
                If $lastCol = -1 Or $x > $lastCol Then $lastCol = $x
            EndIf
        Next
    Next

    If $firstRow = -1 Or $lastRow = -1 Or $firstCol = -1 Or $lastCol = -1 Then
        MsgBox($MB_SYSTEMMODAL, "LineNumber:" & @ScriptLineNumber, "No color found" & @TAB & @TAB)
        Exit
    EndIf

    ; each number Display is a matrix of 14x20 pixels
    Local $numberWidth = 14 ; set number width per digit
    Local $numberSpace = 2  ; set Space between digits

    Local $Empty = 0, $Corection = 0

    ; Corection for boundaries if 1st digit = 1
    For $x = $firstCol To $lastCol - 1
        If $aColorMap[$x][$firstRow] = 0x000000 Then
            $Empty += 1
            If $Empty = 3 Then
                ;found 1st Space between digits
                $Corection = $firstCol - 1 - Abs($x - $numberWidth - $numberSpace)
                ExitLoop
            EndIf
        Else
            $Empty = 0
        EndIf
    Next

    If $Corection > 0 Then $firstCol = $firstCol - $Corection

    Local $numberStart = $firstCol

;~  ConsoleWrite("$Corection=" & $Corection & @CRLF)
;~  ConsoleWrite("Display_boundaries firstRow:" & $firstRow & " lastRow:" & $lastRow & " firstCol:" & $firstCol & " lastCol:" & $lastCol & @CRLF)
;~  ConsoleWrite("" & @CRLF)

    Local $aNum[11][3]

    ; Define boundaries for each number position
    For $i = 1 To 10
        $aNum[$i][0] = $numberStart
        $aNum[$i][1] = $numberStart + $numberWidth
        If $numberStart + $numberWidth + $numberSpace > $lastCol Then ExitLoop
        $numberStart += $numberWidth + $numberSpace
        $aNum[0][0] = $i + 1
    Next
;~  _ArrayDisplay($aNum)
    Local $sNumber

    ; Generate patterns for each found number
    For $i = 1 To $aNum[0][0]
        Local $sPattern = ""
        For $y = $firstRow To $lastRow
            $x = $aNum[$i][0]
            If $aColorMap[$x][$y] = $hColor Then
                $sPattern &= "1"
            Else
                $sPattern &= "."
            EndIf
            $sPattern &= @CRLF
        Next
        $sNumber &= _GetPattern($sPattern)
    Next

    Return $sNumber
EndFunc   ;==>FindNumberUsingColorMap
;--------------------------------------------------------------------------------------------------------------------------------
Func _GetPattern($sPattern)  ; patterns for each digit
;~  ConsoleWrite("**************" & @CRLF & $sPattern & @CRLF)

    Local Static $aPNum

    ; Define simplified patterns for each digit

    If Not IsArray($aPNum) Then

        Local $aNumber[10]

        $aNumber[0] = "" ; Pattern for '0'
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "1" & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "." & @CRLF
        $aNumber[0] &= "." & @CRLF

        $aNumber[1] = "" ; Pattern for '1'
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF
        $aNumber[1] &= "." & @CRLF

        $aNumber[2] = "" ; Pattern for '2'
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "1" & @CRLF
        $aNumber[2] &= "1" & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "1" & @CRLF
        $aNumber[2] &= "1" & @CRLF
        $aNumber[2] &= "." & @CRLF
        $aNumber[2] &= "1" & @CRLF
        $aNumber[2] &= "1" & @CRLF

        $aNumber[3] = "" ; Pattern for '3'
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "1" & @CRLF
        $aNumber[3] &= "1" & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "1" & @CRLF
        $aNumber[3] &= "1" & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF
        $aNumber[3] &= "." & @CRLF

        $aNumber[4] = "" ; Pattern for '4'
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "1" & @CRLF
        $aNumber[4] &= "1" & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "1" & @CRLF
        $aNumber[4] &= "1" & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF
        $aNumber[4] &= "." & @CRLF

        $aNumber[5] = "" ; Pattern for '5'
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "1" & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF
        $aNumber[5] &= "." & @CRLF

        $aNumber[6] = "" ; Pattern for '6'
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "1" & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF
        $aNumber[6] &= "." & @CRLF

        $aNumber[7] = "" ; Pattern for '7'
        $aNumber[7] &= "1" & @CRLF
        $aNumber[7] &= "1" & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF
        $aNumber[7] &= "." & @CRLF

        $aNumber[8] = "" ; Pattern for '8'
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "1" & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF
        $aNumber[8] &= "." & @CRLF

        $aNumber[9] = "" ; Pattern for '9'
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "1" & @CRLF
        $aNumber[9] &= "1" & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "1" & @CRLF
        $aNumber[9] &= "1" & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF
        $aNumber[9] &= "." & @CRLF

        $aPNum = $aNumber
    EndIf

    Switch $sPattern
        Case $aPNum[0]
            Return 0
        Case $aPNum[1]
            Return 1
        Case $aPNum[2]
            Return 2
        Case $aPNum[3]
            Return 3
        Case $aPNum[4]
            Return 4
        Case $aPNum[5]
            Return 5
        Case $aPNum[6]
            Return 6
        Case $aPNum[7]
            Return 7
        Case $aPNum[8]
            Return 8
        Case $aPNum[9]
            Return 9
        Case Else
            Return SetError(1, 0, "")
    EndSwitch
EndFunc   ;==>_GetPattern
;--------------------------------------------------------------------------------------------------------------------------------
Func Test($sFolderPath, $hColor = 0x00FF00)   ; _GetNumber for all the *.png files in $sFolderPath directory

    ; List all the *.png files in $sFolderPath directory
    Local $aFileList = _FileListToArray($sFolderPath, "*.png", $FLTA_FILES)
    If @error = 1 Then
        MsgBox($MB_SYSTEMMODAL, "LineNumber:" & @ScriptLineNumber, "Path was invalid.")
        Exit
    EndIf
    If @error = 4 Then
        MsgBox($MB_SYSTEMMODAL, "LineNumber:" & @ScriptLineNumber, "No file(s) were found.")
        Exit
    EndIf

    _ArrayColInsert($aFileList, 1) ; add 1 column to hold the numbers

    ; Initialize GDI+ to work with bitmaps
    _GDIPlus_Startup()

    Local $cnt = 0
    Local $CleanTime = 0
    Local $hTimer = TimerInit()

    For $j = 1 To 100
    For $i = 1 To $aFileList[0][0]
        $cnt += 1
        ConsoleWrite("$cnt: " & $cnt & @CRLF)

        ; Capture the color map for the specified area
        Local $aColorMap = CaptureAreaColorMap($sFolderPath & "\" & $aFileList[$i][0])
        If @error = 1 Then
            MsgBox($MB_SYSTEMMODAL, "LineNumber:" & @ScriptLineNumber, "Path was invalid.")
            Exit
        EndIf

        $aFileList[$i][1] = FindNumberUsingColorMap($aColorMap, $hColor)

        ; Rename a file using FileMove and overwrite the new file if it exists.
;~          FileMove($sFolderPath & "\" & $aFileList[$i][0], $sFolderPath & "\" & $aFileList[$i][1] & ".png", $FC_OVERWRITE)

;~          $aProcessMemoryInfo = _WinAPI_GetProcessMemoryInfo()
;~          ConsoleWrite('The peak working set: ' & $aProcessMemoryInfo[1] / 1024 / 1014 & ' MB' & @CRLF)

    Next
    Next

    ConsoleWrite("processed in: " & Round(TimerDiff($hTimer) / 1000, 3) & " seconds " & @LF)

    _ArrayDisplay($aFileList, "$aFileList")

    ; Cleanup resources
    _GDIPlus_Shutdown()
EndFunc   ;==>Test
;--------------------------------------------------------------------------------------------------------------------------------

 

I see what you mean now by only capturing part of the pattern rather than all of it. It's a genius idea for anyone who needs it, but unless it saves a serious amount of time I probably won't implement it in my own script - I like the readability of the original bitmap ID script, it's much easier to understand what's going on with the digits and be able to modify it for another bitmap font if I ever need to do anything like this again 10 years from now when I have early onset dementia.

10 hours ago, argumentum said:

lol. It is faster :P
I feel that we all in this thread are getting off course.
The next problem the OP presented, was not the speed, but the crash.

Thanks for the laserlike focus, I appreciate it! It was a weird bug and definitely caused by something in the _GetCapture function, but probably just a system memory issue caused by the lack of disposal that Nines pointed out. Either way I've now managed to pass over 300,000 outputs in a single run (took about 12 hours) and I only interrupted it because I had to use my computer for something else, so I think it's safe to say the issue is solved.

The only problem with it now is that my error handling doesn't appear to be working for the few hundred serial numbers where no output is recognised. I'm guessing these are because the script is now too fast for the application and needs a few sleep calls somewhere, and I'm fine with just adding those few hundred manually instead of slowing down the whole script - the problem is I have to use Bash/GNU tools to find them in the file instead of the images saving to Bitmap ID Failed like they should be. Error-handling is harder than it looks, I suppose. Here's the current script for anyone interested:
 

#ce ----------------------------------------------------------------------------
#include <GDIPlus.au3>
#include <ScreenCapture.au3>
#include <WinAPIFiles.au3>
#include "Bitmap Digit Identifier.au3" ; https://www.autoitscript.com/forum/topic/211521-ocr-from-a-small-area/?do=findComment&comment=1538057

Run("Tool.exe");
Local $tool = WinWaitActive("Tool", "", 3)
ControlClick($tool, "", "TTabSheet1", "primary", 1, 210, 50)
ControlCommand($tool, "" , "TComboBox3" , "SelectString" , "Open")

$db = FileOpen(@ScriptDir & "\tool-db.txt", 1)

DirRemove ("Bitmap ID Failed", 1)
DirCreate ("Bitmap ID Failed")

_GDIPlus_Startup()

For $loop = 0 To 999999

    $serial = StringFormat('%06i', $loop)

    ControlSetText($tool, "", "TMaskEdit3", "S.N: 9" & $serial)
    ControlClick($tool, "", "TTabSheet3", "primary", 1, 69, 50)

    $hHBitmap = _ScreenCapture_Capture("", 792, 450, 927, 480)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)

    Local $output = _GetNumber($hBitmap)
    If @error Then
        MsgBox(0, "Encountered fatal error", @error)
        ExitLoop
    EndIf
    If ((StringLen($output) <> 3) or not StringIsDigit($output)) Then
        _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\Bitmap ID Failed\" & $serial & ".png")
    EndIf

    _WinAPI_DeleteObject($hHBitmap)
    _GDIPlus_BitmapDispose($hBitmap)

    FileWrite($db, $serial & "," & $output & @LF)
Next

_GDIPlus_Shutdown()
FileClose($db)

Exit

 

Edited by Hashim
Link to comment
Share on other sites

6 minutes ago, Hashim said:

The only problem with it now is that my error handling doesn't appear to be working for the few hundred serial numbers where no output is recognized. I'm guessing these are because the script is now too fast for the application and needs a few sleep calls somewhere,

well, you  ControlClick() and immediately _ScreenCapture_Capture().
I would given that it seems to be that fast, and only so often slightly slower, move the ScreenCap() and _GetNumber() into a function that will retry.
Something like this: 

Spoiler
#ce ----------------------------------------------------------------------------
#include <GDIPlus.au3>
#include <ScreenCapture.au3>
#include <WinAPIFiles.au3>
#include "Bitmap Digit Identifier.au3" ; https://www.autoitscript.com/forum/topic/211521-ocr-from-a-small-area/?do=findComment&comment=1538057

Run("Tool.exe");
Local $tool = WinWaitActive("Tool", "", 3)
ControlClick($tool, "", "TTabSheet1", "primary", 1, 210, 50)
ControlCommand($tool, "" , "TComboBox3" , "SelectString" , "Open")

$db = FileOpen(@ScriptDir & "\tool-db.txt", 1)

DirRemove ("Bitmap ID Failed", 1)
DirCreate ("Bitmap ID Failed")

_GDIPlus_Startup()

For $loop = 0 To 999999

    $serial = StringFormat('%06i', $loop)

    ControlSetText($tool, "", "TMaskEdit3", "S.N: 9" & $serial)
    ControlClick($tool, "", "TTabSheet3", "primary", 1, 69, 50)

    Local $output = _GetNumberOrGetNumberNoMatterWhat()
;~     $hHBitmap = _ScreenCapture_Capture("", 792, 450, 927, 480)
;~     $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)

;~     Local $output = _GetNumber($hBitmap)
;~     If @error Then
;~         MsgBox(0, "Encountered fatal error", @error)
;~         ExitLoop
;~     EndIf
;~     If ((StringLen($output) <> 3) or not StringIsDigit($output)) Then
;~         _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\Bitmap ID Failed\" & $serial & ".png")
;~     EndIf

;~     _WinAPI_DeleteObject($hHBitmap)
;~     _GDIPlus_BitmapDispose($hBitmap)

    FileWrite($db, $serial & "," & $output & @LF)
Next

_GDIPlus_Shutdown()
FileClose($db)


Func _GetNumberOrGetNumberNoMatterWhat()
    
    Local $hHBitmap, $hBitmap, $output
    Do
    $hHBitmap = _ScreenCapture_Capture("", 792, 450, 927, 480)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
    $output = _GetNumber($hBitmap)
    If @error Then
        Sleep(10)
        ContinueLoop
;~         MsgBox(0, "Encountered fatal error", @error)
;~         ExitLoop
    EndIf
    If ((StringLen($output) <> 3) or not StringIsDigit($output)) Then
        _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\Bitmap ID Failed\" & $serial & ".png")
    EndIf
    _WinAPI_DeleteObject($hHBitmap)
    _GDIPlus_BitmapDispose($hBitmap)
    Until $output <> ""
    Return $output
EndFunc


Exit

 

Untested but that's the idea.
 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Check the findbmp code func GetImage on lockbits _GDIPlus_BitmapLockBits and search the forum on bitblt and lockbits.
Probably (much) faster then getting the color per pixel with _GDIPlus_BitmapGetPixel($hBitmap, $x, $y)

And rotating can help: Function _GDIPlus_ImageRotateFlip as you then have directly the pixels as sequence instead of calculating x,y and iterating x, y

 

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride, $imgBytes=3)
local $Scan0, $pixelData, $hbScreen, $pBitmap, $pBitmapCap, $handle

; Load the bitmap to search in
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("",0,0,-1,-1,False)
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap
    Else
;try to get a handle
        $handle = WinGetHandle($BMPFile)
        If @error Then
;Assume its an unknown handle so correct filename should be given
            $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
        Else
            $hbScreen=_ScreenCapture_CaptureWnd("",$handle,0,0,-1,-1,False)
            $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap
        EndIf
    EndIf

;Get $tagGDIPBITMAPDATA structure
;~  ConsoleWrite("Bitmap Width:    " & _GDIPlus_ImageGetWidth($pBitmap) & @CRLF )
;~  ConsoleWrite("Bitmap Height:      " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF)

;~  24 bits (3 bytes) or 16 bits (2 bytes) comparison
    if ($imgBytes=2) then
        $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF16RGB555)
;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Else
        $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    endIf

    If @ERROR Then MsgBox(0,"","Error locking region " & @error)

    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    $pixelData = DllStructCreate("ubyte lData[" & (abs($Stride) * $Height-1) & "]", $Scan0)
    $BMPDataStart = $BMPDataStart & DllStructGetData($pixeldata,"lData")

    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose ($pBitmap)
    _WinAPI_DeleteObject ($pBitmap)

EndFunc;==>GetImage

 

Link to comment
Share on other sites

12 hours ago, argumentum said:

well, you  ControlClick() and immediately _ScreenCapture_Capture().
I would given that it seems to be that fast, and only so often slightly slower, move the ScreenCap() and _GetNumber() into a function that will retry.
Something like this: 

  Hide contents
#ce ----------------------------------------------------------------------------
#include <GDIPlus.au3>
#include <ScreenCapture.au3>
#include <WinAPIFiles.au3>
#include "Bitmap Digit Identifier.au3" ; https://www.autoitscript.com/forum/topic/211521-ocr-from-a-small-area/?do=findComment&comment=1538057

Run("Tool.exe");
Local $tool = WinWaitActive("Tool", "", 3)
ControlClick($tool, "", "TTabSheet1", "primary", 1, 210, 50)
ControlCommand($tool, "" , "TComboBox3" , "SelectString" , "Open")

$db = FileOpen(@ScriptDir & "\tool-db.txt", 1)

DirRemove ("Bitmap ID Failed", 1)
DirCreate ("Bitmap ID Failed")

_GDIPlus_Startup()

For $loop = 0 To 999999

    $serial = StringFormat('%06i', $loop)

    ControlSetText($tool, "", "TMaskEdit3", "S.N: 9" & $serial)
    ControlClick($tool, "", "TTabSheet3", "primary", 1, 69, 50)

    Local $output = _GetNumberOrGetNumberNoMatterWhat()
;~     $hHBitmap = _ScreenCapture_Capture("", 792, 450, 927, 480)
;~     $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)

;~     Local $output = _GetNumber($hBitmap)
;~     If @error Then
;~         MsgBox(0, "Encountered fatal error", @error)
;~         ExitLoop
;~     EndIf
;~     If ((StringLen($output) <> 3) or not StringIsDigit($output)) Then
;~         _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\Bitmap ID Failed\" & $serial & ".png")
;~     EndIf

;~     _WinAPI_DeleteObject($hHBitmap)
;~     _GDIPlus_BitmapDispose($hBitmap)

    FileWrite($db, $serial & "," & $output & @LF)
Next

_GDIPlus_Shutdown()
FileClose($db)


Func _GetNumberOrGetNumberNoMatterWhat()
    
    Local $hHBitmap, $hBitmap, $output
    Do
    $hHBitmap = _ScreenCapture_Capture("", 792, 450, 927, 480)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
    $output = _GetNumber($hBitmap)
    If @error Then
        Sleep(10)
        ContinueLoop
;~         MsgBox(0, "Encountered fatal error", @error)
;~         ExitLoop
    EndIf
    If ((StringLen($output) <> 3) or not StringIsDigit($output)) Then
        _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "\Bitmap ID Failed\" & $serial & ".png")
    EndIf
    _WinAPI_DeleteObject($hHBitmap)
    _GDIPlus_BitmapDispose($hBitmap)
    Until $output <> ""
    Return $output
EndFunc


Exit

 

Untested but that's the idea.
 

Wow, the Do while/until loops to retry the numbers is clever, something like that wouldn't have even occurred to me. Just finished testing over another ~300,000 runs and didn't see a single empty output (and I trust no false positives either) so it clearly works, thank you! One question, what's the purpose of the sleep and ContinueLoop? In my tests I replaced this with my original MsgBox, as if there's a significant failure I'd probably want to know about it instead of skipping the output check and going to the next loop, or am I misunderstanding how it works?

3 hours ago, junkew said:

Check the findbmp code func GetImage on lockbits _GDIPlus_BitmapLockBits and search the forum on bitblt and lockbits.
Probably (much) faster then getting the color per pixel with _GDIPlus_BitmapGetPixel($hBitmap, $x, $y)

And rotating can help: Function _GDIPlus_ImageRotateFlip as you then have directly the pixels as sequence instead of calculating x,y and iterating x, y

 

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride, $imgBytes=3)
local $Scan0, $pixelData, $hbScreen, $pBitmap, $pBitmapCap, $handle

; Load the bitmap to search in
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("",0,0,-1,-1,False)
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap
    Else
;try to get a handle
        $handle = WinGetHandle($BMPFile)
        If @error Then
;Assume its an unknown handle so correct filename should be given
            $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
        Else
            $hbScreen=_ScreenCapture_CaptureWnd("",$handle,0,0,-1,-1,False)
            $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap
        EndIf
    EndIf

;Get $tagGDIPBITMAPDATA structure
;~  ConsoleWrite("Bitmap Width:    " & _GDIPlus_ImageGetWidth($pBitmap) & @CRLF )
;~  ConsoleWrite("Bitmap Height:      " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF)

;~  24 bits (3 bytes) or 16 bits (2 bytes) comparison
    if ($imgBytes=2) then
        $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF16RGB555)
;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    Else
        $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
;~      $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB)
    endIf

    If @ERROR Then MsgBox(0,"","Error locking region " & @error)

    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    $pixelData = DllStructCreate("ubyte lData[" & (abs($Stride) * $Height-1) & "]", $Scan0)
    $BMPDataStart = $BMPDataStart & DllStructGetData($pixeldata,"lData")

    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose ($pBitmap)
    _WinAPI_DeleteObject ($pBitmap)

EndFunc;==>GetImage

 

It's mind-boggling to me how people keep coming up with ways to make this faster, suppose that's the difference between being a computer scientist and a simple programmer like myself. I no longer have the time to implement something like this and I've already spent too long writing the code instead of just doing the work I should be doing, but this is definitely appreciated and if I start needing to use this script more regularly I'll definitely be benchmarking Werty/ioa747's first-column solution against this one.

Edited by Hashim
Link to comment
Share on other sites

20 minutes ago, Hashim said:

...what's the purpose of the sleep and ContinueLoop? In my tests I replaced this with my original MsgBox, as if...

If the error is fatal, quit. If the error is circumstantial, retry. If the expected circumstances never come, ... then is a good idea to add a retry counter and a MsgBox() with "Retry, continue, cancel options", with a timeout of a certain default, to accommodate all foreseeable circumstances and desired default option, in case the operator/user is not at the keyboard.
Look at it like a game of chess, where you are coding in advance, a reaction to any of the opponent's ( the darn PC ) possible moves.

Once you become more familiar with this adventure of coding, forking opens a world of possibilities. But that is 3D chess.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

On 11/8/2024 at 6:41 AM, argumentum said:

Greenfied
Thank you ( and AndyG ) for the code :)

Haha...thx, i made this centuries ago...if I remember right, it was a contribution to some other "digital desktop clock" scripts which took some hundret lines of code...my intention was to write a script in less than 50 lines of code. The "bitbanging" aka producing a polygon, means the position and orientation of an "equal shape",  I taught myself about 40 years ago when I programmed "graphical" games for a calculator (sharp PC1401) with a 7-segment LC display in machine code.

 

15 hours ago, junkew said:

Check the findbmp code func GetImage on lockbits _GDIPlus_BitmapLockBits and search the forum on bitblt and lockbits.
Probably (much) faster then getting the color per pixel with _GDIPlus_BitmapGetPixel($hBitmap, $x, $y)

And rotating can help: Function _GDIPlus_ImageRotateFlip as you then have directly the pixels as sequence instead of calculating x,y and iterating x, y

yepp, rotation is the key...and it is not necessary to use "graphical stuff" like GDI(+) or something like that to read or write "pixels"!

A bitmap is an array of bytes/words/dwords representing the "pixels". If you put an AutoIt dllstruct "over" a bitmap (at the bitmap's starting address in memory), you can read or write "pixels" with a simple DllStructGetData() / DllStructSetData(), or, much faster, define/read the dllstruct as a "string" (characters) and use the super-fast AutoIt string commands like StringInstr() to locate a specific sequence of "bytes" (characters).

As Werty mentioned, the first column of the digit is unique. This requires seven "bit tests" due to the way the pixels are arranged in memory, as each "pixel" is in a different area of the bitmap. After rotating the image, only one test is required, as the "pixels" of the first column are now in a row, one after the other. So only one read from memory is required to get the "number". And since the position of the next "number" on the screen is known in the bitmap's memory, a search for a sequence of 4 consecutive "numbers" would only require 4 reads from memory. This means that the first step is to look for the first "digit", then look at the (known) next position in memory and check if the byte at that position is the second "digit", and so on...

With this technique the search of a sequence of "numbers" on a screenshot is possible in a few (milli)-Seconds. In native AutoIt code....

Link to comment
Share on other sites

On 11/9/2024 at 8:30 AM, AndyG said:

a few (milli)-Seconds

Yup, ~2 milliseconds on my pc, and that's without the fancy rotating stuff. :D

The basics

#include <GDIPlus.au3>
#include <ScreenCapture.au3>

;Be sure $posx and $posy is pointing at the correct spot!
Global $posx = 1718, $posy = 699, $result = ""
;Lookup table to avoid searching
Global $digit[115] = [1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,6,0,0,0,3,2,0,0,0,0, _
                      0,0,0,0,0,0,0,0,9,0,0,0,0,0,8,0,0,0,0,0, _
                      0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,5]
                      
     $capture = _ScreenCapture_Capture("", $posx, $posy, $posx+34, $posy+20)
  $bitmap = _GDIPlus_BitmapCreateFromHBITMAP($capture); for some reason createfromhbitmap is not a fully compatible gdiplus bitmap
   $clone = _GDIPlus_BitmapCloneArea($bitmap, 0, 0, 34, 20, $GDIP_PXF32ARGB); so we have to do this before it works correctly
            _GDIPlus_BitmapDispose($bitmap)
            
Func Getnumber($result)
    
Local $code[3] = [0,0,0], $value = 64
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))

For $loop = 1 To 680 Step 102
    $code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
    $code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
    $code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0
    $value /= 2
Next
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)
    Return String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
EndFunc

And a runable script (zipped folder with script and test images attached)

#include <GDIPlus.au3>
#include <ScreenCapture.au3>
HotKeySet("{ESC}", "_exit")

;Be sure $posx and $posy is pointing at the correct spot!
Global $posx = 1718, $posy = 699, $result = ""
;Lookup table to avoid searching
Global $digit[115] = [1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,6,0,0,0,3,2,0,0,0,0, _
                      0,0,0,0,0,0,0,0,9,0,0,0,0,0,8,0,0,0,0,0, _
                      0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,5]
;Test section
;--------------------------------------------------------------
$gui = GUICreate("Getnumber", 640, 480, -1, -1)
       GUISetState()
       WinWaitActive($gui); <--- necessary or screencapture fails

            _GDIPlus_Startup()
$graphics = _GDIPlus_GraphicsCreateFromHWND($gui)
   $image = _GDIPlus_BitmapCreateFromFile("518.png")
            _GDIPlus_GraphicsDrawImageRect($graphics, $image, 280, 200, 136, 31)
Sleep(10); <------- necessary because drawimagerect() is too slow, script occasionally fails without
;--------------------------------------------------------------
;/Test section

$time = TimerInit()
 $capture = _ScreenCapture_Capture("", $posx, $posy, $posx+34, $posy+20)
  $bitmap = _GDIPlus_BitmapCreateFromHBITMAP($capture); for some reason createfromhbitmap is not a fully compatible gdiplus bitmap
   $clone = _GDIPlus_BitmapCloneArea($bitmap, 0, 0, 34, 20, $GDIP_PXF32ARGB); so we have to do this before it works correctly
            _GDIPlus_BitmapDispose($bitmap)

$result = Getnumber()

Consolewrite("Result: " & $result & " Time: " & TimerDiff($time)/1000 & @crlf)

While 1
   Sleep(10)
WEnd

Func Getnumber()
    
Local $code[3] = [0,0,0]
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))
$value = 64

For $loop = 1 To 680 Step 102
    $code[0] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop   ), 8), 1) = 1 ? $value:0
    $code[1] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop+16), 8), 1) = 1 ? $value:0
    $code[2] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop+32), 8), 1) = 1 ? $value:0
    $value /= 2
Next

    $result = String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
    $code = 0
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)

    Return $result
EndFunc

Func _exit()
    _GDIPlus_Shutdown()
    Exit
 EndFunc

Not much code it takes.

I havent checked for memoryleaks and stuff, too tired now, if you spot anyting please tell. :)

Be sure to change the $posx and $posy to point at the correct spot, as it is now it fits a 3440x1440 monitor.

(you can comment out line 56 that gives error so you can take a screenshot to find the correct spot)

 

/edit

Changed, zip not updated

Changed again,

$code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
$code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
$code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0

posxposy.png

getnumber.zip

Edited by Werty

Some guy's script + some other guy's script = my script!

Link to comment
Share on other sites

1st try
(58) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.

do i have to do something?


Edit:
ok now it works.
Now I have to understand how it works?  :)

  

1 hour ago, Werty said:

When you found the spot and got it running please post your screen resolution and your posx posy result so others with same resolution can use your result so they dont have to do the same

@DesktopWidth x  @DesktopHeight = 1920x1080
Global $posx = 958, $posy = 499, $result = ""

Edited by ioa747

I know that I know nothing

Link to comment
Share on other sites

Do what i wrote, find the correct spot, comment out that line that gives error and run the script again, then you'll see the test image, and while the script is running press printscreen on your keyboard to take a screenshot, open that screenshot in an image editor and point the cursor at the spot on the picture i posted and note down the coords, those are the coords for $posx and $posy.

When you found the spot and got it running please post your screen resolution and your posx posy result so others with same resolution can use your result so they dont have to do the same.

It could ofcourse be made so it checks automatically, it's autoit, but not now, sleep now, yes. 🥱

Edited by Werty

Some guy's script + some other guy's script = my script!

Link to comment
Share on other sites

On 11/10/2024 at 10:42 AM, Werty said:

Yup, ~2 milliseconds on my pc, and that's without the fancy rotating stuff. :D

The basics

#include <GDIPlus.au3>
#include <ScreenCapture.au3>

;Be sure $posx and $posy is pointing at the correct spot!
Global $posx = 1718, $posy = 699, $result = ""
;Lookup table to avoid searching
Global $digit[115] = [1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,6,0,0,0,3,2,0,0,0,0, _
                      0,0,0,0,0,0,0,0,9,0,0,0,0,0,8,0,0,0,0,0, _
                      0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,5]
                      
     $capture = _ScreenCapture_Capture("", $posx, $posy, $posx+34, $posy+20)
  $bitmap = _GDIPlus_BitmapCreateFromHBITMAP($capture); for some reason createfromhbitmap is not a fully compatible gdiplus bitmap
   $clone = _GDIPlus_BitmapCloneArea($bitmap, 0, 0, 34, 20, $GDIP_PXF32ARGB); so we have to do this before it works correctly
            _GDIPlus_BitmapDispose($bitmap)
            
Func Getnumber()
    
Local $code[3] = [0,0,0], $value = 64
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))

For $loop = 1 To 680 Step 102
    $code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
    $code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
    $code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0
    $value /= 2
Next
    $result = String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
    $code = 0
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)
    Return $result
EndFunc

And a runable script (zipped folder with script and test images attached)

#include <GDIPlus.au3>
#include <ScreenCapture.au3>
HotKeySet("{ESC}", "_exit")

;Be sure $posx and $posy is pointing at the correct spot!
Global $posx = 1718, $posy = 699, $result = ""
;Lookup table to avoid searching
Global $digit[115] = [1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,6,0,0,0,3,2,0,0,0,0, _
                      0,0,0,0,0,0,0,0,9,0,0,0,0,0,8,0,0,0,0,0, _
                      0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, _
                      0,0,0,0,0,0,0,0,0,0,0,0,0,0,5]
;Test section
;--------------------------------------------------------------
$gui = GUICreate("Getnumber", 640, 480, -1, -1)
       GUISetState()
       WinWaitActive($gui); <--- necessary or screencapture fails

            _GDIPlus_Startup()
$graphics = _GDIPlus_GraphicsCreateFromHWND($gui)
   $image = _GDIPlus_BitmapCreateFromFile("518.png")
            _GDIPlus_GraphicsDrawImageRect($graphics, $image, 280, 200, 136, 31)
Sleep(10); <------- necessary because drawimagerect() is too slow, script occasionally fails without
;--------------------------------------------------------------
;/Test section

$time = TimerInit()
 $capture = _ScreenCapture_Capture("", $posx, $posy, $posx+34, $posy+20)
  $bitmap = _GDIPlus_BitmapCreateFromHBITMAP($capture); for some reason createfromhbitmap is not a fully compatible gdiplus bitmap
   $clone = _GDIPlus_BitmapCloneArea($bitmap, 0, 0, 34, 20, $GDIP_PXF32ARGB); so we have to do this before it works correctly
            _GDIPlus_BitmapDispose($bitmap)

$result = Getnumber()

Consolewrite("Result: " & $result & " Time: " & TimerDiff($time)/1000 & @crlf)

While 1
   Sleep(10)
WEnd

Func Getnumber()
    
Local $code[3] = [0,0,0]
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))
$value = 64

For $loop = 1 To 680 Step 102
    $code[0] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop   ), 8), 1) = 1 ? $value:0
    $code[1] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop+16), 8), 1) = 1 ? $value:0
    $code[2] += BitAnd(BitShift(DllStructGetData($pixels, 1, $loop+32), 8), 1) = 1 ? $value:0
    $value /= 2
Next

    $result = String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
    $code = 0
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)

    Return $result
EndFunc

Func _exit()
    _GDIPlus_Shutdown()
    Exit
 EndFunc

Not much code it takes.

I havent checked for memoryleaks and stuff, too tired now, if you spot anyting please tell. :)

Be sure to change the $posx and $posy to point at the correct spot, as it is now it fits a 3440x1440 monitor.

(you can comment out line 56 that gives error so you can take a screenshot to find the correct spot)

 

/edit

Changed, zip not updated

$code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
$code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
$code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0

posxposy.png

getnumber.zip 8.66 kB · 5 downloads

 

Since you were kind enough to write the code I decided to go ahead and waste some more time by testing this, and HOLY SHIT is this solution fast. I also tested the first-column bitmap solution a few days ago and didn't personally notice a massive improvement - both take at least 24 hours to output the whole 1M dataset. However, this genius solution blows them out of the water, doing the whole thing in 6-8 hours - 4 times as fast! Combined with a modified version of argumentum's Do-Until loop to retry outputs until they're in valid format, I got less than 40 false positives out of a million - not as effective as the bitmap solutions but still low enough to manually correct them without issue, and this is without my version having any sleep calls at all. I've also noticed that without any sleep calls the first code is almost always a false positive, which may be something to do with the start-up time of LockBits.

This is almost definitely the solution I'm going to be using going forward because it's too genius not to, but I'd love to understand more about the maths behind it and how exactly it does what it does, especially so much faster than the bitmap solutions, if either you or @AndyG would care to explain. I'm guessing 4278255360 is a uint32, but what exactly do the numbers in the lookup table represent? What's the significance of 680 and 102? And how exactly does the ternary:

$code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? 64 : 0

...work to recognise the first digit?

Edited by Hashim
Link to comment
Share on other sites

I'll write up a walkthrough later, meanwhile, have you seen the update of the getnumber() func I made, it's shorter and slightly faster now...

Func Getnumber($result)
    
Local $code[3] = [0,0,0], $value = 64
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))

For $loop = 1 To 680 Step 102
    $code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
    $code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
    $code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0
    $value /= 2
Next
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)
    Return String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
EndFunc

Edit, doh, I see in your quote that you've seen it.

Edited by Werty

Some guy's script + some other guy's script = my script!

Link to comment
Share on other sites

5 minutes ago, Werty said:

I'll write up a walkthrough later, meanwhile, have you seen the update of the getnumber() func I made, it's shorter and slightly faster now...

Func Getnumber($result)
    
Local $code[3] = [0,0,0], $value = 64
$bitmap1 = _GDIPlus_BitmapLockBits($clone, 0, 0, 34, 20, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$pixels =  DllStructCreate("dword[" & 680 & "];", DllStructGetData($bitmap1, "Scan0"))

For $loop = 1 To 680 Step 102
    $code[0] += DllStructGetData($pixels, 1, $loop   ) = 4278255360 ? $value:0
    $code[1] += DllStructGetData($pixels, 1, $loop+16) = 4278255360 ? $value:0
    $code[2] += DllStructGetData($pixels, 1, $loop+32) = 4278255360 ? $value:0
    $value /= 2
Next
    _GDIPlus_BitmapUnlockBits($clone, $bitmap1)
    Return String($digit[$code[0]]) & String($digit[$code[1]]) & String($digit[$code[2]])
EndFunc

Edit, doh, I see in your quote that you've seen it.

I noticed the removal of the bitwise operations but not the rest of your edits to Getnumber(), I'll update them now. I look forward to the walkthrough, and thanks so much for the solution!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...