![]() |
AW: weniger Scanline aufrufe ... Graustufenbild
Zitat:
|
AW: weniger Scanline aufrufe ... Graustufenbild
Zitat:
|
AW: weniger Scanline aufrufe ... Graustufenbild
Eigentlich wollte ich ja nix mehr dazu schreiben, aber es "nervt" halt doch ein wenig, wenn behauptet statt gemessen wird ;-).
Fünf Mal 1000 Bilder der Grösse 1000x1000 umgewandelt. Die in #1 vorgestellte Lösung benötigt auf meinem nicht mehr allzu frischen Notebook im Schnitt 4950ms. Meine in #7 benötigt 2011ms und diese hier (unten) 1373ms (wobei man das ganz sicher schöner und auch ein wenig schneller...)
Delphi-Quellcode:
procedure CreateSpecialImage2(const InBmp, OutBmp: TBitmap; Threshold: Byte);
var StartPixel : Pointer; OutPixel: PRgbTriple; deltascan : NativeInt; height, width, x, y: Integer; begin OutBmp.Assign(InBmp); StartPixel := OutBmp.ScanLine[0]; deltascan := NativeInt(OutBmp.ScanLine[1]) - NativeInt(StartPixel); Height := OutBmp.Height; Width := OutBmp.Width; for y := 0 to Height - 1 do begin OutPixel := StartPixel; 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(StartPixel), deltascan); end; end; |
AW: weniger Scanline aufrufe ... Graustufenbild
OK, man kann natürlich auch noch mit SSE/MMX anfangen, also mit nur einem Befehl jeweils 8 Bytes auf einmal,
oder mit CUDA bzw. OpenCL (OK, das wohl eher nicht). Und natürlich noch Assembler. |
AW: weniger Scanline aufrufe ... Graustufenbild
Zitat:
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:
Delphi-Quellcode:
BitmapBytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8;
Das ist auch schneller als ein Auruf von
Delphi-Quellcode:
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:
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.
InPixel := AddToPtr(InScanLine0, InBytesPerLine * y + x * BytesPerPixel);
// ... OutPixel := AddToPtr(OutScanLine0, OutBytesPerLine * y + x * BytesPerPixel); Als "Beweis", hier der Code aus ![]()
Delphi-Quellcode:
Und hier
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; ![]()
Delphi-Quellcode:
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.
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; 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. |
AW: weniger Scanline aufrufe ... Graustufenbild
Zitat:
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. |
AW: weniger Scanline aufrufe ... Graustufenbild
Zitat:
|
AW: weniger Scanline aufrufe ... Graustufenbild
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. |
AW: weniger Scanline aufrufe ... Graustufenbild
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} |
AW: weniger Scanline aufrufe ... Graustufenbild
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:
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:
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;
Delphi-Quellcode:
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.
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; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:15 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