~关于TBitMap、Canvas.Pixels、ScanLine和图像处理的问题~(分数多多)(300分)

L

lha

Unregistered / Unconfirmed
GUEST, unregistred user!
我在写一段图像处理的代码时遇到一个问题,
如用Canvas.Pixels[x,y]来处理象素点,速度很慢,
于是就用ScanLine[y],如下代码:

for y := 0 to FHeight - 1 do
begin
pRGB := BitMap.ScanLine[y];
for x:= 0 to FWidth - 1 do
begin
...
// to do something;
...
Inc(pRGB);
end;
end;

这段代码从左到右的逐个处理象素点(按行来处理) ,从第一行的第一个象素开始到第一行
的最后一个象素(即等于:Canvas.Pixels[0,0] -> Canvas.Pixels[0,FWidth-1]);
然后再处理下一行(即等于:Canvas.Pixels[x,0] -> Canvas.Pixels[x,FWidth-1]);

可是我现在需要从上到下逐个处理象素点(按列来处理),从第一行的第一个象素到最后一行
的第一个象素(即等于:Canvas.Pixels[0,0] -> Canvas.Pixels[FHeight-1,0]); );
然后再处理下一列(即等于:Canvas.Pixels[y,0] -> Canvas.Pixels[y,FWidth-1]);

其实 就类似下面这段代码:

for x:= 0 to FWidth - 1 do
begin
for y := 0 to FHeight - 1 do
begin
...
Color := Canvas.Pixels[x,y]; // <---- 像这样速度很慢,可是怎么用ScanLine呢?
// to do something;
...
end;
end;

用Canvas.Pixels速度太慢,请各位指点一二;分数好说,不够可再加。


 
我想你所指的太慢是用程序在处理时太慢吧,其实慢的主要原因不在你的程序,而是因为
在你程序之外的系统主线程处理你改过的Image消息时,每改一个点就重画一遍,你最好在
处理之前把image改为不可视,等改完了再画一遍。
 
to ht99:
谢谢你的参与; 可是问题并不在:
“而是因为在你程序之外的系统主线程处理你改过的Image消息时,每改一个点就重画一遍,”
我是用一个FBitMap先处理好,再Assign给Image.Picture.BitMap;

而且就算直接用Image中的BitMap也不会“每改一个点就重画一遍”;



 
大家快帮帮忙呀
 
考虑一下你的算法吧,看是否可以转化成为ScanLine访问方式的。
 
你可以看看BLAND VASULE C++图像处理这本书,你的方法是正确的,不过有待改进,与你选择]
的色彩位数有关,应选择24位色
 
问题提前
 
to lha :
你还是应该运用ScanLine,不过,你应该首先将ScanLine旋转90度!
我给你做好了,来信!forevertyn@sina.com
但是分数我全要!
 
1.把ScanLine复制到一个指针数组,毕竟变量的访问是最快的
2.还是用双重循环
for y:=0 to FHeight-1 do pp[y]:=BitMap.ScanLine[y];

for x := 0 to FWidth - 1 do
begin
for y:= 0 to FHeight - 1 do
begin
cRGB := pp[y]^[x];
...
// to do something;
end;
end;

3.要是还觉得慢可以用汇编,速度可以提高10倍以上
 
我以前也是写了这样一个程序,靠,太慢了,面积太大时就会出错。
 
分成几个线程来工作。
 
这个问题我也碰到过,好象这里用两个嵌套的FOR循环就是慢,听说
外层用Whil...do...内层用For这样会加快点速度不知是不是真的.
这个我没试过:)
 
还是我来试试吧!将像素读到以SCANLINE得到的长度为X,以图象高度为Y的二维数组中,
处理后在写到文件流里,具体的实现方法是从BITMAP创建一个内存流,然后在图象数据
部分写入你修改后的象素,再存回原来的BITMAP,但是不同颜色深度的图象不一样,对于256灰度图象来说,图
象数锯部分存放的是象素的索引值,真正的象素要查前面的颜色表,对24位真彩图形来说
没有颜色表,象素是以三字节的形式依次存放的,找一下相关文档,你会轻易的想到应该
怎么做!
 
思路 :
首先将位图顺时针旋转90度,这样对列的处理就转换为对行的处理,然后,
再逆时针旋转90度。
我的代码发给你了!
 
可以这样写代码:
for x:= 0 to FWidth - 1 do
begin
for y := 0 to FHeight - 1 do
begin
pRGB := BitMap.ScanLine[y];
Color := pRGB[x];

// to do something;
...
end;
end;

 
对cqhxping的代码,我补充:
var pRGB:pByteArray;
 
谢谢各位。
对不起了,最近我一直没空,过几天我试一试各位的方法再来给分。
 
谢谢大家的帮助,分数不多,略表心意。
若各位对分数的分配有何异议,请提出。

另:卷起千堆雪tyn给我的Code,花了我300分,我要公开它。

to 吕雪松:
谢谢你的提示。 分数 : 15

to 党sir:
谢谢你推荐这本书,可是我找不到这本书。 分数 : 30

to 卷起千堆雪tyn:
本问题的分数是300分,我另开一题300分给你。
请注意进去拿分。
本题的分数要分给其他人。

to lxddd:
谢谢你的提示。
“3.要是还觉得慢可以用汇编,速度可以提高10倍以上”
你否再指点一下用汇编如何实现?分数可另行讨论。
分数 : 60

to 林沐:
“太慢了,面积太大时就会出错。”
用ScanLine应该不慢的,出错大概是程序中数组或指针越界了
分数 : 15
to micony:
"外层用Whil...do...内层用For这样会加快点速度"
这个说法很有创意,什么时候我试一试 分数 : 15

to only you:
谢谢,你的方法很不错,只是不太详细,再加上我的水平不够让
你的方法成为code。还好,后来看了 卷起千堆雪tyn
发过来的Code。 分数 : 100

to cqhxping:
你的方法不错,谢谢了。 分数: 50

to micony:
呵呵,谢谢参与。 分数 : 15
 
对不起,刚才分数给的有点小误差。

卷起千堆雪tyn给我的Code,花了我300分,我要公开它:

//----------

unit ImgFlp;

interface
uses Windows, Classes, SysUtils, Graphics;

type
EInvalidPixelFormat = class(Exception);

procedure ImageFlipH(aBitmap: TBitmap);
// flips the image along the center horizontal axis

procedure ImageFlipV(aBitmap: TBitmap);
// flips the image along the center vertical axis

procedure ImageRotate90(aBitmap: TBitmap);
// rotates the bitmap counterclockwise in increments of 90degrees

implementation

const
BitsPerByte = 8; // Assume there are 8 bits in one byte, this may not be true in the future. (JIC)

{ this functions returns the number of bytes needed to represent 1 pixel
in the current PixelFormat of the bitmap. }
function GetPixelSize(aBitmap: TBitmap): integer;
var
nBitCount, nMultiplier: integer;
begin
case aBitmap.PixelFormat of
pfDevice:
begin
nBitCount := GetDeviceCaps(aBitmap.Canvas.Handle, BITSPIXEL);
nMultiplier := nBitCount div BitsPerByte;
if (nBitCount mod BitsPerByte)>0 then
begin
Inc(nMultiplier);
end;
end;
pf1bit: nMultiplier := 1;
pf4bit: nMultiplier := 1;
pf8bit: nMultiplier := 1;
pf15bit: nMultiplier := 2;
pf16bit: nMultiplier := 2;
pf24bit: nMultiplier := 3;
pf32bit: nMultiplier := 4;
else raise EInvalidPixelFormat.Create('Bitmap pixelformat is unknown.');
end;
Result := nMultiplier;
end;

procedure ImageFlipH(aBitmap: TBitmap);
var
nMultiplier,
nMemSize, y, y2,
nHalfHeight, nFullHeight: integer;
aScanLine: PByteArray;

begin
nMultiplier := GetPixelSize(ABitmap);

nMemSize := aBitmap.Width * nMultiplier;
GetMem(aScanLine, nMemSize);
try

nFullHeight := aBitmap.Height;
nHalfHeight := nFullHeight div 2;

{
This FOR-LOOP basically swaps the top and the bottom lines converging
into the center lines.
}
for y := 0 to nHalfHeight do
begin
Move(aBitmap.ScanLine[y]^, aScanLine^, nMemSize);
y2 := nFullHeight - y - 1;
Move(aBitmap.ScanLine[y2]^, aBitmap.ScanLine[y]^, nMemSize);
Move(aScanLine^, aBitmap.ScanLine[y2]^, nMemSize);
end;

finally
FreeMem(aScanLine);
end;
end;

{
This procedure swaps the pixel information in a scanline, in other words, the
leftmost pixel is swapped with the rightmost pixel moving to the middle of the
scanline. nSize is the total byte size of the scanline. nElemWidth is the
number of bytes per pixel. (e.g. for 24bit, 1 pixel is 3 bytes)
}
procedure ReverseScanLine(AScanLine: PByteArray; nSize, nElemWidth: integer);
var
i, j, w, w2, x1, x2: integer;
aByte: Byte;
begin
w := nSize div nElemWidth;
w2 := w div 2;
for i := 0 to w2 do
begin
x1 := i * nElemWidth;
x2 := (w - i - 1) * nElemWidth;
for j := 0 to nElemWidth-1 do
begin
aByte := AScanLine[x1 + j];
AScanLine[x1 + j] := AScanLine[x2 + j];
AScanLine[x2 + j] := aByte;
end;
end;
end;

procedure ImageFlipV(aBitmap: TBitmap);
var
nMultiplier,
nMemSize, y,
nFullHeight: integer;
aScanLine: PByteArray;
begin

nMultiplier := GetPixelSize(ABitmap);

nMemSize := aBitmap.Width * nMultiplier;
GetMem(aScanLine, nMemSize);
try

nFullHeight := aBitmap.Height;

{ this FOR-LOOP read the scanlines into a buffer and swaps the pixels and
then writes it back into the bitmap scanlines. the buffer is necessary
because it is faster working with a copy of the scanline than using the
scanline itself }
for y := 0 to nFullHeight-1 do
begin
Move(aBitmap.ScanLine[y]^, aScanLine^, nMemSize);
ReverseScanLine(aScanLine, nMemSize, nMultiplier);
Move(aScanLine^, aBitmap.ScanLine[y]^, nMemSize);
end;

finally
FreeMem(aScanLine);
end;
end;

{ this procedure rotates the image counterclockwise by 90 degrees.
i could have made this routine rotate clockwise but doing it in
counter-clockwise is easier and thus less prone to bugs.

what we do here is grab the first scanline and distribute each pixel
along one column of the new image (or in this case new scanlines buffer)
we do this until we run out of scanlines then we are done.}
procedure ImageRotate90(aBitmap: TBitmap);
var
nIdx, nOfs,
x, y, i,
nMultiplier: integer;
nMemWidth, nMemHeight, nMemSize,
nScanLineSize: LongInt;

aScnLnBuffer: PChar;
aScanLine: PByteArray;
begin

nMultiplier := GetPixelSize(ABitmap);

nMemWidth := aBitmap.Height;
nMemHeight := aBitmap.Width;
nMemSize := nMemWidth * nMemHeight * nMultiplier;

GetMem(aScnLnBuffer, nMemSize);
try
nScanLineSize := aBitmap.Width * nMultiplier;
GetMem(aScanLine, nScanLineSize);
try
for y := 0 to aBitmap.Height-1 do
begin
Move(aBitmap.ScanLine[y]^, aScanLine^, nScanLineSize);
for x := 0 to aBitmap.Width-1 do
begin
nIdx := ((aBitmap.Width-1) - x) * nMultiplier;
nOfs := (x * nMemWidth * nMultiplier) + // y component of the dst
(y * nMultiplier); // x component of the dst
for i := 0 to nMultiplier-1 do
Byte(aScnLnBuffer[nOfs + i]) := aScanLine[nIdx+i];
end;
end;

aBitmap.Height := nMemHeight;
aBitmap.Width := nMemWidth;

for y := 0 to nMemHeight-1 do
begin
nOfs := y * nMemWidth * nMultiplier;
Move((@(aScnLnBuffer[nOfs]))^, aBitmap.ScanLine[y]^, nMemWidth * nMultiplier);
end;

finally
FreeMem(aScanLine, nScanLineSize);
end;
finally
FreeMem(aScnLnBuffer, nMemSize);
end;

end;

end.
 
好好好同志,免得我又花300。
 

Similar threads

回复
0
查看
671
不得闲
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
523
import
I
顶部 底部