AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

TBitmap mit 50.000 x 50.000 pixel

Ein Thema von bernhard_LA · begonnen am 24. Nov 2021 · letzter Beitrag vom 24. Nov 2021
Antwort Antwort
bernhard_LA

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

TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 09:26
das Thema *.tif mit dieser PixelAnzahl und FMX Framework ist leider immer noch nicht gelöst https://www.delphipraxis.net/198053-...000-pixel.html

Aktuell würde mir schon eine Lösung mit TBitmap und VCL Framework schon helfen , die Datei kommt entweder als *.bmp oder deutlich Platzsparender im *.jpg Format.

mit unserer Anwendung können wir max. 25.000 * 25.000 Pixel laden als *.bmp, die Pixel manipulieren wir mit scanline.

Der Wettbewerber wie IRFANVIEW hat hier keinerlei Probleme ! Ohne Lösung müsste ich dann auf Matlab oder .... umsteigen
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 11:03
Ich vermute, das Problem besteht am Ende darin, weil die gängige Implementation für Bitmaps immer Speicherbereiche am Stück allozieren möchte.
Das ist dann bei 50000 x 50000 Pixel x 4 Byte (32-Bit Farbraum - Default-Farbtiefe) schnell mal 9.536,74 MiB unfragmentierter Speicher, den der Speichermanager anfordert.
Und das, obwohl ein leeres weißes TIFF dieses Ausmaßes auf der Festplatte/SSD nur 6,53 MB (6.850.250 Bytes) belegt.
Da ist bei vielen Systemen mit einer RAM-Ausstattung von 16 GB schnell Schicht im Schacht.

Anwendungen wie IrfanView oder Paint.Net verarbeiten höchstwahrscheinlich solche Dateien mit Techniken wie das einlesen von einzelnen Regions/Tiles (also kleineren Einzelbildern) anstatt alles am Stück und die werden dann entsprechend der Anzeigengröße noch skaliert dargestellt.
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.490 Beiträge
 
Delphi 7 Professional
 
#3

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 11:09
Der Speicherverbrauch bei Bildern ist im Arbeitsspeicher (soweit ich weiß) nicht von der Größe der Datei abhängig, sondern von der Größe des darzustellenden Bildes.

Der Speicherbedarf lässt sich wie folgt berechnen:
Speicherbedarf = (Breite in Pixeln x Höhe in Pixeln) x (Farbtiefe in Bit / 8)

Bei 50.000 * 50.000 * 24 / 8 kommen da dann mal eben 7.500.000.000 Byte zusammen. Das sind dann gerade mal so um die 6,98 GB.
Bei 25.000 * 25.000 * 24 / 8 sind es immernoch 1,75 GB, also ein Viertel.

Je nach Implementierung werden (wie TiGü schon schrieb) pro Pixel 4 Byte benötigt. Damit steigt der Speicherverbrauch dann noch um 25%. Die muss man dann auch erstmal zur Verfügung haben.

IrfanView scheint nicht das ganze Bild vollständig in den Speicher zu laden, sondern nur den Teil, der gerade angezeigt / bearbeitet wird.

Ansonsten schau Dir mal ImageEn an, die können in Bezug auf Bildmanipulation recht viel. Eventuell gehen die ja mit großen Bildern etwas speicherschonender um, als TBitmap ...
  Mit Zitat antworten Zitat
Benutzerbild von Sinspin
Sinspin

Registriert seit: 15. Sep 2008
Ort: Dubai
677 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 12:06
Bitmaps sind vom Dateiformat her extrem simpel, wenn da die Standardkomponenten aufgeben kann man sich schnell selber was schreiben (was mit fragmentierbaren Speicher arbeitet)

Ich hatte mir Irfan schon öfter Kontakt bezüglich exotischer Bildformate. Wenn du Hilfe brauchst, einfach mal anschreiben. Er spicht auch deutsch.
Stefan
Nur die Besten sterben jung
A constant is a constant until it change.
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#5

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 12:28
Ansonsten mal die ImageEn Komponenten versuchen, kosten ein wenig Geld, sind aber sehr flexibel. Es gibt auch Demos, einfach mal testen.
https://www.imageen.com/demos/

......
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#6

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 12:35
Nachtrag aus der Hilfe:

property Location: TIELocation;
Specifies how the image is stored (e.g. in memory).

ieFile: Uses memory mapped files. Canvas not available. Used for very large images.

Auf jeden Fall testen

......
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
generic

Registriert seit: 24. Mär 2004
Ort: bei Hannover
2.416 Beiträge
 
Delphi XE5 Professional
 
#7

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 14:03
Du kannst auch probieren die Datei direkt und vollständig mit GDI+ zu verarbeiten.
Wobei FMX auch DX bzw. GDI+ nutzen sollte.
Coding BOTT - Video Tutorials rund um das Programmieren - https://www.youtube.com/@codingbott
  Mit Zitat antworten Zitat
bernhard_LA

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

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 14:33
mit diesem Code fragment aus dem Swiss Delphi center, kann ich zumindest schon mal große BMPs zur Anzeige bringen,
nachdem die Datentypen jetzt 64 bit breit sind


Delphi-Quellcode:

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm2 = class(TForm)
    OpenDialog1: TOpenDialog;
    Panel1: TPanel;
    Image1: TImage;
    Button1: TButton;
    ListBox1: TListBox;
    CheckBox_autosize: TCheckBox;
    CheckBox_stretched: TCheckBox;
    CheckBox_proportional: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure CheckBox_autosizeClick(Sender: TObject);
    procedure CheckBox_proportionalClick(Sender: TObject);
    procedure CheckBox_stretchedClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }


    FLargeBitmap : TBitmap;

  public
    { Public declarations }


  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}





function MyGetMem(Size: DWORD): Pointer;
begin
  Result := Pointer(GlobalAlloc(GPTR, Size));
end;

procedure MyFreeMem(p: Pointer);
begin
  if p = nil then Exit;
  GlobalFree(THandle(p));
end;


/// <summary>
/// This code will fill a bitmap by stretching an image coming from a big
/// bitmap on disk. <br /><br />
/// </summary>
/// <param name="FileName">
/// Name of the uncompressed bitmap toread
/// </param>
/// <param name="DestBitmap">
/// Target bitmapwhere the bitmap on disk will be resampled
/// </param>
/// <param name="BufferSize">
/// The size of a memory buffer used for reading scanlines fromthe physical
/// bitmap on disk. <br />This value will decide how manyscanlines can be
/// read from disk at the same time, with always a <br />minimum value of 2
/// scanlines
/// </param>
/// <param name="Proportional">
/// Adjust the size of the des
/// </param>
/// <param name="TotalBitmapWidth">
/// nr. of pixels
/// </param>
/// <param name="TotalBitmapHeight">
/// nr. of pixels
/// </param>
/// <returns>
/// Will return false on error.
/// </returns>
function GetDIBInBands(const FileName: string;
  DestBitmap: TBitmap; BufferSize: Integer; Proportional : Boolean;
  out TotalBitmapWidth, TotalBitmapHeight: Integer): Boolean;
var
  FileSize: int64; // calculated file size
  ImageSize: int64; // calculated image size
  dest_MaxScans: int64; // number of scanline from source bitmap
  dsty_top: int64; // used to calculate number of passes
  NumPasses: int64; // number of passed needed
  dest_Residual: int64; // number of scanlines on last band
  Stream: TStream; // stream used for opening the bitmap
  bmf: TBITMAPFILEHEADER; // the bitmap header
  lpBitmapInfo: PBITMAPINFO; // bitmap info record
  BitmapHeaderSize: int64; // size of header of bitmap
  SourceIsTopDown: Boolean; // is reversed bitmap ?
  SourceBytesPerScanLine: int64; // number of bytes per scanline
  SourceLastScanLine: Extended; // last scanline processes
  SourceBandHeight: Extended; //
  BitmapInfo: PBITMAPINFO;
  img_start: int64;
  img_end: int64;
  img_numscans: int64;
  OffsetInFile: int64;
  OldHeight: int64;
  bits: Pointer;
  CurrentTop: int64;
  CurrentBottom: int64;
begin
  Result := False;

  // open the big bitmap
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);

  // total size of bitmap
  FileSize := Stream.Size;
  // read the header
  Stream.ReadBuffer(bmf, SizeOf(TBITMAPFILEHEADER));
  // calculate header size
  BitmapHeaderSize := bmf.bfOffBits - SizeOf(TBITMAPFILEHEADER);
  // calculate size of bitmap bits
  ImageSize := FileSize - Integer(bmf.bfOffBits);
  // check for valid bitmap and exit if not
  if ((bmf.bfType <> $4D42) or
    (Integer(bmf.bfOffBits) < 1) or
    (FileSize < 1) or (BitmapHeaderSize < 1) or (ImageSize < 1) or
    (FileSize < (SizeOf(TBITMAPFILEHEADER) + BitmapHeaderSize + ImageSize))) then
  begin
    Stream.Free;
    Exit;
  end;
  lpBitmapInfo := MyGetMem(BitmapHeaderSize);
  try
    Stream.ReadBuffer(lpBitmapInfo^, BitmapHeaderSize);
    // check for uncompressed bitmap
    if ((lpBitmapInfo^.bmiHeader.biCompression = BI_RLE4) or
      (lpBitmapInfo^.bmiHeader.biCompression = BI_RLE8)) then
    begin
      Exit;
    end;

    // bitmap dimensions
    TotalBitmapWidth := lpBitmapInfo^.bmiHeader.biWidth;
    TotalBitmapHeight := abs(lpBitmapInfo^.bmiHeader.biHeight);


            //
        if Proportional then
             begin
               DestBitmap.Height := Round( DestBitmap.Width * TotalBitmapHeight / TotalBitmapWidth );
             end;


    // is reversed order ?
    SourceIsTopDown := (lpBitmapInfo^.bmiHeader.biHeight < 0);

    // calculate number of bytes used per scanline
    SourceBytesPerScanLine := ((((lpBitmapInfo^.bmiHeader.biWidth *
      lpBitmapInfo^.bmiHeader.biBitCount) + 31) and not 31) div 8);

    // adjust buffer size
    if BufferSize < Abs(SourceBytesPerScanLine) then
      BufferSize := Abs(SourceBytesPerScanLine);

    // calculate number of scanlines for every pass on the destination bitmap
    dest_MaxScans := round(BufferSize / abs(SourceBytesPerScanLine));
    dest_MaxScans := round(dest_MaxScans * (DestBitmap.Height / TotalBitmapHeight));

    if dest_MaxScans < 2 then
      dest_MaxScans := 2; // at least two scan lines

    // is not big enough ?
    if dest_MaxScans > TotalBitmapHeight then
      dest_MaxScans := TotalBitmapHeight;

    { count the number of passes needed to fill the destination bitmap }
    dsty_top := 0;
    NumPasses := 0;
    while (dsty_Top + dest_MaxScans) <= DestBitmap.Height do
    begin
      Inc(NumPasses);
      Inc(dsty_top, dest_MaxScans);
    end;
    if NumPasses = 0 then Exit;

    // calculate scanlines on last pass
    dest_Residual := DestBitmap.Height mod dest_MaxScans;

    // now calculate how many scanlines in source bitmap needed for every band on the destination bitmap
    SourceBandHeight := (TotalBitmapHeight * (1 - (dest_Residual / DestBitmap.Height))) /
      NumPasses;

    // initialize first band
    CurrentTop := 0;
    CurrentBottom := dest_MaxScans;

    // a floating point used in order to not loose last scanline precision on source bitmap
    // because every band on target could be a fraction (not integral) on the source bitmap
    SourceLastScanLine := 0.0;

    while CurrentTop < DestBitmap.Height do
    begin
      // scanline start of band in source bitmap
      img_start := Round(SourceLastScanLine);
      SourceLastScanLine := SourceLastScanLine + SourceBandHeight;
      // scanline finish of band in source bitmap
      img_end := Round(SourceLastScanLine);
      if img_end > TotalBitmapHeight - 1 then
        img_end := TotalBitmapHeight - 1;
      img_numscans := img_end - img_start;
      if img_numscans < 1 then Break;
      OldHeight := lpBitmapInfo^.bmiHeader.biHeight;
      if SourceIsTopDown then
        lpBitmapInfo^.bmiHeader.biHeight := -img_numscans
      else
        lpBitmapInfo^.bmiHeader.biHeight := img_numscans;

      // memory used to read only the current band
      bits := MyGetMem(Abs(SourceBytesPerScanLine) * img_numscans);

      try
        // calculate offset of band on disk
        OffsetInFile := TotalBitmapHeight - (img_start + img_numscans);
        Stream.Seek(Integer(bmf.bfOffBits) + (OffsetInFile * abs(SourceBytesPerScanLine)),
          soFromBeginning);
        Stream.ReadBuffer(bits^, abs(SourceBytesPerScanLine) * img_numscans);

        SetStretchBltMode(DestBitmap.Canvas.Handle, COLORONCOLOR);



        // now stretch the band readed to the destination bitmap
        StretchDIBits(DestBitmap.Canvas.Handle,
          0,
          CurrentTop,
          DestBitmap.Width,
          Abs(CurrentBottom - CurrentTop),
          0,
          0,
          TotalBitmapWidth,
          img_numscans,
          Bits,
          lpBitmapInfo^,
          DIB_RGB_COLORS, SRCCOPY);
      finally
        MyFreeMem(bits);
        lpBitmapInfo^.bmiHeader.biHeight := OldHeight;
      end;

      CurrentTop := CurrentBottom;
      CurrentBottom := CurrentTop + dest_MaxScans;
      if CurrentBottom > DestBitmap.Height then
        CurrentBottom := DestBitmap.Height;
    end;
  finally
    Stream.Free;
    MyFreeMem(lpBitmapInfo);
  end;
  Result := True;
end;


procedure TForm2.Button1Click(Sender: TObject);
var
  bmw, bmh: Integer;
  Bitmap : TBitmap;
begin
  Bitmap := TBitmap.Create;
  with TOpenDialog.Create(nil) do
    try
      DefaultExt := 'BMP';
      Filter := 'Bitmaps (*.bmp)|*.bmp|Raw files (*.raw)|*.RAW';
      Title := 'Define bitmap to display';
      if not Execute then Exit;
      { define the size of the required bitmap }
      FLargeBitmap.Width := Self.ClientWidth;
      FLargeBitmap.Height := Self.ClientHeight;
      FLargeBitmap.PixelFormat := pf24Bit;
      Screen.Cursor := crHourglass;
      // use 100 KB of buffer

      if not GetDIBInBands(FileName, FLargeBitmap, 100 * 1024, true, bmw, bmh) then Exit;


      ListBox1.Items.Add('width :' + bmw.ToString);
      ListBox1.Items.Add('height:' + bmh.ToString);

      // Self.Canvas.Draw(0,0,Bitmap);

      image1.Picture.Bitmap.Assign(FLargeBitmap)
    finally
      Free;

      Screen.Cursor := crDefault;
    end;
end;

procedure TForm2.CheckBox_autosizeClick(Sender: TObject);
begin
     image1.AutoSize := CheckBox_autosize.Checked;
end;

procedure TForm2.CheckBox_proportionalClick(Sender: TObject);
begin
    image1.Proportional := CheckBox_proportional.Checked;
end;

procedure TForm2.CheckBox_stretchedClick(Sender: TObject);
begin
    image1.Stretch := CheckBox_stretched.Checked;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
     FLargeBitmap :=TBitmap.Create;
end;

end.
  Mit Zitat antworten Zitat
Benutzerbild von Sinspin
Sinspin

Registriert seit: 15. Sep 2008
Ort: Dubai
677 Beiträge
 
Delphi 10.3 Rio
 
#9

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 17:31
Ich habe keine Ahnung ob die mit so großen Dateien umgehen können graphics32, sonst wäre das auch noch eine Alternative.
Stefan
Nur die Besten sterben jung
A constant is a constant until it change.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: TBitmap mit 50.000 x 50.000 pixel

  Alt 24. Nov 2021, 19:13
Im Prinzip kann Delphi auch mit sehr großen Bildern umgehen,
Delphi-Referenz durchsuchenTWICImage
aber einige Funktionen in der Klasse sind einfach nur grauenhaft implementiert.

z.B. StretchDraw und Co.
Anstatt über die Funktionen/Interfaces der WIC (Windows Imaging Component) zu gehen, versucht Delphi das "ganze" Bild in ein TBitmap zu zeichnen und anschließend das zu stretchen, was dann natürlich knallt, weil BITMAP und der RAM schnell an seine Grenzen kommt.
(am Ende hab ich mir den Teil dann selber implementiert, was auch wieder nicht sonderlich schön war ... hätte einfacher gehen können, wenn niemand so pervers gewesen wäre und wichtige Teil in privat deklariert hätte, oder verwendete Interfaces nicht sofort wieder weggeworfen worden wären, wenn man sie später hätte gebrauchen können)
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (24. Nov 2021 um 19:15 Uhr)
  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 10:57 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz