如何加速算法(二值化,积分图像)

时间:2010-02-17 12:38:20

标签: delphi

我根据此网址描述的积分图像算法编写了此程序

http://people.scs.carleton.ca/~roth/iit-publications-iti/docs/gerh-50002.pdf

有没有办法更快地完成此代码?

指针比动态数组快得多?

procedure TForm1.bBinarizationClick(Sender: TObject);
var
  iX1, iY1,
  iX2, iY2,
  ii, jj,
  s, s2,
  iSum, iCount,  index,
  iHeight, iWidth : Integer;
  iSize: Integer;

  row : ^TRGBTriple;
  black : TRGBTriple;
  aIntegralIm: array  of Integer;
  aGrays : array of Byte;

  startTime : Cardinal;

begin
  iWidth := bBitmap.Width;
  iHeight := bBitmap.Height;
  iSize := iWidth * iHeight;

  SetLength(aGrays, iSize);
  SetLength(aIntegralIm, iSize);

  black.rgbtRed  := (clBlack and $0000FF);
  black.rgbtGreen := (clBlack and $00FF00) shr 8;
  black.rgbtBlue := (clBlack and $FF0000) shr 16;

  bBitmap2.Canvas.Brush.Color := clWhite;
  bBitmap2.Canvas.FillRect(Rect(0, 0, bBitmap2.Width, bBitmap2.Height));

  s := Round(iWidth / TrackBar2.Position);
    s2 := Round(s / 2);

  startTime := GetTickCount();

  index := 0;

  for ii := 0 to iHeight - 1 do begin
     row := bBitmap.ScanLine[ii];
     for jj := 0 to iWidth - 1 do begin
       aGrays[index] := ((row.rgbtRed * 77 + row.rgbtGreen * 150 + row.rgbtBlue * 29) shr 8);
       inc(index);
       inc(row);
     end;
  end;


  for ii := 0 to iWidth - 1 do begin
     iSum := 0;
     for jj := 0 to iHeight - 1 do begin
       index := jj*iWidth+ii;
       iSum := iSum + aGrays[index];
       if ii = 0 then aIntegralIm[index] := iSum
       else aIntegralIm[index] := aIntegralIm[index - 1] + iSum;
     end;
  end;


  for jj := 0 to iHeight - 1 do begin
     row := bBitmap2.ScanLine[jj];
     for ii := 0 to iWidth - 1 do begin

       index := jj*iWidth+ii;

       iX1 := ii-s2;
       iX2 := ii+s2;
       iY1 := jj-s2;
       iY2 := jj+s2;

       if (iX1 < 0) then iX1 := 0;
         if (iX2 >= iWidth) then  iX2 := iWidth-1;
           if (iY1 < 0) then  iY1 := 0;
             if (iY2 >= iHeight) then  iY2 := iHeight-1;

       iCount := (iX2 - iX1) * (iY2 - iY1);

       iSum := aIntegralIm[iY2*iWidth+iX2]
              - aIntegralIm[iY1*iWidth+iX2]
              - aIntegralIm[iY2*iWidth+iX1]
              + aIntegralIm[iY1*iWidth+iX1];

       if (aGrays[index] * iCount) < (iSum * (100 - TrackBar1.Position) / 100) then  row^ :=  black;

       inc(row);

     end;
  end;

  ePath.Text :=  'Time: ' + inttostr(GetTickCount() - startTime) + ' ms';

  imgOryginal.Picture.Bitmap.Assign(bBitmap2);

end;

4 个答案:

答案 0 :(得分:2)

你至少可以做一些简单的事情:

  • 预先计算(100 - TrackBar1.Position)到变量
  • 而不是划分:/ 100使用* 100在另一边。您可能不需要任何浮点值。
  • 使用查找表进行以下操作(注意解释btw的标识?):

代码:

if (iX1 < 0) then iX1 := 0;
if (iX2 >= iWidth) then  iX2 := iWidth-1;
if (iY1 < 0) then  iY1 := 0;
if (iY2 >= iHeight) then  iY2 := iHeight-1;
  • 尝试保持索引和icremnet,减少而不是乘法:index:= jj * iWidth + ii;

答案 1 :(得分:2)

我的猜测是第二个循环是慢位。

诀窍是避免一直重新计算第二个循环中的所有内容

如果S是常数(相对于我的意思是环,不是绝对的)

  • iy​​1,iy2只与主(jj)循环一起变化,因此iy1 *宽度(和iy2 *宽度)也是如此。 预先计算它们,或者以与行相同的方式优化它们。 (每行预先计算一次,增量间隔)
  • 将ii循环更改为三个循环:
    • 第一位,其中ix1 = 0
    • 第二个,其中ix1 = ii-s ix2 = ii + s;
    • 第三个,其中ix1 = ii-s,ix2 = iwidth-1

这会从循环中删除大量检查,只需执行一次。

  • 为条件if(aGrays [index] * iCount)&lt; (iSum *(100 - TrackBar1.Position)/ 100)然后行^:=黑色;所以不对每个像素进行评估,因为你可以预先计算出发生这种情况的区域吗?

  • 将指针引入灰色计算循环,这样您就不必重新计算每个像素的索引(但仅限于行循环,每个像素递增一个ptr)

如果你很耐心,你也可以预先计算线之间的跳跃。请记住,abs(scanline [j] -scanline [i]) - width是每行对齐字节数的度量标准。

更高级的是在算法级别上优化缓存效果。看到 rotating bitmaps. In code 了解这是如何工作的。这里也演示了一些指针技巧(但仅适用于8位元素)

答案 2 :(得分:1)

我首先使用分析器来查找CPU使用率重新分区,以找出最有利于优化的代码的最小部分。 然后我会根据结果调整工作量。如果某些代码代表90%的CPU负载并执行数十亿次,即使是极端措施(使用内联汇编语言重新编码一些序列)也可能有意义。

答案 3 :(得分:0)

使用优秀且免费的SamplingProfiler来找出代码中的瓶颈。然后再次优化并运行探查器以找到下一个瓶颈。这种方法比猜测需要优化什么要好得多,因为即使是专家也常常对此不以为然。