AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

schnelle getPixel Funktion

Ein Thema von schwarzerlotus · begonnen am 16. Aug 2011 · letzter Beitrag vom 17. Aug 2011
Antwort Antwort
Seite 1 von 3  1 23   
schwarzerlotus

Registriert seit: 25. Jul 2011
22 Beiträge
 
#1

schnelle getPixel Funktion

  Alt 16. Aug 2011, 10:47
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:
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;
benötigt der Algorithmus 0,017 Sekunden.

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:
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.
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.

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
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#2

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 11:14
Keine Ahnung ob es Dir hilft...meine Experimente ....
Angehängte Dateien
Dateityp: zip PerformanceTest_Scanline_Pixels.zip (84,5 KB, 35x aufgerufen)
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
8.503 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 11:22
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
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!

Geändert von jaenicke (16. Aug 2011 um 11:24 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
40.504 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 11:50
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:
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;
Diese Berechnug stimmt nur für pf32bit (für die anderen Formate müßte man noch einen breitenabhängigen Offset je Zeile mit einrechnen).
Das dann in eine Funktion und diese als "inline" markieren.

Die eine Scannline-Adresse oder das Array aller Scanlines natürlich vorher zwischenspeichern
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list

Geändert von himitsu (16. Aug 2011 um 11:56 Uhr)
  Mit Zitat antworten Zitat
schwarzerlotus

Registriert seit: 25. Jul 2011
22 Beiträge
 
#5

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:01
@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
  Mit Zitat antworten Zitat
Benutzerbild von ChrisE
ChrisE

Registriert seit: 15. Feb 2006
Ort: Hechingen
504 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#6

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:10
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:
function TMyBitmap.getPixelRed(x,y:Integer): Byte;
begin
  result := bits[y][redtable[x]];
end;
Das lohnt sich definitiv nicht, wenn nur ein Bild bearbeitet wird - aber wenn viele bearbeitet werden, dann lohnt es sich.

Gruß, Chris
Christian E.
Es gibt 10 Arten von Menschen, die die Binär lesen können und die die es nicht können

Delphi programming rules
  Mit Zitat antworten Zitat
schwarzerlotus

Registriert seit: 25. Jul 2011
22 Beiträge
 
#7

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:17
@himitsu

Zitat:
Das dann in eine Funktion und diese als "inline" markieren.
inline gibt es in Delphi 7 noch nicht oder? Ich meine zumindest das ich gelesen hätte, dass das erst mit 2007 oder so eingeführt wurde - dachte mir auch ich mach das wie in c++

@ChrisE

interessante Idee gleich mal ausprobieren
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.674 Beiträge
 
Delphi 2007 Enterprise
 
#8

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:18
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?
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)

Geändert von Medium (16. Aug 2011 um 13:21 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von ChrisE
ChrisE

Registriert seit: 15. Feb 2006
Ort: Hechingen
504 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#9

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:20
Da ich mit Delphi 7 arbeite hab ich leider noch kein inline, um das eventuell zu optimieren.
Christian E.
Es gibt 10 Arten von Menschen, die die Binär lesen können und die die es nicht können

Delphi programming rules
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.674 Beiträge
 
Delphi 2007 Enterprise
 
#10

AW: schnelle getPixel Funktion

  Alt 16. Aug 2011, 13:23
Jup, Redbox kam, ich hab sie aber mutwillig ignoriert
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:09 Uhr.
Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf