Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Bitmap Kantenglättung (https://www.delphipraxis.net/130855-bitmap-kantenglaettung.html)

marius0702 14. Mär 2009 12:52


Bitmap Kantenglättung
 
ich hab heute eine kantenglättung für bitmaps geschrieben und wollte mal fragen ob man das villeicht noch etwas beschleunigen kann :gruebel: .
mein pc braucht für ein bild mit 299x342 pixeln und 1xAA ca 3,8 ms.
für größere bilder und für animationen wären ein paar ms weniger nicht schlecht :-D .

Delphi-Quellcode:

function AA(bmp:tbitmap;haeufigkeit:integer;intensitaet:integer;backgroundcolor:tcolor):real;
var
x,y,i:integer;
col2,col4,col5,col6,col8,c1,c2,c3:tcolor;
background:tcolor;
relevance,r,g,b:integer;
b1,b2,b3,b4:pbytearray;

//Zeitmessung
  freq: Int64;
  startTime: Int64;
  endTime: Int64;
  tempbmp:tbitmap;
begin
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(startTime);
//Zeitmessung

background:=backgroundcolor;

relevance:=intensitaet+1;

if bmp.PixelFormat<>pf24bit then
bmp.PixelFormat:=pf24bit;

tempbmp:=tbitmap.Create;
tempbmp.Assign(bmp);

for i:=1 to haeufigkeit do begin
  for y:=bmp.Height-2 downto 1 do begin


//     ____________
//  <-| b1| b1| b1|->
//  <-|___|___|___|->
//  <-| b2| b2| b2|->
//  <-|___|___|___|->
//  <-| b3| b3| b3|->
//  <-|___|___|___|->

    b1:=bmp.ScanLine[y-1];
    b2:=bmp.ScanLine[y];
    b3:=bmp.ScanLine[y+1];

    b4:=tempbmp.ScanLine[y];

    for x:=1 to bmp.Width-2 do begin

//   ____________
//  |   | 2 |   |
//  |___|___|___|
//  | 4 | 5 | 6 |
//  |___|___|___|
//  |   | 8 |   |
//  |___|___|___|



    //pixel mitte-oben
    r:=b1[(x)*3+2];
    g:=b1[(x)*3+1];
    b:=b1[(x)*3+0];
    col2:=rgb(r,g,b);

    //pixel mitte-links
    r:=b2[(x-1)*3+2];
    g:=b2[(x-1)*3+1];
    b:=b2[(x-1)*3+0];
    col4:=rgb(r,g,b);

    //pixel mitte-mitte
    r:=b2[(x)*3+2];
    g:=b2[(x)*3+1];
    b:=b2[(x)*3+0];
    col5:=rgb(r,g,b);

    //pixel mitte-rechts
    r:=b2[(x+1)*3+2];
    g:=b2[(x+1)*3+1];
    b:=b2[(x+1)*3+0];
    col6:=rgb(r,g,b);

    //pixel unten-mitte
    r:=b3[(x)*3+2];
    g:=b3[(x)*3+1];
    b:=b3[(x)*3+0];
    col8:=rgb(r,g,b);



//   ____________
//  |   | 2 |   |
//  |___|___|___|
//  | 4 | 5 |   |
//  |___|___|___|
//  |   |   |   |
//  |___|___|___|
     c1:=col2;
     c2:=col4;

     if (c1<>col5) and (c2<>col5) and (col5=background) then begin

       b4[x*3+2]:=(getrvalue(c1)+getrvalue(c2)+getrvalue(col5)*relevance) div (3+relevance);
       b4[x*3+1]:=(getgvalue(c1)+getgvalue(c2)+getgvalue(col5)*relevance) div (3+relevance);
       b4[x*3+0]:=(getbvalue(c1)+getbvalue(c2)+getbvalue(col5)*relevance) div (3+relevance);

     end;

//   ____________
//  |   | 2 |   |
//  |___|___|___|
//  |   | 5 | 6 |
//  |___|___|___|
//  |   |   |   |
//  |___|___|___|
     c1:=col2;
     c2:=col6;

     if (c1<>col5) and (c2<>col5) and (col5=background) then begin

       b4[x*3+2]:=(getrvalue(c1)+getrvalue(c2)+getrvalue(col5)*relevance) div (3+relevance);
       b4[x*3+1]:=(getgvalue(c1)+getgvalue(c2)+getgvalue(col5)*relevance) div (3+relevance);
       b4[x*3+0]:=(getbvalue(c1)+getbvalue(c2)+getbvalue(col5)*relevance) div (3+relevance);

     end;


//   ____________
//  |   |   |   |
//  |___|___|___|
//  | 4 | 5 |   |
//  |___|___|___|
//  |   | 8 |   |
//  |___|___|___|
     c1:=col8;
     c2:=col4;

     if (c1<>col5) and (c2<>col5) and (col5=background) then begin

       b4[x*3+2]:=(getrvalue(c1)+getrvalue(c2)+getrvalue(col5)*relevance) div (3+relevance);
       b4[x*3+1]:=(getgvalue(c1)+getgvalue(c2)+getgvalue(col5)*relevance) div (3+relevance);
       b4[x*3+0]:=(getbvalue(c1)+getbvalue(c2)+getbvalue(col5)*relevance) div (3+relevance);

     end;


//   ____________
//  |   |   |   |
//  |___|___|___|
//  |   | 5 | 6 |
//  |___|___|___|
//  |   | 8 |   |
//  |___|___|___|
     c1:=col8;
     c2:=col6;

     if (c1<>col5) and (c2<>col5) and (col5=background) then begin

       b4[x*3+2]:=(getrvalue(c1)+getrvalue(c2)+getrvalue(col5)*relevance) div (3+relevance);
       b4[x*3+1]:=(getgvalue(c1)+getgvalue(c2)+getgvalue(col5)*relevance) div (3+relevance);
       b4[x*3+0]:=(getbvalue(c1)+getbvalue(c2)+getbvalue(col5)*relevance) div (3+relevance);

     end;

    end;
  end;
bmp.Assign(tempbmp);
end;


//Zeitmessung
  QueryPerformanceCounter(endTime);

  result:=(endTime - startTime) * 1000 / freq;
//Zeitmessung
end;

procedure TForm1.BAAClick(Sender: TObject);
begin
Lzeit.Caption :=
    floattostr(AA(image1.Picture.Bitmap,spinedit_haeufigkeit.Value,spinedit_intensitaet.Value,clblack))+' ms';
end;
die kantenglättung glättet nicht alles sondern nur die übergänge von der hintergrundfarbe zu den restlichen farben.

[edit=mkinzler]Code umgebrochen wegen Layout Mfg, mkinzler[/edit]

Cicaro 14. Mär 2009 13:45

Re: Bitmap Kantenglättung
 
Falsches Forum für diesen Thread.

lbccaleb 14. Mär 2009 13:55

Re: Bitmap Kantenglättung
 
Zitat:

Zitat von Cicaro
Falsches Forum für diesen Thread.

Wieso das denn??

SirThornberry 14. Mär 2009 14:02

Re: Bitmap Kantenglättung
 
Weil es sich hier nicht darum handelt ein Projekt mit anderen zu haben. :)

DP-Maintenance 14. Mär 2009 14:02

DP-Maintenance
 
Dieses Thema wurde von "SirThornberry" von "Projekte" nach "VCL / WinForms / Controls" verschoben.

Laufi 14. Mär 2009 14:27

Re: Bitmap Kantenglättung
 
Hallo!

Ist es absichtlich das der code redundat ist und nicht schön aussieht damit man das selber macht! :shock:

Liebe Grüsse
Laufi

Namenloser 14. Mär 2009 15:16

Re: Bitmap Kantenglättung
 
Ich würde zuersteinmal das Format auf 32Bit umstellen, weil 32Bit-Werte vom Prozessor in der regel schneller verarbeitet werden können (auf 32Bit-systemen) als 24Bit-Werte.

Ich würde vor der eigentlichen Optimierung aber deinen Code umstrukturieren!

Zuersteinmal solltest du den Aufbau der Funktion zu überdenken, d.h. versuchen, den Code sinnvoll in Subfunktionen auszulagern. Es gibt sehr viele Codeblöcke die durch Copy&Paste entsatnden sind. Copy&Paste ist immer ein starkes Anzeichen dafür, dass man den Code ausgliedern sollte. Bitte gib deinen Variablen aussagekräftigere Namen. Dein aktueller Code ist trotz Kommentierung für einen Außenstehenden schwer nachzuvollziehen.

Fangen wir mal an, aufzuräumen:
Ich frage mich z.B., warum du die Variablen col2 - col8 hast, aber nie direkt benutzt, sondern immer in c1 und c2 kopierst. Ich vermute, du hast den Code mehrfach kopiert und eingefügt, und warst zu faul ihn zu verändern. Genau aus diesem Grund gibt es Sub-Routinen! Performanceeinbußen sind dadurch in der Regel nicht zu erwarten, weil die Funktion vom Compiler in der Regel ge-inlined werden sollte, wenn nicht, kann man dies durch
Delphi-Quellcode:
function MyFunction(...); inline;
erzwingen.

Das hier ließe sich super ausgliedern:
Delphi-Quellcode:
c1:=col2;
c2:=col4;

if (c1<>col5) and (c2<>col5) and (col5=background) then begin
  b4[x*3+2]:=(getrvalue(c1)+getrvalue(c2)+getrvalue(col5)*relevance) div (3+relevance);
  b4[x*3+1]:=(getgvalue(c1)+getgvalue(c2)+getgvalue(col5)*relevance) div (3+relevance);
  b4[x*3+0]:=(getbvalue(c1)+getbvalue(c2)+getbvalue(col5)*relevance) div (3+relevance);
end;

{ ... weiterer gleichartiger Code ...}
Hier mein (ungetesteter) Code:
Delphi-Quellcode:
function GetAntialiasedValue(Center, Value1, Value2: Byte; {relevance:integer}): byte; inline;
// relevance ist auskommentiert, muss man nicht unbedingt als Parameter übergeben
begin
  result := (Value1+Value2+Center*relevance) div (3+relevance);
end;

procedure ProcessPixel(PixelPointer: PByte; Center, Color1, Color2: TColor); inline;
begin
  PixelPointer := GetAntialiasedValue(GetBValue(Center), GetBValue(Color1), GetBValue(Color2));
  inc(PixelPointer);
  PixelPointer^ := GetAntialiasedValue(GetGValue(Center), GetGValue(Color1), GetGValue(Color2));
  inc(PixelPointer);
  PixelPointer^ := GetAntialiasedValue(GetRValue(Center), GetRValue(Color1), GetRValue(Color2));
end;

...

if col5 = background then
begin
  if (col2<>col5) and (col4<>col5) then begin
    ProcessPixel(@b4[x*3], col5, col2, col4);
  if (col2<>col5) and (col6<>col5) then begin
    ProcessPixel(@b4[x*3], col5, col2, col6);
  if (col8<>col5) and (col6<>col5) then begin
    ProcessPixel(@b4[x*3], col5, col8, col6);
  if (col8<>col5) and (col6<>col5) then begin
    ProcessPixel(@b4[x*3], col5, col8, col6);
end;
Siehst du, wie viel übersichtlicher das ganze jetzt ist? Dadurch wird es viel einfacher, Optimierungen durchzuführen, weil man zusammenhängenden Code immer sofort mit einem Blick erfassen kann. Z.B. fällt nun das häufige @b4[x*3] auf: Hier könnte man stattdessen einen Pointer benutzen, denn man nach jedem Durchlauf der x-Schleife um 3 (bzw 4, bei 32Bit) erhöht. Zudem habe ich die if-Abfragen verschachtelt, sodass die weiteren Abfragen nicht mehr durchgeführt werden müssen, wenn die erste bereits False ergibt. Auch dies ist ein Schluss, der durch die bessere Übersichtlichkeit begünstigt wurde. Desweiteren sparst du durch die Funktionen eine Menge Variablen!

Das ganze ist natürlich noch nicht fertig, bis jetzt wurde auch noch nicht viel optimiert. Als nächstes würde ich wie gesagt versuchen, von den Arrays wegzukommen, und stattdessen komplett auf Pointer umzustellen, was wohl noch am ehesten einen Geschwindigkeitsschub geben sollte. Ansonsten kann hier wohl nicht mehr sehr viel Performance herausgeholt werden. Trotzdem ist ein "Refactoring" sinnvoll, um den Code verständlicher zu machen. Wenn du dir deine alte Funktion in einem Jahr anguckst, wirst du dich erst einmal eine haleb Stunde lang einarbeiten müssen, damit du wieder weißt, was der Code eigentlich macht... ich spreche da aus eigener Erfahrung!

Also vereinfache das ganze noch ein bisschen, vielleicht fällt dir noch die ein oder andere Optimierungsmöglichkeit dabei auf.

marius0702 14. Mär 2009 17:28

Re: Bitmap Kantenglättung
 
Danke erstmal, für eure beiträge :thumb: .
sorry das mein code villeicht etwas unübersichtlich ist :roll: .
ich werd die vorschläge mal in die funktion einbauen und gucken ob es etwas schneller ist.

sx2008 14. Mär 2009 17:33

Re: Bitmap Kantenglättung
 
Delphi-Quellcode:
tempbmp:=tbitmap.Create;
tempbmp.Assign(bmp);
Man braucht "bmp" nicht auf "tempbmp" kopieren, da "tempbmp" sowieso überschrieben wird.
Delphi-Quellcode:
tempbmp:=tbitmap.Create;
tempbmp.Width := bmp.Width;
tempbmp.Height:= bmp.Height;
tempbmp.PxelFormat := bmp.PixelFormat;


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz