How to get gray scale pixel values from "Microsoft Windows Bitmap Image"?

Posted on 2004-10-25
Last Modified: 2008-01-09

  I have been playing around with ImageMagick (hopefully you are familiar) and ran across a problem getting pixel values from bitmaps. The images I am using are Microsoft Windows Bitmap Images (24-bit bitmaps). The images are in black and white. However, when I use:

#!/usr/bin/perl -w
use Image::Magick;
$im = new Image::Magick;


($red, $green, $blue, $opacity) = split /,/, $im->Get("pixel[$x,$y]");
if($red==0 && $green==0 && $blue==0) {
    print "Color is black\n";
} elsif($red==255 && $green==255 && $blue==255) {
    print "Color is white\n";
} else {
    print "Color is $red,$green,$blue\n";

I get pixel values in RGB format. I want Gray scale format.

How do I get the pixels values in (ideally, 8-bit) gray scale format?

I would rather like it if you could suggest a less cumbersome and MUCH FASTER image module other than ImageMagick

I really just need to open a greyscale bitmap, read the pixel values into an array, and do point operations. However, I get pixel values in RGB, when I am expecting 8-bit Gray scale. Not really sure what to do.
Question by:sapbucket
    LVL 84

    Expert Comment

    greyscale value is sometimes estimated as 0.299R + 0.587G + 0.114B
    LVL 3

    Author Comment

    Yes, I used:

    Y = 0.3*R + 0.59*G + 0.11*B

    However, after this conversion I am left with an array of gray scale values - which is great for processing - but now how do I save the image to file?

    Do I need to convert back to RGB?

    This is what I tried: (from )

    binmode STDOUT;
    $filename = "c:\\image.bmp";
    open(IMAGE, ">$filename");
    $image->Write(file=>\*IMAGE, filename=>$filename);

     And the above is supposed to write the image buffer to a specified filename and image format (in this case bitmap).

    But, I get the following error with the above line:
    $image->Write(file=>\*IMAGE, filename=>$filename);

    It says:
    perl.exe - Application Error
    The instruction at "0x7c369c37" referenced memory at "0x00000010". The memory could not be "read".

    and crashes the application.

    I'm sorta lost as what to do. This all seems so cumbersome and anti-PERL in how difficult it is to figure out.

    Any more suggestions?

    LVL 18

    Expert Comment

       use Image::Magick;
        $image = Image::Magick->new;
        $image->Draw(primitive=>'rectangle', points=>'1,1 49,49', method=>'Floodfill', fill=>'green');
        $image->Draw(primitive=>'rectangle', points=>'50,50 99,99', method=>'Floodfill', fill=>'yellow');
        $image->Draw(primitive=>'rectangle', points=>'1,50 49,99', method=>'Floodfill', fill=>'blue');
        $image->Set('type' => 'Grayscale');

    Also see the Fx method:
    with more details at:
    LVL 3

    Author Comment

    Kandura and others,

      This looks like I am almost there. However, when you use the Draw() function you are not setting individual pixels. In your above example you are using method 'Floodfill.' Is there a method that is efficient at single points?

       I am getting the impression that Image::Magick is not what I want. Image::Magick is more like Photoshop then an efficient image processor.

       I only need the following functionality, and while I am sure Image::Magick can handle it, I am sure there must be an PERL module that is more appropriate:
       1. Open a .bmp.
       2. Get it's height and width information as integers
       3. Get the pixel values from the bitmap and store in array
       4. Process the pixel values in the array (use height and width for ROW / COL operations)
       5. use the processed pixel values to create a bitmap image in memory
       6. save the bitmap image to the harddrive.

        This seems like a simple enough process that MANY people must have done already.
        Can anyone suggest a better way than Image::Magick?
        Can anyone suggest how this can be done using Image::Magick?

        I am open to using other image formats besides bmp.

        Many many thanks!

    LVL 84

    Accepted Solution

    Image::Pbm ?
    LVL 5

    Expert Comment

    Check out GD module.

    Look at:
    width and height: getBounds,
    Read Pixel Color:  getPixel,
    Write Pixel        : setPixel,

    You can also mix and match, by reading from Image::Magic and writing using GD.
    or vice-versa. Profiling the code will tell you which way it's more efficient.
    LVL 3

    Author Comment


      you got it! That was what I needed. I really used Image::XPM, which uses Image::PBM.

      Image::XPM gave me all the functionality I needed. Wouldn't of ever found it without you suggesting Image::PBM.


    LVL 3

    Author Comment

    If anyone ever encounters this problem let it be known that XPM is not a good Windows solution.

    XPM is a popular X Window image file format for storing color icons. It isn't really suited for Windows. The problem for me is that I have to convert XPM to bitmap to view my images and that creates a lot of system overhead. If I were on a X window machine I would not have to do this. Besides that, if you do not have to care about system frequency, it is fine.

    Good luck trying to find a Windows XP XPM viewer that works.

    LVL 3

    Author Comment


      I found the best way to deal with Windows Bitmaps using PERL. I am most pleased with the result.

      Here is a test script:

    #!/usr/bin/perl -w
    # filename:

    ### Declarations ###
    use Win32::GUI::DIBitmap;
    use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);

    ### Process Profiler ###
    ($seconds1, $microseconds1) = gettimeofday;

    ### Core ###
    $dib = newFromFile Win32::GUI::DIBitmap ($filename);
    for($h=0;$h<$height;$h++) {
        for($w=0;$w<$width;$w++) {
            $hexp = sprintf("%X", $p);
            my $r=reverse((chop $hexp).(chop $hexp));
            $r=255-hex($r); # invert the image to test image processing
    print "width: $w, height $h\n";

    ### Process Profiler ###
    ($seconds2, $microseconds2) = gettimeofday;
    $sec=$eum / 1000000;
    print "seconds elapsed: $es, microseconds: $eum ($sec of a second)\n";

    You will need to install the necessary modules:

    Good luck! :)

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone. Privacy Policy Terms of Use

    Featured Post

    Highfive + Dolby Voice = No More Audio Complaints!

    Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

    Suggested Solutions

    On Microsoft Windows, if  when you click or type the name of a .pl file, you get an error "is not recognized as an internal or external command, operable program or batch file", then this means you do not have the .pl file extension associated with …
    Email validation in proper way is  very important validation required in any web pages. This code is self explainable except that Regular Expression which I used for pattern matching. I originally published as a thread on my website : http://www…
    Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
    To add imagery to an HTML email signature, you have two options available to you. You can either add a logo/image by embedding it directly into the signature or hosting it externally and linking to it. The vast majority of email clients display l…

    877 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    19 Experts available now in Live!

    Get 1:1 Help Now