Форум: "Прочее";
Текущий архив: 2007.08.12;
Скачать: [xml.tar.bz2];
ВнизНевероятно медленная обработка пикселей Найти похожие ветки
← →
Mr. D. (2007-07-11 18:34) [0]У меня есть такая задача - имеется BMP картинка, из BMP нужно сделать картинку размером куда меньше. Например, была 100x100, а надо сделать 10x10, чтобы она в какой-то степени может быть повторяла исходную.
Признаюсь, что реализовал крайне элементарно, но тем не менее для меня способ может подойти. Оригинальное изображение делится на нужное число квадратов (соответствующее конечному числу пикселей). И внутри каждого квадрата происходит простое сложение интенсивностей RGB-цветов и потом деление. Фактически, получается средне-арифмитическая интенсивность цветов RGB в конечном пикселе.
Таким образом получается конечная картинка.
С разбиением области на квадраты проблем нет.
Проблема в том, что это осреднение области в пиксель происходит очень медленно! Настолько медленно, что я не понимаю как это может быть. Вот код для одного прямоугольника:
var
Rect: TRect; // координаты прямоугольника
NumRda, NumX, NumY, Deliter, i: integer;
Blue, Green, Red: int64;
PixelColor: TColor;
.......................................................
Blue := 0;
Green := 0;
Red := 0;
Deliter := 0;
for NumY := Rect.Top to Rect.Bottom do
for NumX := Rect.Left to Rect.Right do
begin
Blue := Blue + ( (FScreenBitmap.Canvas.Pixels[NumX, NumY] and $00FF0000) shr 16 ) ;
Green := Green + ( (FScreenBitmap.Canvas.Pixels[NumX, NumY] and $0000FF00) shr 8 ) ;
Red := Red + ( (FScreenBitmap.Canvas.Pixels[NumX, NumY] and $000000FF) ) ;
inc(Deliter);
end;
Blue := Round( Blue / Deliter );
Green := Round( Green / Deliter );
Red := Round( Red / Deliter );
PixelColor := ( integer(Blue) shl 16 ) +
( integer(Green) shl 8 ) +
( integer(Red) ) ;
Если брать весь экран 1024x768 и разбивать на 9 прямоугольников (результирующих пикселей), то на машине порядка 2 Ггц процессор время обработки около 4-ех секунд!!!
Не могу понять в чем причина тормозов.
← →
jack128_ (2007-07-11 18:37) [1]TBitmap.ScanLine + F1
← →
@!!ex © (2007-07-11 18:45) [2]патамучта Pixels одна из самый медленных операций при работе с битмапом.
ScanLine.
И вообще почему мы не пользуемся софтом, указывающим узкие места программы?
например AQTime.
← →
P_ (2007-07-11 18:54) [3]
> FScreenBitmap.Canvas.Pixels[NumX, NumY]
Гы. Я тоже в первый раз работы с графикой на эти грабли наступил. Почему в справке не говорится, что это ОЧЕНЬ МЕДЛЕННАЯ ОПЕРАЦИЯ.
← →
default © (2007-07-11 19:10) [4]и в .NET также;)
там с этой целью есть возможность получить кусок памяти содержащий все цвета изображения, потом поработав с этим куском памяти напрямую он сливается в исходное изображение
← →
Mr. D. (2007-07-11 19:45) [5]Я так понимаю дело в медленной работе API функции GetPixel ?
Интересно, а почему так происходит? Наверняка битмап хранится в памяти как последовательность байтов. Ведь это простейшая операция - вытащить нужный байт из массива по известному смещению (ака по заданным X и Y)
← →
Anton_K © (2007-07-11 20:37) [6]
> Я так понимаю дело в медленной работе API функции GetPixel
Да
> Интересно, а почему так происходит?
Наверное потому что битмапы разными бывают... 8 бит там, 24... а функция одна на всех и поэтому должна сначала определить формат битмапа.
← →
palva © (2007-07-11 21:07) [7]
> Почему в справке не говорится, что это ОЧЕНЬ МЕДЛЕННАЯ ОПЕРАЦИЯ.
Потому что TBitmap предназначен для ОТОБРАЖЕНИЯ картинки. Это, надеюсь, в справке сказано. А для обработки картинку надо читать непосредственно из файла в массив (надеюсь, формат BMP общеизвестный) и в массиве обрабатывать. Это потребует меньше времени и памяти.
← →
Юрий Зотов © (2007-07-11 21:38) [8]А ежели Stretch, а потом скопировать? Не будет ли это проще и быстрее?
← →
wicked © (2007-07-11 22:31) [9]
> Потому что TBitmap предназначен для ОТОБРАЖЕНИЯ картинки.
> Это, надеюсь, в справке сказано.
TBitmap предназначен для хранения картинки... для отображения есть TImage
> А для обработки картинку надо читать непосредственно из
> файла в массив (надеюсь, формат BMP общеизвестный) и в массиве
> обрабатывать. Это потребует меньше времени и памяти.
CreateDIBSection + StretchDIBits -- молотим сырые пиксели в том формате, в каком нам надо
> А ежели Stretch, а потом скопировать? Не будет ли это проще
> и быстрее?
будет
SetStretchBltMode(hdc, HALFTONE) и далее StretchBlt или StretchDIBits
хотя как то еретически это - опираться на api при обработке изображения
ну и на win9x/me опция HALFTONE не поддерживается
← →
Efir © (2007-07-11 22:35) [10]
> и в .NET также;)
А мне показалось, что в GDI+ GetPixel работает чуть быстрее, правда только чуть.
← →
Mr. D. (2007-07-11 23:56) [11]
> будет
> SetStretchBltMode(hdc, HALFTONE) и далее StretchBlt или
> StretchDIBits
а зачем SetStretchBltMode? Сразу StretchBlt нельзя?
← →
@!!ex © (2007-07-11 23:58) [12]> [11] Mr. D. (11.07.07 23:56)
Можно. Просто эта функция позволяет настраивать функцию растяжения. Ведь есть несколько вариантов, дажеющие разный конечный результат.
Можно не использовать, тогда используются настройки по умолчанию.
← →
P_ (2007-07-12 03:44) [13]
> palva © (11.07.07 21:07) [7]
>
>
> > Почему в справке не говорится, что это ОЧЕНЬ МЕДЛЕННАЯ
> ОПЕРАЦИЯ.
>
> Потому что TBitmap предназначен для ОТОБРАЖЕНИЯ картинки.
> Это, надеюсь, в справке сказано. А для обработки картинку
> надо читать непосредственно из файла в массив (надеюсь,
> формат BMP общеизвестный) и в массиве обрабатывать. Это
> потребует меньше времени и памяти.
Зачем из файлв в массив, все стырено до нас: Bitmap.ScanLine
← →
Думкин © (2007-07-12 07:09) [14]http://www.delphimaster.ru/articles/pixels/index.html
Поставить точку... Быстрый доступ к пикселам TBitmap
При работе с растровой графикой очень часто возникает задача попиксельного доступа к Bitmap. Удобно и очень просто при этом пользоваться свойством Canvas.Pixels, но работает такой метод очень медленно - прочитать, установить или поменять цвета нескольких точек или не слишком большой области можно, но вот проводить какую-либо цифровую обработку - затруднительно. Для быстрого доступа к данным у класса TBitmap имеется свойство ScanLine - скорость при этом вырастает на порядок, но возникают и существенные проблемы - обращение к пикселам будет выглядеть совершенно по-разному в зависимости от формата растра (цветовой глубины - 8, 16, 24 бит/пиксел и т.д.), т.е. страдает универсальность разрабатываемых процедур.
Причины низкой скорости доступа к Pixels вкратце таковы: при каждом таком обращении вызываются функции API SetPixel или GetPixel, которые должны заблокировать передаваемый контекст устройства (далее DC - Device Context), определить текущее преобразование координат, с его учетом проверить, попадает ли пиксел в доступный регион DC, установить или прочитать значение цвета пиксела с преобразованием к нужному цветовому формату, что осуществляется c помощью "блиттинга" - копирования прямоугольного участка DC, после чего разблокировать DC. Все это требует существенных затрат процессорного времени (в том числе и на переход в режим ядра и назад).
← →
Хаванагил (2007-07-12 08:30) [15]
> Думкин © (12.07.07 07:09) [14]
хорошая статья. активно пользовался в свое время.
← →
Карелин Артем © (2007-07-12 08:42) [16]http://g32.org/ - можно попиксельно и жутко быстро обрабатывать
← →
Однокамушкин (2007-07-12 09:20) [17]Ещё учтите, что ScanLine работает быстро только с теми картинками, у которых HandleType=bmDIB, т.е. картинка хранится как DIB-секция, а если HandleType=bmDDB, то при каждом обращении к ScanLine создаётся новая DIB-секция, туда копируется картинка, и только потом возвращается указатель на соответствующий байт в этой DIB-секции... Соответственно, каждый вызов ScanLine обходится очень дорого из-за необходимости создания DIB-секции и копирования туда картинки...
← →
Mr. D. (2007-07-14 17:07) [18]К сожалению, ScanLine показал свою несостоятельность :( Или я не умею его использовать.
Вот пример:const
Pixels = MaxInt div SizeOf(TRGBTriple);
type
PRGBArray = ^TRGBArray;
TRGBArray = array[0..Pixels-1] of TRGBTriple;
var
Rect: TRect; // координаты прямоугольника
NumRda, NumX, NumY, Deliter, i: integer;
Blue, Green, Red: int64;
PixelColor: TColor;
Row: PRGBArray;
....................................................
Blue := 0;
Green := 0;
Red := 0;
Deliter := 0;
for NumY := Rect.Top to Rect.Bottom do
begin
Row := FScreenBitmap.ScanLine[NumY];
for NumX := Rect.Left to Rect.Right do
begin
Blue := Blue + Row[NumX].rgbtBlue ;
Green := Green + Row[NumX].rgbtGreen ;
Red := Red + Row[NumX].rgbtRed ;
inc(Deliter);
end;
end;
Blue := Round( Blue / Deliter );
Green := Round( Green / Deliter );
Red := Round( Red / Deliter );
PixelColor := ( integer(Blue) shl 16 ) +
( integer(Green) shl 8 ) +
( integer(Red) ) ;
Как показалы опыты, при обработке тестового прямоугольника размерами 342x256 - код выполняется непомерно долго.
В частности, если сложить все время, которое было затрачено на вызов ScanLine (вызывов, соответственно, было 256), то получится порядка 11 секунд!!!!
В результате для всех девяти прямоугольниов время зашкаливает за 90 секунд. В то время как с GetPixels время было порядка 4-ех секунд.
Я не понимаю :(
← →
Mr. D. (2007-07-14 17:15) [19]Однокамушкин (12.07.07 9:20) [17]
если HandleType=bmDDB, то при каждом обращении к ScanLine создаётся новая DIB-секция
да, в моем случае HandleType=bmDDB. Видимо, поэтому так долго.
Что делать, чтобы перевести из одного формата в другой?
А что будет, если у меня картинка окажется не TrueColor ?! :(
Моя картинка - это собственно снимок с экрана. Снимок я делаю таким образом:procedure TfrmMain.CaptureScreen(ABitmap: TBitmap; Area: TRect);
// Area - координаты прямоугольника, внутри которого съемка экрана
const
CAPTUREBLT = $40000000;
var
hdcScreen: HDC;
hdcCompatible: HDC;
hbmScreen: HBITMAP;
begin
hdcScreen := CreateDC("DISPLAY", nil, nil, nil);
hdcCompatible := CreateCompatibleDC(hdcScreen);
hbmScreen := CreateCompatibleBitmap(hdcScreen,
Area.Right - Area.Left,
Area.Bottom - Area.Top);
SelectObject(hdcCompatible, hbmScreen);
ABitmap.Handle := hbmScreen;
BitBlt(hdcCompatible,
0, 0,
ABitmap.Width, ABitmap.Height,
hdcScreen,
Area.Left, Area.Top,
SRCCOPY or CAPTUREBLT);
DeleteDC(hdcScreen);
DeleteDC(hdcCompatible);
end;
← →
P_ (2007-07-14 18:30) [20]ABitmap.Dormant
или же просто
HandleType=bmDIB
← →
homm © (2007-07-15 12:29) [21]> сожалению, ScanLine показал свою несостоятельность
Пока свою несостоятельность показали только Вы ;)procedure TForm1.Button1Click(Sender: TObject);
const
Pixels = MaxInt div SizeOf(TRGBQuad);
N = 4; {Vo stoka raz ymenshaem
!!!shirina i dlina dolghni nacelo delitsya na eto shislo}
type
PRGBArray = ^TRGBArray;
TRGBArray = array[0..Pixels-1] of TRGBQuad;
var
bFrom, bTo: TBitmap;
i, j, k, l, W: Integer;
R, G, B, T: DWORD;
ToRow, FromRow: PRGBArray;
begin
bFrom := TBitmap.Create();
bFrom.LoadFromFile("D:\Photo\Luser-Puser.bmp");
bFrom.HandleType := bmDIB;
bFrom.PixelFormat := pf32bit;
bTo := TBitmap.Create();
bTo.Width := bFrom.Width div N;
bTo.Height := bFrom.Height div N;
bTo.HandleType := bmDIB;
bTo.PixelFormat := pf32bit;
T := GetTickCount();
for W := 0 to 9 do begin
for i := 0 to bTo.Height-1 do begin
ToRow := bTo.ScanLine[i];
for j := 0 to bTo.Width-1 do begin
R := 0;
B := 0;
G := 0;
for k := i*N to ((i+1)*N)-1 do begin
FromRow := bFrom.ScanLine[k];
for l := j*N to ((j+1)*N)-1 do begin
B := B + FromRow[l].rgbBlue;
G := G + FromRow[l].rgbGreen;
R := R + FromRow[l].rgbRed;
end;
end;
ToRow[j].rgbBlue := B div (N*N);
ToRow[j].rgbGreen := G div (N*N);
ToRow[j].rgbRed := R div (N*N);
end;
end;
end;
Canvas.Draw(0, 0, bTo);
bTo.Free;
//Canvas.Draw(0, 0, bFrom);
bFrom.Free;
ShowMessage(IntToStr(GetTickCount()-T)+" mSec elapsed to resample 10 times");
end;
Celeron 500MHz. Тестовая картинка 640*480 дает 660 mSec. Т.е. по 66 mSec на один проход.
Если бы я писал для себя, я еше переписал бы на ассемблере с MMX, было бы еше в 2 раза быстрее.
← →
homm © (2007-07-15 14:16) [22]Есть еше вот такой код, правда для КОЛ"овского PBitmap, а не VCL"овского TBitmap. Перевести думаю не сложно
UseMMX := CPUisMMX();
function CPUisMMX: Boolean;
var I: Integer;
begin
I := 0;
Result := false;
asm // check if bit 21 of EFLAGS can be set and reset
PUSHFD
POP EAX
OR EAX, 1 shl 21
PUSH EAX
POPFD
PUSHFD
POP EAX
TEST EAX, 1 shl 21
JZ @@1
AND EAX, not( 1 shl 21 )
PUSH EAX
POPFD
PUSHFD
POP EAX
TEST EAX, 1 shl 21
JNZ @@1
INC [ I ]
@@1:
end;
if I = 0 then Exit; // CPUID not supported
asm // get CPU features flags using CPUID command
MOV EAX, 1
PUSH EDX
PUSH EBX
PUSH ECX
DB $0F, $A2
MOV [ I ], EDX // I := features information
POP ECX
POP EBX
POP EDX
end;
if (I and (1 shl 23)) <> 0 then
Result := true;
end;
procedure BitmapAntialias4X(SrcBitmap, DstBitmap: PBitmap);
type AGRBQuad = array [0..0] of TRGBQuad;
PAGRBQuad = ^AGRBQuad;
var yDest: integer;
xDest: integer;
xSrc: integer;
i: integer;
R: integer;
G: integer;
B: integer;
rowDest: PAGRBQuad;
rowSrc: array [0..3] of PAGRBQuad;
_rowSrc: PAGRBQuad;
SrcBits: DWORD;
DstBits: DWORD;
dHeight: DWORD;
dWidth: DWORD;
Delta: DWORD;
begin
if UseMMX then begin
SrcBits := DWORD(SrcBitmap.DIBBits);
DstBits := DWORD(DstBitmap.DIBBits);
dHeight := DstBitmap.Height;
dWidth := DstBitmap.Width;
Delta := SrcBitmap.ScanLineSize;
asm
pushad
mov esi, SrcBits
mov edi, DstBits
//pxor mm2, mm2
db $0f, $ef, $d2
mov eax, dHeight
@LM1: push eax
mov eax, dWidth
@LM2: /////////
mov ecx, esi
//movd mm1, [ecx]
db $0f, $6e, $09
//punpcklbw mm1, mm2
db $0f, $60, $ca
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+8]
db $0f, $6e, $59, $08
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+12]
db $0f, $6e, $59, $0c
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
add ecx, Delta
//movd mm3, [ecx]
db $0f, $6e, $19
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+8]
db $0f, $6e, $59, $08
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+12]
db $0f, $6e, $59, $0c
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
add ecx, Delta
//movd mm3, [ecx]
db $0f, $6e, $19
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+8]
db $0f, $6e, $59, $08
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+12]
db $0f, $6e, $59, $0c
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
add ecx, Delta
//movd mm3, [ecx]
db $0f, $6e, $19
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+8]
db $0f, $6e, $59, $08
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+12]
db $0f, $6e, $59, $0c
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//psrlw mm1, 4
db $0f, $71, $d1, $04
//packuswb mm1, mm2
db $0f, $67, $ca
//movd [edi], mm1
db $0f, $7e, $0f
/////////
add edi, 4
add esi, 16
sub eax, 1
jnz @LM2
mov ecx, Delta
lea esi, [esi + ecx*2]
add esi, ecx
pop eax
sub eax, 1
jnz @LM1
//emms
db $0f, $77
popad
end;
end else
for yDest := 0 to DstBitmap.Height -1 do begin
rowDest := DstBitmap.ScanLine[yDest];
for i := 0 to 3 do
rowSrc[i] := SrcBitmap.ScanLine[yDest*4+i];
for xDest := 0 to DstBitmap.Width-1 do begin
xSrc := xDest*4;
R:=0; G:=0; B:=0;
for i := 0 to 3 do begin
_rowSrc := rowSrc[i];
R:= R+_rowSrc[xSrc+0].rgbRed
+ _rowSrc[xSrc+1].rgbRed
+ _rowSrc[xSrc+2].rgbRed
+ _rowSrc[xSrc+3].rgbRed;
G:= G+_rowSrc[xSrc+0].rgbGreen
+ _rowSrc[xSrc+1].rgbGreen
+ _rowSrc[xSrc+2].rgbGreen
+ _rowSrc[xSrc+3].rgbGreen;
B:= B+_rowSrc[xSrc+0].rgbBlue
+ _rowSrc[xSrc+1].rgbBlue
+ _rowSrc[xSrc+2].rgbBlue
+ _rowSrc[xSrc+3].rgbBlue;
end;
DWORD(rowDest[xDest]) := ((R and $0ff0) shl 12) or ((G and $0ff0) shl 4) or (B shr 4);
end;
end;
end;
(подолжение следует…)
← →
homm © (2007-07-15 14:17) [23](…продолжение)
procedure BitmapAntialias2X(SrcBitmap, DstBitmap: PBitmap);
type AGRBQuad = array [0..0] of TRGBQuad;
PAGRBQuad = ^AGRBQuad;
var yDest: integer;
xDest: integer;
xSrc: integer;
i: integer;
R: integer;
G: integer;
B: integer;
rowDest: PAGRBQuad;
rowSrc: array [0..3] of PAGRBQuad;
_rowSrc: PAGRBQuad;
SrcBits: DWORD;
DstBits: DWORD;
dHeight: DWORD;
dWidth: DWORD;
Delta: DWORD;
begin
if UseMMX then begin
SrcBits := DWORD(SrcBitmap.DIBBits);
DstBits := DWORD(DstBitmap.DIBBits);
dHeight := DstBitmap.Height;
dWidth := DstBitmap.Width;
Delta := SrcBitmap.ScanLineSize;
asm
pushad
mov esi, SrcBits
mov edi, DstBits
//pxor mm2, mm2
db $0f, $ef, $d2
mov eax, dHeight
@LM1: push eax
mov eax, dWidth
@LM2: /////////
mov ecx, esi
//movd mm1, [ecx]
db $0f, $6e, $09
//punpcklbw mm1, mm2
db $0f, $60, $ca
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
add ecx, Delta
//movd mm3, [ecx]
db $0f, $6e, $19
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//movd mm3, [ecx+4]
db $0f, $6e, $59, $04
//punpcklbw mm3, mm2
db $0f, $60, $da
//paddusw mm1, mm3
db $0f, $dd, $cb
//psrlw mm1, 2
db $0f, $71, $d1, $02
//packuswb mm1, mm2
db $0f, $67, $ca
//movd [edi], mm1
db $0f, $7e, $0f
/////////
add edi, 4
add esi, 8
sub eax, 1
jnz @LM2
add esi, Delta
pop eax
sub eax, 1
jnz @LM1
//emms
db $0f, $77
popad
end;
end else
for yDest := 0 to DstBitmap.Height -1 do begin
rowDest := DstBitmap.ScanLine[yDest];
for i := 0 to 1 do
rowSrc[i] := SrcBitmap.ScanLine[yDest*2+i];
for xDest := 0 to DstBitmap.Width-1 do begin
xSrc := xDest*2;
R:=0; G:=0; B:=0;
for i := 0 to 1 do begin
_rowSrc := rowSrc[i];
R:= R+_rowSrc[xSrc+0].rgbRed
+ _rowSrc[xSrc+1].rgbRed;
G:= G+_rowSrc[xSrc+0].rgbGreen
+ _rowSrc[xSrc+1].rgbGreen;
B:= B+_rowSrc[xSrc+0].rgbBlue
+ _rowSrc[xSrc+1].rgbBlue;
end;
DWORD(rowDest[xDest]) := ((R and $03fc) shl 14) or ((G and $03fc) shl 6) or (B shr 2);
end;
end;
end;
← →
homm © (2007-07-15 14:30) [24]
procedure TForm1.Button2Click(Sender: TObject);
var
bFrom, bTo: KOL.PBitmap;
pf: TPixelFormat;
T: DWORD;
W: Integer;
begin
bFrom := NewDIBBitmap(0, 0, pf32bit);
bFrom.LoadFromFile("D:\Photo\Luser-Puser.bmp");
bFrom.PixelFormat := pf32bit;
bTo := NewDIBBitmap( bFrom.Width div 4, bFrom.Height div 4, pf32bit);
T := GetTickCount();
for W := 0 to 99 do
BitmapAntialias4X(bFrom, bTo);
bTo.Draw(Canvas.Handle, 0, 0);
ShowMessage(IntToStr(GetTickCount()-T)+" mSec elapsed to resample 100 times");
bTo.Free;
bFrom.Free;
end;
Вот такой тест уже дает 5,5 mSec на ту-же самую картинку 640*480, при уменьшении ее в 4 раза.
← →
wicked © (2007-07-15 15:14) [25]хех.... даже без mmx код из [21] можно намного улучшить, заберя или сведя к минимуму операции умножения и (о, ужас) деления в циклах
← →
homm © (2007-07-15 15:28) [26]> [25] wicked © (15.07.07 15:14)
Жду твоего варианта, особенно насчет «(о, ужас) деления в циклах» :)
← →
homm © (2007-07-15 15:32) [27]> [25] wicked © (15.07.07 15:14)
Если иммется ввиду так:ToRow[j].rgbBlue := B shr 4;
ToRow[j].rgbGreen := G shr 4;
ToRow[j].rgbRed := R shr 4;
То
1) Достаточно проблематично выразить четверку через N, которая, как видно, константа.
2) Ни на грам не прибавляет скорости, так что никакой это не «о, ужас».
← →
homm © (2007-07-15 15:35) [28]> [25] wicked © (15.07.07 15:14)
А вот так вот:t1 := i*N;
for k := t1 to t1+N-1 do begin
FromRow := bFrom.ScanLine[k];
t2 := j*N;
for l := t2 to t2+N-1 do begin
B := B + FromRow[l].rgbBlue;
G := G + FromRow[l].rgbGreen;
R := R + FromRow[l].rgbRed;
end;
end;
…даже немного проигрываем в скорости :p
← →
wicked © (2007-07-15 15:39) [29]> homm © (15.07.07 15:28) [26]
у меня delphi нету :)
← →
homm © (2007-07-15 16:23) [30]> [29] wicked © (15.07.07 15:39)
На самом деле оптимизировать конечно можно, причем очень существенно, правда все умножения и деления остаются в теле цикла ;)procedure TForm1.Button1Click(Sender: TObject);
const
Pixels = MaxInt div SizeOf(TRGBQuad);
N = 4; {Vo stoka raz ymenshaem
!!!shirina i dlina dolghni nacelo delitsya na eto shislo}
type
PRGBArray = ^TRGBArray;
TRGBArray = array[0..Pixels-1] of TRGBQuad;
var
bFrom, bTo: Graphics.TBitmap;
i, j, k, l, W: Integer;
R, G, B, T: DWORD;
ToRow: PRGBArray;
FromRow: array [0..N-1] of PRGBArray;
begin
bFrom := Graphics.TBitmap.Create();
bFrom.LoadFromFile("D:\Photo\Luser-Puser.bmp");
bFrom.PixelFormat := Graphics.pf32bit;
bTo := Graphics.TBitmap.Create();
bTo.Width := bFrom.Width div N;
bTo.Height := bFrom.Height div N;
bTo.PixelFormat := Graphics.pf32bit;
T := GetTickCount();
for W := 0 to 99 do begin
for i := 0 to bTo.Height-1 do begin
ToRow := bTo.ScanLine[i];
for j := 0 to N-1 do
FromRow[j] := bFrom.ScanLine[i*N+j];
for j := 0 to bTo.Width-1 do begin
R := 0;
B := 0;
G := 0;
for k := 0 to N-1 do begin
for l := j*N to j*N+N-1 do begin
B := B + FromRow[k][l].rgbBlue;
G := G + FromRow[k][l].rgbGreen;
R := R + FromRow[k][l].rgbRed;
end;
end;
ToRow[j].rgbBlue := B div (N*N);
ToRow[j].rgbGreen := G div (N*N);
ToRow[j].rgbRed := R div (N*N);
end;
end;
end;
Canvas.Draw(0, 0, bTo);
bTo.Free;
//Canvas.Draw(0, 0, bFrom);
bFrom.Free;
ShowMessage(IntToStr(GetTickCount()-T)+" mSec elapsed to resample 100 times");
end;
У меня получилось 16 mSec. Думаю этот вариант самый подходящий для автора. Все-же асемблерные процедурки сильно заточены под конкретное уменьшение в 2 и 4 раза, а разобраться в них достаточно сложно.
← →
homm © (2007-07-15 17:17) [31]
procedure TForm1.Button1Click(Sender: TObject);
const
Pixels = MaxInt div SizeOf(TRGBQuad);
N = 4; {Vo stoka raz ymenshaem
!!!shirina i dlina dolghni nacelo delitsya na eto shislo}
type
PRGBArray = ^TRGBArray;
TRGBArray = array[0..Pixels-1] of TRGBQuad;
var
bFrom, bTo: Graphics.TBitmap;
i, j, k, l, W: Integer;
R, G, B, T: DWORD;
ToRow: PRGBArray;
FromRow: array [0..N-1] of PRGBArray;
CurRow: PRGBArray;
begin
bFrom := Graphics.TBitmap.Create();
bFrom.LoadFromFile("D:\Photo\Luser-Puser.bmp");
bFrom.PixelFormat := Graphics.pf32bit;
bTo := Graphics.TBitmap.Create();
bTo.Width := bFrom.Width div N;
bTo.Height := bFrom.Height div N;
bTo.PixelFormat := Graphics.pf32bit;
T := GetTickCount();
for W := 0 to 99 do begin
for i := 0 to bTo.Height-1 do begin
ToRow := bTo.ScanLine[i];
for j := 0 to N-1 do
FromRow[j] := bFrom.ScanLine[i*N+j];
for j := 0 to bTo.Width-1 do begin
R := 0;
B := 0;
G := 0;
for k := 0 to N-1 do begin
for l := j*N to j*N+N-1 do begin
CurRow := FromRow[k];
B := B + CurRow[l].rgbBlue;
G := G + CurRow[l].rgbGreen;
R := R + CurRow[l].rgbRed;
end;
end;
ToRow[j].rgbBlue := B div (N*N);
ToRow[j].rgbGreen := G div (N*N);
ToRow[j].rgbRed := R div (N*N);
end;
end;
end;
Canvas.Draw(0, 0, bTo);
bTo.Free;
//Canvas.Draw(0, 0, bFrom);
bFrom.Free;
ShowMessage(IntToStr(GetTickCount()-T)+" mSec elapsed to resample 10 times");
end;
Еще немного шаманства, и результат уже 12 mSec :)
← →
Sdubaruhnul (2007-07-15 21:37) [32]2 homm:
Можно и ещё, не прибегая к ассемблеру и зная свойства Scanline:
const
Pixels = MaxInt div SizeOf(TRGBQuad);
type
PRGBArray = ^TRGBArray;
TRGBArray = array [0..Pixels-1] of TRGBQuad;
procedure TformMain.btnTestClick(Sender: TObject);
const
N = 4;
NN = N*N;
var
bFrom, bTo: Graphics.TBitmap;
i, j, k, l, W: Integer;
R, G, B, T: DWORD;
ToRow: PRGBArray;
FromRow: array [0..N-1] of PRGBArray;
CurRow: PRGBArray;
ToBpR, FromBpR: Integer;
begin
bFrom := Graphics.TBitmap.Create();
bFrom.LoadFromFile("test.bmp");
bFrom.PixelFormat := Graphics.pf32bit;
bTo := Graphics.TBitmap.Create();
bTo.Width := bFrom.Width div N;
bTo.Height := bFrom.Height div N;
bTo.PixelFormat := Graphics.pf32bit;
try
T := GetTickCount();
for W := 0 to 999 do
begin
ToRow := bTo.ScanLine[0];
ToBpR := Integer(bTo.Scanline[1]) - Integer(ToRow);
FromRow[0] := bFrom.ScanLine[0];
FromBpR := Integer(bFrom.Scanline[1]) - Integer(FromRow[0]);
for i := 0 to bTo.Height-1 do
begin
for j := 1 to N-1 do
Integer(FromRow[j]) := Integer(FromRow[j-1]) + FromBpR;
for j := 0 to bTo.Width-1 do
begin
R := 0;
B := 0;
G := 0;
for k := 0 to N-1 do
begin
for l := j*N to j*N+N-1 do
begin
CurRow := FromRow[k];
B := B + CurRow[l].rgbBlue;
G := G + CurRow[l].rgbGreen;
R := R + CurRow[l].rgbRed;
end;
end;
ToRow[j].rgbBlue := B div (N*N);
ToRow[j].rgbGreen := G div (N*N);
ToRow[j].rgbRed := R div (N*N);
end;
Integer(FromRow[0]) := Integer(FromRow[N-1]) + FromBpR;
Inc(Integer(ToRow), ToBpR);
end;
end;
Canvas.Draw(0, 0, bTo);
finally
bTo.Free;
bFrom.Free;
end;
ShowMessageFmt("%d ms after 1000 times", [GetTickCount()-T])
end;
Было 2250 мс в оригинале, стало 2050 мс.
← →
homm © (2007-07-16 00:21) [33]> [32] Sdubaruhnul (15.07.07 21:37)
Смысл сего колдунства мне ясен, уменьшает количество вызовов GetScanLine уменьшается с 600 до 4-х, но на тестовой машине эфекта не возимело, видимо у Вас дельфи более старшая, в D5 вызов GetScanLine обходится дешевле.
← →
Думкин © (2007-07-16 06:30) [34]
> Mr. D. (14.07.07 17:15) [19]
Я тебе ссылку для собственного удовольствия привел? Ходить по граблям, видимо, твое хобби. Добрый путь.
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2007.08.12;
Скачать: [xml.tar.bz2];
Память: 0.62 MB
Время: 0.048 c