Hallo zusammen,
ich habe gesucht, aber nicht gefunden:
Gibt es über AHK eine Möglichkeit (GDI+?) innerhalb einer Bilddatei (JPG/TIFF/PNG) eine Suche nach einem Bildteil zu machen und als Rückgabewert die Koordinaten zu bekommen? Also ImageSearch für Bilddateien statt für den Screen?
Input:
- gescannte TIFF (Datei 1)
- TIFF mit einem Ausschnitt (Datei 2)
Verarbeitung:
- Suche innerhalb eines Bereichs des Inputbildes Datei 1 (Eingabe Beginn X + Y Koordinate, Eingabe Ende X+Y-Koordinate) nach Datei 2 mit Angabe der prozentualen Übereinstimmung, evtl noch Angabe der Auflösung
Output:
- Koordinaten der gefundenen Position
So ungefähr oder ähnlich stelle ich es mir vor, alle Vorschläge sind willkommen und auch Commandlinetools.
Ich habe im Forum gesucht, aber nichts passendes gefunden.
Danke schon mal im Voraus!
ImageSearch innerhalb JPGs/PNG/TIFF Topic is solved
Moderator: jNizM
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Kenne mich da null/nada/nix/niente aus, doch könnte mir vorstellen das, sofern Bildelemente als identischer/deckungsgleicher code vorliegen (hätte,wäre,wenn) ein Abgleich machbar wäre? Also, laienhafte Vorstellung - ein Bild in Base64 umgewandelt enthält ein Teilbild wenn der code des Teilbildes im Vollbild vorhanden ist <Platzhalter für 10hoch64 Fragezeichen>. Klingt blöd, isses ziemlich sicher auch, denn, warum baut Google Bilder(ähnlichkkeits)suchalgorithmen wenn das schon die Lösung wäre??
https://towardsdatascience.com/creating-your-own-object-detector-ad69dda69c85 Good luck
https://towardsdatascience.com/creating-your-own-object-detector-ad69dda69c85 Good luck
Re: ImageSearch innerhalb JPGs/PNG/TIFF
danke..ich muss mich da einfach...
es müssen ja unterschiedliche Dinge betrachtet werden
- Skalierung (scale)
- Verdrehung (skew/deskew) --> da habe ich ein Tool, bastel mir aber gerade mit GDIP ein eigenes, dass es mir die Verdrehung vom Bild ermittelt und korrigiert , ganz schön schwierig
- Farbkodierung (sw/grau/farbe)
- prozentuale Übereinstimmung?
ich habe mal eine Auftragsbestätigung und das Suchbild angehängt, im Endeffekt brauche ich die Koordinaten X=1058 / Y=655 und/oder result 1, dass was gefunden wurde.
eine Möglichkeit wäre über OpenCV, aber da hab ich echt keine Ahnung
--> OpenCV
https://www.autohotkey.com/boards/viewtopic.php?t=60442
https://www.autohotkey.com/boards/viewtopic.php?f=76&t=77046
https://github.com/manciuszz/AHK-OpenCV
es müssen ja unterschiedliche Dinge betrachtet werden
- Skalierung (scale)
- Verdrehung (skew/deskew) --> da habe ich ein Tool, bastel mir aber gerade mit GDIP ein eigenes, dass es mir die Verdrehung vom Bild ermittelt und korrigiert , ganz schön schwierig
- Farbkodierung (sw/grau/farbe)
- prozentuale Übereinstimmung?
ich habe mal eine Auftragsbestätigung und das Suchbild angehängt, im Endeffekt brauche ich die Koordinaten X=1058 / Y=655 und/oder result 1, dass was gefunden wurde.
eine Möglichkeit wäre über OpenCV, aber da hab ich echt keine Ahnung
--> OpenCV
https://www.autohotkey.com/boards/viewtopic.php?t=60442
https://www.autohotkey.com/boards/viewtopic.php?f=76&t=77046
https://github.com/manciuszz/AHK-OpenCV
- Attachments
-
- test_suchpix.jpg (2.53 KiB) Viewed 1757 times
-
- test_AB.jpg (280.95 KiB) Viewed 1757 times
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Moin,
wegen Deines Beispiels: Wäre OCR eine Alternative? Für Win 10 gibt es da einen sehr gelungenen Ansatz von malcev Optical character recognition (OCR) with UWP API.
Für Deine Auftragsbestätigung liefert das bei einem Vollscan:Die Positionen der Texte lassen sich auch abgreifen.
wegen Deines Beispiels: Wäre OCR eine Alternative? Für Win 10 gibt es da einen sehr gelungenen Ansatz von malcev Optical character recognition (OCR) with UWP API.
Für Deine Auftragsbestätigung liefert das bei einem Vollscan:
Code: Select all
---------------------------
BilddateiOCR.ahk
---------------------------
hagebau
bauen + modernisieren
Auftragsbestätigung
Standardbeleg
Ansprechpartner:
Telefon:
Telefax:
E-Mail:
Kunden-Nr.
21
51
Bauvorhaben
Auftrag-Nr.
12
61
I I I I I II I I I I I I I I II I I I I I III I I I I I I I I I II I I I I I I I I I I II II
Belegdatum
19.11.2019
Kommission Abholung
Vielen Dank für Ihren Auftrag, den wir wie folgt bestätigen.
Wir liefern gemäß unseren Lieferungs- und Zahlungsbedingungen.
Pos
Menge
Seite
Einzelpreis €
13,75
20,90
4,33
1 von 1
PE Gesamtpreis €
Artikel
20002156
20002156
20200241
Nettowert
400,63
Bezeichnung
Remmers Epoxy BS 2000 10kg/GEB
Grundierung basaltgrau RAL7012
Kommission Abholung
Remmers Epoxy BS 3000 10kg/GEB
Versiegelung basaltgrau RAL7012
Kommission Abholung
Knauf Intol weiß
Innendispersionsfarbe
12,51tr/GEB O, 151tr/qm
32GEB/PAL
Abholung ab Lager
10,000
10,000
12,500
1 EIM
Gesamtbetrag €
476,75
E
E
MwSt.
19,000/0
MwSt.Betrag
76,12
137,50
209,00
54, 13
Endbetrag €
476,75
2% Skonto in 10 Tagen, 30 Tage netto
Skonto Wird nur nach besonderer Vereinbarung gewährt. Skontierfähig ist ausschließlich der Warenwert ohne Frachtanteile und Dienstleistungen.
1m Warenwert enthaltene Vorfrachten und Dienstleistungen werden mittels einer Pauschale (4% bei Strecke, 14,5% bei Lager) in Abzug gebracht.
Mit diesem Schein darf keine Warenausgabe erfolgen!
Gesamtgewicht: 21 ,OOO kg
Legende Preiseinheit (PE): E = pro 1, Z = pro 10, H = pro 100, T = pro 1000
---------------------------
OK
---------------------------
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Hi Danke! Das kenne und nutze ich auch, das UWP ist echt schnell aber bei schlechten Scans nicht so gut wie tesseract, zur Zeit mache ich es so, dass ich bei Scans mit hoher Auflösung UWP nehme und mit schlechter Auflösung tesseract.
Das Problem bei der Sache ist, dass z.B.
- Wenn ich nach dem Endbetrag im erkannten Text suche, dann steht ja die Summe nicht nebendran, sondern irgendwo anders (unten drunter) und durch einen Strich getrennt. d.h. ich kenne eigentlich die Position des €-Betrags nicht.
Was ich aktuell mache:
Durch einen Anlernmodus zeige ich 3 Rechnungen nacheinander in einer Gui an, markiere die Bereiche (z.b: Auftrag-Nr), die relevant sind und speicher mir die Koordinaten in eine Datenbank mit einer %-Toleranz . Beim Einlesen einer PDF vom Scanner werden nur die markierten Bereiche als Bild extrahiert und per OCR überprüft, das macht das sehr viel schneller. Ausserdem habe ich dann im jeweils erkannten Bereich nur die Daten, die benötige, d.h. extrahieren Bereich mit "Auftrag-Nr" x1:1000/y1:650 / x2:1250 /y2:850--> damit habe ich auch die Nummer selbst, die ja drunter steht.
Wo ich Probleme hab ist z.B. die Endsumme, weil die Position variabel, je nachdem wieviele Positionen es gibt. Das hatte ich bisher so gelöst, dass ich die X-Koordinaten gelassen habe und die Y-Koordinaten über das ganze Bild hab laufen lassen, auch wenn z.B. die Rechnung 2-seitig war. Aber das hat viel Zeit gekostet, da ich dann entsprechend viele Bilder per OCR und auf Sinnhaftigkeit überprüft hab. Um das zu umgehen wollte ich ähnlich ImageSearch nach einem per BLOB gespeicherten Referenzbild suchen lassen und dann entsprechnend die Koordinaten ermitteln und danach den Ausschnitt erstellen lassen für das OCR.
Ich habe gestern abend gefunden:
https://github.com/MasterFocus/AutoHotkey/tree/master/Functions/Gdip_ImageSearch
und das Funktioniert echt gut!
Hier noch mein Beispiel:
Das Problem bei der Sache ist, dass z.B.
- Wenn ich nach dem Endbetrag im erkannten Text suche, dann steht ja die Summe nicht nebendran, sondern irgendwo anders (unten drunter) und durch einen Strich getrennt. d.h. ich kenne eigentlich die Position des €-Betrags nicht.
Was ich aktuell mache:
Durch einen Anlernmodus zeige ich 3 Rechnungen nacheinander in einer Gui an, markiere die Bereiche (z.b: Auftrag-Nr), die relevant sind und speicher mir die Koordinaten in eine Datenbank mit einer %-Toleranz . Beim Einlesen einer PDF vom Scanner werden nur die markierten Bereiche als Bild extrahiert und per OCR überprüft, das macht das sehr viel schneller. Ausserdem habe ich dann im jeweils erkannten Bereich nur die Daten, die benötige, d.h. extrahieren Bereich mit "Auftrag-Nr" x1:1000/y1:650 / x2:1250 /y2:850--> damit habe ich auch die Nummer selbst, die ja drunter steht.
Wo ich Probleme hab ist z.B. die Endsumme, weil die Position variabel, je nachdem wieviele Positionen es gibt. Das hatte ich bisher so gelöst, dass ich die X-Koordinaten gelassen habe und die Y-Koordinaten über das ganze Bild hab laufen lassen, auch wenn z.B. die Rechnung 2-seitig war. Aber das hat viel Zeit gekostet, da ich dann entsprechend viele Bilder per OCR und auf Sinnhaftigkeit überprüft hab. Um das zu umgehen wollte ich ähnlich ImageSearch nach einem per BLOB gespeicherten Referenzbild suchen lassen und dann entsprechnend die Koordinaten ermitteln und danach den Ausschnitt erstellen lassen für das OCR.
Ich habe gestern abend gefunden:
https://github.com/MasterFocus/AutoHotkey/tree/master/Functions/Gdip_ImageSearch
und das Funktioniert echt gut!
Hier noch mein Beispiel:
Re: ImageSearch innerhalb JPGs/PNG/TIFF Topic is solved
Ok! Wenn man mal davon ausgeht, dass die Bildqualität der Scans ausreicht, wäre das Folgende dann hilfreich?
Code: Select all
#NoEnv
SetBatchLines, -1
Words := ocr("test_AB2_deskew.png", "de")
Gui, Add, ListView, w800 r25, #|X|Y|W|H|Text
For Line, Array In Words
For Each, Word In Array
LV_Add("", Line, Word*)
LV_ModifyCol()
Gui, Show, , Words
Return
GuiClose:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
ocr(file, lang := "FirstFromAvailableLanguages")
{
static OcrEngineStatics, OcrEngine, MaxDimension, LanguageFactory, Language, CurrentLanguage, BitmapDecoderStatics, GlobalizationPreferencesStatics
if (OcrEngineStatics = "")
{
CreateClass("Windows.Globalization.Language", ILanguageFactory := "{9B0252AC-0C27-44F8-B792-9793FB66C63E}", LanguageFactory)
CreateClass("Windows.Graphics.Imaging.BitmapDecoder", IBitmapDecoderStatics := "{438CCB26-BCEF-4E95-BAD6-23A822E58D01}", BitmapDecoderStatics)
CreateClass("Windows.Media.Ocr.OcrEngine", IOcrEngineStatics := "{5BFFA85A-3384-3540-9940-699120D428A8}", OcrEngineStatics)
DllCall(NumGet(NumGet(OcrEngineStatics+0)+6*A_PtrSize), "ptr", OcrEngineStatics, "uint*", MaxDimension) ; MaxImageDimension
}
if (file = "ShowAvailableLanguages")
{
if (GlobalizationPreferencesStatics = "")
CreateClass("Windows.System.UserProfile.GlobalizationPreferences", IGlobalizationPreferencesStatics := "{01BF4326-ED37-4E96-B0E9-C1340D1EA158}", GlobalizationPreferencesStatics)
DllCall(NumGet(NumGet(GlobalizationPreferencesStatics+0)+9*A_PtrSize), "ptr", GlobalizationPreferencesStatics, "ptr*", LanguageList) ; get_Languages
DllCall(NumGet(NumGet(LanguageList+0)+7*A_PtrSize), "ptr", LanguageList, "int*", count) ; count
loop % count
{
DllCall(NumGet(NumGet(LanguageList+0)+6*A_PtrSize), "ptr", LanguageList, "int", A_Index-1, "ptr*", hString) ; get_Item
DllCall(NumGet(NumGet(LanguageFactory+0)+6*A_PtrSize), "ptr", LanguageFactory, "ptr", hString, "ptr*", LanguageTest) ; CreateLanguage
DllCall(NumGet(NumGet(OcrEngineStatics+0)+8*A_PtrSize), "ptr", OcrEngineStatics, "ptr", LanguageTest, "int*", bool) ; IsLanguageSupported
if (bool = 1)
{
DllCall(NumGet(NumGet(LanguageTest+0)+6*A_PtrSize), "ptr", LanguageTest, "ptr*", hText)
buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
text .= StrGet(buffer, "UTF-16") "`n"
}
ObjRelease(LanguageTest)
}
ObjRelease(LanguageList)
return text
}
if (lang != CurrentLanguage) or (lang = "FirstFromAvailableLanguages")
{
if (OcrEngine != "")
{
ObjRelease(OcrEngine)
if (CurrentLanguage != "FirstFromAvailableLanguages")
ObjRelease(Language)
}
if (lang = "FirstFromAvailableLanguages")
DllCall(NumGet(NumGet(OcrEngineStatics+0)+10*A_PtrSize), "ptr", OcrEngineStatics, "ptr*", OcrEngine) ; TryCreateFromUserProfileLanguages
else
{
CreateHString(lang, hString)
DllCall(NumGet(NumGet(LanguageFactory+0)+6*A_PtrSize), "ptr", LanguageFactory, "ptr", hString, "ptr*", Language) ; CreateLanguage
DeleteHString(hString)
DllCall(NumGet(NumGet(OcrEngineStatics+0)+9*A_PtrSize), "ptr", OcrEngineStatics, ptr, Language, "ptr*", OcrEngine) ; TryCreateFromLanguage
}
if (OcrEngine = 0)
{
msgbox Can not use language "%lang%" for OCR, please install language pack.
ExitApp
}
CurrentLanguage := lang
}
if (SubStr(file, 2, 1) != ":")
file := A_ScriptDir "\" file
if !FileExist(file) or InStr(FileExist(file), "D")
{
msgbox File "%file%" does not exist
ExitApp
}
VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", file, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
DllCall(NumGet(NumGet(BitmapDecoderStatics+0)+14*A_PtrSize), "ptr", BitmapDecoderStatics, "ptr", IRandomAccessStream, "ptr*", BitmapDecoder) ; CreateAsync
WaitForAsync(BitmapDecoder)
BitmapFrame := ComObjQuery(BitmapDecoder, IBitmapFrame := "{72A49A1C-8081-438D-91BC-94ECFC8185C6}")
DllCall(NumGet(NumGet(BitmapFrame+0)+12*A_PtrSize), "ptr", BitmapFrame, "uint*", width) ; get_PixelWidth
DllCall(NumGet(NumGet(BitmapFrame+0)+13*A_PtrSize), "ptr", BitmapFrame, "uint*", height) ; get_PixelHeight
if (width > MaxDimension) or (height > MaxDimension)
{
msgbox Image is to big - %width%x%height%.`nIt should be maximum - %MaxDimension% pixels
ExitApp
}
BitmapFrameWithSoftwareBitmap := ComObjQuery(BitmapDecoder, IBitmapFrameWithSoftwareBitmap := "{FE287C9A-420C-4963-87AD-691436E08383}")
DllCall(NumGet(NumGet(BitmapFrameWithSoftwareBitmap+0)+6*A_PtrSize), "ptr", BitmapFrameWithSoftwareBitmap, "ptr*", SoftwareBitmap) ; GetSoftwareBitmapAsync
WaitForAsync(SoftwareBitmap)
DllCall(NumGet(NumGet(OcrEngine+0)+6*A_PtrSize), "ptr", OcrEngine, ptr, SoftwareBitmap, "ptr*", OcrResult) ; RecognizeAsync
WaitForAsync(OcrResult)
DllCall(NumGet(NumGet(OcrResult+0)+6*A_PtrSize), "ptr", OcrResult, "ptr*", LinesList) ; get_Lines
DllCall(NumGet(NumGet(LinesList+0)+7*A_PtrSize), "ptr", LinesList, "int*", count) ; count
WordArray := []
loop % count
{
LineIndex := A_Index
DllCall(NumGet(NumGet(LinesList+0)+6*A_PtrSize), "ptr", LinesList, "int", A_Index-1, "ptr*", OcrLine)
DllCall(NumGet(NumGet(OcrLine+0)+6*A_PtrSize), "ptr", OcrLine, "ptr*", WordsList) ; get_Words
DllCall(NumGet(NumGet(WordsList+0)+7*A_PtrSize), "ptr", WordsList, "int*", WordsCount) ; Words count
loop % WordsCount
{
DllCall(NumGet(NumGet(WordsList+0)+6*A_PtrSize), "ptr", WordsList, "int", A_Index-1, "ptr*", OcrWord)
VarSetCapacity(RECT, 16, 0)
DllCall(NumGet(NumGet(OcrWord+0)+6*A_PtrSize), "ptr", OcrWord, "ptr", &RECT) ; get_BoundingRect
DllCall(NumGet(NumGet(OcrWord+0)+7*A_PtrSize), "ptr", OcrWord, "ptr*", hText) ; get_Text
buffer := DllCall("Combase.dll\WindowsGetStringRawBuffer", "ptr", hText, "uint*", length, "ptr")
x := NumGet(&RECT, 0, "float")
y := NumGet(&RECT, 4, "float")
w := NumGet(&RECT, 8, "float")
h := NumGet(&RECT, 12, "float")
text := StrGet(buffer, "UTF-16")
WordArray[LineIndex, A_Index] := [x, y, w, h, text]
ObjRelease(OcrWord)
}
ObjRelease(WordsList)
ObjRelease(OcrLine)
}
Close := ComObjQuery(IRandomAccessStream, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}")
DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close) ; Close
ObjRelease(Close)
Close := ComObjQuery(SoftwareBitmap, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}")
DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close) ; Close
ObjRelease(Close)
ObjRelease(IRandomAccessStream)
ObjRelease(BitmapDecoder)
ObjRelease(BitmapFrame)
ObjRelease(BitmapFrameWithSoftwareBitmap)
ObjRelease(SoftwareBitmap)
ObjRelease(OcrResult)
ObjRelease(LinesList)
return WordArray
}
; ----------------------------------------------------------------------------------------------------------------------
CreateClass(string, interface, ByRef Class)
{
CreateHString(string, hString)
VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
if (result != 0)
{
if (result = 0x80004002)
msgbox No such interface supported
else if (result = 0x80040154)
msgbox Class not registered
else
msgbox error: %result%
ExitApp
}
DeleteHString(hString)
}
; ----------------------------------------------------------------------------------------------------------------------
CreateHString(string, ByRef hString)
{
DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}
; ----------------------------------------------------------------------------------------------------------------------
DeleteHString(hString)
{
DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}
; ----------------------------------------------------------------------------------------------------------------------
WaitForAsync(ByRef Object)
{
AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
loop
{
DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status) ; IAsyncInfo.Status
if (status != 0)
{
if (status != 1)
{
DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode) ; IAsyncInfo.ErrorCode
msgbox AsyncInfo status error: %ErrorCode%
ExitApp
}
ObjRelease(AsyncInfo)
break
}
sleep 10
}
DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult) ; GetResults
ObjRelease(Object)
Object := ObjectResult
}
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Diese Ausgabe kann man sehr gut auch mit den Boxen in tesseract vergleichen, da gibt es auch solche Ausgaben.
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Hab ich mich auch gefragt. Selbst nach Erkennungsrate bei einem identischen Formular kann ein Suchbegriff eben unterschiedlich positioniert sein, und bei monetär-relevanten Sachverhalten kann sowas dann ins Geld gehen Wenn du allerdings ausschließlich soft-copies aus einer einzigen Quelle prozessierst dürften die Probleme beherrschbar sein, da die Suchmuster deckungsgleich sind und sich ggf sogar anlernen lassen. Good luckWenn ich nach dem Endbetrag im erkannten Text suche, dann steht ja die Summe nicht nebendran, sondern irgendwo anders (unten drunter)
Re: ImageSearch innerhalb JPGs/PNG/TIFF
Hallo,
ich möchte die Uhrzeit auf dem Bild detektieren, aber genau diese wird nicht gescannt/erkannt.
Auch nicht auf anderen Bildern, Ich habe es an 900 solcher Bilder probiert. Im Resultat waren nur wenige Scanns erfolgreich.
Ich weiß nicht ob es am Doppelpunkt liegt.
Kann man mit AHK die BildDatei in schwarz weiß konvertieren? und vielleicht auch invertieren/negieren?
Vielleicht arbeitet die OCR besser wenn die Schrift schwarz ist?
viewtopic.php?p=395438#p395438
ich möchte die Uhrzeit auf dem Bild detektieren, aber genau diese wird nicht gescannt/erkannt.
Auch nicht auf anderen Bildern, Ich habe es an 900 solcher Bilder probiert. Im Resultat waren nur wenige Scanns erfolgreich.
Ich weiß nicht ob es am Doppelpunkt liegt.
Kann man mit AHK die BildDatei in schwarz weiß konvertieren? und vielleicht auch invertieren/negieren?
Vielleicht arbeitet die OCR besser wenn die Schrift schwarz ist?
viewtopic.php?p=395438#p395438
Who is online
Users browsing this forum: No registered users and 23 guests