Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 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.041 c
4-1172262979
Comp
2007-02-23 23:36
2007.08.12
TWMMouseWheel


2-1183970865
Newser
2007-07-09 12:47
2007.08.12
тип поля - Дата/время


2-1184166994
AZIZE
2007-07-11 19:16
2007.08.12
Как в цикле уменьнить на еденицу переменную цикла


6-1163349166
__nick__
2006-11-12 19:32
2007.08.12
Подскажите как извлечь картинку с Webbrowser


1-1180686550
Inna_Z
2007-06-01 12:29
2007.08.12
Как послать e-mail с русским текстом.





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский