前几天在做一个摄像头的程序,由于镜头本身是超广角的,所以出来的图像畸变很严重,影像了识别,所以查阅了一番资料,写了一个简单的畸变处理算法,如下:
class procedure TPic.CorrectLensDistortion(const ABitmap: TBitmap; const k1, k2: Single);
var
x, y, cx, cy: Integer;
u, v, r2, r4, radial_distortion: Double;
SrcColor: TAlphaColor;
CorrectedBitmap: TBitmap;
DistortionCache: array of TPointF;
SrcData, DstData: TBitmapData;
begin
if not Assigned(ABitmap) then exit;
CorrectedBitmap := TBitmap.Create(ABitmap.Width, ABitmap.Height);
try
cx := ABitmap.Width div 2;
cy := ABitmap.Height div 2;
SetLength(DistortionCache, ABitmap.Width * ABitmap.Height);
// 预计算畸变缓存
for y := 0 to ABitmap.Height - 1 do
begin
for x := 0 to ABitmap.Width - 1 do
begin
u := (x - cx) / cx;
v := (y - cy) / cy;
r2 := u * u + v * v;
r4 := r2 * r2;
radial_distortion := 1 + k1 * r2 + k2 * r4;
u := u / radial_distortion;
v := v / radial_distortion;
u := u * cx + cx;
v := v * cy + cy;
DistortionCache[y * ABitmap.Width + x] := PointF(u, v);
end;
end;
if ABitmap.Map(TMapAccess.Read, SrcData) then
try
if CorrectedBitmap.Map(TMapAccess.Write, DstData) then
try
for y := 0 to ABitmap.Height - 1 do
begin
for x := 0 to ABitmap.Width - 1 do
begin
u := DistortionCache[y * ABitmap.Width + x].X;
v := DistortionCache[y * ABitmap.Width + x].Y;
if (u >= 0) and (u < ABitmap.Width) and (v >= 0) and (v < ABitmap.Height) then
begin
SrcColor := SrcData.GetPixel(Round(u), Round(v));
DstData.SetPixel(x, y, SrcColor);
end
else
begin
DstData.SetPixel(x, y, TAlphaColorRec.Null);
end;
end;
end;
finally
CorrectedBitmap.Unmap(DstData);
end;
finally
ABitmap.Unmap(SrcData);
end;
ABitmap.Assign(CorrectedBitmap);
finally
CorrectedBitmap.Free;
DistortionCache:=nil;
end;
end;
写完后试跑了一下,并调整了k1和k2的系数,基本可以矫正回来一些,但运行速度很慢,视频会变得很卡,优化了一些地方,但还是无用,改进不大,于是想用线程来处理,其实这个算法耗时的地方是需要对一个bitmap的像素进行扫描,由于现在的CPU一般都是有好几个核,如果我们启动线程池来做,是不是可以提高一下速度,于是,我在这个算法基础上使用了Delphi自带的一个类:TParallel
这个类其实挺好用的,可以自动帮你处理线程池,在我这个算法中,只要能同时扫描每一行并计算,就能提高速度了,于是,我把代码改了一下:
class procedure TPic.CorrectLensDistortionByParallel(const ABitmap: TBitmap; const k1, k2: Single);
var
cx, cy: Integer;
DistortionCache: array of TPointF;
SrcData, DstData: TBitmapData;
CorrectedBitmap: TBitmap;
begin
if not Assigned(ABitmap) then exit;
CorrectedBitmap := TBitmap.Create(ABitmap.Width, ABitmap.Height);
try
cx := ABitmap.Width div 2;
cy := ABitmap.Height div 2;
SetLength(DistortionCache, ABitmap.Width * ABitmap.Height);
// 预计算畸变缓存
TParallel.For(0, ABitmap.Height - 1, procedure(y: Integer)
var
x: Integer;
u, v, r2, r4, radial_distortion: Double;
begin
for x := 0 to ABitmap.Width - 1 do
begin
u := (x - cx) / cx;
v := (y - cy) / cy;
r2 := u * u + v * v;
r4 := r2 * r2;
radial_distortion := 1 + k1 * r2 + k2 * r4;
u := u / radial_distortion;
v := v / radial_distortion;
u := u * cx + cx;
v := v * cy + cy;
DistortionCache[y * ABitmap.Width + x] := PointF(u, v);
end;
end);
if ABitmap.Map(TMapAccess.Read, SrcData) then
try
if CorrectedBitmap.Map(TMapAccess.Write, DstData) then
try
TParallel.For(0, ABitmap.Height - 1, procedure(y: Integer)
var
x: Integer;
u, v: Double;
SrcColor: TAlphaColor;
begin
for x := 0 to ABitmap.Width - 1 do
begin
u := DistortionCache[y * ABitmap.Width + x].X;
v := DistortionCache[y * ABitmap.Width + x].Y;
if (u >= 0) and (u < ABitmap.Width) and (v >= 0) and (v < ABitmap.Height) then
begin
SrcColor := SrcData.GetPixel(Round(u), Round(v));
DstData.SetPixel(x, y, SrcColor);
end
else
begin
DstData.SetPixel(x, y, TAlphaColorRec.Null);
end;
end;
end);
finally
CorrectedBitmap.Unmap(DstData);
end;
finally
ABitmap.Unmap(SrcData);
end;
ABitmap.Assign(CorrectedBitmap);
finally
CorrectedBitmap.Free;
DistortionCache:=nil;
end;
end;
又跑了一下,正如我所料,并行处理后速度全面提升,整个视频就不卡顿了,非常流畅,在此分享给坚持用Delphi的朋友们。