• delphi验证码识别学习之图像的灰度化、二值化及反色


    图像的灰度化、二值化和反色是些较简单的图像像素处理过程,我在《GDI+ 在Delphi程序的应用 -- ColorMatrix与图像灰度化》和《GDI+ 在Delphi程序的应用 -- 图像二值化》二篇文章中讲了如何利用GDI+的ColorMatrix实现图像灰度化和二值化,但是那种处理只适合GDI+的图像类,本文的方法同时适用于GDI+图像和Delphi的TGraphic图像。

    过程定义:
    
      // 图像求反
      procedure ImageInvert(var Data: TImageData);
      // 灰度化图像
      procedure ImageGray(var Data: TImageData);
      // 灰度统计。GrayData为灰度统计结构;缺省统计彩色图灰度,
      procedure ImageGrayStat(const Data: TImageData; var GrayData: TGrayStatData;
        IsGrayImage: Boolean = False);
      // 二值化图像。Threshold阀值,需要32位灰度图数据
      procedure ImageTwoValues(var Data: TImageData; Threshold: LongWord = 127);
      // 动态阀值(子图灰度平均值)二值化图像,SubSize子图大小,需要32位灰度图数据
      procedure ImageDynamTwoValues(var Data: TImageData; SubSize: Integer);
    
    实现代码:
    
    procedure ImageInvert(var Data: TImageData);
    asm
        push    edi
        push    ebx
        call    IsValid32
        jc      @@Exit
        call    SetDataRegs32
        mov     eax, 0ffffffh
    @@yLoop:
        push    ecx
    @@xLoop:
        xor     [edi], eax
        add     edi, 4
        loop    @@xLoop
        pop     ecx
        add     edi, ebx
        dec     edx
        jnz     @@yLoop
    @@Exit:
        pop     ebx
        pop     edi
    end;
    
    procedure ImageGray(var Data: TImageData);
    asm
        push    ebp
        push    esi
        push    edi
        push    ebx
        call    IsValid32
        jc      @@Exit
        call    SetDataRegs32
    @@yLoop:
        push    ecx
    @@xLoop:
        movzx   eax, [edi].TARGBQuad.Blue
        movzx   esi, [edi].TARGBQuad.Green
        movzx   ebp, [edi].TARGBQuad.Red
        imul    eax, 117        // blue   0.114 * 1024
        imul    esi, 601        // green  0.587 * 1024
        imul    ebp, 306        // red    0.299 * 1024
        add     eax, esi
        add     eax, ebp
        add     eax, 512        // Rounding
        shr     eax, 10         // eax = Round((R * 306 + G * 601 + B * 117) / 1024)
        mov     [edi].TARGBQuad.Red, al
        mov     [edi].TARGBQuad.Green, al
        mov     [edi].TARGBQuad.Blue, al
        add     edi, 4
        loop    @@xLoop
        pop     ecx
        add     edi, ebx
        dec     edx
        jnz     @@yLoop
    @@Exit:
        pop     ebx
        pop     edi
        pop     esi
        pop     ebp
    end;
    
    procedure GrayStat(const Data: TImageData; var GrayData: TGrayStatData; IsGrayImage: Boolean);
    asm
        push    ebp
        push    esi
        push    edi
        push    ebx
        mov     edi, edx        // edi = GrayData
        mov     edx, ecx
        mov     esi, [eax].TImageData.Scan0
        mov     ecx, [eax].TImageData.Width
        mov     ebx, [eax].TImageData.Height
        mov     ebp, [eax].TImageData.Stride
        mov     eax, ecx
        shl     eax, 2
        sub     ebp, eax            // ebp = offset
        mov     eax, ecx
        imul    eax, ebx
        push    eax                 // Total pixel count
        push    ecx                 // init Grays
        push    edi
        mov     ecx, 256
        xor     eax, eax
        rep     stosd
        pop     edi
        pop     ecx
        cmp     edx, TRUE
        je      @@yGrayLoop
        // 建立彩色图的灰度数组
    @@yLoop:
        push    ecx
    @@xLoop:
        movzx   eax, [esi].TARGBQuad.Blue
        movzx   edx, [esi].TARGBQuad.Green
        imul    eax, 117            // blue   0.114 * 1024
        imul    edx, 601            // green  0.587 * 1024
        add     edx, eax
        movzx   eax, [esi].TARGBQuad.Red
        imul    eax, 306            // red    0.299 * 1024
        add     eax, edx
        add     eax, 512            // Rounding
        shr     eax, 10             // eax = Round((R * 306 + G * 601 + B * 117) / 1024)
        inc     dword ptr [edi].TGrayStatData.Grays[eax*4]// grayData.Grays[eax] ++
        add     esi, 4              // esi += 4
        loop    @@xLoop
        pop     ecx
        add     esi, ebp
        dec     ebx
        jnz     @@yLoop
        jmp     @@SumStart
        // 建立灰度图的灰度数组
    @@yGrayLoop:
        push    ecx
    @@xGrayLoop:
        movzx   eax, [esi].TARGBQuad.Blue// eax = Gray = *esi
        inc     dword ptr [edi].TGrayStatData.Grays[eax*4]// grayData.Grays[eax] ++
        add     esi, 4              // esi +=4
        loop    @@xGrayLoop
        pop     ecx
        add     esi, ebp
        dec     ebx
        jnz     @@yGrayLoop
        // 计算总的灰度值、最大灰度值及最小灰度值
    @@SumStart:
        push    edi
        xor     eax, eax            // edx:eax = 0 (GrayData.Total)
        xor     edx, edx
        mov     esi, edi            // esi = ebx = &GrayData.Grays[0]
        mov     ebx, edi
        xor     ecx, ecx            // for (index = 0; index < 256; index ++)
    @@SumLoop:                      // {
        mov     ebp, [edi]
        cmp     [esi], ebp
        jae     @@1
        mov     esi, edi            //   if (*esi < *edi) esi = edi
    @@1:
        cmp     [ebx], ebp
        jbe     @@2
        mov     ebx, edi            //   if (*ebx > *edi) ebx = edi
    @@2:
        imul    ebp, ecx            //   ebp = *edi * index
        add     eax, ebp            //   edx:eax += ebp
        adc     edx, 0
        add     edi, 4              //   edi += 4
        inc     ecx
        cmp     ecx, 255
        jle     @@SumLoop           // }
        pop     edi
        sub     ebx, edi
        shr     ebx, 2              // ebx = (ebx - &GrayData.Grays[0]) / 4
        mov     [edi].TGrayStatData.MinGray, ebx// GrayData.MinGray = ebx
        sub     esi, edi
        shr     esi, 2              // esi = (esi - &GrayData.Grays[0]) / 4
        mov     [edi].TGrayStatData.MaxGray, esi// GrayData.MaxGray = esi
        pop     ebx
        mov     [edi].TGrayStatData.Count, ebx // GrayData.Count = data.Width * data.Height
        mov     dword ptr [edi].TGrayStatData.Total, eax // GrayData.Total = edx:eax
        mov     dword ptr [edi].TGrayStatData.Total+4, edx
        mov     ecx, ebx
        shr     ecx, 1
        add     eax, ecx
        adc     edx, 0
        idiv    ebx
        // GrayData.Average = (GrayData.Total + GrayData.Count / 2) / GrayData.Count)
        mov     [edi].TGrayStatData.Average, eax
    @@Exit:
        pop     ebx
        pop     edi
        pop     esi
        pop     ebp
    end;
    
    procedure ImageGrayStat(const Data: TImageData;
      var GrayData: TGrayStatData; IsGrayImage: Boolean);
    begin
      if not ImageEmpty(Data) then
        GrayStat(Data, GrayData, IsGrayImage);
    end;
    
    procedure TwoValues(var Data: TImageData; Threshold: LongWord);
    asm
        push    ebp
        push    esi
        push    edi
        push    ebx
        push    edx
        call    SetDataRegs32
        pop     eax
        mov     esi, 000ffffffh
        mov     ebp, esi
        not     ebp
    @@yLoop:
        push    ecx
    @@xLoop:
        cmp     [edi], al
        jb      @@1
        or      [edi], esi
        jmp     @@2
    @@1:
        and     [edi], ebp
    @@2:
        add     edi, 4
        loop    @@xLoop
        pop     ecx
        add     edi, ebx
        dec     edx
        jnz     @@yLoop
    @@Exit:
        pop     ebx
        pop     edi
        pop     esi
        pop     ebp
    end;
    
    procedure ImageTwoValues(var Data: TImageData; Threshold: LongWord);
    begin
      if not ImageEmpty(Data) then
        TwoValues(Data, Threshold);
    end;
    
    procedure ImageDynamTwoValues(var Data: TImageData; SubSize: Integer);
    var
      Sub: TImageData;
      GrayData: TGrayStatData;
      x, y: Integer;
    begin
      if ImageEmpty(Data) then Exit;
      if SubSize <= 0 then
      begin
        GrayStat(Data, GrayData, True);
        TwoValues(Data, GrayData.Average);
        Exit;
      end;
      y := 0;
      while y < Data.Height do
      begin
        x := 0;
        while x < Data.Width do
        begin
          Sub := GetSubData(Data, x, y, SubSize, SubSize);
          GrayStat(Sub, GrayData, True);
          TwoValues(Sub, GrayData.Average);
          Inc(x, SubSize);
        end;
        Inc(y, SubSize);
      end;
    end;

        灰度化过程还是依照大多数图像灰度处理惯例,计算YUV颜色空间的Y分量作为灰度图,公式为:

    Y = 0.299 * R + 0.587 * G + 0.114 * B

        本文灰度过程使用了定点数处理,将上面公式中的常数乘上1024,加快了处理过程,伪代码为:

           Y = (306 * R + 601 * G + 117 * B + 512) >> 10

        代码中的+512是做四舍五入,右移10位等于除以1024。

        图像灰度统计过程和图像灰度化过程采用了相同的原理和计算过程,只不过没有改变图像,而是以计算结果作为256色灰度阶数组的下标,增加该灰度阶的个数而已。所有图像灰度统计指标都存放在TGrayStatData类型的结构中。见《Delphi图像处理 -- 数据类型及内部过程》。

        图像二值化过程是在图像灰度处理基础上进行的,由于R、G、B三个分量相等,所以只要把任何其中一个与阀值比较即可:大于阀值为255,否则为0。因灰度图像素格式是32位的,所以过程中直接以0x00FFFFFF或RGB三个分量为255,以0xFF000000与RGB三个分量为0,图像二值化的黑白效果取决于阀值的大小。

        因有些图像的灰度分布不太均匀,为了加强图像的二值特征,本文尝试写了一个图像动态分组二值化过程ImageDynamTwoValues,即将图像分组为一定大小的子图,对各子图分别进行灰度统计后,以该子图的灰度平均值为阀值进行子图的二值化,不过在测试过程中,发现如果子图尺寸确定的不合适,各子图之间有很明显的区别,这对图像的二值分析显然是不利的。

        至于图像的反色处理更简单,直接用0xFFFFFF和RGB异或就成。

    图像动态分组二值化例子:

    var
      jpg: TJPEGImage;
      Data: TImageData;
      GrayData: TGrayStatData;
    begin
      jpg := TJPEGImage.Create;
      jpg.LoadFromFile('D:\VclLib\GdiplusDemo\Media\20041001.jpg');
      Canvas.Draw(0, 0, jpg);
      Data := GetImageData(jpg);
      ImageGray(Data);
    //  ImageTwoValues(Data);
      ImageDynamTwoValues(Data, 128);
      ImageDataAssignTo(Data, jpg);
      Canvas.Draw(0, 0, jpg);
      FreeImageData(Data);
      jpg.Free;
    end;
  • 相关阅读:
    线程阶段性总结——APM,ThreadPool,Task,TaskScheduler ,CancellationTokenSource
    研究BackgroundWorker后发现:AsyncOperation和SynchronizationContext的差异真的很大!
    线程同步——优势、劣势
    APM异步编程模型的优势
    DataGridView的VirtualMode,在大容量数据加载时特别有用
    【C】——C语言的位运算的优势
    【linux】——Linux tar打包命令
    【C】用文件和链表实现学生信息管理
    【C】——回调函数的好处
    【C】strcpy()需谨慎使用;
  • 原文地址:https://www.cnblogs.com/hsapphire/p/1929661.html
Copyright © 2020-2023  润新知