Autor Beitrag
user32
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Mo 08.06.15 05:38 
Der beabsichtigte Ablauf ist wie folgt:
Zitat:
1. Aus Image1 Bitmap lesen
2. die Werte in temporäres Bitmap im Speicher kopieren und verändern
3. Bitmap auf Image2. anzeigen


Der Zweck davon ist einfach, einen direkten Zugriff auf das Bitmap im Speicher zu bekommen.
Es muss nur ohne Scanline sein, denn Image.Picture.Bitmap.Scanline ist mir zu langsam!


Das funktioniert auch, aber irgendwo ist noch der Wurm drin. Irgendwelche Handles nicht richtig freigegeben?



Hier der Code + Beispielprojekt


ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
var
  DirectDC : hdc;

  srcbmpHandle: HBITMAP;
  dstbmpHandle: HBITMAP;

  src: Bitmap;
  dst: Bitmap;

  dstbmpInfo: BITMAPINFO;
  i, x,y :integer;
  p1,p2   : pRGB;
begin
  GetObject(Image1.Picture.Bitmap.Handle, sizeof(BITMAP), @src);

  ZeroMemory(@dstbmpInfo,sizeof(BITMAPINFO));
  dstbmpInfo.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
  dstbmpInfo.bmiHeader.biWidth := src.bmWidth;
  dstbmpInfo.bmiHeader.biHeight := src.bmHeight;
  dstbmpInfo.bmiHeader.biPlanes := 1;
  dstbmpInfo.bmiHeader.biBitCount := src.bmBitsPixel;

  DirectDC := CreateCompatibleDC(0);
  dstbmpHandle := CreateDIBSection(DirectDC, dstbmpInfo, DIB_RGB_COLORS, dst.bmBits, 00);
  GetObject(dstbmpHandle, sizeof(BITMAP), @dst);


  FillChar(dst.bmBits^, dst.bmWidth*dst.bmHeight*sizeof(dstbmpInfo.bmiColors), $30);  // dunkelgrau für debug



  p1 := src.bmBits;
  p2 := dst.bmBits;

  for i:=0 to dst.bmWidth*dst.bmHeight-1 do
  begin
       p2^.rBlue  := p2^.rBlue*2 ;
       p2^.rGreen := p1^.rGreen +50;
       p2^.rRed   := p1^.rRed - (p2^.rRed div 2);
       inc(p1);
       inc(p2);
  end;

  SelectObject(DirectDC, dstbmpHandle);
  BitBlt(Image2.Canvas.Handle, 00, dst.bmWidth, dst.bmHeight, directdc, 0,0, SRCCOPY);

  DeleteObject(dstbmpHandle);
  DeleteObject(srcbmpHandle);
  DeleteDC(DirectDC);
end;
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von user32 am Mo 08.06.15 06:22, insgesamt 3-mal bearbeitet
Boldar
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1555
Erhaltene Danke: 70

Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
BeitragVerfasst: Mo 08.06.15 05:52 
Soweit ich weiss, gibt doch Scanline im Endeffekt auch nur einen Pointer zurück, soltle also von der Geschwindigkeit her genauso schnell sein wie direkter Speicherzugriff?
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Mo 08.06.15 05:59 
user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Soweit ich weiss, gibt doch Scanline im Endeffekt auch nur einen Pointer zurück, soltle also von der Geschwindigkeit her genauso schnell sein wie direkter Speicherzugriff?

Schon, aber das läuft ja erstmal über die Graphics und andere VCL Sachen..
Wenn jede Millisekunde wichtig ist spürt man sogar das.

Hier hat mal ein Foraner geschrieben, dass es mit CreateDIBSection schneller als mit Scanline geht. Darum bin ich auf den Trip gekommen.
Wenn es nicht geht, dann bin ich enttäuscht und komme mir verarscht vor weil dann war die ganze Arbeit umsonst. :cry:

Die ganzen Optimierungs-Fetischisten hier im Forum sind wohl alle nicht mehr aktiv :?
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 307
Erhaltene Danke: 29

Windows 8.1
Delphi 7 Pers.
BeitragVerfasst: Mo 08.06.15 22:51 
Hallo,

ich hatte ein ähnliches Problem und nach diversen Hilfen im Internet herausgefunden (Delphi 7, Windows 8.1):

"Pixeloperationen mit scanline gehen viel schneller als mit pixels[]. Das gilt aber nur für DIBs mit Bitmaps "pf24bit".
Bitblt sortiert das anscheinend um, wenn eine Zielbitmap ein anderes Format hat."

Ich habe da meine Bitmap gleich auf pf24 geladen. Hat mir einen Geschwindigkeitsgewinn um den Faktor 10 gebracht.

Tiefer verstanden habe ich das aber nicht, brauchte ich auch nicht.

Hilft vielleicht,

Gruß GuaAck
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 307
Erhaltene Danke: 29

Windows 8.1
Delphi 7 Pers.
BeitragVerfasst: Mo 08.06.15 23:03 
Nachtrag,

falls es ganz schnell gehen soll und die Algorithmen passen, dann wären auch DirectX und OpenGL Alternativen. Beides aber deutlich umständlicher zu programmieren als einfach BitBlt.

Gruß nochmals
GuaAck
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 165
Erhaltene Danke: 42



BeitragVerfasst: Di 09.06.15 12:01 
Ich habe das Programm bei mir getestet. Alle drei Methoden benötigen jeweils etwa 0,6ms (Millisekunden!) um die Operation abzuschließen.
Die Unterschiede liegen im Bereich der Messungenauigkeit.
Deine Vermutung das Scanline langsamer ist, kann ich nicht bestätigen.

Die Geschwindigkeit von ScanLine ist nicht abhängig vom Pixelformat.
Wenn Scanline unter bestimmten Umständen langsam ist, dann hat das äußere Ursachen (z.B. das eine Grafik erst einmal in eine Bitmap umgewandelt werden muss).

Die seltsamen Grafikeffekte sind darauf zurückzuführen, dass der Wertebereich der einzelnen Farbanteile 0..255 für B, G und R bei deinen Berechnungen überschritten wird.
Einloggen, um Attachments anzusehen!
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Di 09.06.15 14:05 
user profile iconBlup hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe das Programm bei mir getestet. Alle drei Methoden benötigen jeweils etwa 0,6ms (Millisekunden!) um die Operation abzuschließen.
Die Unterschiede liegen im Bereich der Messungenauigkeit.
Deine Vermutung das Scanline langsamer ist, kann ich nicht bestätigen.

Die Geschwindigkeit von ScanLine ist nicht abhängig vom Pixelformat.
Wenn Scanline unter bestimmten Umständen langsam ist, dann hat das äußere Ursachen (z.B. das eine Grafik erst einmal in eine Bitmap umgewandelt werden muss).

Die seltsamen Grafikeffekte sind darauf zurückzuführen, dass der Wertebereich der einzelnen Farbanteile 0..255 für B, G und R bei deinen Berechnungen überschritten wird.

Ja die Grafikeffekte waren jetzt gar nicht mal das Problem, hab halt irgendwelche beliebigen Werte genommen damit was verändert wird. Ich dachte nur, ich hätte bei den WinApi Funktionen irgendwo geschlampt. Aber da stimmt wohl alles?

0,6 Millisekunden ist bei mir auch ähnlich. Beim ersten so 0,42ms im Schnitt
Aber....ABER: Probier stattdessen mal eine 512x512 Textur, und führe noch ein paar Vektorberechnungen für jeden Pixel durch....
Oder was mit Fourier...Bei 1024x1024 wirds dann schon ekelhaft.
Dann sieht die Sache schon anders aus...So eine kleine 256x256 Textur kopieren ist natürlich ein Witz für heutige CPUs.

Edit: Deine Method 3 ist jetzt bei mir sogar schneller! Jetzt: DoColorWork1 ~0,5ms, DoColorWork3 ~0,33ms
Frage mich gerade warum.


user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Nachtrag,

falls es ganz schnell gehen soll und die Algorithmen passen, dann wären auch DirectX und OpenGL Alternativen. Beides aber deutlich umständlicher zu programmieren als einfach BitBlt.

Gruß nochmals
GuaAck
Wird wohl auf OpenGL hinauslaufen.
Aber das ist ja schon ein ziemlicher Mehraufwand für so einfaches 2D Zeugs.. Hab noch nie was mit Shadern gemacht und das sieht ziemlich übel aus :(
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Di 09.06.15 15:17 
Man kann natürlich mit der WinAPI herumspielen, aber warum sich die Mühe machen, wenn es auch so geht:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
procedure TForm1.Button4Click(Sender: TObject);
var
  x, y, w, h, d, yn: integer;
  p: PRGBQuad;
  qa, qb, qf: Int64;
begin
  // Image1 nach Image2 kopieren und dabei in 32 bit Bitmap umwandeln
  with Image2.Picture.Bitmap do
  begin
    PixelFormat := pf32bit;
    w := Image1.Picture.Width;
    h := Image1.Picture.Height;
    Width := w;
    Height := h;
    Canvas.Draw(00, Image1.Picture.Graphic);
  end;

  QueryPerformanceCounter(qa);

  for y := 0 to h - 1 do
  begin
    p := Image2.Picture.Bitmap.ScanLine[y];
    yn := h - 1 - y;
    d := 255 - 0 - yn;
    for x := 0 to w - 1 do
    begin
      // d := 255 - x - yn;
      if d < 0 then d := 0;
      p^.rgbBlue := (d + p^.rgbBlue) shr 1;
      p^.rgbGreen := (yn + p^.rgbGreen) shr 1;
      p^.rgbRed := (x + p^.rgbRed) shr 1;
      Inc(p);
      Dec(d);
    end;
  end;

  QueryPerformanceCounter(qb);
  QueryPerformanceFrequency(qf);
  Label2.Caption := Format('%.5f ms', [(qb - qa) / qf * 1000]);
end;


Braucht kein temporäres Bitmap, führt die Änderung "in-place" auf dem Zielbitmap in Image2 aus, ist dadurch auch etwas schneller (besseres Cache-Verhalten).
Man kann sich natürlich seinen eigenen RGB-Record definieren, aber ich habe hier TRGBQuad aus der Windows-Unit benutzt (gibt auch TRGBTriple für 24 bit).
Ein Aufruf von Bitmap.ScanLine braucht höchstens etwas Zeit, wenn das Bitmap ein DDB (device dependent bitmap) war, denn dann muss es zuerst in ein DIB umgewandelt werden.
Das ist aber hier nicht der Fall, da vorher PixelFormat explizit auf 32 bit gesetzt wurde.
Man könnte wie in deinem Code ScanLine nur einmal aufrufen, den Pointer speichern und richtig inkrementieren, aber das bringt keinen großen Geschwindigkeitsvorteil und setzt außerdem voraus, dass man den genauen Aufbau des Bitmaps kennt (bottom-up oder top-down, wieviele Bytes pro Zeile... ok, standardmäßig sind Bitmaps bottom-up und bei 32 bits per Pixel hat man Width * 4 Bytes pro Zeile).

Du könntest dir auch mal Graphics32 anschauen. Enthält optimierte Komponenten und Routinen für 32 bit Bitmaps.
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 458
Erhaltene Danke: 90

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mi 10.06.15 00:01 
user profile iconuser32 hat folgendes geschrieben Zum zitierten Posting springen:
Probier stattdessen mal eine 512x512 Textur, und führe noch ein paar Vektorberechnungen für jeden Pixel durch....
Oder was mit Fourier...Bei 1024x1024 wirds dann schon ekelhaft.
Dann sieht die Sache schon anders aus...So eine kleine 256x256 Textur kopieren ist natürlich ein Witz für heutige CPUs.

Aber dann spielt der Zugriff auf den einzelnen Pixel nur noch eine tertiäre Rolle. Die wirklichen Zeitfresser sind dann die Vektorrechnungen bzw. Fouriers. Und natürlich darf man nicht vergessen: Verdoppelt man die Höhe und Breite eines Bitmaps, vervierfacht sich die Pixelzahl, ergo explodieren dir allein deswegen schon die Rechenzeiten. Vierfache Pixelzahl braucht nun mal die vierfache Rechenzeit.

Dagegen hilft nicht mehr viel: Multithreading und/oder mehr CPU-Power (CUDA oder OpenCL ist da oftmals durchaus eine Maßnahme).

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.