Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.08.12;
Скачать: CL | DM;

Вниз

Невероятно медленная обработка пикселей   Найти похожие ветки 

 
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;
Скачать: CL | DM;

Наверх




Память: 0.62 MB
Время: 0.034 c
2-1184093695
Ivolg
2007-07-10 22:54
2007.08.12
Вывести Hint в определенных координатах


2-1184298375
Интересующийся
2007-07-13 07:46
2007.08.12
Защита программы


15-1184583715
StriderMan
2007-07-16 15:01
2007.08.12
Commit


15-1184382310
Riply
2007-07-14 07:05
2007.08.12
Велик могучим С++ языка ! :)


2-1184660136
Sergey_G
2007-07-17 12:15
2007.08.12
Подключение сетевой базы данных