主頁 > 後端開發 > delphi高級驗證碼識別引擎原始碼

delphi高級驗證碼識別引擎原始碼

2020-09-24 11:52:52 後端開發

小弟水平低,代碼寫的很亂,請諒解
需用到calcexprress,pngimage和gifimage,這里不再提供!

unit OCR;

interface

uses Windows, SysUtils, Graphics, Classes, PNGImage, GIFImage, JPEG, Math, AsphyreZlib;

type
  TOCRLibSetting = record  //驗證碼庫設定
    SaveBMP: Boolean; //存盤轉換后的Bmp檔案
    BmpPath: String; //Bmp存盤路徑
    BmpPrefix: String; //Bmp檔案前綴
    BmpSuffix: String; //Bmp檔案后綴
  end;

type
  //影像大小類
  TOCRSz = record
    W,H: Byte;   //寬,高
  end;
  //特征碼模板庫類
  TOCRTemplates = record
    Count: Byte;    //數量
    Names: array of String; //名稱
    OCRFiles: array of String; //檔案名/路徑
    OCRSz: array of TOCRSz; //影像大小
    YaoqiuSS: array of Byte;  //是否為算式
  end;

//初始化驗證碼庫
function InitOCRLib: Boolean;
//取消使用Dll
procedure CancelUseDLL;
//加載驗證碼模板庫
function LoadOCRLib(const AFileName: String = ''): Boolean;
//影像轉換為BMP
function ALL2BMP(const Filename : String; const Bmp: TBitmap): Boolean;
//加載資源dll
function LoadOCRResourceDLL(const ADllName: String): Boolean;
//識別驗證碼
function RecogOCR(var Success: Boolean; const ImageFile: String): String;
//更改特征碼模板
function LoadOCRTemplate(const TmplID: Integer): Boolean;
//加載特征碼檔案
function LoadOCRFile(const ocrFile: String; const IsAutoSS: Boolean = False): Boolean;
//查找驗證碼特征檔案
function FetchOCR(const StartIndex: Integer; const Width,Height: Integer; const AOCRName: String = ''): Integer;
//驗證碼庫設定
function SetOCRLib(const ASetting: TOCRLibSetting): Boolean;
//獲得驗證碼庫設定
function GetOCRLibSetting: TOCRLibSetting;
//獲得驗證碼模板庫
function GetOCRTemplates: TOCRTemplates;
//獲取最后識別時間(毫秒)
function GetLastRecogTime: DWORD;
//呼叫AspriseOcr
//function RecogOCRByOCRLib(const FileName: String): String;
//釋放驗證碼庫/清除特征碼檔案
function FreeOcr: Boolean;

//procedure SetPicFormat(Format: Byte);

const
  FMT_AUTO = 4; //自動
  FMT_PNG = 2; //png
  FMT_BMP = 1; //bmp
  FMT_GIF = 3; //gif
  FMT_JPEG = 0; //jpg/jpeg

implementation

uses IniFiles, SSUtils;

type
  RSpeicalEffects = record  //特殊效果
    To1Line: Boolean;   //字符歸位
    RemoveZD: Boolean;  //消除噪點
    Y0: Byte;           //Y軸偏移
    XcZD: Byte;         //噪點閥值
  end;

type //字符特征碼
  RChar = record
    MyChar: char;          //字符
    used: Boolean;         //已使用
    MyCharInfo: array[0..49, 0..49] of byte;  //字符影像
  end;

type //字符特征檔案
  RCharInfo = record
    charwidth: byte; //字符寬度
    charheight: byte; //字符高度
    X0: byte; //第一個字符開始x偏移
    TotalChars: byte; //圖象字符總數
    CusDiv : boolean;  //自定義二值化運算
    DivCmp : Byte; //  0:>  1:=  2:<
    DivColr : TColor;  //二值化閥值
    _CmpChr,_CmpBg: Boolean;  //比較字符(黑色),比較背景(白色)
    _ClrRect: Boolean;   //清除矩形
    _RectLen: Byte;     //矩形長度

    allcharinfo: array[0..42] of RChar; //字符特征碼串列
  end;

type
  TOcrVersionSng = array [0..1] of Byte;
  TOcrVersion = record    //版本號
    First,Minjor: Byte;   //版本
    Author: String[10];   //作者
    Name: String[20];     //特征碼名稱
  end;

  ROcrLibFile = record
    Sng: TOcrVersionSng;  //版本標識
    Ver: TOcrVersion;     //版本
    W,H: Byte;            //影像寬,高
    Effect: RSpeicalEffects;  //特殊效果
    CharInfo: RCharInfo;     //特征碼
    EffectBLW: Boolean;     //通用二值化
  end;

  TOcrLibDllInfo = record
    DllFile: String;
    MDLRPrefix: String;
    MDLRType: String;
  end;

var
  _BITMAP: TBitmap;  //識別影像
  MycharInfo: RCharInfo; //特征碼
  _Effect: RSpeicalEffects;  //特效
  _EffBLW: Boolean;  //通用二值化
  SSCode: Byte;   //是否為算式

var
  BmW,BmH: Integer;  //特征碼影像寬,高
  OcrName: String;  //特征碼名稱
  _PicFormat: Byte; //影像格式
  _PicWidth,_PicHeight: Byte; //實際影像寬,高
  Templates: TOCRTemplates; //模板串列
  Setting: TOCRLibSetting;
  LastRecogTime: DWORD;

var
  UseDll: Boolean;
  DllInfo: TOcrLibDllInfo;

const
  SP = '@';

procedure CancelUseDLL;
begin
  UseDll := False;
end;

function GetLastRecogTime: DWORD;
begin
  Result := LastRecogTime;
end;

function GetOCRLibSetting: TOCRLibSetting;
begin
  Result := Setting;
end;

function GetOCRTemplates: TOCRTemplates;
begin
  Result := Templates;
end;

function LoadOCRResourceDLL(const ADllName: String): Boolean;
var
  strm: TResourceStream;
  hDll: THandle;
  S: String;
  function GetTempPathFileName: String;
  var
    SPath, SFile : PChar;
  begin
    SPath := AllocMem(MAX_PATH);
    SFile := AllocMem(MAX_PATH);
    GetTempPath(MAX_PATH, SPath);
    GetTempFileName(SPath, '~OC', 0, SFile);
    Result := String(SFile);
    FreeMem(SPath, MAX_PATH);
    FreeMem(SFile, MAX_PATH);
    DeleteFile(Result);
  end;
begin
  Result := False;
  try
    hDll := LoadLibrary(PChar(ADllName));
    if hDll <> 0 then
    begin
      try
        strm := TResourceStream.Create(hDll,
          'SDSOFT_OCR',
          PChar('OCR'));

        S := GetTempPathFileName;
        strm.SaveToFile(S);
        try
          UseDll := True;
          Result := LoadOCRLib(S);
        except
          UseDll := False;
        end;
        if Result = False then UseDll := False;
        if UseDll = True then DllInfo.DllFile := ADllName;

        DeleteFile(S);
      finally
        FreeLibrary(hDll);
      end;
    end;
    Result := True;
  except
  end;
end;

function SetOCRLib(const ASetting: TOCRLibSetting): Boolean;
begin
  Result := False;
  try
    Setting := ASetting;
    Result := True;
  except
  end;
end;

function InitOCRLib: Boolean;
begin
  Result := False;
  try
    UseDll := False;
    DllInfo.DllFile := '';
    DllInfo.MDLRPrefix := '';
    DllInfo.MDLRType := '';

    _BITMAP := nil;
    FillChar(MycharInfo,SizeOf(RCharInfo),#0);
    MycharInfo.DivCmp := 3;
    MycharInfo.DivColr := $7FFFFF;
    MycharInfo._CmpChr := True;
    MycharInfo._CmpBg := False;
    MycharInfo.X0 := 0;
    MycharInfo.charwidth := 0;
    MycharInfo.CusDiv := False;
    MycharInfo.charheight := 0;
    FillChar(_Effect,SizeOf(RSpeicalEffects),#0);
    _Effect.To1Line := False;
    _Effect.RemoveZD := False;
    Setting.SaveBMP := False;
    Setting.BmpPrefix := 'OCR';
    Setting.BmpSuffix := '';
    LastRecogTime := 0;
  except
  end;
end;

function FetchOCR(const StartIndex: Integer; const Width,Height: Integer; const AOCRName: String = ''): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := StartIndex to Integer(Templates.Count) - 1 do
  begin
    if (Templates.Names[I] = AOCRName) or
         ((Templates.OCRSz[I].W = Width) and (Templates.OCRSz[I].H = Height))
           then
    begin
      Result := I;
      Break;
    end;
  end;
end;

function LoadOCRLib(const AFileName: String = ''): Boolean;
var
  Ini: TIniFile;
  S,S2: String;
  I,J: Integer;

  FileName: String;
begin
  Result := False;
  FileName := AFileName;
  if FileName = '' then
    FileName := ExtractFilePath(ParamStr(0))+'OCR.INI';
  try
    Templates.Count := 0;
    SetLength(Templates.Names,0);
    SetLength(Templates.OCRFiles,0);
    Ini := TIniFile.Create(FileName);
    Templates.Count := Byte(Ini.ReadInteger('OCRLIB','TCNT',0));
    SetLength(Templates.Names,Templates.Count*SizeOf(String));
    SetLength(Templates.OCRFiles,Templates.Count*SizeOf(String));
    SetLength(Templates.OCRSz,Templates.Count*SizeOf(TOCRSz));
    SetLength(Templates.YaoqiuSS,Templates.Count*SizeOf(Byte));
    for I := 0 to Templates.Count - 1 do
    begin
      S := Ini.ReadString('OCRLIB','T'+IntToStr(I),'');
      if S <> '' then
      begin
        J := Pos(';',S);
        S2 := Copy(S,1,J-1);
        S := Copy(S,J+1,Length(S)-J+1);
        if UseDll then Templates.OCRFiles[I] := S2
        else Templates.OCRFiles[I] := ExtractFilePath(ParamStr(0))+S2;
        J := Pos(';',S);
        S2 := Copy(S,1,J-1);
        S := Copy(S,J+1,Length(S)-J+1);
        Templates.OCRSz[I].W := Byte(StrToInt(S2));
        J := Pos(';',S);
        S2 := Copy(S,1,J-1);
        S := Copy(S,J+1,Length(S)-J+1);
        Templates.OCRSz[I].H := Byte(StrToInt(S2));
        Templates.YaoqiuSS[I] := Byte(StrToInt(S));
        Templates.Names[I] := Ini.ReadString('OCRNAME','T'+IntToStr(I),'');
      end;
    end;
    if UseDll = True then
    begin
      DllInfo.MDLRPrefix := Ini.ReadString('DLLSETTING','Prefix','');
      DllInfo.MDLRType := Ini.ReadString('DLLSETTING','ResourceType','OCR');
    end;
    Ini.Free;
    Result := True;
  except
  end;
end;

function LoadOCRFile(const ocrFile: String; const IsAutoSS: Boolean = False): Boolean;
var
  Fstrm: TFileStream;
  strm: TMemoryStream;
  dat: ROcrLibFile;
  function VersVerify: Boolean;
  begin
    Result := (dat.Sng[0] = Byte('O')) and (dat.Sng[1] = Byte('C'));
  end;
begin
  Result := False;
  try
    Fstrm := TFileStream.Create(ocrFile,fmOpenRead);
    strm := TMemoryStream.Create;
    try
      Fstrm.Position := 0;
      ZDecompressStream(FStrm,strm);
      Fstrm.Free;

      strm.Position := 0;
      strm.Read(dat,SizeOf(ROcrLibFile));
      if VersVerify = True then
      begin
        MycharInfo := dat.CharInfo;
        _Effect := dat.Effect;
        BmW := dat.W;
        BmH := dat.H;
        OcrName := dat.Ver.Name;
        _EffBLW := dat.EffectBLW;
        Result := True;
      end;
    finally
      strm.Free;
    end;
    if IsAutoSS = True then SSCode := 1
    else SSCode := 0;
  except
  end;
end;

uj5u.com熱心網友回復:


procedure To1Line(const Bmp: TBitmap; Y0,X0,Chw,CharL: Byte);
type
  xByteArray = array of Byte;
var
  X,Y: Integer;
  Ch: TBitmap;
  MinJL: xByteArray;
  function MinArr(const Data: xByteArray; const Count: Integer): Byte;
  var
    I: Integer;
  begin
    if Count = 0 then Exit;
    Result := Data[0];
    for I := 0 to Count - 1 do
    begin
      if Data[I] < Result then Result := Data[I];
    end;
  end;
  procedure GetMinJL(const nChar: Byte);
  var
    K,L,M: Byte;
    c: TColor;
    MinJLS: xByteArray;
  begin
    K := X0 + nChar * Chw;
    SetLength(MinJLS,Chw);
    for L := 0 to Chw - 1 do
    begin
      M := 0;
      c := Bmp.Canvas.Pixels[K+L,M+Y0];
      while (c <> clBlack) and (M <= Bmp.Height) do
      begin
        inc(M);
        c := Bmp.Canvas.Pixels[K+L,M+Y0];
      end;
      MinJLS[L] := M;
    end;
    MinJL[nChar] := MinArr(MinJLS,Chw);
    SetLength(MinJLS,0);
  end;
begin
  SetLength(MinJL,CharL);
  Ch := TBitmap.Create;
  for X := 0 to CharL - 1 do
  begin
    GetMinJL(X);
    Y := X0 + X * Chw;

    Ch.Width := Chw;
    Ch.Height := Bmp.Height - MinJL[X];
    Ch.Canvas.Brush.Color := clWhite;
    Ch.Canvas.Brush.Style := bsSolid;
    Ch.Canvas.Pen.Color := clWhite;
    Ch.Canvas.Pen.Style := psSolid;
    Ch.Canvas.Rectangle(0,0,Ch.Width,Ch.Height);
    Ch.Canvas.CopyRect(Rect(0,0,Ch.Width,Ch.Height),Bmp.Canvas,Rect(Y,MinJL[X],Y+Chw,Bmp.Height));

    Bmp.Canvas.Brush.Color := clWhite;
    Bmp.Canvas.Brush.Style := bsSolid;
    Bmp.Canvas.Pen.Color := clWhite;
    Bmp.Canvas.Pen.Style := psSolid;
    Bmp.Canvas.Rectangle(Y,MinJL[X],Y+Chw,Bmp.Height);
    Bmp.Canvas.CopyRect(Rect(Y,Y0,Y+Chw,Bmp.Height-MinJL[X]),Ch.Canvas,Rect(0,0,Ch.Width,Ch.Height));
  end;
  Ch.Free;
  SetLength(MinJL,0);
end;

function GetTail(str,sp : String): Integer;
var
  Temp : String;
begin
  Temp := Str;
  Delete(Temp,1,Pos(sp,str)+length(sp)-1);
  Result := StrToInt(Temp);
end;

procedure SlQuickSort(Sl : TStringList; iLo, iHi: Integer);
var
  Lo, Hi, Mid : Integer;
  T : String;
begin
  Lo := iLo;
  Hi := iHi;
  Mid := GetTail(Sl[(Lo + Hi) div 2],Sp);
  repeat
    while GetTail(Sl[Lo],Sp) < Mid do Inc(Lo);
    while GetTail(Sl[Hi],Sp) > Mid do Dec(Hi);
    if Lo <= Hi then
    begin
      T := sl[Lo];
      sl[Lo] := sl[Hi];
      sl[Hi] := T;
      Inc(Lo);
      Dec(Hi);
    end;
  until Lo > Hi;
  if Hi > iLo then SlQuickSort(Sl, iLo, Hi);
  if Lo < iHi then SlQuickSort(Sl, Lo, iHi);
end;

Function HexToInt(Hex :String):Int64;
Var Sum : Int64;
    I,L : Integer;
Begin
  L := Length(Hex);
  Sum := 0;
  For I := 1 to L Do
   Begin
   Sum := Sum * 16;
   If ( Ord(Hex[I]) >= Ord('0')) and (Ord(Hex[I]) <= Ord('9')) then
      Sum := Sum + Ord(Hex[I]) - Ord('0')
   else If ( Ord(Hex[I]) >= Ord('A') ) and (Ord(Hex[I]) <= Ord('F')) then
      Sum := Sum + Ord(Hex[I]) - Ord('A') + 10
   else If ( Ord(Hex[I]) >= Ord('a') ) and ( Ord(Hex[I]) <= Ord('f')) then
      Sum := Sum + Ord(Hex[I]) - Ord('a') + 10
   else
      Begin
      Sum := -1;
      break;
      End;
   End;
  Result := Sum;
End;

function GetHead(str,sp : String):string;
begin
  Result:=copy(str,1,pos(sp,str)-1);
end;

procedure WhiteBlackImgEx(const bmp: TBitmap);
type
  xByteArray = array of Byte;
var
  p: PByteArray;
  J,Y,W: Integer;
  arr: xByteArray;
  function AverageArr(const Data: xByteArray; const Count: Integer): Int64;
  var
    I: Integer;
  begin
    Result := 0;
    if Count = 0 then Exit;
    for I := 0 to Count - 1 do
    begin
      Result := Result + Data[I];
    end;
    Result := Round(Result/Count);
  end;
begin
  bmp.PixelFormat := pf24bit;
  SetLength(arr,bmp.Height*bmp.Width);
  for Y := 0 to bmp.Height - 1 do
  begin
    p := bmp.ScanLine[Y];
    J := 0;
    while J < bmp.Width*3 do
    begin
      arr[(Y*bmp.Width)+J div 3] := Round((p[J]+p[J+1]+p[J+2])/3);
      Inc(J,3);
    end;
  end;
  W := Byte(AverageArr(Arr,bmp.Height*bmp.Width));
  for Y := 0 to bmp.Height - 1 do
  begin
    p := bmp.ScanLine[Y];
    J := 0;
    while J < bmp.Width*3 do
    begin
      if Round((p[J]+p[J+1]+p[J+2])/3) >= W then
      begin
        p[J] := 0;
        p[J+1] := 0;
        p[J+2] := 0;
      end else
      begin
        p[J] := MaxByte;
        p[J+1] := MaxByte;
        p[J+2] := MaxByte;
      end;
      Inc(J,3);
    end;
  end;
  SetLength(Arr,0);
end;

procedure Ranse(const bmp: TBitmap; const Color: TColor);
var
  c: TColor;
  X,Y: Integer;
  r1,g1,b1: Byte;
  r2,g2,b2: Byte;
begin
  r1 := GetRValue(Color);
  g1 := GetGValue(Color);
  b1 := GetBValue(Color);
  for X := 0 to bmp.Width - 1 do
  begin
    for Y := 0 to bmp.Height - 1 do
    begin
      c := Bmp.Canvas.Pixels[X,Y];
      r2 := GetRValue(c);
      g2 := GetGValue(c);
      b2 := GetBValue(c);
     // if (c <> clWhite) and (c <> clBlack) then
     // begin
        r2 := Round(r1*Min(Abs(r2-MaxByte),MaxByte-r2)/MaxByte);
        g2 := Round(g1*Min(Abs(g2-MaxByte),MaxByte-g2)/MaxByte);
        b2 := Round(b1*Min(Abs(b2-MaxByte),MaxByte-b2)/MaxByte);
        c := RGB(r2,g2,b2);
        Bmp.Canvas.Pixels[X,Y] := c;
    //  end;
    end;
  end;
end;

procedure Grayscale(const bmp: TBitmap);
var
  p: PByteArray;
  J,Y,W: Integer;
begin
  bmp.PixelFormat := pf24bit;
  for Y := 0 to bmp.Height - 1 do
  begin
    p := bmp.ScanLine[Y];
    J := 0;
    while J < bmp.Width*3 do
    begin
      W := (P[J] * 28 + P[J+1] *151 + P[J+2] * 77);
      W := W shr 8;
      P[J] := Byte(W);
      P[J+1] := Byte(W);
      P[J+2] := Byte(W);
      Inc(J,3);
    end;
  end;
  //bmp.PixelFormat := pf1bit;
  //bmp.PixelFormat := pf24bit;
end;

function ALL2BMP(const Filename : String; const Bmp: TBitmap): Boolean;
var
  GIF: TGIFImage;
  jpg: TJPEGImage;
  PNG: TPNGobject;
  FileEx: String;
begin
  Result := False;
  try
    FileEx := UpperCase(ExtractFileExt(filename));
    if FileEx = '.PNG' then
    begin
      PNG := TPNGobject.Create;
      try
        PNG.LoadFromFile(filename);
        _PicFormat := 2;
        BMP.Assign(PNG);
      except
        //not png image
      end;
      PNG.Free;
    end else if FileEx = '.BMP' then
      try
        BMP.LoadFromFile(filename);
        _PicFormat := 1;
      except
        //not bmp image
      end
    else if FileEx = '.GIF' then
    begin
      GIF := TGIFImage.Create;
      try
        GIF.LoadFromFile(filename);
        _PicFormat := 3;
        BMP.Assign(GIF);
      except
        //not gif image
      end;
      GIF.Free;
    end else if (FileEx = '.JPG') or (FileEx = '.JPEG') then
    begin
      JPG := TJPEGImage.Create;
      try
        JPG.LoadFromFile(filename);
        _PicFormat := 4;
        BMP.Assign(JPG);
      except
        //not jpg image
      end;
      JPG.Free;
    end;
    //
    if _PicFormat = 0 then
      try
        BMP.LoadFromFile(FileName);
        _PicFormat := 1;
      except
      end;
    if _PicFormat = 0 then
    begin
      PNG := TPNGobject.Create;
      try
        PNG.LoadFromFile(FileName);
        _PicFormat := 2;
        BMP.Assign(PNG);
      finally
        PNG.Free;
      end;
    end;
    if _PicFormat = 0 then
    begin
      GIF := TGIFImage.Create;
      try
        GIF.LoadFromFile(FileName);
        _PicFormat := 3;
        BMP.Assign(GIF);
      finally
        GIF.Free;
      end;
    end;
    if _PicFormat = 0 then
    begin
      JPG := TJPEGImage.Create;
      try
        JPG.LoadFromFile(FileName);
        BMP.Assign(JPG);
        _PicFormat := 4;
      finally
        JPG.Free;
      end;
    end;
    Result := True;
  except
  end;
end;

uj5u.com熱心網友回復:


function PIC2BMP(filename : String): TBITMAP;
var
  GIF: TGIFImage;
  jpg: TJPEGImage;
  BMP: TBITMAP;
  PNG: TPNGobject;
  FileEx: String;
  i, j, x: Byte;
  b : boolean;
  //
  SrcRGB : pByteArray;
  ClPixel : TColor;
begin
  b := False;
  ClPixel := 0;
  FileEx := UpperCase(ExtractFileExt(filename));
  BMP := TBITMAP.Create;
  if FileEx = '.PNG' then
  begin
    PNG := TPNGobject.Create;
    try
      PNG.LoadFromFile(filename);
      _PicFormat := 2;
      BMP.Assign(PNG);
    except
      //not png image
    end;
    PNG.Free;
  end else if FileEx = '.BMP' then
    try
      BMP.LoadFromFile(filename);
      _PicFormat := 1;
    except
      //not bmp image
    end
  else if FileEx = '.GIF' then
  begin
    GIF := TGIFImage.Create;
    try
      GIF.LoadFromFile(filename);
      _PicFormat := 3;
      BMP.Assign(GIF);
    except
      //not gif image
    end;
    GIF.Free;
  end else if (FileEx = '.JPG') or (FileEx = '.JPEG') then
  begin
    JPG := TJPEGImage.Create;
    try
      JPG.LoadFromFile(filename);
      _PicFormat := 4;
      JPG.Grayscale := TRUE;
      BMP.Assign(JPG);
    except
      //not jpg image
    end;
    JPG.Free;
  end;
  //
  if _PicFormat = 0 then
    try
      BMP.LoadFromFile(FileName);
      _PicFormat := 1;
    except
    end;
  if _PicFormat = 0 then
  begin
    PNG := TPNGobject.Create;
    try
      PNG.LoadFromFile(FileName);
      _PicFormat := 2;
      BMP.Assign(PNG);
    finally
      PNG.Free;
    end;
  end;
  if _PicFormat = 0 then
  begin
    GIF := TGIFImage.Create;
    try
      GIF.LoadFromFile(FileName);
      _PicFormat := 3;
      BMP.Assign(GIF);
    finally
      GIF.Free;
    end;
  end;
  if _PicFormat = 0 then
  begin
    JPG := TJPEGImage.Create;
    try
      JPG.LoadFromFile(FileName);
      JPG.Grayscale := TRUE;
      BMP.Assign(JPG);
      _PicFormat := 4;
    finally
      JPG.Free;
    end;
  end;

  _PicWidth := BMP.Width;
  _PicHeight := BMP.Height;
  //BMP.SaveToFile(_PicFile+'.BMP');

  //Fetch(_BbsType,_PicWidth,_PicHeight,_PicFormat,_CodeUrl);
  if _EffBLW then
  begin
    Grayscale(bmp);
    Ranse(bmp,clRed);
    WhiteBlackImgEx(bmp);
  end else
  begin
    Bmp.PixelFormat := pf24Bit;

  // make picture only black and white
    for j := 0 to BMP.Height - 1 do
    begin
      SrcRGB := BMP.ScanLine[j];
      for i := 0 to BMP.Width - 1 do
      begin
        if MycharInfo._ClrRect then
        begin
          x := MycharInfo._RectLen;
          if (i<x)or(j<x)or(i>BMP.Width-1-x)or(j>BMP.Height-1-x) then
          begin
            SrcRGB[i*3]   := $ff;
            SrcRGB[i*3+1] := $ff;
            SrcRGB[i*3+2] := $ff;
            continue;
          end;
        end;
        ClPixel := HexToInt(IntToHex(SrcRGB[i*3],2)+
                              IntToHex(SrcRGB[i*3+1],2)+
                              IntToHex(SrcRGB[i*3+2],2));
        if MycharInfo.CusDiv then
        begin
          case MycharInfo.DivCmp of
          0:  b := ClPixel > MycharInfo.DivColr;
          1:  b := ClPixel = MycharInfo.DivColr;
          2:  b := ClPixel < MycharInfo.DivColr;
          4:  b := ClPixel <> MycharInfo.DivColr;
          end;
        end else
          b := ClPixel > MycharInfo.DivColr;
        if b then begin
          SrcRGB[i*3]   := $ff;
          SrcRGB[i*3+1] := $ff;
          SrcRGB[i*3+2] := $ff;
        end else begin
          SrcRGB[i*3]   := 0;
          SrcRGB[i*3+1] := 0;
          SrcRGB[i*3+2] := 0;
        end;
      end;
    end;
  end;
  {BMP.Canvas.lock;
  for i := 0 to BMP.Width - 1 do
    for j := 0 to BMP.Height - 1 do
    begin
      if _ClrRect then
      begin
        x := _RectLen;
        if (i<x)or(j<x)or(i>BMP.Width-1-x)or(j>BMP.Height-1-x) then
        begin
          BMP.Canvas.Pixels[i, j] := clwhite;
          continue;
        end;
      end;
      if _CusDiv then
      begin
        case _DivCmp of
        0:  b := BMP.Canvas.Pixels[i, j] > _DivColr;
        1:  b := BMP.Canvas.Pixels[i, j] = _DivColr;
        2:  b := BMP.Canvas.Pixels[i, j] < _DivColr;
        end;
      end else
        b := BMP.Canvas.Pixels[i, j] > _DivColr;
      if b then
        BMP.Canvas.Pixels[i, j] := clwhite
      else
        BMP.Canvas.Pixels[i, j] := clblack;
    end;
  BMP.Canvas.Unlock;  }
  result := BMP;
end;

function CMPBMP(SBMP: TBITMAP; x0, m: integer): integer;
var
  i, j: integer;
  //
  SrcRGB : pByteArray;
begin
  result := 0;
  for j := 0 to MycharInfo.charheight -1 do
  begin
    SrcRGB := SBMP.ScanLine[j];
    for i := 0 to MycharInfo.charwidth -1 do
    begin
      if MycharInfo._CmpChr and (SrcRGB[(x0+i)*3] = 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 0) then
        Inc(Result);
      if MycharInfo._CmpBg and (SrcRGB[(x0+i)*3] > 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 1) then
        Inc(Result);
    end;
  end;

  {
  result := 0;
  SBMP.Canvas.Lock;
  for i := 0 to MycharInfo.charwidth - 1 do
    for j := 0 to MycharInfo.charHeight - 1 do
    begin
      if _CmpChr and (SBMP.Canvas.Pixels[x0 + i, j] = 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 0) then
        Inc(Result);
      if _CmpBg and (SBMP.Canvas.Pixels[x0 + i, j] > 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 1) then
        Inc(Result);
    end;
  SBMP.Canvas.Unlock;  }
end;


function CMPBMPPRO(SBMP: TBITMAP; x0, m: integer): integer;
var
  i, j : integer;
  xj : byte;
  Ret : Integer;
  //
  SrcRGB : pByteArray;
begin
  result := 99999;
  for xj := 0 to _BITMAP.Height - MycharInfo.charheight do
  begin
    Ret := 0;
    for j := 0 to MycharInfo.charHeight - 1 do
    begin
      SrcRGB := SBMP.ScanLine[j+xj];
      for i := 0 to MycharInfo.charwidth - 1 do
      begin
        if MycharInfo._CmpChr and (SrcRGB[(x0+i)*3] = 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 0) then
          Inc(Ret);
        if MycharInfo._CmpBg  and (SrcRGB[(x0+i)*3] > 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 1) then
          Inc(Ret);
      end;
    end;
    if result > Ret then
    result := Ret;
  end;

  {result := 99999;
  SBMP.Canvas.Lock;
  for xj := 0 to _BITMAP.Height - MycharInfo.charheight do
  begin
    Ret := 0;
    for i := 0 to MycharInfo.charwidth - 1 do
      for j := 0 to MycharInfo.charHeight - 1 do
      begin
        if _CmpChr and (SBMP.Canvas.Pixels[x0 + i, j+xj] = 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 0) then
          Inc(Ret);
        if _CmpBg  and (SBMP.Canvas.Pixels[x0 + i, j+xj] > 0) and (MycharInfo.allcharinfo[m].MyCharInfo[i, j] = 1) then
          Inc(Ret);
      end;
    if result > Ret then
    result := Ret;
  end;
  SBMP.Canvas.Unlock;   }
end;

function GetStringFromImage(SBMP: TBITMAP): String;
//const
//  SpeicalChars: array [0..6] of String = ('+','-','*','/','(',')','=');
var
  k, m, x: integer;
  alike : Integer;
  S : String;
  Sort : boolean;
  SlAlike : TStringList;
begin
  //DebugStr('SBMP_W_H',IntToStr(SBMP.Width)+'*'+IntToStr(SBMP.Height),'e:');
  result := '';
  if _Effect.To1Line = True then
  begin
    try
      To1Line(SBMP,_Effect.Y0,MycharInfo.X0,MycharInfo.charwidth,Mycharinfo.TotalChars);
    except
    end;
  end;
  SlAlike := TStringList.Create;
  for k := 0 to MycharInfo.TotalChars - 1 do
  begin
    x := MycharInfo.X0 + MyCharInfo.charwidth * k;
    //DebugLog('k:'+IntToStr(k)+'  '+'x:'+IntToStr(x));
    SlAlike.Clear;
    Sort := True;
    for m := 0 to 42 do
    begin
      if Mycharinfo.allcharinfo[m].used = True then
      begin
        {if m>35 then
          S := SpeicalChars[m-36]
        else if m>9 then
          S := Chr(m+87)
        else
          S := IntToStr(m); }
        S := Mycharinfo.allcharinfo[m].MyChar;
        if SBMP.Height = MycharInfo.charheight then
          Alike := CMPBMP(SBMP, x, m)
        else
          Alike := CMPBMPPRO(SBMP, x, m);
      //DebugLog('m:'+s+'  '+'Alike:'+IntToStr(Alike));
        if Alike = 0 then
        begin
          Result := Result + S;
          //DebugLog('get_it:'+s);
          //DebugStr('GET_IT','GET '+S+ ' AS '+IntToStr(k+1)+ 'TH NUM','e:');

          Sort := False;
          break;
        end else
          SlAlike.Add(S + Sp + IntToStr(Alike));
      end;
    end;
    if Sort then
    begin
      SlQuickSort(SlAlike,0,SlAlike.Count-1);
      result := result + GetHead(SlAlike[0],Sp);
      //DebugLog('get_it_by_sort:'+GetHead(SlAlike[0],Sp));
      //DebugStr('GET_IT_SORT','GET '+GetHead(SlAlike[0],Sp)+ ' AS '+IntToStr(k)+ 'TH NUM','e:');

      //SlAlike.SaveToFile('f:\'+IntToStr(k)+'.txt');
    end;
  end;
  SlAlike.Free;
end;

function RecogOCR(var Success: Boolean; const ImageFile: String): String;
begin
  Success := False;
  try
    _BITMAP := nil;
    LastRecogTime := GetTickCount;
    _BITMAP := PIC2BMP(ImageFile);
    Result := GetStringFromImage(_BITMAP);
    LastRecogTime := GetTickCount-LastRecogTime;
    SaveBmp;
    _BITMAP.Free;
    Success := True;
    if SSCode = 1 then Result := SSUtils.RecogSuanshi(Result);
  except
    LastRecogTime := 0;
  end;
end;
end.

uj5u.com熱心網友回復:

ssutils單元:

unit SSUtils;

interface

uses Windows, SysUtils, CalcExpress;

function RecogSuanshi(const S: String): String;

implementation

function DeleteFh(const S: String; const Fh: Char): String;
var
  I: Integer;
begin
  Result := '';
  for I := 1 to Length(S) do
  begin
    if S[I] <> Fh then
    begin
      Result := Result + S[I];
    end;
  end;
end;

function RecogSuanshi(const S: String): String;
const
  argv: array [0..1] of Extended = (0,1);
var
  S2: String;
  cexp: TCalcExpress;
begin
  Result := '計算錯誤!';
  try
    cexp := TCalcExpress.Create(nil);
    try
      S2 := DeleteFh(S,'?');
      S2 := DeleteFh(S,'=');
      S2 := StringReplace(S2,'加','+',[rfReplaceAll]);
      S2 := StringReplace(S2,'減','-',[rfReplaceAll]);
      S2 := StringReplace(S2,'乘','*',[rfReplaceAll]);
      S2 := StringReplace(S2,'除','/',[rfReplaceAll]);
      S2 := StringReplace(S2,'×','*',[rfReplaceAll]);
      S2 := StringReplace(S2,'÷','/',[rfReplaceAll]);
      S2 := StringReplace(S2,'+','+',[rfReplaceAll]);
      S2 := StringReplace(S2,'-','-',[rfReplaceAll]);

      cexp.Formula := S2;
      Result := IntToStr(Round(cexp.calc(argv)));
    except
    end;
  finally
    cexp.Free;
  end;
end;

end.

uj5u.com熱心網友回復:

不錯,寫個demo更好

uj5u.com熱心網友回復:

謝謝分享

uj5u.com熱心網友回復:

謝謝分享!摘錄下來,慢慢理解、學習。

參考 5 樓  的回復:
學習一下先

呵呵,藍鳥哥這么謙虛啊。

uj5u.com熱心網友回復:

樓主分享代碼,能讓后來者得到啟迪,若能提供下參考的單元在何處下載,那就更完美了。
不是懶得去搜索,主要是:如參考了同名而不同內容的單元時,就無法正確地使用和領略你以上的代碼的編程風采了。

就如下列單元,是否即是你所參考的單元?還是僅同名而已?
AsphyreZlib.pas,我搜到的:http://www.bvbcode.com/code/28lypjot-1662551-down
Math.pas,我搜到的:http://bbs.cnpack.org/viewthread.php?tid=1858
PNGImage.pas,我搜到的:http://www.koders.com/delphi/fidF09E1376A88CB583BB67F5329E88B1BA3B570D79.aspx
(我認為PNGImage.pas原始碼有兩個地方有誤——見http://topic.csdn.net/u/20120602/19/2ef4450a-ac20-4ccb-823a-b721a431d151.html 一文,如沒理解,可索取我修改后的代碼)
GIFImage.pas,我搜到的:http://download.csdn.net/detail/doorsky123/3003816
CalcExpress.pas,我搜到的:http://read.pudn.com/downloads152/sourcecode/math/665167/Source.Net/CalcExpress.pas__.htm

再次謝謝樓主!

uj5u.com熱心網友回復:

再有,OCR單元中所加載的幾個庫,是從哪里可以得到呢?或它的結構是如何的?如果這些沒弄清的話,同樣也是無法學習和理解樓主的代碼,樓主貼出代碼,絕對不是為了招搖,所以,還望指點迷津。再三謝謝!!

uj5u.com熱心網友回復:

AsphyreZlib.pas就是zlibex.pas(zlibex組件包里),重命名一下就可以了
CalcExpress.pas是CalcExpress組件包
PNGImage是PNGImage組件包
GIFImage.pas是GIFImage組件包
Math.pas, delphi7自帶的檔案啊,如果沒有,重裝delphi!

uj5u.com熱心網友回復:

想要特征碼dll,加我qq: 2484365584

uj5u.com熱心網友回復:

http://www.pudn.com/downloads457/sourcecode/graph/texture_mapping/115157719OcrCtrl.rar

uj5u.com熱心網友回復:

非常感謝的說

uj5u.com熱心網友回復:

下載了不會用啊

uj5u.com熱心網友回復:

咦!奇哉怪哉!!5樓被人拆了。

uj5u.com熱心網友回復:

分享代碼太好了,多謝

uj5u.com熱心網友回復:

uj5u.com熱心網友回復:

學習。。。。

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/118732.html

標籤:GAME 圖形處理/多媒體

上一篇:通過十六進制的變換要得到十進制的數?急!

下一篇:二維碼運行出錯呀

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more