![]() |
schnelle getPixel Funktion
Hallo zusammen,
ich rechne in meinem Projekt eine ganze Menge über Bitmap Bilder hin und her und hab mir deshalb mal ein Test Programm geschrieben, um eine schnelle Variante zu finden ein Bild zu durchlaufen. Da ich außerdem bei den Berechnungen teilweise auch mehrere Bilder in einer Schleife durchlaufe hätte ich gerne eine getPixel Funktion auf dem Bitmap die mir möglichst ohne zeitlichen Overhead den Wert an der Position gibt und ich mich nicht immer um Scanline und Inc des Pointers kümmern muss. Zum Testen der Laufzeit gehe ich einmal über das Bild, rechne alle Pixel einer Farbe zusammen und messe dabei die Zeit. Bei der ersten Variante mit Pointern und Scanline
Delphi-Quellcode:
benötigt der Algorithmus 0,017 Sekunden.
procedure TForm1.Button1Click(Sender: TObject);
var i,j: INTEGER; Row: ^TRGBTriple; calc: Int64; time1, time2, DiffTime : TDateTime; begin calc := 0; time1 := time; for j := 0 to Image1.Picture.Bitmap.Height-1 do begin row := Image1.Picture.Bitmap.Scanline[j]; for i := 0 to Image1.Picture.Bitmap.Width-1 do begin calc := calc + (getColorPixel(row,Red)); inc (row); end; end; time2 := time; DiffTime := ( time2 - time1 ) *60 *60 *24; Edit3.Text := Format ( '%2.5f', [DiffTime] ); Edit1.Text := IntToStr(calc); end; Nun hab ich mir eine Klasse beschrieben, die von TBitmap ableitet und eine zusätzliche getPixel Funktion zur Verfügung stellt. Damit hier nicht immer mit scanline die Zeile bestimmt werden muss, erzeuge ich mir in der load Methode ein Array of PByteArray mit allen Zeilenpointern.
Delphi-Quellcode:
Mit dieser Unit benötige ich für das Durchlaufen des Bildes 0,024 Sekunden also im Schnitt 7 ms länger - klingt nicht viel aber bei der Masse an Bilder macht es schon etwas aus.
unit MyBitmap;
interface uses Graphics, SysUtils, types; type TMyBitmap = class(TBitmap) private bits: array of PByteArray; public procedure Init(); function getPixel(x,y:Integer): Byte; procedure LoadFromFile(const Filename: string); override; end; implementation procedure TMyBitmap.Init(); var i: Integer; begin self.PixelFormat := pf32bit; SetLength(bits, self.Height); for i := 0 to self.Height -1 do begin bits[i] := self.ScanLine[i]; end; Pbase := self.ScanLine[0]; end; procedure TMyBitmap.LoadFromFile(const Filename: string); begin inherited; init; end; function TMyBitmap.getPixel(x,y:Integer): Byte; begin result := bits[y][3*x + 2]; end; end. Ich hab auch die Graphics32 Unit ausprobiert, die jedoch mit durchschnittlich 0,054 noch mehr Zeit benötigt. Nun frage ich mich ob es eine Möglichkeit gibt das ganze doch so zu optimieren, dass die Berechnung möglichst schnell abläuft ich aber trotzdem komfortabel mit getPixel auf die Werte zugreifen kann und ich mich nicht um das inkrementieren des Pointers kümmern muss. Habt ihr eine Idee? Liegt der zeitliche Overhead nur am Funktionsaufruf? Da ich mit Delphi 7 arbeite hab ich leider noch kein inline, um das eventuell zu optimieren. Grüße Maick |
AW: schnelle getPixel Funktion
Liste der Anhänge anzeigen (Anzahl: 1)
Keine Ahnung ob es Dir hilft...meine Experimente ....
|
AW: schnelle getPixel Funktion
Der zeitliche Overhead liegt einerseits am Funktionsaufruf, vor allem aber an der Berechnung der Adresse des Pixelwertes...
Schau dir dein "einfaches" getpixel mal im Assemblercode an...
Code:
XYZ.pas.26: begin
004AB71C 55 push ebp 004AB71D 8BEC mov ebp,esp 004AB71F 83C4F0 add esp,-$10 004AB722 894DF4 mov [ebp-$0c],ecx 004AB725 8955F8 mov [ebp-$08],edx 004AB728 8945FC mov [ebp-$04],eax XYZ.pas.27: result := bits[y][3*x + 2]; 004AB72B 8B45FC mov eax,[ebp-$04] 004AB72E 8B8088030000 mov eax,[eax+$00000388] 004AB734 8B55F4 mov edx,[ebp-$0c] 004AB737 8B0490 mov eax,[eax+edx*4] 004AB73A 8B55F8 mov edx,[ebp-$08] 004AB73D 8D1452 lea edx,[edx+edx*2] 004AB740 8A441002 mov al,[eax+edx+$02] 004AB744 8845F3 mov [ebp-$0d],al XYZ.pas.28: end; 004AB747 8A45F3 mov al,[ebp-$0d] 004AB74A 8BE5 mov esp,ebp 004AB74C 5D pop ebp 004AB74D C3 ret |
AW: schnelle getPixel Funktion
pf32bit und getPixel: Byte kann schonmal nicht stimmen, da ein Pixel hier 4 Byte ist.
Warum ist es langsamer? - du änderst das Pixelformat, also den ganzen Bildinhalt auf pf32bit - der andere Code geht einfach davon ausß daß das bild schon pf24bit ist (wenn nicht, dann knallt's oder so)
Delphi-Quellcode:
type
TColorArray = array[0..0] of TColor; PColorArray = ^TColorArray; PixelColor := PColorArray(Self.ScanLine[Self.Height - 1])[x * Self.Width + (Self.Height - y - 1)]
Delphi-Quellcode:
Diese Berechnug stimmt nur für pf32bit (für die anderen Formate müßte man noch einen breitenabhängigen Offset je Zeile mit einrechnen).
var P: PColorArray;
P := PColorArray(Self.ScanLine[Self.Height - 1]); PixelColor := P[(Self.Height - y - 1) * Self.Width + x]; // entspricht PixelColor := P + ((Self.Height - y - 1) * Self.Width + x); // entspricht PixelColor := PByte(P) + ((Self.Height - y - 1) * Self.Width + x) * 4; Das dann in eine Funktion und diese als "inline" markieren. Die eine Scannline-Adresse oder das Array aller Scanlines natürlich vorher zwischenspeichern |
AW: schnelle getPixel Funktion
@himitsu
da ich meist nur den rot oder den grün Kanal benötige würde ich mir dafür dann im Endeffekt zwei Methoden mit festem Offset für das jeweilige Farb Byte schreiben - hatte ich vergessen zu erwähnen. Pf32Bit ist in der Klasse natürlich quatsch. Hatte etwas ausprobiert und die Zeile vergessen wieder raus zunehmen. Ich arbeite immer auf RGB Bildern mit je 8 bit also pf24bit von daher sind feste Offsets kein Problem an dieser Stelle. @jaenicke ok ist schon etwas her das ich mit Assembler gearbeitet hab ;) die Berechnung von der Adresse des Pixels benötigt also eine ganze Menge an Assembler Befehlen aber mir fällt keine Lösung ein das zu optimieren. Ich hab noch nicht so viel mit Delphi gemacht - von daher kann an sich die Assembler Übersetzung in der IDE anzeigen lassen oder hast du dafür ein externes Tool benutzt? Ich probier dann mal die Beispiele von himitsu Bummi aus - mal sehen wie schnell die das berechnen. danke schonmal |
AW: schnelle getPixel Funktion
Hallo,
mal angenommen du arbeitest immer mit pf24Bit und mal angenommen Deine Bilder hätten nie mehr als n mal m Pixel, dann könntest du Dir eine Table erschaffen die für alle zu bearbeitenden Bilder gilt. Also ein Array mit [0..m-1]. Dort musst du einmal alle Koordinaten umrechnen von x auf Byte-Pos (3*x + 2). Diese berechnungszeit brauchst du dann nur einmal für alle Bilder. Dein Aufruf von getPixelRed wäre dann nur noch:
Delphi-Quellcode:
Das lohnt sich definitiv nicht, wenn nur ein Bild bearbeitet wird - aber wenn viele bearbeitet werden, dann lohnt es sich.
function TMyBitmap.getPixelRed(x,y:Integer): Byte;
begin result := bits[y][redtable[x]]; end; Gruß, Chris |
AW: schnelle getPixel Funktion
@himitsu
Zitat:
@ChrisE interessante Idee gleich mal ausprobieren |
AW: schnelle getPixel Funktion
Und so lange sich das auf eine Zeile beschränkt, würde ich den Funktionsaufruf komplett weg lassen. Delphi kann imho ab einer gewissen Version inlinen, aber darauf habe ich mich bisher nicht verlassen. Kostet zwar Eleganz und Modularität, aber das ist zum Zwecke des Performancegewinns ja nicht selten so :)
Edit: Musst du eigentlich wahlfrei zugreifen können? Imho ist es insgesammt für die Hardware komfortabler Daten am Stück zu verarbeiten, so dass wenn du eh Zeilenweise/Pixelweise durchgehst die explizite Berechnung durch ein gelegentliches schlankes inc() ersetzen könntest. Damit ließe sich evtl. auch noch was raus holen. Und zuguterletzt: Wäre MMX nicht evtl. etwas für dich? |
AW: schnelle getPixel Funktion
Zitat:
|
AW: schnelle getPixel Funktion
Jup, Redbox kam, ich hab sie aber mutwillig ignoriert :)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz