mirror of
https://github.com/moparisthebest/Simba
synced 2024-11-25 18:52:15 -05:00
Comments for OCR.
git-svn-id: http://www.villavu.com/repositories/merlijn/mufasa@349 3f818213-9676-44b0-a9b4-5e4c4e03d09d
This commit is contained in:
parent
9a7803d3f7
commit
e9db9383e3
@ -76,47 +76,37 @@ uses
|
|||||||
colour_conv, client, files, tpa, mufasatypesutil;
|
colour_conv, client, files, tpa, mufasatypesutil;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
{ Very rough limits for R, G, B }
|
||||||
ocr_Limit_High = 190;
|
ocr_Limit_High = 190;
|
||||||
ocr_Limit_Med = 130;
|
ocr_Limit_Med = 130;
|
||||||
ocr_Limit_Low = 65;
|
ocr_Limit_Low = 65;
|
||||||
|
|
||||||
|
|
||||||
|
{ `base' Colours of the Uptext }
|
||||||
|
|
||||||
|
{ White }
|
||||||
ocr_White = 16777215;
|
ocr_White = 16777215;
|
||||||
|
|
||||||
|
{ Level < Your Level }
|
||||||
ocr_Green = 65280;
|
ocr_Green = 65280;
|
||||||
|
|
||||||
|
{ Level > Your Level }
|
||||||
ocr_Red = 255;
|
ocr_Red = 255;
|
||||||
|
|
||||||
|
{ Interact or Level = Your Level }
|
||||||
ocr_Yellow = 65535;
|
ocr_Yellow = 65535;
|
||||||
|
|
||||||
|
{ Object }
|
||||||
ocr_Blue = 16776960;
|
ocr_Blue = 16776960;
|
||||||
|
|
||||||
|
{ Item }
|
||||||
ocr_ItemC = 16744447;
|
ocr_ItemC = 16744447;
|
||||||
|
|
||||||
|
{ Shadow }
|
||||||
ocr_Purple = 8388736;
|
ocr_Purple = 8388736;
|
||||||
|
|
||||||
{
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
When splitting the shadows, having the `original' points of the chars would be very helpful when splitting!
|
|
||||||
|
|
||||||
Short description:
|
|
||||||
Merge shadow and char; (Same colour; simple)
|
|
||||||
Split with spacing of 1.
|
|
||||||
Remove character points; leaves only shadow. Voila, perfect splitting.
|
|
||||||
Splitting could go wrong for shadows of characters with two much spacing; like 'h'.
|
|
||||||
|
|
||||||
In some cases, this may `bind' characters to each other.
|
|
||||||
In this case, we need to remove the shadow and split again. After this split
|
|
||||||
we can put the shadows back in, and see to what group they belong.
|
|
||||||
|
|
||||||
We can also just split the chars, and then use their shadow.
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
**************************************************************************************************
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ Constructor }
|
||||||
constructor TMOCR.Create(Owner: TObject);
|
constructor TMOCR.Create(Owner: TObject);
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -128,6 +118,7 @@ begin
|
|||||||
Self.Fonts := TMFonts.Create;
|
Self.Fonts := TMFonts.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Destructor }
|
||||||
destructor TMOCR.Destroy;
|
destructor TMOCR.Destroy;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@ -136,6 +127,10 @@ begin
|
|||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
|
InitTOCR loads all fonts in path
|
||||||
|
We don't do this in the constructor because we may not yet have the path.
|
||||||
|
}
|
||||||
function TMOCR.InitTOCR(path: string): boolean;
|
function TMOCR.InitTOCR(path: string): boolean;
|
||||||
var
|
var
|
||||||
dirs: array of string;
|
dirs: array of string;
|
||||||
@ -157,19 +152,30 @@ begin
|
|||||||
Fonts.LoadFont('UpChars', true); // shadow
|
Fonts.LoadFont('UpChars', true); // shadow
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Get the current pointer to our list of Fonts }
|
||||||
function TMOCR.GetFonts:TMFonts;
|
function TMOCR.GetFonts:TMFonts;
|
||||||
begin
|
begin
|
||||||
Exit(Self.Fonts);
|
Exit(Self.Fonts);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Set new Fonts. We set it to a Copy of NewFonts }
|
||||||
procedure TMOCR.SetFonts(NewFonts: TMFonts);
|
procedure TMOCR.SetFonts(NewFonts: TMFonts);
|
||||||
begin
|
begin
|
||||||
Self.Fonts := NewFonts.Copy();
|
Self.Fonts := NewFonts.Copy();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{
|
{
|
||||||
Non optimised. ;-)
|
Filter UpText by a very rough colour comparison / range check.
|
||||||
|
We first convert the colour to RGB, and if it falls into the following
|
||||||
|
defined ranges, it may be part of the uptext. Also get the possible
|
||||||
|
shadows.
|
||||||
|
|
||||||
|
We have large ranges because we rather have extra (fake) pixels than less
|
||||||
|
uptext pixels... This because we can filter most of the noise out easily.
|
||||||
|
|
||||||
|
Non optimised. We can make it use direct data instead of fastgetpixel and
|
||||||
|
fastsetpixel, but speed isn't really an issue. The entire algorithm is still
|
||||||
|
fast enough.
|
||||||
}
|
}
|
||||||
|
|
||||||
procedure TMOCR.FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer);
|
procedure TMOCR.FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer);
|
||||||
@ -254,6 +260,35 @@ begin
|
|||||||
bmp.fastsetpixel(bmp.Width-1, y, 0);
|
bmp.fastsetpixel(bmp.Width-1, y, 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
This filter assumes the previous colour filter has been applied first.
|
||||||
|
I like to call this the `characteristics' filter because we really only filter
|
||||||
|
on characteristics.
|
||||||
|
|
||||||
|
For the uptext, a few things apply...
|
||||||
|
First of all:
|
||||||
|
|
||||||
|
*** Remove False Shadow ***
|
||||||
|
if shadow[x,y] then not shadow[x-1,y-1]
|
||||||
|
If there is a shadow at x,y then there should not be a shadow at x-1, y-1; if
|
||||||
|
there is one, then shadow[x, y] is not a shadow.
|
||||||
|
|
||||||
|
(One could also say, if shadow[x,y] and shadow[x+1,y+1] then shadow[x+1,y+1]
|
||||||
|
is no shadow; because it essentially means the same. However, a smart mind
|
||||||
|
will soon see that this algorithm will be a *lot* more efficient if we
|
||||||
|
start at the right bottom, instead of the left top. Which means we should
|
||||||
|
work with x-1 and y-1, rather than x+1,y+1
|
||||||
|
Yeah.... My comments are vague.
|
||||||
|
)
|
||||||
|
|
||||||
|
*** UpText chars identity 1 and 2 ***
|
||||||
|
if UpTextChar[x,y] then (UpTextChar[x+1,y+1] or shadow[x+1,y+1])
|
||||||
|
If this is not true, then UpTextChar[x,y] cannot be part of uptext - it
|
||||||
|
has no shadow, and it doesn't have a `friend' (at x+1,y+1) either.
|
||||||
|
We don't need to do this from the right bottom to left top.
|
||||||
|
|
||||||
|
}
|
||||||
procedure TMOCR.FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer);
|
procedure TMOCR.FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer);
|
||||||
var
|
var
|
||||||
x,y: Integer;
|
x,y: Integer;
|
||||||
@ -289,7 +324,7 @@ begin
|
|||||||
bmp.fastsetpixel(x,y,clSilver);
|
bmp.fastsetpixel(x,y,clSilver);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Now we do another filter like
|
// Now we do another filter, with uptext chars identity 1 and 2.
|
||||||
for y := bmp.Height - 2 downto 0 do
|
for y := bmp.Height - 2 downto 0 do
|
||||||
for x := bmp.Width - 2 downto 0 do
|
for x := bmp.Width - 2 downto 0 do
|
||||||
begin
|
begin
|
||||||
@ -297,18 +332,24 @@ begin
|
|||||||
continue;
|
continue;
|
||||||
if bmp.fastgetpixel(x,y) = clBlack then
|
if bmp.fastgetpixel(x,y) = clBlack then
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// identity 1
|
||||||
if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then
|
if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// identity 2
|
||||||
if bmp.fastgetpixel(x+1,y+1) <> clPurple then
|
if bmp.fastgetpixel(x+1,y+1) <> clPurple then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,clOlive);
|
bmp.fastsetpixel(x,y,clOlive);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// If we make it to here, it means the pixel is part of the uptext.
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{$IFDEF OCRDEBUG}
|
{$IFDEF OCRDEBUG}
|
||||||
|
{ Write to our debugbmp }
|
||||||
procedure TMOCR.DebugToBmp(bmp: TMufasaBitmap; hmod, h: integer);
|
procedure TMOCR.DebugToBmp(bmp: TMufasaBitmap; hmod, h: integer);
|
||||||
var
|
var
|
||||||
x,y: integer;
|
x,y: integer;
|
||||||
@ -319,6 +360,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
|
{
|
||||||
|
Return the shadows of the points in charpoint on bitmap shadowsbmp.
|
||||||
|
|
||||||
|
Pseudo:
|
||||||
|
if shadow[charpoint[i].x+1, charpoint[i].y+1] then addtoResult;
|
||||||
|
}
|
||||||
function getshadows(shadowsbmp:TMufasaBitmap; charpoint: tpointarray): tpointarray;
|
function getshadows(shadowsbmp:TMufasaBitmap; charpoint: tpointarray): tpointarray;
|
||||||
var
|
var
|
||||||
i,c:integer;
|
i,c:integer;
|
||||||
@ -336,6 +383,7 @@ begin
|
|||||||
setlength(result,c);
|
setlength(result,c);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Remove anything but the shadows on the bitmap (Shadow = clPurple, remember?) }
|
||||||
procedure TMOCR.FilterShadowBitmap(bmp: TMufasaBitmap);
|
procedure TMOCR.FilterShadowBitmap(bmp: TMufasaBitmap);
|
||||||
var
|
var
|
||||||
x,y:integer;
|
x,y:integer;
|
||||||
@ -351,6 +399,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
|
Remove all but uptext colours clWhite,clGreen, etc.
|
||||||
|
See constants above.
|
||||||
|
|
||||||
|
This assumes that the bitmap only consists of colour 0, and the other
|
||||||
|
constants founds above the functionss
|
||||||
|
}
|
||||||
procedure TMOCR.FilterCharsBitmap(bmp: TMufasaBitmap);
|
procedure TMOCR.FilterCharsBitmap(bmp: TMufasaBitmap);
|
||||||
var
|
var
|
||||||
x,y: integer;
|
x,y: integer;
|
||||||
@ -378,6 +433,19 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
This uses the two filters, and performs a split on the bitmap.
|
||||||
|
A split per character, that is. So we can more easily identify it.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
*
|
||||||
|
Remove more noise after we have split, it should be possible to identify
|
||||||
|
noise; weird positions or boxes compared to the rest, etc.
|
||||||
|
*
|
||||||
|
Split each colours seperately, and combine only later, after removing noise.
|
||||||
|
|
||||||
|
}
|
||||||
function TMOCR.getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean;
|
function TMOCR.getTextPointsIn(sx, sy, w, h: Integer; shadow: boolean;
|
||||||
var _chars, _shadows: T2DPointArray): Boolean;
|
var _chars, _shadows: T2DPointArray): Boolean;
|
||||||
var
|
var
|
||||||
@ -427,6 +495,7 @@ begin
|
|||||||
DebugToBmp(bmp,1,h);
|
DebugToBmp(bmp,1,h);
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
|
// Filter 2
|
||||||
FilterUpTextByCharacteristics(bmp,w,h);
|
FilterUpTextByCharacteristics(bmp,w,h);
|
||||||
|
|
||||||
{$IFDEF OCRSAVEBITMAP}
|
{$IFDEF OCRSAVEBITMAP}
|
||||||
@ -469,6 +538,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
setlength(chars,charscount);
|
setlength(chars,charscount);
|
||||||
|
|
||||||
|
// split chars
|
||||||
chars_2d := SplitTPAEx(chars,1,charsbmp.height);
|
chars_2d := SplitTPAEx(chars,1,charsbmp.height);
|
||||||
SortATPAFrom(chars_2d, point(0,0));
|
SortATPAFrom(chars_2d, point(0,0));
|
||||||
for x := 0 to high(chars_2d) do
|
for x := 0 to high(chars_2d) do
|
||||||
@ -509,6 +579,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// sort, split messes with the order of chars
|
||||||
SortATPAFromFirstPoint(chars_2d, point(0,0));
|
SortATPAFromFirstPoint(chars_2d, point(0,0));
|
||||||
for y := 0 to high(chars_2d) do
|
for y := 0 to high(chars_2d) do
|
||||||
begin
|
begin
|
||||||
@ -518,7 +589,6 @@ begin
|
|||||||
finalchars[high(finalchars)] := chars_2d[y];
|
finalchars[high(finalchars)] := chars_2d[y];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
SortATPAFromFirstPoint(shadows, point(0,0));
|
SortATPAFromFirstPoint(shadows, point(0,0));
|
||||||
for x := 0 to high(shadows) do
|
for x := 0 to high(shadows) do
|
||||||
begin
|
begin
|
||||||
@ -540,6 +610,14 @@ begin
|
|||||||
shadowsbmp.Free;
|
shadowsbmp.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
|
GetUpTextAtEx combines/uses the functions above.
|
||||||
|
|
||||||
|
It will identify each character, and also keep track of the previous
|
||||||
|
chars' final `x' bounds. If the difference between the .x2 of the previous
|
||||||
|
character and the .x1 of the current character is bigger than 5, then there
|
||||||
|
was a space between them. (Add ' ' to result)
|
||||||
|
}
|
||||||
|
|
||||||
function TMOCR.GetUpTextAtEx(atX, atY: integer; shadow: boolean): string;
|
function TMOCR.GetUpTextAtEx(atX, atY: integer; shadow: boolean): string;
|
||||||
var
|
var
|
||||||
@ -552,11 +630,14 @@ var
|
|||||||
lbset: boolean;
|
lbset: boolean;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
result:='';
|
||||||
ww := 400;
|
ww := 400;
|
||||||
hh := 20;
|
hh := 20;
|
||||||
|
|
||||||
getTextPointsIn(atX, atY, ww, hh, shadow, chars, shadows);
|
getTextPointsIn(atX, atY, ww, hh, shadow, chars, shadows);
|
||||||
|
|
||||||
|
// Get font data for analysis.
|
||||||
|
|
||||||
if shadow then
|
if shadow then
|
||||||
begin
|
begin
|
||||||
font := Fonts.GetFont('UpChars_s');
|
font := Fonts.GetFont('UpChars_s');
|
||||||
@ -575,7 +656,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
lbset:=false;
|
lbset:=false;
|
||||||
//writeln(format('FFont Width/Height: (%d, %d)', [font.width,font.height]));
|
|
||||||
setlength(n, (font.width+1) * (font.height+1));
|
setlength(n, (font.width+1) * (font.height+1));
|
||||||
for j := 0 to high(thachars) do
|
for j := 0 to high(thachars) do
|
||||||
begin
|
begin
|
||||||
@ -603,13 +683,7 @@ begin
|
|||||||
n[(thachars[j][i].x) + ((thachars[j][i].y) * font.width)] := 1;
|
n[(thachars[j][i].x) + ((thachars[j][i].y) * font.width)] := 1;
|
||||||
end;
|
end;
|
||||||
result := result + GuessGlyph(n, font);
|
result := result + GuessGlyph(n, font);
|
||||||
//writeln('--'+GuessGlyph(n, font));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
//Result := ocrDetect(n, ww-1, hh-1, font);
|
|
||||||
//Result:='To do';
|
|
||||||
//Result:='';
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMOCR.GetUpTextAt(atX, atY: integer; shadow: boolean): string;
|
function TMOCR.GetUpTextAt(atX, atY: integer; shadow: boolean): string;
|
||||||
|
Loading…
Reference in New Issue
Block a user