Bitmap, Pixel-Position im Stream

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.

Bitmap, Pixel-Position im Stream

Beitragvon Adrian » 20. Sep 2017, 06:43 Bitmap, Pixel-Position im Stream

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 21. Sep 2017, 07:19, insgesamt 1-mal geändert.
Adrian
 
Beiträge: 18
Registriert: 12. Nov 2007, 12:41
OS, Lazarus, FPC: Winux (L 1.6 FPC 3.0.0) | 
CPU-Target: 32Bit
Nach oben

Beitragvon marcov » 20. Sep 2017, 07:41 Re: Bitmap, Pixel-Position im Stream

( y div (480 div 4))*4+(x div (640 div 4)) ?
marcov
 
Beiträge: 999
Registriert: 5. Aug 2008, 08:37
Wohnort: Eindhoven (Niederlande)
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk) | 
CPU-Target: 32/64,PPC(+64), ARM
Nach oben

Beitragvon siro » 20. Sep 2017, 09:05 Re: Bitmap, Pixel-Position im Stream

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
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Zuletzt geändert von siro am 20. Sep 2017, 10:32, insgesamt 1-mal geändert.
Grüße von Siro
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 222
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 20. Sep 2017, 10:13 Re: Bitmap, Pixel-Position im Stream

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.
wp_xyz
 
Beiträge: 2251
Registriert: 8. Apr 2011, 08:01

Beitragvon Adrian » 21. Sep 2017, 07:15 Re: Bitmap, Pixel-Position im Stream

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

Gruß,
Adrian
Adrian
 
Beiträge: 18
Registriert: 12. Nov 2007, 12:41
OS, Lazarus, FPC: Winux (L 1.6 FPC 3.0.0) | 
CPU-Target: 32Bit
Nach oben

• Themenende •

Zurück zu Sonstiges



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste

porpoises-institution
accuracy-worried