Klipping Bitmap Berukuran Besar (> 10MB) langsung dari filenya

Baru kemaren berurusan sama file bitmap (.BMP) yang ukurannya mendekati 50 MB. Persoalannya adalah bagaimana mengambil beberapa bagian (klipping/cropping) yang berukuran relatif kecil (100×100 pixel) tanpa harus mengorbankan kecepatan dan memori.

cara trivialnya adalah dengan membaca bitmap tersebut (pakai TBitmap), lalu digambarkan di bitmap lain yang ukurannya lebih kecil.. yeah, so… trivial.. tapi persoalannya file ini kalau dibuka pakai penampil citra (irfanview/paint.net) aja perlu waktu beberapa detik, dan lagipula kalau membuka file ini menggunakan TBitmap peak-memory langsung naik ke 60 MB (walaupun hanya sesaat). Kalau lagi luang sih baik-baik aja, tapi kalau dalam kondisi ekstrim memori hampir penuh bisa-bisa gagal..

akhirnya terpaksa membaca deskripsi format file dari sini dan membaca langsung ke file. Ternyata file bitmap banyak macamnya jadi untuk mempermudah saya hanya berurusan dengan bitmap dengan format 8 dan 24 bit per pixel yang tidak dimampatkan (menggunakan RLE).

so, this is the code..

record yang perlu dipersiapkan adalah header dan entri utk palet (sebetulnya bisa dilewat jika menggunakan bitmap yang bit per pixelnya > 16). struktur headernya seperti ini :

TBMPHeader = packed record
    sID : array[1..2] of char;
    dFileSize : dword;
    dReserved : dword;
    dBitmapDataOffset : dword;
    dHeaderSize : cardinal;
    dWidth, dHeight : dword;
    wNumPlanes : word;
    wBitPerPixel : word;
    dCompressionType : dword;
    dDataSize : dword;
    dHRes, dVRes : dword;
    dNumColors : dword;
    dNumImportantColors : dword;
  end;

TBMPPaletteEntry=packed record
    r,g,b,unused:byte;
  end;

Untuk mengakses file, saya menggunakan turunan TStream (TFileStream). Kodenya ada dalam prosedur LoadBMP. dalam kode di bawah, saya membaca bitmap dalam matriks datamatrix

procedure TForm1.loadBMP(filename: string);
var
  stream : TFileStream;
  header : TBMPHeader;
  x,y : integer; {offset posisi yang ingin diambil}
  msize : integer; {dimensi matriks}

  procedure read_palette;
  var
    i : integer;
    pal : array of TBMPPaletteEntry;
  begin
    if header.wBitPerPixel < 16 then begin
      setlength(pal, header.dNumColors);
      if length(pal) > 0 then
      stream.Read(pal[0], header.dNumColors * sizeof(TBMPPaletteEntry));
    end;
  end;

  procedure read_bitmap_data;
  var
    i, j : integer;
    linespan : dword;
    oldpos : dword;
    calcSize : dword;
    datamatrix : array of array of byte;
    mstream : TMemoryStream;
  begin
    setlength(datamatrix, msize);
    for i := 0 to high(datamatrix) do
      setlength(datamatrix[i], msize * (header.wBitPerPixel shr 3));

    stream.Position := header.dBitmapDataOffset;
    { kadang field DataSize bernilai 0 }
    calcSize := stream.Size-stream.Position;
    if header.dDataSize <> calcSize then header.dDataSize := calcSize;

    { jumlah byte yang diperlukan untuk menyimpan tiap baris pixel }
    linespan := header.dDataSize div header.dHeight;

    { skip some lines }
    stream.Position := stream.Position 
            + (linespan * (header.dHeight-y-length(datamatrix))) 
            + (x * (header.wBitPerPixel shr 3));

    { read upward }
    i := y+length(datamatrix)-1;
    while i >= y do begin
      oldpos := stream.Position;
      stream.Read(datamatrix[i-y][0], length(datamatrix[i-y]));
      stream.Position := oldpos + linespan;
      dec(i);
    end;

    { dump as raw bytes }
    mStream := TMemoryStream.Create;
    for i := 0 to high(datamatrix) do begin
      mStream.Write(datamatrix[i][0], length(datamatrix[i]));
    end;
    mStream.SaveToFile(changefileext(filename, '.raw'));
    mStream.Free;
  end;

begin
  x := 410;
  y := 280;
  msize := 120;
  stream := TFileStream.Create(filename, fmOpenRead);
  stream.Read(header, sizeof(header));
  if header.sID <> 'BM' then begin 
    showmessage('this is not a valid bitmap file'); 
    exit;
  end;
  //read_palette; //comment jika di skip
  read_bitmap_data;
  stream.Free;
end;

oya, dalam kode diatas pembacaannya dimulai dari baris terbawah karena penyimpanan pixel dalam file BMP terbalik (baris pixel paling bawah disimpan terlebih dahulu dan seterusnya ke baris pertama paling atas).

2 comments

  1. yadi · Desember 31, 2007

    mas peb..gimana cara untuk mempercepat proses perhitungan DCT pada gambar grayscale
    karena agak lama apalagi kalo ukurannya pixelnya sudah agak besar..
    seperti 128 x 128…

  2. Ping-balik: FloodFill ke Bitmap External « GAIBlog

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s