Pixelweise Bildbearbeitung: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Zugriff durch Canvas.Pixels)
(Zugriff durch Canvas.Pixels)
 
(3 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 3: Zeile 3:
 
* Sehr einfach anzuwenden
 
* Sehr einfach anzuwenden
 
* Funktioniert für alle Farbtiefen/Pixelformate
 
* Funktioniert für alle Farbtiefen/Pixelformate
* Geringes risiko falsche Bereiche zu überschreiben
+
* Geringes Risiko falsche Bereiche zu überschreiben
 
Nachteile:
 
Nachteile:
 
* Sehr langsam
 
* Sehr langsam
 
+
* Alpha-Kanal wird nicht berücksichtigt
 +
 
Man greift über die Pixels Eigenschaft des Canvas auf die Pixeldaten des Bitmaps zu. Dabei wird der Farbe des Pixels automatisch von/zu TColor konvertiert. Diese Methode ist jedoch extrem langsam, und daher höchstens zum ändern einzelner Pixel, jedoch nicht für ein ganzes Bild geeignet.
 
Man greift über die Pixels Eigenschaft des Canvas auf die Pixeldaten des Bitmaps zu. Dabei wird der Farbe des Pixels automatisch von/zu TColor konvertiert. Diese Methode ist jedoch extrem langsam, und daher höchstens zum ändern einzelner Pixel, jedoch nicht für ein ganzes Bild geeignet.
  
 
Beispiel:
 
Beispiel:
procedure Invert(bmp:TBitmap);
+
<source lang="pascal">
var x,y:integer;
+
procedure Invert(Bmp: TBitmap);
begin
+
var  
   for y:=0 to bmp.height-1 do
+
  x, y: Integer;
  for x:=0 to bmp.width-1 do
+
begin
    bmp.canvas.pixels[x,y]:=bmp.canvas.pixels[x,y]xor $FFFFFF;
+
   for y := 0 to Bmp.Height - 1 do
end;
+
    for x := 0 to Bmp.Width - 1 do
 +
      Bmp.Canvas.Pixels[x, y] := Bmp.Canvas.Pixels[x, y] xor $FFFFFF;
 +
end;
 +
</source>
  
 
== Zugriff mittels scanline ==
 
== Zugriff mittels scanline ==
Zeile 29: Zeile 33:
  
 
Beschreibung:
 
Beschreibung:
Man holt sich mittels der scanline eigenschaft des Bitmaps einen direkten zeiger auf die zeilenanfänge. Mittels Zeigermanipulation bearbeitet man anschließend die Pixeldaten. Das ist natürlich sehr schnell, besonders für 8,16 und 32bit. Man kann jedoch nur schwer mittels koordinaten auf einzelne pixel zugreifen, man arbeitet die pixel üblicherweise in zeilen nacheinander ab. Es ist außerdem Vorsicht geboten, damit man nicht unabsichtlich über den Datenbereich hinaus liest/schreibt.
+
Man holt sich mittels der scanline eigenschaft des Bitmaps einen direkten zeiger auf die zeilenanfänge. Mittels Zeigermanipulation bearbeitet man anschließend die Pixeldaten. Das ist natürlich sehr schnell, besonders für 8,16 und 32bit. Man kann jedoch nur schwer mittels koordinaten auf einzelne pixel zugreifen, man arbeitet die pixel üblicherweise in zeilen nacheinander ab. Es ist außerdem Vorsicht geboten, damit man nicht unabsichtlich über den Datenbereich hinaus liest/schreibt. Diese Methode spielt ihren Geschwindigkeitsvorteil(gegenüber der Arraymethode) vor allem dann aus, wenn man einfache Änderungen an Pixeldaten durchführt, die nicht von anderen Pixeln abhängen und die Pixel direkt in einen Integertyp geladen werden können.
  
 
Beispiel:
 
Beispiel:
Type TPixel=packed record B,G,R:byte;
+
<source lang="pascal">
procedure Invert(bmp:TBitmap);
+
type
var p:^TPixel;
+
  TPixel = packed record  
    x,y:integer;
+
    B,G,R: Byte;
begin
+
  end;
   if bmp.pixelformat<>pf24bit then raise exception.create('Pixelformat not supported');
+
 
   for y:=0 to bmp.height-1 do
+
procedure Invert(bmp:TBitmap);
  begin
+
var  
     p:=bmp.scanline[y];
+
  P:^TPixel;
 +
  X,Y:integer;
 +
begin
 +
   if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
 +
   for Y:=0 to bmp.Height-1 do
 +
  begin
 +
     P:=bmp.Scanline[y];
 
     for x:=0 to bmp.width-1 do
 
     for x:=0 to bmp.width-1 do
    begin
+
    begin
       p.r:=p.r xor $FF;
+
       P.R := P.R xor $FF;
       p.g:=p.g xor $FF;
+
       P.G := P.G xor $FF;
       p.b:=p.b xor $FF;
+
       P.B := P.B xor $FF;
       inc(p);//3 Byte weiter
+
       Inc(P);//1Pixel weiter
    end;
 
 
   end;
 
   end;
end;
+
  end;
 +
end;
 +
</source>
  
 
== Zugriff mittels dynamischer arrays ==
 
== Zugriff mittels dynamischer arrays ==
Zeile 60: Zeile 71:
  
 
Beschreibung:
 
Beschreibung:
Auch hier holt man sich wieder per scanline die Zeiger auf die Zeilenanfänge. Statt sie jedoch direkt zu verwenden schreibt man sie in ein dynamisches Array, dessen Größe auf die höhe des Bitmaps gesetzt wird. Dabei wird der untypisierte Pointer auf den Zeilenanfang in einen Zeiger auf ein array von Pixeln umgewandelt. Anschließend kann man mittels Pixels[y,x] auf die einzelnen Pixel zugreifen. Für andere Pixelformate muss muss TPixel anders definiert werden. Man sollte jedoch darauf achten nur koordinaten aus dem gültigen bereich zu übergeben.
+
Auch hier holt man sich wieder per scanline die Zeiger auf die Zeilenanfänge. Statt sie jedoch direkt zu verwenden schreibt man sie in ein dynamisches Array, dessen Größe auf die höhe des Bitmaps gesetzt wird. Dabei wird der untypisierte Pointer auf den Zeilenanfang in einen Zeiger auf ein array von Pixeln umgewandelt. Anschließend kann man mittels Pixels[y,x] auf die einzelnen Pixel zugreifen. Für andere Pixelformate muss muss TPixel anders definiert werden. Man sollte jedoch darauf achten nur Koordinaten aus dem gültigen Bereich zu übergeben.
  
 
Beispiel:
 
Beispiel:
  Type TPixel=packed record b,g,r;end;
+
<source lang="pascal">
      TPixelArray=array[0..715827881]of TPixel;//Knapp unter 2GB
+
  Type  
      PPixelArray=^TPixelarray;
+
  TPixel = packed record  
      TPixels=array of PPixelarray;
+
    B, G, R: Byte;
procedure Invert(bmp:TBitmap);
+
  end;
var Pixels:TPixels;
+
  TPixelArray = array [0..715827881] of TPixel;//Knapp unter 2GB
    x,y:integer;
+
  PPixelArray = ^TPixelarray;
begin
+
  TPixels = array of PPixelarray;
 +
 
 +
procedure Invert(bmp:TBitmap);
 +
var  
 +
  Pixels:TPixels;
 +
  x, y:integer;
 +
begin
 +
  if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
 
   //Pixels initialisieren
 
   //Pixels initialisieren
   setlength(Pixels,bmp.height);
+
   SetLength(Pixels,bmp.Height);
   for y:=0 to bmp.height-1 do
+
  // Hier eventuell umgekehrte Reihenfolge von Zeilen in der Bitmap beachten!
  Pixels[y]:=bmp.scanline[y];
+
   for y:=0 to bmp.Height-1 do
 +
    Pixels[y]:=bmp.Scanline[y];
 
   //Bild bearbeiten
 
   //Bild bearbeiten
   for y:=0 to bmp.height-1 do
+
   for y:=0 to bmp.Height-1 do
  for x:=0 to bmp.width-1 do
+
    for x:=0 to bmp.Width-1 do
 
     begin
 
     begin
    Pixels[y,x].r:=Pixels[y,x].r xor $FF;
+
      Pixels[y,x].R := Pixels[y,x].R xor $FF;
    Pixels[y,x].g:=Pixels[y,x].g xor $FF;
+
      Pixels[y,x].G := Pixels[y,x].G xor $FF;
    Pixels[y,x].b:=Pixels[y,x].b xor $FF;
+
      Pixels[y,x].B := Pixels[y,x].B xor $FF;
 
     end;
 
     end;
end;
+
end;
+
 
procedure Grayscale(bmp:TBitmap);
+
procedure Grayscale(bmp:TBitmap);
var Pixels:TPixels;
+
var  
    x,y:integer;
+
  Pixels:TPixels;
begin
+
  x, y:integer;
 +
begin
 +
  if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
 
   //Pixels initialisieren
 
   //Pixels initialisieren
   setlength(Pixels,bmp.height);
+
   setlength(Pixels,bmp.Height);
   for y:=0 to bmp.height-1 do
+
  // Hier eventuell umgekehrte Reihenfolge von Zeilen in der Bitmap beachten!
  Pixels[y]:=bmp.scanline[y];
+
   for y:=0 to bmp.Height-1 do
 +
    Pixels[y]:=bmp.Scanline[y];
 
   //Bild bearbeiten
 
   //Bild bearbeiten
   for y:=0 to bmp.height-1 do
+
   for y:=0 to bmp.Height-1 do
  for x:=0 to bmp.width-1 do
+
    for x:=0 to bmp.Width-1 do
 
     begin
 
     begin
    Pixels[y,x].r:=(Pixels[y,x].r+Pixels[y,x].g+Pixels[y,x].b)div 3;
+
      Pixels[y,x].R := (Pixels[y,x].R + Pixels[y,x].G + Pixels[y,x].B) div 3;
    Pixels[y,x].g:=Pixels[y,x].r;
+
      Pixels[y,x].G := Pixels[y,x].R;
    Pixels[y,x].b:=Pixels[y,x].r;
+
      Pixels[y,x].B := Pixels[y,x].R;
 
     end;
 
     end;
end;
+
end;
 +
</source>

Aktuelle Version vom 15. Juli 2018, 19:09 Uhr

Zugriff durch Canvas.Pixels

Vorteile:

  • Sehr einfach anzuwenden
  • Funktioniert für alle Farbtiefen/Pixelformate
  • Geringes Risiko falsche Bereiche zu überschreiben

Nachteile:

  • Sehr langsam
  • Alpha-Kanal wird nicht berücksichtigt

Man greift über die Pixels Eigenschaft des Canvas auf die Pixeldaten des Bitmaps zu. Dabei wird der Farbe des Pixels automatisch von/zu TColor konvertiert. Diese Methode ist jedoch extrem langsam, und daher höchstens zum ändern einzelner Pixel, jedoch nicht für ein ganzes Bild geeignet.

Beispiel:

procedure Invert(Bmp: TBitmap);
var 
  x, y: Integer;
begin
  for y := 0 to Bmp.Height - 1 do
    for x := 0 to Bmp.Width - 1 do
      Bmp.Canvas.Pixels[x, y] := Bmp.Canvas.Pixels[x, y] xor $FFFFFF;
end;

Zugriff mittels scanline

Vorteile:

  • Sehr schnell

Nachteile:

  • Bei komplexeren Manipulationen umständlich
  • Kein zugriff über x,y -Koordinate
  • Hohes Risiko falsche Bereiche zu überschreiben
  • Man muss den code für jedes Pixelformat extra erstellen.
  • Für 24BPP ungünstig, da es keinen 3byte großen primitiven typ gibt. (Geschwindigkeitsvorteiteil gegenüber Arraymethode sinkt)

Beschreibung: Man holt sich mittels der scanline eigenschaft des Bitmaps einen direkten zeiger auf die zeilenanfänge. Mittels Zeigermanipulation bearbeitet man anschließend die Pixeldaten. Das ist natürlich sehr schnell, besonders für 8,16 und 32bit. Man kann jedoch nur schwer mittels koordinaten auf einzelne pixel zugreifen, man arbeitet die pixel üblicherweise in zeilen nacheinander ab. Es ist außerdem Vorsicht geboten, damit man nicht unabsichtlich über den Datenbereich hinaus liest/schreibt. Diese Methode spielt ihren Geschwindigkeitsvorteil(gegenüber der Arraymethode) vor allem dann aus, wenn man einfache Änderungen an Pixeldaten durchführt, die nicht von anderen Pixeln abhängen und die Pixel direkt in einen Integertyp geladen werden können.

Beispiel:

type 
  TPixel = packed record 
    B,G,R: Byte;
  end;
  
procedure Invert(bmp:TBitmap);
var 
  P:^TPixel;
  X,Y:integer;
begin
  if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
  for Y:=0 to bmp.Height-1 do
  begin
    P:=bmp.Scanline[y];
    for x:=0 to bmp.width-1 do
    begin
      P.R := P.R xor $FF;
      P.G := P.G xor $FF;
      P.B := P.B xor $FF;
      Inc(P);//1Pixel weiter
   end;
  end;
end;

Zugriff mittels dynamischer arrays

Vorteile:

  • Schnell
  • Flexibel (Zugriff über x,y-Koordinate)

Nachteile

  • Mittleres Risiko falsche Bereiche zu überschreiben
  • Man muss den code für jedes Pixelformat extra erstellen.

Beschreibung: Auch hier holt man sich wieder per scanline die Zeiger auf die Zeilenanfänge. Statt sie jedoch direkt zu verwenden schreibt man sie in ein dynamisches Array, dessen Größe auf die höhe des Bitmaps gesetzt wird. Dabei wird der untypisierte Pointer auf den Zeilenanfang in einen Zeiger auf ein array von Pixeln umgewandelt. Anschließend kann man mittels Pixels[y,x] auf die einzelnen Pixel zugreifen. Für andere Pixelformate muss muss TPixel anders definiert werden. Man sollte jedoch darauf achten nur Koordinaten aus dem gültigen Bereich zu übergeben.

Beispiel:

 Type 
  TPixel = packed record 
    B, G, R: Byte;
  end;
  TPixelArray = array [0..715827881] of TPixel;//Knapp unter 2GB
  PPixelArray = ^TPixelarray;
  TPixels = array of PPixelarray;
  
procedure Invert(bmp:TBitmap);
var 
  Pixels:TPixels;
  x, y:integer;
begin
  if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
  //Pixels initialisieren
  SetLength(Pixels,bmp.Height);
  // Hier eventuell umgekehrte Reihenfolge von Zeilen in der Bitmap beachten!
  for y:=0 to bmp.Height-1 do
    Pixels[y]:=bmp.Scanline[y];
  //Bild bearbeiten
  for y:=0 to bmp.Height-1 do
    for x:=0 to bmp.Width-1 do
    begin
      Pixels[y,x].R := Pixels[y,x].R xor $FF;
      Pixels[y,x].G := Pixels[y,x].G xor $FF;
      Pixels[y,x].B := Pixels[y,x].B xor $FF;
    end;
end;

procedure Grayscale(bmp:TBitmap);
var 
  Pixels:TPixels;
  x, y:integer;
begin
  if bmp.PixelFormat <> pf24Bit then raise Exception.Create('Pixelformat not supported');
  //Pixels initialisieren
  setlength(Pixels,bmp.Height);
  // Hier eventuell umgekehrte Reihenfolge von Zeilen in der Bitmap beachten!
  for y:=0 to bmp.Height-1 do
    Pixels[y]:=bmp.Scanline[y];
  //Bild bearbeiten
  for y:=0 to bmp.Height-1 do
    for x:=0 to bmp.Width-1 do
    begin
      Pixels[y,x].R := (Pixels[y,x].R + Pixels[y,x].G + Pixels[y,x].B) div 3;
      Pixels[y,x].G := Pixels[y,x].R;
      Pixels[y,x].B := Pixels[y,x].R;
    end;
end;