AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia weniger Scanline aufrufe ... Graustufenbild
Thema durchsuchen
Ansicht
Themen-Optionen

weniger Scanline aufrufe ... Graustufenbild

Ein Thema von bernhard_LA · begonnen am 10. Feb 2024 · letzter Beitrag vom 6. Apr 2024
Antwort Antwort
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.735 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 11. Feb 2024, 17:08
Zitat:
aber es "nervt" halt doch ein wenig, wenn behauptet statt gemessen wird .
Hey, ich hatte gemessen, nur eben vor 5 Jahren und nicht heute.

In #13 ist immernoch ein Aufruf von Scanline zuviel, denn Scanline[0] hattest Du ja bereits in StartPixel abgespeichert.

Theoretisch kann man die Differenz zwischen zwei Scanlines auch direkt berechnen, das spart dann nochmal einen Aufruf pro Bitmap:

  BitmapBytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8;
Das ist auch schneller als ein Auruf von

  Graphics.BytesPerScanline(w, BytesPerPixel * 8, 32)
(Ja, das hatte ich auch gemessen, wobei ein Blick in den BytesPerScanline-Code ausreicht, um zu verstehen wieso.)

BytesPerPixel = 1 für pf8Bit, und = 3 für pf24Bit.

Weshalb allerdings die Lösung mit TBitmap.Assign schneller sein soll als die ohne, ist mir gerade unklar, denn wie gesagt, ich hatte das damals gemessen. Bei neueren Delphis gibt es TBitmap.SetSize, was schneller ist als Höhe und Breite getrennt zu setzen wie in #1. Es kann auch einen Unterschied machen, ob man PixelFormat zuerst setzt und dann die Größe ändert oder umgekehrt.

Edit: Es ist vermutlich die unnötige Berechnung von InPixel und OutPixel für jedes Pixel:

Delphi-Quellcode:
  InPixel := AddToPtr(InScanLine0, InBytesPerLine * y + x * BytesPerPixel);
// ...
  OutPixel := AddToPtr(OutScanLine0, OutBytesPerLine * y + x * BytesPerPixel);
Da kann man in beiden for-Schleife mit Inc bzw. Dec arbeiten, wie Du das in #13 gemacht hast. Das hatte ich in meinem Code später noch geändert, nachdem ich den Blogpost geschrieben hatte.

Als "Beweis", hier der Code aus u_dzGraphicUtils, wie ich ihn schließlich verwendet habe, wobei das der generische Code mit einem Callback ist. Den Filter direkt in der Schleife zu implementieren ist natürlich schneller, da man jeweils den Callback-Aufruf spart.

Delphi-Quellcode:
procedure TBitmap24_FilterPixels(_SrcBmp, _DstBmp: TBitmap; _Callback: TPixel24FilterCallback);
const
  BytesPerPixel = 3;
var
  x: Integer;
  y: Integer;
  w: Integer;
  h: Integer;
  SrcLine: PByte;
  DstLine: PByte;
  SrcPixel: PByte;
  DstPixel: PByte;
  BytesPerLine: Integer;
begin
  Assert(Assigned(_SrcBmp));

  _SrcBmp.PixelFormat := pf24bit;
  _DstBmp.PixelFormat := pf24bit;
  w := _SrcBmp.Width;
  h := _SrcBmp.Height;
  TBitmap_SetSize(_DstBmp, w, h);

  if h = 0 then
    Exit; //==>

  BytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8;
  Assert(BytesPerLine = Graphics.BytesPerScanline(w, BytesPerPixel * 8, 32));

  SrcLine := _SrcBmp.ScanLine[0];
  DstLine := _DstBmp.ScanLine[0];
  for y := 0 to h - 1 do begin
    Assert(SrcLine = _SrcBmp.ScanLine[y]);
    Assert(DstLine = _DstBmp.ScanLine[y]);
    SrcPixel := SrcLine;
    DstPixel := DstLine;
    for x := 0 to w - 1 do begin
      PdzRgbTriple(DstPixel)^ := PdzRgbTriple(SrcPixel)^;
      _Callback(x, y, PdzRgbTriple(DstPixel)^);
      Inc(SrcPixel, BytesPerPixel);
      Inc(DstPixel, BytesPerPixel);
    end;
    Dec(SrcLine, BytesPerLine);
    Dec(DstLine, BytesPerLine);
  end;
end;
Und hier derselbe Code fuer eine 8 Bit Graustufen-Bitmap, nach dem der OP ja gefragt hatte:

Delphi-Quellcode:
procedure TBitmap8_FilterPixels(_SrcBmp, _DstBmp: TBitmap; _Callback: TPixel8FilterCallback);
const
  BytesPerPixel = 1;
var
  x: Integer;
  y: Integer;
  w: Integer;
  h: Integer;
  SrcLine: PByte;
  DstLine: PByte;
  SrcPixel: PByte;
  DstPixel: PByte;
  BytesPerLine: Integer;
begin
  Assert(Assigned(_SrcBmp));

  _SrcBmp.PixelFormat := pf8bit;
  _DstBmp.Assign(nil);
  _DstBmp.PixelFormat := pf8bit;
  w := _SrcBmp.Width;
  h := _SrcBmp.Height;
  _DstBmp.Palette := MakeGrayPalette;
  TBitmap_SetSize(_DstBmp, w, h);

  if h = 0 then
    Exit; //==>

  BytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8;
  Assert(BytesPerLine = Graphics.BytesPerScanline(w, BytesPerPixel * 8, 32));

  SrcLine := _SrcBmp.ScanLine[0];
  DstLine := _DstBmp.ScanLine[0];
  for y := 0 to h - 1 do begin
    Assert(SrcLine = _SrcBmp.ScanLine[y]);
    Assert(DstLine = _DstBmp.ScanLine[y]);
    SrcPixel := SrcLine;
    DstPixel := DstLine;
    for x := 0 to w - 1 do begin
      DstPixel^ := SrcPixel^;
      _Callback(x, y, DstPixel^);
      Inc(SrcPixel, BytesPerPixel);
      Inc(DstPixel, BytesPerPixel);
    end;
    Dec(SrcLine, BytesPerLine);
    Dec(DstLine, BytesPerLine);
  end;
end;
Die Assertions sollte man natürlich ausschalten oder rauslöschen, denn sonst wird unnötig Graphics.BytesPerScanline aufgerufen. Ebenso sollte Overflow Checking und Range Checking ausgeschaltet sein. Vgl. die ganzen IFDEFs am Anfang der Unit.

Wobei ich mich gerade selbst frage, weshalb ich da in der inneren Schleife nicht mit Inc ohne zweiten Parameter und dem passenden Pointer-Typ für SrcPixel und DstPixel gearbeitet habe. Vielleicht aus Kompatiblitätsgründen mit uralt-Delphi-Versionen? Deshalb gibt es auch die (inline)-Prozedur TBitmap_SetSize, welches für neuere Delphis TBitmap.SetSize aufruft, und für ältere notgedrungen TBitmap.Width und .Height getrennt setzt.
Thomas Mueller

Geändert von dummzeuch (11. Feb 2024 um 17:13 Uhr)
  Mit Zitat antworten Zitat
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
778 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 15:02
Weshalb allerdings die Lösung mit TBitmap.Assign schneller sein soll als die ohne, ist mir gerade unklar, denn wie gesagt, ich hatte das damals gemessen.
Bin gerade zu müde, um es nachzumessen .

Du kopierst in deinen Schleifen Pixel für Pixel von Src nach Dst. Mit einem Assign wird das in einem Rutsch vor der Schleife getan und du kannst dich dann in der Schleife nur noch mit Dst beschäftigen und Src komplett weglassen.

Wie gesagt: Gemessen habe ich nicht - aber wenn das Assign() (seit es Assign() gibt) nicht schneller sein sollte als dein "Pixel für Pixel" und Src mitschleifen, dann stimmt was mit Assign() nicht.

(Es gibt Situationen in welchen eine Pixel für Pixel Verarbeitung sinnvoll oder notwendig ist.)


Nebenbei: Wenn ich dein 8Bit Graustufenbeispiel richtig interpretiere, gehst du davon aus, dass Src die gleiche Palette verwendet wie du sie für Dst in deinem Code festlegst.
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.735 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 17:15
Nebenbei: Wenn ich dein 8Bit Graustufenbeispiel richtig interpretiere, gehst du davon aus, dass Src die gleiche Palette verwendet wie du sie für Dst in deinem Code festlegst.
Das ist korrekt. Ich habe noch nie ein 8Bit-Graustufen-Bild gesehen, das eine andere Palette als die Standard-Palette benutzt hat, welche dem Pixel-Wert x den RGB-Wert RGB(x,x,x) zuordnet, also das, was Kameras unter MONO8 verstehen. Es mag natürlich sein, dass es solche Spezialfälle gibt, aber der Aufwand, sie zu unterstützen, ist mir einfach viel zu groß.
Thomas Mueller
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.580 Beiträge
 
Delphi 12 Athens
 
#4

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 17:19
Aber natürlich hätte man da dann auch die Qual der Wahl
* einfach nur die kleine Farbtabelle umrechnen
* oder ben jedes Pixel, wo man dann aber aufassen muß, dass wirklich nur eine sortierte Grautabelle vorliegt.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
bernhard_LA

Registriert seit: 8. Jun 2009
Ort: Bayern
1.153 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 17:51
very Basic Nachfrage : Die Definitionen in windows unit wurden nicht verwendet, weil zu langsam ... ?


Delphi-Quellcode:

{$ALIGN 1}
  PRGBTriple = ^TRGBTriple;
  {$EXTERNALSYM tagRGBTRIPLE}
  tagRGBTRIPLE = record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;
  TRGBTriple = tagRGBTRIPLE;
  {$EXTERNALSYM RGBTRIPLE}
  RGBTRIPLE = tagRGBTRIPLE;
{$ALIGN ON}
  Mit Zitat antworten Zitat
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
778 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 20:38
Hallo Thomas dein in #15 gezeigter 24Bit Bitmap Code auf #1 angewendet läuft bei mir nun in 1820ms (1000 Mal 1000x1000Pixel) durch.

Delphi-Quellcode:
procedure DZeuch(_SrcBmp, _DstBmp: TBitmap; Threshold: Byte);
const
  BytesPerPixel = 3;
var
  x: Integer;
  y: Integer;
  w: Integer;
  h: Integer;
  SrcLine: PByte;
  DstLine: PByte;
  SrcPixel: PByte;
  DstPixel: PByte;
  BytesPerLine: Integer;
begin
  _SrcBmp.PixelFormat := pf24bit;
  _DstBmp.PixelFormat := pf24bit;
  w := _SrcBmp.Width;
  h := _SrcBmp.Height;
  _DstBmp.SetSize( w, h);

  if h = 0 then
    Exit; //==>

  BytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8;

  SrcLine := _SrcBmp.ScanLine[0];
  DstLine := _DstBmp.ScanLine[0];
  for y := 0 to h - 1 do begin
    SrcPixel := SrcLine;
    DstPixel := DstLine;
    for x := 0 to w - 1 do begin
      PdzRgbTriple(DstPixel)^ := PdzRgbTriple(SrcPixel)^;
      if PdzRgbTriple(DstPixel)^.Blue > Threshold then PdzRgbTriple(DstPixel)^.Blue := Threshold;
      if PdzRgbTriple(DstPixel)^.Green > Threshold then PdzRgbTriple(DstPixel)^.Green:= Threshold;
      if PdzRgbTriple(DstPixel)^.Red > Threshold then PdzRgbTriple(DstPixel)^.Red:= Threshold;
      Inc(SrcPixel, BytesPerPixel);
      Inc(DstPixel, BytesPerPixel);
    end;
    Dec(SrcLine, BytesPerLine);
    Dec(DstLine, BytesPerLine);
  end;
end;
Du hast Recht. Assign macht (für den vorliegenden Fall) viele unnötige Dinge. Wenn ich darauf verzichte (man gewinnt ca. 0.5ms), dann läuft mein Code in 810ms:

Delphi-Quellcode:
procedure CreateSpecialImage3(const InBmp, OutBmp : TBitmap; Threshold: Byte);
var // InBmp, OutBmp pf24Bit
  SrcScanline, DstScanline : Pointer;
  OutPixel: PRGBTriple;
  deltascan : NativeInt;
  height, width, x, y: Integer;
begin
  Height := InBmp.Height;
  Width := InBmp.Width;
  OutBmp.PixelFormat := pf24Bit;
  OutBmp.SetSize(width,height);
  if height = 0 then exit;

  DstScanline := OutBmp.ScanLine[height-1];
  SrcScanline := InBmp.ScanLine[height-1];

  if height > 1 then
  deltascan := NativeInt(OutBmp.ScanLine[height-2]) - NativeInt(DstScanline) else deltascan := 0;

  Move( SrcScanline^, DstScanline^, deltascan*height);

  for y := Height - 1 downto 0 do
  begin
    OutPixel := DstScanline;
    for x := 0 to Width - 1 do
    begin
      if OutPixel^.Blue > Threshold then OutPixel^.Blue := Threshold;
      if OutPixel^.Red > Threshold then OutPixel^.Red := Threshold;
      if OutPixel^.Green > Threshold then OutPixel^.Green := Threshold;
      inc(OutPixel);
    end;
     inc(PByte(DstScanline), deltascan);
  end;
end;
Natürlich gibt's wie erwähnt wurde noch Assembler und breite Register (habe ich auch schon mal woanders genutzt - mein Rechner wurde dann wegen der Hitze runter getaktet...), Grpahics32, GDI+ (Clone)... - aber 1000 Bilder zu 1000*1000*24 Bit Bitmaps mit den "doofen" alten Grafikfunktionen in 810ms ist schon recht schnell.
Michael Gasser

Geändert von Michael II (13. Feb 2024 um 15:24 Uhr)
  Mit Zitat antworten Zitat
bernhard_LA

Registriert seit: 8. Jun 2009
Ort: Bayern
1.153 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 12. Feb 2024, 22:06
@ Michael :

die Zeile will bei mir nicht :

Delphi-Quellcode:
 
  ....

  Move( InBmp.ScanLine[height-1]^, OutBmp.ScanLine[height-1]^, abs(deltascan)*height);
  ---
Angehängte Grafiken
Dateityp: png move.png (4,3 KB, 26x aufgerufen)
  Mit Zitat antworten Zitat
Kas Ob.

Registriert seit: 3. Sep 2023
459 Beiträge
 
#8

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 13. Feb 2024, 08:27
@Michael II, Nice ! this is the right way, by changing the operation from read and write in two different places stressing the CPU cache, do it once in sequential and fast move after that perform the the local operation in one place, CPU can perform 2-3 reads and one write per cycle, but let be real, if Delphi compiler managed to spit code that perform one read in 3 cycle i consider this is a Delphi win.

This way will win with Delphi every time :
Code:
  Move( InBmp.ScanLine[height-1]^, OutBmp.ScanLine[height-1]^, abs(deltascan)*height);

  for y := 0 to Height - 1 do
  begin
    OutPixel := DstScanline;
    for x := 0 to Width - 1 do
    begin
      if OutPixel^.Blue > Threshold then OutPixel^.Blue := Threshold;
      if OutPixel^.Red > Threshold then OutPixel^.Red := Threshold;
      if OutPixel^.Green > Threshold then OutPixel^.Green := Threshold;
      inc(OutPixels);
    end ;
     inc(PByte(DstScanline), deltascan);
  end ;
One thing though :
Check if InBmp is (=) OutBmp then skip the copy altogether.
Kas
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.735 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#9

AW: weniger Scanline aufrufe ... Graustufenbild

  Alt 13. Feb 2024, 08:22
very Basic Nachfrage : Die Definitionen in windows unit wurden nicht verwendet, weil zu langsam ... ?

Delphi-Quellcode:
{$ALIGN 1}
  PRGBTriple = ^TRGBTriple;
  {$EXTERNALSYM tagRGBTRIPLE}
  tagRGBTRIPLE = record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;
  TRGBTriple = tagRGBTRIPLE;
  {$EXTERNALSYM RGBTRIPLE}
  RGBTRIPLE = tagRGBTRIPLE;
{$ALIGN ON}
Ich erinnere mich nicht mehr, warum ich damals die Deklaration selbst gemacht habe. Evtl. gab es sie in älteren Delphi-Versionen nicht. Mein Code muss immer mindestens bis Delphi 2007 rückwärtscompatibel sein, da viele meiner Projekte noch nicht umgestellt sind und damls 2019 waren es eigentlich noch fast alle. Außerdem habe ich für einige Funktionen Enhanced Records verwendet. Das könnte man heute zwar mit Record Helpern implementieren, aber nicht mit Delphi 2007, wenn ich mich recht erinnere.
Thomas Mueller
  Mit Zitat antworten Zitat
Antwort Antwort


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 19:24 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