Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

BMP file manipulation

by Anonymous Monk
on Dec 17, 2003 at 05:44 UTC ( [id://315223]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Greetings,

My question isn't so much a perl question as a programming/logic question, but since I'm doing it in perl, might as well post it here. I have a .bmp picture file in memory stored as a scalar ($picture) with a size of $picx by $picy. I want to take a subset of this picture starting at $startx,$starty and going through $endx,$endy. The .bmp file foremat can be found here. The most relevant information is at the bottom.

For those of you who don't feel like reading, basically, the picture is stored upside down. Left to right order of each line of the picture is as you would expect, but it starts with the bottom line of the picture first. I need to keep the data of the subset in the right order, so that I can redisplay the smaller picture, and I have been mangling this code every which way with no success.

I know that this can be done with perlmagick, but I'm going to have to be doing this 1-2 times a second, and from what I can tell, PerlMagick has to read the bmp from a file, so I'd have to write the file from memoryto disk, and then read it back into a perlmagick structure before I could do anything.

Thanks for your time!

-John

Replies are listed 'Best First'.
Re: BMP file manipulation
by tachyon (Chancellor) on Dec 17, 2003 at 07:10 UTC

    Do it with a C program if you want some good advice, even if you have to write to disk.

    However you can of course do it in Perl, but it is not for the faint of heart. Essentially you need to:

    1. Read the heder info
    2. Modify the width and height attributes
    3. Modify the file size attr
    4. Get the bitmap data starting at the offset and modify it pixel linewise to get what you want, remebering that you need to pack with nulls to a multiple of 4 - so if the image is 10pix wide at 24 bits each line will be 10+*24/8 = 30 bytes + 2 = 32 bytes to make it divisible by 4.
    5. Repack all the data into a scalar and write to a file.

    Here is some code to get you started. This gets the header, modifies just the height (easy :-) modifies the header and writes the file out again. I leave the data alone but as you can see you just see half the bitmap now so the rest of the pixel data could have been thrown away. Modifying the width is where it gets harder.

    #!/usr/bin/perl -w use strict; my @hdr = qw( bfType bfSize bfReserved1 bfReserved2 bfOffBits biSize biWidth biHeight biPlanes biBitCount biCompression biSizeImage biXPelsPerMeter biYPelsPerMeter biClrUsed biClrImportant ); binmode STDIN; binmode STDOUT; my $BMP = 'c:/df.bmp'; open BMP, $BMP or die $!; binmode BMP; my $data = <BMP>; close BMP; my @hdr_dat = unpack "SLSSLLLLSSLLLLLL", $data; my %header; @header{@hdr}=@hdr_dat; print "$_\t$header{$_}\n" for @hdr; # chop in half! $header{biHeight} = int($header{biHeight}/2); my $new_hdr = pack "SLSSLLLLSSLLLLLL", @header{@hdr}; $data =~ s/^.{54}/$new_hdr/; open OUT, ">$BMP.munge.bmp" or die $!; binmode OUT; print OUT $data; close OUT;

    cheers

    tachyon

      Dear Tachyon
      Thank you for your very helpful example. I am trying to adapt it my own needs and I am struggling with parts 4 and 5 in your example.

      I am working with images that are 1024 high x 1280 wide using 8 bit grey scale to represent each color. So for instance I have used the following to get the color table:
      <c> my @rgbQuad = wq ( rgbBlue rgbGreen rgbRed rgbReserved ); my @rgb_dat = unpack "LLLL", $data; my %rgbQuadList; @rgbQuadList{@rgbQuad} = @rgb_dat; print "$_\t$rgbQuad{$_}\n" for @rgbQuad; <\c>
      to retrieve the rgb color table data. This returns one line of data for the Blue, Green, Red and Reserved colors. What I am not understanding is how to get every instance of this color table by reiterating through $data. I have also tried assigning < BMP > to an array but that does not seem to behave like the standard file handles I am more typically used to.

      Secondly I don't understand how to extract the data lines for the image. So my offset is 1078 bits. According to your calculation, and using an 8 bit pixel assignment, I need to capture 10242 bytes per line for my bit maps. How do I set this for unpack?

      Thank you for taking the time to answer my questions.

      MadraghRua
      yet another biologist hacking perl....

Re: BMP file manipulation
by jdtoronto (Prior) on Dec 17, 2003 at 05:57 UTC
    No PerlMagick does not have to read and/or write to/from files.

    It has ImageToBlob and BlobToImage methods of its own that works with database BLOB's.

    jdtoronto

Re: BMP file manipulation
by bart (Canon) on Dec 18, 2003 at 00:28 UTC
    It seems to me llike you're reinventing an operation called "BitBlt" (short for "Bit BLock Transfer"). There's a few Win32 API calls that exist for it, you might want to check them out, if this is for Windows.

    If it's for another platform, I wouldn't necessarily stick to the BMP format. For example, I'm quite sure the image manipulation modules on CPAN (GD, Image::Magick, Imager) support a form of raw pixel data manipulation, perhaps even BitBlt.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://315223]
Approved by jdtoronto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (10)
As of 2024-03-28 12:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found