{
作者 : 刘留
参考文献为 :
Jean-Yves Bouguet "Lucas Kanade 기능 추적기의 피라미드 구현 알고리즘의 설명"
http://www.aivisoft.net/
geo.cra [at] gmail [dot] com
}
단위 광학 플로우 크;
인터페이스
용도
수학, 창문, 수, 변형, 클래스, 그래픽, 유니티, 컬러 컨;
유형
topticalflowlk = 클래스
사적인
ImageOld, imagenew : ttriplelongintarray;
ImageGray, dx, dy, dxy : tdoublelongintarray;
고유 값 : tdoubleextendedArray;
WBPoint : TdoublebooleanArray;
높이, 너비, L, Radiox, Radioy : Longint;
절차 CornerDetect (Swidth, Sheight : Longint; 품질 : 확장);
절차 makepyramid (var 이미지 : ttriplelongintarray; swidth, sheight, sl : longint);
공공의
프레임 : tbitmap;
특징 : tsinglepointinfoarray;
FeatureCount, SupportCount : Longint;
속도, 라디오 : 확장;
절차 init (swidth, sheight, sl : longint);
절차 initfeatures (프레임 : tbitmap);
절차 calopticalflowlk (프레임 : tbitmap);
소멸자 파괴; 보수;
끝;
구현
절차 topticalflowlk.cornerdetect (swidth, sheight : longint; Quality : Extended);
var
I, J, FI, FJ : Longint;
a, b, c, sum, minaccept, maxeigenvalue : 확장;
시작하다
FeatureCount : = 0;
{
下面采用 추적하기에 좋은 기능 feature
J. Shi와 C. Tomasi "추적하기 좋은 기능", CVPR 94
}
i : = 1 to swidth -2 do
j : = 1 to sheight -2 시작
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]);
끝;
{sobel 算子的 dx, dy, dxy
DX :
| 1 0-1 |
| 2 0 -2 |
| 1 0-1 |
Dy :
| -1 -2 -1 |
| 0 0 |
| 1 2 1 |
dxy
| -1 0 1 |
| 0 0 |
| 1 0-1 |}
MaxeigenValue : = 0;
i : = 2에서 swidth -3 do
j : = 2 to sheight -3 시작
A : = 0; B : = 0; C : = 0;
fi : = i -1 ~ i + 1 do
fj : = j -1 ~ j + 1의 경우 시작
a : = a + sqr (dx [fi, fj]);
b : = b + dxy [fi, fj];
C : = c + sqr (dy [fi, fj]);
끝;
A : = A / 2; C : = C / 2;
고유 값 [i, j] : = (a + c -sqrt ((a -c) * (a -c) + b * b));
고유 값 [i, j]> maxeigenValue 인 경우 maxeigenValue : = 고유 값 [i, j];
끝;
{求取矩阵
| ∑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 * 품질;
{设置最小允许阀值}
i : = 8에서 swidth -9
j : = 8 to sheight -9 do
고유 값 [i, j]> minaccept가 시작되면 시작하십시오
wbpoint [i, j] : = true;
Inc (featurecount);
다른 끝
wbpoint [i, j] : = false;
i : = 8에서 swidth -9
j : = 8 to sheight -9 do
WBPoint [i, j]가 시작되면 시작하십시오
합 : = 고유 값 [i, j];
fi : = i -8 ~ i + 8의 경우 시작
fj : = j -8 ~ j + 8의 경우
SQR (fi -i) + sqr (fj -j) <= 64 인 경우
if (eigenValues [fi, fj]> = sum) 및 ((fi <> i) 또는 (fj <> j)) 및 (wbpoint [fi, fj])를 시작합니다.
wbpoint [i, j] : = false;
12 월 (FeatureCount);
부서지다;
끝;
WBPoint [i, j]가 아니라면 파손하십시오.
끝;
끝;
{用非最大化抑制来抑制假角点}
setLength (기능, featurecount); fi : = 0;
i : = 8에서 swidth -9
j : = 8 to sheight -9 do
WBPoint [i, j]가 시작되면 시작하십시오
특징 [fi] .info.x : = i;
특징 [fi] .info.y : = j;
특징 [fi]. index : = 0;
Inc (fi);
끝;
{输出最终的点序列}
끝;
절차 topticalflowlk.init (swidth, sheight, sl : longint);
시작하다
너비 : = SWIDTH; 높이 : = sheight; L : = SL;
setLength (ImageOld, 너비, 높이, l);
setLength (imageNew, 너비, 높이, l);
프레임 : = tbitmap.create;
frame.width : = 너비; frame.height : = 높이;
frame.pixelformat : = pf24bit;
setLength (ImageGray, 너비, 높이);
setLength (고유 값, 너비, 높이);
setLength (dx, 너비, 높이);
setLength (dy, 너비, 높이);
setLength (dxy, 너비, 높이);
setLength (wbpoint, 너비, 높이);
FeatureCount : = 0;
끝;
절차 topticalflowlk.makepyramid (var 이미지 : ttriplelongintarray; swidth, sheight, sl : longint);
var
i, j, k, ii, jj, nwidth, nheight, owidth, aheight : longint;
시작하다
{生成金字塔图像}
owidth : = swidth; 오이 라이트 : = 셰이트;
K : = 1에서 sl -1을 위해 시작합니다.
nwidth : = (owidth + 1) shr 1; nheight : = (Oheight + 1) SHR 1;
i : = 1 ~ nwidth -2 시작
II : = I Shl 1;
j : = 1에서 nheight -2가 시작됩니다.
JJ : = J Shl 1;
이미지 [i, j, k] : = (이미지 [ii, jj, k -1] shl 2 +
이미지 [ii -1, jj, k -1] shl 1 + 이미지 [ii + 1, jj, k -1] shl 1 + 이미지 [ii, jj -1, k -1] shl 1 + 이미지 [ii, jj + 1, k -1] shl 1 +
이미지 [ii -1, jj -1, k -1] + 이미지 [ii + 1, jj -1, k -1] + 이미지 [ii -1, jj + 1, k -1] + 이미지 [ii + 1 , JJ + 1, K -1]) SHR 4;
{高斯原则, shl 右移位, shr 左移位}
끝;
끝;
i : = 1 ~ nwidth -2 시작
II : = I Shl 1;
이미지 [i, 0, k] : = (이미지 [ii, 0, k -1] shl 2 +
이미지 [ii -1, 0, k -1] shl 1 + 이미지 [ii + 1, 0, k -1] shl 1 + 이미지 [ii, 0, k -1] shl 1 + 이미지 [ii, 1, k -1] shl 1 +
이미지 [ii -1, 0, k -1] + 이미지 [ii + 1, 0, k -1] + 이미지 [ii -1, 1, k -1] + 이미지 [ii + 1, 1, k -1 ])) SHR 4;
이미지 [i, nheight -1, k] : = (이미지 [ii, aheight -1, k -1] shl 2 +
이미지 [ii -1, aheight -1, k -1] shl 1 + 이미지 [ii + 1, aheight -1, k -1] shl 1 + 이미지 [ii, aheight -2, k -1] shl 1 + 이미지 [II, OHEIGHT -1, K -1] SHL 1 +
이미지 [ii -1, aheight -2, k -1] + 이미지 [ii + 1, aheight -2, k -1] + 이미지 [ii -1, aheight -1, k -1] + 이미지 [ii + 1 , aheight -1, k -1]) shr 4;
끝;
{处理上下边}
j : = 1에서 nheight -2가 시작됩니다.
JJ : = J Shl 1;
이미지 [0, j, k] : = (이미지 [0, jj, k -1] shl 2 +
이미지 [0, jj, k -1] shl 1 + 이미지 [1, jj, k -1] shl 1 + 이미지 [0, jj -1, k -1] shl 1 + 이미지 [0, jj + 1, k -1] shl 1 +
이미지 [0, jj -1, k -1] + 이미지 [1, jj -1, k -1] + 이미지 [0, jj + 1, k -1] + 이미지 [1, jj + 1, k -1 ])) SHR 4;
이미지 [nwidth -1, j, k] : = (이미지 [owidth -1, jj, k -1] shl 2 +
이미지 [owidth -2, jj, k -1] shl 1 + 이미지 [owidth -1, jj, k -1] shl 1 + 이미지 [owidth -1, jj -1, k -1] shl 1 + 이미지 [Owidth -1, jj + 1, k -1] shl 1 +
이미지 [Owidth -2, JJ -1, K -1] + 이미지 [Owidth -1, JJ -1, K -1] + 이미지 [Owidth -2, JJ + 1, K -1] + 이미지 [Owidth -1 , JJ + 1, K -1]) SHR 4;
끝;
{处理左右边}
이미지 [0, 0, k] : = (이미지 [0, 0, k -1] shl 2 +
이미지 [0, 0, k -1] shl 1 + 이미지 [1, 0, k -1] shl 1 + 이미지 [0, 0, K -1] shl 1 + 이미지 [0, 1, k -1] shl 1 +
이미지 [0, 0, K -1] + 이미지 [1, 0, K -1] + 이미지 [0, 1, K -1] + 이미지 [1, 1, K -1]) SHR 4;
{处理左上点}
이미지 [0, nheight -1, k] : = (images [0, aheight -1, k -1] shl 2 +
이미지 [0, aheight -1, k -1] shl 1 + 이미지 [1, aheight -1, k -1] shl 1 + 이미지 [0, Oheight -2, K -1] shl 1 + 이미지 [0, Oheight -1, k -1] shl 1 +
이미지 [0, aheight -2, k -1] + 이미지 [1, aheight -2, k -1] + 이미지 [0, aheight -1, k -1] + 이미지 [1, aheight -1, k -1 ])) SHR 4;
{处理左下点}
이미지 [nwidth -1, 0, k] : = (이미지 [owidth -1, 0, k -1] shl 2 +
이미지 [owidth -2, aheight -1, k -1] shl 1 + 이미지 [owidth -1, aheight -1, k -1] shl 1 + 이미지 [owidth -1, aheight -1, k -1] shl 1 + 이미지 [Owidth -1, Oheight -1, K -1] shl 1 +
이미지 [owidth -2, aheight -1, k -1] + 이미지 [owidth -1, aheight -1, k -1] + 이미지 [owidth -2, aheight -1, k -1] + 이미지 [Owidth -1 , aheight -1, k -1]) shr 4;
{处理右上点}
이미지 [nwidth -1, nheight -1, k] : = (이미지 [owidth -1, aheight -1, k -1] shl 2 +
이미지 [owidth -2, aheight -1, k -1] shl 1 + 이미지 [owidth -1, aheight -1, k -1] shl 1 + 이미지 [owidth -1, aheight -2, k -1] shl 1 + 이미지 [Owidth -1, Oheight -1, K -1] shl 1 +
이미지 [owidth -2, aheight -2, k -1] + 이미지 [owidth -1, aheight -2, k -1] + 이미지 [owidth -2, aheight -1, k -1] + 이미지 [Owidth -1 , aheight -1, k -1]) shr 4;
{处理右下点}
끝;
끝;
절차 topticalflowlk.initfeatures (프레임 : tbitmap);
var
I, J : Longint;
라인 : prgbtriple;
시작하다
setstretchBltMode (frame.canvas.handle, rector_halftone);
StretchBlt (frame.canvas.handle, 0, 0, 너비, 높이, 프레임.
i : = 0 ~ 높이 -1은 시작됩니다.
선 : = frame.scanline [i];
j : = 0에서 너비에서 -1이 시작됩니다.
ImageOld [j, i, 0] : = (line^.rgbtblue * 11 + line^.rgbtgreen * 59 + line^.rgbtred * 30) div 100;
ImageGray [j, i] : = imageOld [j, i, 0];
경사);
끝;
끝;
{初始化金字塔图像第一层 ImageOld [x, y, 0]}
MakePyramid (ImageOld, 너비, 높이, L);
{生成金字塔图像}
CornerDetect (너비, 높이, 0.01);
{进行强角点检测}
끝;
절차 topticalflowlk.calopticalflowlk (프레임 : 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 : 확장;
ix, iy : tdoubleextendedArray;
라인 : prgbtriple;
변화 : 부울;
시작하다
setstretchBltMode (frame.canvas.handle, rector_halftone);
StretchBlt (frame.canvas.handle, 0, 0, 너비, 높이, 프레임.
i : = 0 ~ 높이 -1은 시작됩니다.
선 : = frame.scanline [i];
j : = 0에서 너비에서 -1이 시작됩니다.
imagenew [j, i, 0] : = (line^.rgbtblue * 11 + line^.rgbtgreen * 59 + line^.rgbtred * 30) div 100;
경사);
끝;
끝;
{初始化金字塔图像第一层 Imagenew [x, y, 0]}
MakePyramid (ImageNew, 너비, 높이, L);
{生成金字塔图像}
setLength (ix, 15, 15); setLength (IY, 15, 15);
{申请差分图像临时数组}
m : = 0의 경우 featurecount -1 시작
{算法细节见 :
Jean-Yves Bouguet "Lucas Kanade 기능 추적기의 피라미드 구현 알고리즘의 설명"}
gx : = 0; Gy : = 0;
ll : = l -1 downto 0 시작
px : = 특징 [m] .info.x shr ll;
py : = 특징 [m] .info.y shr ll;
{对应当前金字塔图像的 u 对应当前金字塔图像的 : u [l] : = u/2^l}
nwidth : = 너비 shr ll; nheight : = 높이 shr ll;
A : = 0; B : = 0; C : = 0;
i : = max (px -7, 1) ~ min (px + 7, nwidth -2) do
j : = max (py -7, 1) ~ min (py + 7, nheight -2) 시작
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];
끝;
{计算 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;
ABS (d)> 1e-8 인 경우 시작하십시오
K : = 1 ~ 10이 시작됩니다
e : = 0; F : = 0;
i : = max (px -7, 1) ~ min (px + 7, nwidth -2) do
j : = max (py -7, 1) ~ min (py + 7, nheight -2) 시작
fi : = i -px + 7; fj : = j -py + 7;
kx : = i + gx + dx; KY : = J + Gy + DY;
kx <0이면 kx : = 0; kx> nwidth -1이면 kx : = nwidth -1;
Ky <0이면 ky : = 0; Ky> nheight -1이면 Ky : = nheight -1;
IK : = ImageOld [I, J, LL] - ImageNew [KX, KY, LL];
e : = e + ik * ix [fi, fj];
f : = f + ik * iy [fi, fj];
끝;
{计算 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}
끝;
끝;
gx : = (gx + dx) shl 1; gy : = (gy + dy) shl 1;
{得到下一层的预测运动向量 g}
끝;
gx : = gx div 2; Gy : = Gy Div 2;
px : = px + gx; PY : = py + gy;
특징 [m] .info.x : = px;
특징 [m] .info.y : = py;
특징 [m] .vector.x : = gx;
특징 [m] .vector.y : = gy;
if (px> width -1) 또는 (px <0) 또는 (py> 높이 -1) 또는 (py <0)이면 [m]. index : = 1;
{失去特征点处理}
끝;
K : = 0 ~ l -1의 경우 시작
nwidth : = 너비 shr k; nheight : = 높이 shr k;
i : = 0에서 nwidth -1 do의 경우
j : = 0에서 nheight -1 do
ImageOld [i, j, k] : = imagenew [i, j, k];
끝;
{复制 j 复制 i}
반복하다
변경 : = 거짓;
i : = 0 to featurecount -1 시작 시작
특징 [i]. index = 1이면
J : = i + 1의 경우 featurecount -1 시작
특징 [j -1] : = 특징 [j];
변경 : = 참;
끝;
변경되면 파손됩니다.
끝;
변경되면 Dec (featurecount);
변하지 않을 때까지;
setLength (기능, featurecount);
{删除失去的特征点}
속도 : = 0; 라디오 : = 0; Radiox : = 0; Radioy : = 0;
featurecount> 0이면 시작하십시오
supportCount : = 0;
i : = 0의 경우 featurecount -1 do
if (feature [i] .vector.x <> 0) 및 (특징 [i] .vector.y <> 0) 그런 다음 시작합니다.
Radiox : = Radiox + 특징 [i] .vector.x;
Radioy : = Radioy + 기능 [i] .vector.y;
속도 : = 속도 + sqrt (sqr (feature [i] .vector.x) + sqr (특징 [i] .vector.y));
Inc (supportCount);
끝;
supportCount> 0 인 경우 시작하십시오
속도 : = 속도 / 지원 카운트; //*0.0352778;
라디오 : = ArcTan2 (Radioy, Radiox);
끝;
끝;
{计算平均速度和整体方向}
frame.canvas.pen.style : = pssolid;
frame.canvas.pen.width : = 1;
frame.canvas.pen.color : = cllime;
frame.canvas.brush.style : = bsclear;
i : = 0의 경우 featurecount -1 do
frame.canvas.ellipse (특징 [i] .info.x -4, 특징 [i] .info.y -4, 특징 [i] .info.x + 4, 특징 [i] .info.y + 4) ;
{用绿圈标识特征点}
frame.canvas.pen.color : = clyellow;
i : = 0 to featurecount -1 시작 시작
frame.canvas.moveto (특징 [i] .info.x- 특징 [i] .vector.x, 특징 [i] .info.y- 기능 [i] .vector.y);
frame.canvas.lineto (특징 [i] .info.x, 특징 [i] .info.y);
끝;
{用黄色线条表示运动向量}
if (supportCount> 0) 및 (Speed> 3)을 시작합니다.
frame.canvas.pen.style : = pssolid;
frame.canvas.pen.width : = 6;
frame.canvas.pen.color : = clwhite;
frame.canvas.ELLIPSE (너비 div 2-10, 높이 div 2-10, 너비 2 + 100, 높이 div 2 + 100);
frame.canvas.Moveto (너비 div 2, 높이 div 2);
frame.canvas.pen.color : = clblue;
frame.canvas.lineto (너비 div 2 + trunc (90 * cos (radio)), 높이 div 2 + trunc (90 * sin (radio));
frame.canvas.pen.style : = psclear;
frame.canvas.brush.style : = bssolid;
frame.canvas.brush.color : = clred;
frame.canvas.ELLIPSE (너비 div 2 + trunc (90 * cos (radio)) -8, 높이 div 2 + trunc (90 * sin (radio)) -8, 너비 div 2 + trunc (90 * cos (Radio) ) + 8, 높이 div 2 + trunc (90 * sin (Radio)) + 8);
끝;
끝;
Destructor topticalflowlk. destroy;
시작하다
setLength (ImageOld, 0);
setLength (imageNew, 0);
setLength (ImageGray, 0);
setLength (고유 값, 0);
setLength (dx, 0);
setLength (dy, 0);
setLength (dxy, 0);
setLength (wbpoint, 0);
상속;
끝;
끝.