diff --git a/Source/script/imports/simba.import_pixelocr.pas b/Source/script/imports/simba.import_pixelocr.pas index e0637a769..de33912f4 100644 --- a/Source/script/imports/simba.import_pixelocr.pas +++ b/Source/script/imports/simba.import_pixelocr.pas @@ -21,6 +21,22 @@ implementation PPixelFont = ^TPixelFont; PPixelOCR = ^TPixelOCR; +procedure _LapePixelFont_SameChar(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPixelFont(Params^[0])^.SameGlyph(PChar(Params^[1])^, PChar(Params^[2])^); +end; + +procedure _LapePixelFont_SameText(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PBoolean(Result)^ := PPixelFont(Params^[0])^.SameText(PString(Params^[1])^, PString(Params^[2])^); +end; + +procedure _LapePixelFont_ReplaceSameGlyphs(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PPixelFont(Params^[0])^.ReplaceSameGlyphs(PString(Params^[1])^, PString(Params^[2])^); +end; + + procedure _LapePixelOCR_LoadFont(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PPixelFont(Result)^ := TPixelOCR.LoadFont(PString(Params^[0])^, PInteger(Params^[1])^); @@ -59,6 +75,8 @@ procedure ImportPixelOCR(Script: TSimbaScript); 'record', ' Value: Char;', '', + ' Hash: UInt32;', + '', ' Width: Int16;', ' Height: Int16;', ' ForegroundBounds: TBox;', @@ -114,6 +132,9 @@ procedure ImportPixelOCR(Script: TSimbaScript); if (getGlobalType('TPixelOCR').Size <> SizeOf(TPixelOCR)) then SimbaException('TPixelOCR import is wrong'); + addGlobalFunc('function TPixelFont.SameGlyph(const a, b: Char): Boolean;', @_LapePixelFont_SameChar); + addGlobalFunc('function TPixelFont.SameText(const s1, s2: String): Boolean;', @_LapePixelFont_SameText); + addGlobalFunc('procedure TPixelFont.ReplaceSameGlyphs(var s1: String; const s2: String);', @_LapePixelFont_ReplaceSameGlyphs); addGlobalFunc('function TPixelOCR.LoadFont(Path: String; SpaceWidth: Integer): TPixelFont; static;', @_LapePixelOCR_LoadFont); addGlobalFunc('function TPixelOCR.TextToTPA(constref Font: TPixelFont; Text: String): TPointArray; static;', @_LapePixelOCR_TextToTPA); addGlobalFunc('function TPixelOCR.Locate(Image: TImage; constref Font: TPixelFont; Text: String): Single;', @_LapePixelOCR_Locate); diff --git a/Source/simba.pixelocr.pas b/Source/simba.pixelocr.pas index 4bb495b74..e1f800815 100644 --- a/Source/simba.pixelocr.pas +++ b/Source/simba.pixelocr.pas @@ -25,6 +25,8 @@ interface TPixelFontGlyph = record Value: Char; + Hash: UInt32; + Width: Int16; // image width Height: Int16; // image height ForegroundBounds: TBox; // foreground meaning points and shadow @@ -44,6 +46,11 @@ TPixelFont = record SpaceWidth: Integer; MaxGlyphWidth: Integer; MaxGlyphHeight: Integer; + public + function GetGlyph(const c: Char): PPixelFontGlyph; inline; + function SameGlyph(const a, b: Char): Boolean; inline; + function SameText(const s1, s2: String): Boolean; inline; + procedure ReplaceSameGlyphs(var s1: String; const s2: String); inline; end; PPixelFont = ^TPixelFont; @@ -112,18 +119,58 @@ function IsShadow(const Image: TSimbaImage; const X, Y: Integer; const Tol: Sing Result := (R <= Tol) and (G <= Tol) and (B <= Tol + 5); // allow a little more in the blue channel only end; -function GetGlyph(const Font: PPixelFont; const c: Char): PPixelFontGlyph; inline; +//slacky hashing algorithm +function HashPoints(const points: TPointArray): UInt32; inline; +var + pt: TPoint; +begin + {$PUSH} + {$Q-}{$R-} + Result := $811C9DC5; + for pt in points do + Result := (Result xor (UInt32(pt.X) shl 16 or UInt32(pt.Y))) * $01000193; + {$POP} +end; + +function TPixelFont.GetGlyph(const c: Char): PPixelFontGlyph; inline; var I: Integer; begin - for I := 0 to High(Font^.Glyphs) do - if (Font^.Glyphs[I].Value = c) then - Exit(@Font^.Glyphs[I]); + for I := 0 to High(Self.Glyphs) do + if (Self.Glyphs[I].Value = c) then + Exit(@Self.Glyphs[I]); SimbaException('Character %s does exist in the Font', [c]); Result := nil; end; +function TPixelFont.SameGlyph(const a, b: Char): Boolean; inline; +begin + Result := Self.GetGlyph(a)^.Hash = Self.GetGlyph(b)^.Hash; +end; + +function TPixelFont.SameText(const s1, s2: String): Boolean; inline; +var + i: Integer; +begin + if Length(s1) <> Length(s2) then + Exit(False); + for i := 1 to Length(s1) do + if not Self.SameGlyph(s1[i], s2[i]) then + Exit(False); + Result := True; +end; + +procedure TPixelFont.ReplaceSameGlyphs(var s1: String; const s2: String); inline; +var + i: Integer; +begin + for i := 1 to Min(Length(s1), Length(s2)) do + if Self.SameGlyph(s1[i], s2[i]) then + s1[i] := s2[i]; +end; + + // text contains >= 50% alphanum function IsAlphaNumSym(const Text: String): Boolean; inline; var @@ -364,6 +411,7 @@ class function TPixelOCR.LoadFont(Dir: String; SpaceWidth: Integer): TPixelFont; Character: String; Glyph: TPixelFontGlyph; B: TBox; + Pt: TPoint; begin Result := Default(TPixelFont); Result.SpaceWidth := SpaceWidth; @@ -410,6 +458,8 @@ class function TPixelOCR.LoadFont(Dir: String; SpaceWidth: Integer): TPixelFont; Glyph.BackgroundBounds := B; Glyph.PointsShadowWidth := B.Width; + Glyph.Hash := HashPoints(TPointArray(Glyph.Points + Glyph.Shadow)); + if (Length(Glyph.Shadow) > 0) then Glyph.BestMatch := Length(Glyph.Points) + Length(Glyph.Shadow) else @@ -442,7 +492,7 @@ class function TPixelOCR.TextToTPA(constref Font: TPixelFont; Text: String; out X := 0; Count := 0; for I := 1 to Length(Text) do - with GetGlyph(@Font, Text[I])^ do + with Font.GetGlyph(Text[I])^ do begin if (Points <> nil) then begin @@ -460,7 +510,7 @@ class function TPixelOCR.TextToTPA(constref Font: TPixelFont; Text: String; out Count := 0; for I := 1 to Length(Text) do - with GetGlyph(@Font, Text[I])^ do + with Font.GetGlyph(Text[I])^ do begin for J := 0 to High(Points) do begin @@ -652,4 +702,5 @@ function TPixelOCR.RecognizeLines(Image: TSimbaImage; constref Font: TPixelFont; end; end; + end.