实现Lucas-Kanade光流计算的Delphi类

2016-02-19 20:47 26 1 收藏

下面,图老师小编带您去了解一下实现Lucas-Kanade光流计算的Delphi类,生活就是不断的发现新事物,get新技能~

【 tulaoshi.com - 编程语言 】

  {
  作者:刘留
  参考文献为:
  Jean-Yves Bouguet "Pyramidal Implementation of the Lucas Kanade Feature Tracker Description of the algorithm"
  http://www.aivisoft.net/
  Geo.Cra[at]gmail[dot]com
  }
  unit OpticalFlowLK;

  interface

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  uses
    Math, Windows, SysUtils, Variants, Classes, Graphics, unitypes, ColorConv;

  type

    TOpticalFlowLK = class
    private
      ImageOld, ImageNew: TTripleLongintArray;
      ImageGray, dx, dy, dxy: TDoubleLongintArray;
      Eigenvalues: TDoubleExtendedArray;
      WBPoint: TDoubleBooleanArray;
      Height, Width, L, RadioX, RadioY: longint;
      procedure CornerDetect(sWidth, sHeight: longint; Quality: extended);
      procedure MakePyramid(var Images: TTripleLongintArray; sWidth, sHeight, sL: longint);
    public
      Frame: TBitmap;
      Features: TSinglePointInfoArray;
      FeatureCount, SupportCount: longint;
      Speed, Radio: extended;
      procedure Init(sWidth, sHeight, sL: longint);
      procedure InitFeatures(Frames: TBitmap);
      procedure CalOpticalFlowLK(Frames: TBitmap);
      destructor Destroy; override;
    end;

  implementation

  procedure TOpticalFlowLK.CornerDetect(sWidth, sHeight: longint; Quality: extended);
  var
    i, j, fi, fj: longint;
    a, b, c, sum, MinAccept, MaxEigenvalue: extended;
  begin
    FeatureCount := 0;
    {
    下面采用Good Feature To Track介绍的方法
    J. Shi and C. Tomasi "Good Features to Track", CVPR 94
    }
    for i := 1 to sWidth - 2 do
      for j := 1 to sHeight - 2 do begin
        dx[i, j] := ImageGray[i - 1, j - 1] + 2 * ImageGray[i - 1, j] + ImageGray[i - 1, j + 1]
          - (ImageGray[i + 1, j - 1] + 2 * ImageGray[i + 1, j] + ImageGray[i + 1, j + 1]);
        dy[i, j] := ImageGray[i - 1, j + 1] + 2 * ImageGray[i, j + 1] + ImageGray[i + 1, j + 1]
          - (ImageGray[i - 1, j - 1] + 2 * ImageGray[i, j - 1] + ImageGray[i + 1, j - 1]);
        dxy[i, j] := ImageGray[i + 1, j - 1] + ImageGray[i - 1, j + 1]
          - (ImageGray[i - 1, j - 1] + ImageGray[i + 1, j + 1]);
      end;
    {求取Sobel算子的Dx, Dy, Dxy
    Dx:
    |1 0 -1|
    |2 0 -2|
    |1 0 -1|
    Dy:
    |-1 -2 -1|
    | 0  0  0|
    | 1  2  1|
    Dxy
    |-1  0  1|
    | 0  0  0|
    | 1  0 -1|}
    MaxEigenvalue := 0;
    for i := 2 to sWidth - 3 do
      for j := 2 to sHeight - 3 do begin
        a := 0; b := 0; c := 0;
        for fi := i - 1 to i + 1 do
          for fj := j - 1 to j + 1 do begin
            a := a + sqr(dx[fi, fj]);
            b := b + dxy[fi, fj];
            c := c + sqr(dy[fi, fj]);
          end;
        a := a / 2; c := c / 2;
        Eigenvalues[i, j] := (a + c - sqrt((a - c) * (a - c) + b * b));
        if Eigenvalues[i, j] MaxEigenvalue then MaxEigenvalue := Eigenvalues[i, j];
      end;
    {求取矩阵
      |∑Dx*Dx   ∑Dxy|
    M=|               |
      |∑Dxy   ∑Dy*Dy|
    的特征值
    λ= ∑Dx*Dx + ∑Dy*Dy - ((∑Dx*Dx+∑Dy*Dy)^2-4*(∑Dx*Dx * ∑Dy*Dy - ∑Dxy * ∑Dxy))^1/2}
    MinAccept := MaxEigenvalue * Quality;
    {设置最小允许阀值}

    for i := 8 to sWidth - 9 do
      for j := 8 to sHeight - 9 do
        if Eigenvalues[i, j] MinAccept then begin
          WBPoint[i, j] := true;
          Inc(FeatureCount);
        end else
          WBPoint[i, j] := false;

    for i := 8 to sWidth - 9 do
      for j := 8 to sHeight - 9 do
        if WBPoint[i, j] then begin
          sum := Eigenvalues[i, j];
          for fi := i - 8 to i + 8 do begin
            for fj := j - 8 to j + 8 do
              if sqr(fi - i) + sqr(fj - j) = 64 then
                if (Eigenvalues[fi, fj] = sum) and ((fi i) or (fj j)) and (WBPoint[fi, fj]) then begin
                  WBPoint[i, j] := false;
                  Dec(FeatureCount);
                  break;
                end;
            if not WBPoint[i, j] then break;
          end;
        end;
    {用非最大化抑制来抑制假角点}

    setlength(Features, FeatureCount); fi := 0;
    for i := 8 to sWidth - 9 do
      for j := 8 to sHeight - 9 do
        if WBPoint[i, j] then begin
          Features[fi].Info.X := i;
          Features[fi].Info.Y := j;
          Features[fi].Index := 0;
          Inc(fi);
        end;
    {输出最终的点序列}
  end;

  procedure TOpticalFlowLK.Init(sWidth, sHeight, sL: longint);
  begin
    Width := sWidth; Height := sHeight; L := sL;
    setlength(ImageOld, Width, Height, L);
    setlength(ImageNew, Width, Height, L);
    Frame := TBitmap.Create;
    Frame.Width := Width; Frame.Height := Height;
    Frame.PixelFormat := pf24bit;
    setlength(ImageGray, Width, Height);
    setlength(Eigenvalues, Width, Height);
    setlength(dx, Width, Height);
    setlength(dy, Width, Height);
    setlength(dxy, Width, Height);
    setlength(WBPoint, Width, Height);
    FeatureCount := 0;
  end;

  procedure TOpticalFlowLK.MakePyramid(var Images: TTripleLongintArray; sWidth, sHeight, sL: longint);
  var
    i, j, k, ii, jj, nWidth, nHeight, oWidth, oHeight: longint;
  begin
    {生成金字塔图像}
    oWidth := sWidth; oHeight := sHeight;
    for k := 1 to sL - 1 do begin
      nWidth := (oWidth + 1) shr 1; nHeight := (oHeight + 1) shr 1;
      for i := 1 to nWidth - 2 do begin
        ii := i shl 1;
        for j := 1 to nHeight - 2 do begin
          jj := j shl 1;
          Images[i, j, k] := (Images[ii, jj, k - 1] shl 2 +
            Images[ii - 1, jj, k - 1] shl 1 + Images[ii + 1, jj, k - 1] shl 1 + Images[ii, jj - 1, k - 1] shl 1 + Images[ii, jj + 1, k - 1] shl 1 +
            Images[ii - 1, jj - 1, k - 1] + Images[ii + 1, jj - 1, k - 1] + Images[ii - 1, jj + 1, k - 1] + Images[ii + 1, jj + 1, k - 1]) shr 4;
          {高斯原则,shl右移位,shr左移位}
        end;
      end;
      for i := 1 to nWidth - 2 do begin
        ii := i shl 1;
        Images[i, 0, k] := (Images[ii, 0, k - 1] shl 2 +
          Images[ii - 1, 0, k - 1] shl 1 + Images[ii + 1, 0, k - 1] shl 1 + Images[ii, 0, k - 1] shl 1 + Images[ii, 1, k - 1] shl 1 +
          Images[ii - 1, 0, k - 1] + Images[ii + 1, 0, k - 1] + Images[ii - 1, 1, k - 1] + Images[ii + 1, 1, k - 1]) shr 4;
        Images[i, nHeight - 1, k] := (Images[ii, oHeight - 1, k - 1] shl 2 +
          Images[ii - 1, oHeight - 1, k - 1] shl 1 + Images[ii + 1, oHeight - 1, k - 1] shl 1 + Images[ii, oHeight - 2, k - 1] shl 1 + Images[ii, oHeight - 1, k - 1] shl 1 +
          Images[ii - 1, oHeight - 2, k - 1] + Images[ii + 1, oHeight - 2, k - 1] + Images[ii - 1, oHeight - 1, k - 1] + Images[ii + 1, oHeight - 1, k - 1]) shr 4;
      end;
      {处理上下边}
      for j := 1 to nHeight - 2 do begin
        jj := j shl 1;
        Images[0, j, k] := (Images[0, jj, k - 1] shl 2 +
          Images[0, jj, k - 1] shl 1 + Images[1, jj, k - 1] shl 1 + Images[0, jj - 1, k - 1] shl 1 + Images[0, jj + 1, k - 1] shl 1 +
          Images[0, jj - 1, k - 1] + Images[1, jj - 1, k - 1] + Images[0, jj + 1, k - 1] + Images[1, jj + 1, k - 1]) shr 4;
        Images[nWidth - 1, j, k] := (Images[oWidth - 1, jj, k - 1] shl 2 +
          Images[oWidth - 2, jj, k - 1] shl 1 + Images[oWidth - 1, jj, k - 1] shl 1 + Images[oWidth - 1, jj - 1, k - 1] shl 1 + Images[oWidth - 1, jj + 1, k - 1] shl 1 +
          Images[oWidth - 2, jj - 1, k - 1] + Images[oWidth - 1, jj - 1, k - 1] + Images[oWidth - 2, jj + 1, k - 1] + Images[oWidth - 1, jj + 1, k - 1]) shr 4;
      end;
      {处理左右边}
      Images[0, 0, k] := (Images[0, 0, k - 1] shl 2 +
        Images[0, 0, k - 1] shl 1 + Images[1, 0, k - 1] shl 1 + Images[0, 0, k - 1] shl 1 + Images[0, 1, k - 1] shl 1 +
        Images[0, 0, k - 1] + Images[1, 0, k - 1] + Images[0, 1, k - 1] + Images[1, 1, k - 1]) shr 4;
      {处理左上点}
      Images[0, nHeight - 1, k] := (Images[0, oHeight - 1, k - 1] shl 2 +
        Images[0, oHeight - 1, k - 1] shl 1 + Images[1, oHeight - 1, k - 1] shl 1 + Images[0, oHeight - 2, k - 1] shl 1 + Images[0, oHeight - 1, k - 1] shl 1 +
        Images[0, oHeight - 2, k - 1] + Images[1, oHeight - 2, k - 1] + Images[0, oHeight - 1, k - 1] + Images[1, oHeight - 1, k - 1]) shr 4;
      {处理左下点}
      Images[nWidth - 1, 0, k] := (Images[oWidth - 1, 0, k - 1] shl 2 +
        Images[oWidth - 2, oHeight - 1, k - 1] shl 1 + Images[oWidth - 1, oHeight - 1, k - 1] shl 1 + Images[oWidth - 1, oHeight - 1, k - 1] shl 1 + Images[oWidth - 1, oHeight - 1, k - 1] shl 1 +
        Images[oWidth - 2, oHeight - 1, k - 1] + Images[oWidth - 1, oHeight - 1, k - 1] + Images[oWidth - 2, oHeight - 1, k - 1] + Images[oWidth - 1, oHeight - 1, k - 1]) shr 4;
      {处理右上点}
      Images[nWidth - 1, nHeight - 1, k] := (Images[oWidth - 1, oHeight - 1, k - 1] shl 2 +
        Images[oWidth - 2, oHeight - 1, k - 1] shl 1 + Images[oWidth - 1, oHeight - 1, k - 1] shl 1 + Images[oWidth - 1, oHeight - 2, k - 1] shl 1 + Images[oWidth - 1, oHeight - 1, k - 1] shl 1 +
        Images[oWidth - 2, oHeight - 2, k - 1] + Images[oWidth - 1, oHeight - 2, k - 1] + Images[oWidth - 2, oHeight - 1, k - 1] + Images[oWidth - 1, oHeight - 1, k - 1]) shr 4;
      {处理右下点}
    end;
  end;

  procedure TOpticalFlowLK.InitFeatures(Frames: TBitmap);
  var
    i, j: longint;
    Line: pRGBTriple;
  begin
    SetStretchBltMode(Frame.Canvas.Handle, Stretch_Halftone);
    StretchBlt(Frame.Canvas.Handle, 0, 0, Width, Height, Frames.Canvas.Handle, 0, 0, Frames.Width, Frames.Height, SrcCopy);
    for i := 0 to Height - 1 do begin
      Line := Frame.ScanLine[i];
      for j := 0 to Width - 1 do begin
        ImageOld[j, i, 0] := (Line^.rgbtBlue * 11 + Line^.rgbtGreen * 59 + Line^.rgbtRed * 30) div 100;
        ImageGray[j, i] := ImageOld[j, i, 0];
        Inc(Line);
      end;
    end;
    {初始化金字塔图像第一层ImageOld[x,y,0]}
    MakePyramid(ImageOld, Width, Height, L);
    {生成金字塔图像}
    CornerDetect(Width, Height, 0.01);
    {进行强角点检测}
  end;

  procedure TOpticalFlowLK.CalOpticalFlowLK(Frames: TBitmap);
  var
    i, j, fi, fj, k, ll, m, dx, dy, gx, gy, px, py, kx, ky, ed, edc, nWidth, nHeight: longint;
    nx, ny, vx, vy, A, B, C, D, E, F, Ik: extended;
    Ix, Iy: TDoubleExtendedArray;
    Line: pRGBTriple;
    Change: boolean;
  begin
    SetStretchBltMode(Frame.Canvas.Handle, Stretch_Halftone);
    StretchBlt(Frame.Canvas.Handle, 0, 0, Width, Height, Frames.Canvas.Handle, 0, 0, Frames.Width, Frames.Height, SrcCopy);
    for i := 0 to Height - 1 do begin
      Line := Frame.ScanLine[i];
      for j := 0 to Width - 1 do begin
        ImageNew[j, i, 0] := (Line^.rgbtBlue * 11 + Line^.rgbtGreen * 59 + Line^.rgbtRed * 30) div 100;
        Inc(Line);
      end;
    end;
    {初始化金字塔图像第一层ImageNew[x,y,0]}
    MakePyramid(ImageNew, Width, Height, L);
    {生成金字塔图像}
    setlength(Ix, 15, 15); setlength(Iy, 15, 15);
    {申请差分图像临时数组}
    for m := 0 to FeatureCount - 1 do begin
      {算法细节见:
      Jean-Yves Bouguet "Pyramidal Implementation of the Lucas Kanade Feature Tracker Description of the algorithm"}
      gx := 0; gy := 0;
      for ll := L - 1 downto 0 do begin
        px := Features[m].Info.X shr ll;
        py := Features[m].Info.Y shr ll;
        {对应当前金字塔图像的u点:u[L]:=u/2^L}
        nWidth := Width shr ll; nHeight := Height shr ll;
        A := 0; B := 0; C := 0;
        for i := max(px - 7, 1) to min(px + 7, nWidth - 2) do
          for j := max(py - 7, 1) to min(py + 7, nHeight - 2) do begin
            fi := i - px + 7; fj := j - py + 7;
            Ix[fi, fj] := (ImageOld[i + 1, j, ll] - ImageOld[i - 1, j, ll]) / 2;
            Iy[fi, fj] := (ImageOld[i, j + 1, ll] - ImageOld[i, j - 1, ll]) / 2;
            A := A + Ix[fi, fj] * Ix[fi, fj]; B := B + Ix[fi, fj] * Iy[fi, fj];
            C := C + Iy[fi, fj] * Iy[fi, fj];
          end;
        {计算2阶矩阵G:
          |Ix(x,y)*Ix(x,y)  Ix(x,y)*Iy(x,y)|
        ∑|Ix(x,y)*Iy(x,y)  Iy(x,y)*Iy(x,y)|}
        D := A * C - B * B;
        vx := 0; vy := 0; dx := 0; dy := 0;
        if abs(D) 1E-8 then begin
          for k := 1 to 10 do begin
            E := 0; F := 0;
            for i := max(px - 7, 1) to min(px + 7, nWidth - 2) do
              for j := max(py - 7, 1) to min(py + 7, nHeight - 2) do begin
                fi := i - px + 7; fj := j - py + 7;
                kx := i + gx + dx; ky := j + gy + dy;
                if kx 0 then kx := 0; if kx nWidth - 1 then kx := nWidth - 1;
                if ky 0 then ky := 0; if ky nHeight - 1 then ky := nHeight - 1;
                Ik := ImageOld[i, j, ll] - ImageNew[kx, ky, ll];
                E := E + Ik * Ix[fi, fj];
                F := F + Ik * Iy[fi, fj];
              end;
            {计算2x1阶矩阵b:
              |Ik(x,y)*Ix(x,y)|
            ∑|Ik(x,y)*Iy(x,y)|}
            nx := (C * E - B * F) / D;
            ny := (B * E - A * F) / (-D);
            {计算η=G^-1*b}
            vx := vx + nx; vy := vy + ny;
            dx := trunc(vx); dy := trunc(vy);
            {得到相对运动向量d}
          end;
        end;
        gx := (gx + dx) shl 1; gy := (gy + dy) shl 1;
        {得到下一层的预测运动向量g}
      end;
      gx := gx div 2; gy := gy div 2;
      px := px + gx; py := py + gy;
      Features[m].Info.X := px;
      Features[m].Info.Y := py;
      Features[m].Vector.X := gx;
      Features[m].Vector.Y := gy;
      if (px Width - 1) or (px 0) or (py Height - 1) or (py 0) then Features[m].Index := 1;
      {失去特征点处理}
    end;

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

    for k := 0 to L - 1 do begin
      nWidth := Width shr k; nHeight := Height shr k;
      for i := 0 to nWidth - 1 do
        for j := 0 to nHeight - 1 do
          ImageOld[i, j, k] := ImageNew[i, j, k];
    end;
    {复制J到I}
    repeat
      Change := false;
      for i := 0 to FeatureCount - 1 do begin
        if Features[i].Index = 1 then
          for j := i + 1 to FeatureCount - 1 do begin
            Features[j - 1] := Features[j];
            Change := true;
          end;
        if Change then break;
      end;
      if Change then Dec(FeatureCount);
    until not Change;

    setlength(Features, FeatureCount);
    {删除失去的特征点}
    Speed := 0; Radio := 0; RadioX := 0; RadioY := 0;
    if FeatureCount 0 then begin
      SupportCount := 0;
      for i := 0 to FeatureCount - 1 do
        if (Features[i].Vector.X 0) and (Features[i].Vector.Y 0) then begin
          RadioX := RadioX + Features[i].Vector.X;
          RadioY := RadioY + Features[i].Vector.Y;
          Speed := Speed + sqrt(sqr(Features[i].Vector.X) + sqr(Features[i].Vector.Y));
          Inc(SupportCount);
        end;
      if SupportCount 0 then begin
        Speed := Speed / SupportCount; //*0.0352778;
        Radio := ArcTan2(RadioY, RadioX);
      end;
    end;
    {计算平均速度和整体方向}
    Frame.Canvas.Pen.Style := psSolid;
    Frame.Canvas.Pen.Width := 1;
    Frame.Canvas.Pen.Color := clLime;
    Frame.Canvas.Brush.Style := bsClear;
    for i := 0 to FeatureCount - 1 do
      Frame.Canvas.Ellipse(Features[i].Info.X - 4, Features[i].Info.Y - 4, Features[i].Info.X + 4, Features[i].Info.Y + 4);
    {用绿圈标识特征点}
    Frame.Canvas.Pen.Color := clYellow;
    for i := 0 to FeatureCount - 1 do begin
      Frame.Canvas.MoveTo(Features[i].Info.X - Features[i].Vector.X, Features[i].Info.Y - Features[i].Vector.Y);
      Frame.Canvas.LineTo(Features[i].Info.X, Features[i].Info.Y);
    end;
    {用黄色线条表示运动向量}
    if (SupportCount 0) and (Speed 3) then begin
      Frame.Canvas.Pen.Style := psSolid;
      Frame.Canvas.Pen.Width := 6;
      Frame.Canvas.Pen.Color := clWhite;
      Frame.Canvas.Ellipse(Width div 2 - 100, Height div 2 - 100, Width div 2 + 100, Height div 2 + 100);
      Frame.Canvas.MoveTo(Width div 2, Height div 2);
      Frame.Canvas.Pen.Color := clBlue;
      Frame.Canvas.LineTo(Width div 2 + trunc(90 * Cos(Radio)), Height div 2 + trunc(90 * Sin(Radio)));
      Frame.Canvas.Pen.Style := psClear;
      Frame.Canvas.Brush.Style := bsSolid;
      Frame.Canvas.Brush.Color := clRed;
      Frame.Canvas.Ellipse(Width div 2 + trunc(90 * Cos(Radio)) - 8, Height div 2 + trunc(90 * Sin(Radio)) - 8, Width div 2 + trunc(90 * Cos(Radio)) + 8, Height div 2 + trunc(90 * Sin(Radio)) + 8);
    end;
  end;

  destructor TOpticalFlowLK.Destroy;
  begin
    setlength(ImageOld, 0);
    setlength(ImageNew, 0);
    setlength(ImageGray, 0);
    setlength(Eigenvalues, 0);
    setlength(dx, 0);
    setlength(dy, 0);
    setlength(dxy, 0);
    setlength(WBPoint, 0);
    inherited;
  end;

  end.


来源:http://www.tulaoshi.com/n/20160219/1624519.html

延伸阅读
标签: Delphi
在数据库管理系统中,数据录入是数据处理的基本功能,录入操作方便与否是衡量数据库应用程序交互良莠的指标之一。录入中除了应要对录入数据进行合法检验外,还应为用户提供更多的方便操作,即对于“规范性”数据,如:性别、职称等字段的数据,应尽可能供用户“选择”录入,而非直接文字输入,另外,如:出生年月、联系电话、邮编等类似...
当你做一个多媒体播放器时,难免少不了控制音量的大小和左右声道的播放,下面就介绍一种控制Wave波形输出设备音量的方法,该方法不是设置主音量。先在窗体上放两个TTrackBar,分别命名为TrackBar1,TrackBar2,属性Max都设置为65535,如果觉得刻度太密了,可以把Frequency属性值设置大一些,然后在Uses段加入MMSystem,并在TrackBar1和Trac...
标签: Delphi
  文件关联为我们带来很多的方便。Delphi自带有注册表对象TRegistry,可以通过它取得或改变注册表相关键值的内容。 Function GetAssociatedExec(FileExt: String; var FileDescription, MIMEType: String): String; Var Reg: TRegistry; FileType: String; begin Result := ′′;{函数返回值是打开Fi...
在Windows95/98中,都是使用注册表对系统数据进行管理,有关壁纸的设置数据保存在Hkey_Current_UserControl PanelDesktop的Wallpaper和TileWallpaper 等键值中,只要成功修改了这两个键值,然后发消息给Windows即可更换壁纸。在本例的程序中,使用了一个Tform;两个Tspeedbutton(Speedbutton1用于接受用户的浏览命令,Speedbutton2用于接受用户的...
标签: Delphi
  给单位开发软件,涉及一打印模块,我感到颇有兴趣,就拿来其中的一个小功能模块与读者共享。下面以打印在纸张的矩形框内为例简单介绍: 程序要求: 单击[打印]按钮,把Memo的内容最多分三行打印出来,每行最多能容纳22个三号字,限定汉字上限为50个汉字。 编程思路: 用LineTo和MoveTo函数画一矩形框,根...

经验教程

706

收藏

12
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部