Bitmap, Pixel-Position im Stream

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
Adrian
Beiträge: 31
Registriert: Mo 12. Nov 2007, 12:41
OS, Lazarus, FPC: Winux (L 2.0.6 FPC 3.0.4)
CPU-Target: 64Bit

Bitmap, Pixel-Position im Stream

Beitrag von Adrian »

Servus!

Ich habe eine Bitmap (640x480, 32Bit) in einem TMemoryStream vorliegen. Die ersten 54 Bytes enthalten die Header-Informationen, dann folgt der Rot-Anteil des linken unteren Pixels und das letzte Byte ist der Blau-Anteil des rechten oberen Bildpunkts. Damit ist die Größe 640 (Breite) x 480 (Höhe) x 4 (32Bit) + 54 = 1.228.854 Bytes.
Nun möchte ich das Bild in 4x4=16 Sektionen aufteilen, wobei das Teil links unten die Nummer 1, und das rechts oben die Nummer 16 ist. Alle Pixel-Daten werden nun der Reihe nach bearbeitet und hier ist mein Problem: Ich muß wissen, zu welchem Teilstück die gerade bearbeiteten Daten gehören. Das Ganze mit if-Abfragen aus der Position im Stream zu machen scheidet aus, einerseits ist das viel zu aufwendig, andererseits dauert das auch viel zu lang. Unter Umständen könnte ich mit einer Art Look-Up arbeiten.

Sollte jedoch jemand eine Idee haben, wie man die Sektion schnell berechnen kann, dann wäre mir diese Information höchst willkommen.

Gruß,
Adrian

Anm.: Formel und Header-Bytes korrigiert
Zuletzt geändert von Adrian am Do 21. Sep 2017, 08:19, insgesamt 1-mal geändert.

marcov
Beiträge: 1100
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Bitmap, Pixel-Position im Stream

Beitrag von marcov »

( y div (480 div 4))*4+(x div (640 div 4)) ?

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Bitmap, Pixel-Position im Stream

Beitrag von siro »

Unter Windows würde es mit der BitBlt Funktion recht einfach gehen:
So in etwa sollte das funktionieren:

Code: Alles auswählen

 
var QuellBitmap : TBitmap;
var ZielBitmap  : Array[0..3,0..3] of TBitmap;
 
procedure TForm1.FormCreate(Sender: TObject);
var b:TBitmap;
var x,y,w,h:Integer;
var q:TBitmap;
begin
  QuellBitmap:=TBitmap.create;
  QuellBitmap.LoadFromFile('TestBild.bmp');
  w:=640 DIV 4;
  h:=480 DIV 4;
 
  { Ziel Bitmap Objekte erzeugen }
  for x:=0 to 3 do for y:=0 to 3 do begin
    ZielBitmap[x,y]:=TBitmap.create;
    ZielBitmap[x,y].SetSize(w,h);   { Breite und Höhe der Bitmaps setzen }
 
    BitBlt(
    ZielBitmap[x,y].canvas.handle, { Handle der Ziel Bitmap }
    0,                             { Ziel Koordinate X = 0 }
    0,                             { Ziel Koordinate Y = 0 }
    w,                             { Breite der ZielBitmap }
    h,                             { Höhe   der ZielBitmap }
    QuellBitmap.canvas.Handle,     { Handle der Quell Bitmap }
    w*x,                           { Quellposition X Position innerhalb der großen Bitmap }
    h*y,                           { Quellposition Y Position innerhalb der großen Bitmap }
    SRCCOPY);                      { Kopierfunktion }
  end;
 


Es wird das komplette Bild in eine Bitmap geladen,
dann werden die einzelnen Ausschnitte mittels BitBlt in separate Bitmaps kopiert.

Mit folgendem Code, bei OnPaint, kann man sich das Ergebnis dann ansehen:

Code: Alles auswählen

  for x:=0 to 3 do for y:=0 to 3 do
    canvas.Draw(x*200,y*200,ZielBitmap[x,y]);         
 
 



getesteter Beispiel Code:
project1.zip
(349.42 KiB) 78-mal heruntergeladen
Zuletzt geändert von siro am Mi 20. Sep 2017, 11:32, insgesamt 1-mal geändert.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap, Pixel-Position im Stream

Beitrag von wp_xyz »

Wieder ein schönes Ratespiel, was du genau vorhast... Aus der Bemerkung "Bitmap in einem Memorystream" vermute ich, dass du vielleicht die Blockeinteilung direkt aufgrund der Stream-Position vornehmen möchtest, ohne den Stream in ein TBitmap einzulesen. Für den Spezialfall eines unkomprimierten 32-bit-Bitmaps der Breite AWidth kann man die Pixel-Koordinaten X, Y folgendermaßen berechnen:

Code: Alles auswählen

uses
  bmpcomn;
 
procedure StreamPosToXY(AStream: TStream; AWidth: Integer; out X,Y: Integer);
const
  HeaderSize = SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader)// ist 54, nicht 64!
  PixelSize = 4;   // 1 Pixel umfasst 32 Bit, also 4 Byte
var
  pixelNr: Integer;
begin
  pixelNr := (AStream.Position - HeaderSize) div PixelSize;
  X := pixelNr mod AWidth;
  Y := pixelNr div AWidth;
end;

Anschließend kannst du mit Marco's Formel die Block-Nr berechnen. Das folgende Programm ordnet der Blocknummer eine Farbe zu und schreibt diese direkt in den Stream (Form mit 1 Image und zwei Buttons, beide mit derselben OnClick-Prozedur):

Code: Alles auswählen

uses
  bmpcomn;
 
const
  HEADER_SIZE = SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader)// ist 54, nicht 64!
 
procedure StreamPosToXY(AStream: TStream; AWidth: Integer; out X,Y: Integer);
const
  PIXEL_SIZE = 4;   // 1 Pixel umfasst 32 Bit, also 4 Byte
var
  pixelNr: Integer;
begin
  pixelNr := (AStream.Position - HEADER_SIZE) div PIXEL_SIZE;
  X := pixelNr mod AWidth;
  Y := pixelNr div AWidth;
end;
 
function BlockNr(X, Y, AWidth, AHeight: Integer): Integer;
begin
  Result := (Y div (AHeight div 4))*4 + X div (AWidth div 4);
end;
 
{ TForm1 }
 
procedure TForm1.Button1Click(Sender: TObject);
const
  BLOCK_COLOR: array[0..15] of TColor = (
    clBlack,  clMaroon,  clGreen,  clOlive,
    clNavy,   clPurple,  clTeal,   clGray,
    clSilver, clRed,     clLime,   clYellow,
    clBlue,   clFuchsia, clAqua,   clLtGray);
  W = 640;
  H = 480;
var
  bmp: TBitmap;
  ms: TMemoryStream;
  c: TColor;
  x, y: Integer;
  block: Integer;
begin
  ms := TMemoryStream.Create;
  try
    bmp := TBitmap.Create;
    try
      bmp.PixelFormat := pf32Bit;    // GANZ wichtig!
      bmp.SetSize(W, H);
      bmp.canvas.Brush.Color := clMoneyGreen;
      bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);
      bmp.SaveToStream(ms);
 
      if Sender = Button1 then begin
        Image1.Picture.Assign(bmp);
        exit;
      end;
    finally
      bmp.Free;
    end;
 
    ms.Position := HEADER_SIZE;
    while ms.Position < ms.Size do begin
      StreamPosToXY(ms, W, x, y);
      block := BlockNr(x, y, W, H);
      c := BLOCK_COLOR[block];
      ms.Write(c, Sizeof(TColor));
    end;
 
    ms.Position := 0;
    Image1.Picture.LoadFromStream(ms);
  finally
    ms.Free;
  end;
end;

Übrigens: der BMP-Header ist 54 byte lang, nicht 64.

Adrian
Beiträge: 31
Registriert: Mo 12. Nov 2007, 12:41
OS, Lazarus, FPC: Winux (L 2.0.6 FPC 3.0.4)
CPU-Target: 64Bit

Re: Bitmap, Pixel-Position im Stream

Beitrag von Adrian »

Danke, vor allem an marcov, das war genau die Lösung, die ich nicht gefunden habe.

Gruß,
Adrian

Antworten