bugGNU Octave - Bugs: bug #45779, Writing 8-bit grayscale image to...

 
 

bug #45779: Writing 8-bit grayscale image to bmp file should write an indexed image

Submitter:  Ian Journeaux <ijourneaux>
Submitted:  Mon 17 Aug 2015 01:58:33 PM UTC
   
 
Category:  Octave Function Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Matlab Compatibility
Status:  Need Info Assigned to:  None
Originator Name:  Ian Journeaux Open/Closed:  * Open
Release:  * dev Operating System:  * Any
Fixed Release:  None Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Tue 25 Aug 2015 04:22:57 PM UTC, comment #9: 

On comment #8 Ian Journeaux said:

> 1. Matlab compatibility. Whether it is explicitly stated in the documentation or not, given a uint8 array, Matlab writes a 8-bit-per-sample indexed grayscale image. Specifying the map might be more precise but this is a place where Matlab compatibilty would be valuable.


Why is this place specially valuable?  It is very simple and much clearer for the user to be specific what he wants imwrite to write.  If he wants an indexed image to be written, the user should specify an colormap.  From Matlab's own imwrite documentation "If A contains indexed image data, you should additionally specify the map input argument".

>  2. Within Octave, if you write a uint8 array using imwrite, and read the array back in, it comes back in as an 3 channel RGB array instead of the original 1 channel array.


If the format does support grayscale images that is to be expected.  This is not a reason and is perfectly normal. Just like calling imwrite() to save a image of floating points in the range [0 1] will then be read as uint8 in the range [0 255].

Carnë Draug <carandraug>
Group Member
Tue 25 Aug 2015 02:56:28 PM UTC, comment #8: 

I see two reasons to implement this change

1. Matlab compatibility. Whether it is explicitly stated in the documentation or not, given a uint8 array, Matlab writes a 8-bit-per-sample indexed grayscale image. Specifying the map might be more precise but this is a place where Matlab compatibilty would be valuable.
2. Within Octave, if you write a uint8 array using imwrite, and read the array back in, it comes back in as an 3 channel RGB array instead of the original 1 channel array.

Ian Journeaux <ijourneaux>
Tue 25 Aug 2015 02:42:55 PM UTC, comment #7: 

@Mike Miller : Yes, you mentioned that on comment #1 but not that you had tested. Sounded like you were assuming it from the documentation.



Well, this wouldn't be very difficult to fix. A possible fix would be identify the bmp format in 'private/__imwrite__.m' and create the gray colormap there before calling '__magick_write__'. Or this could all be done in '__magick_write__'



However, this is a not a fix I would like to implement, and I'm not sure I'd even like to see it implemented. I see a few reasons for it:

  1. We are losing the main advantage of using GM by adding this weird exceptions.
  2. The logic is that if there's a colormap, then it writes an indexed image, if not then it should write an intensity values image (gray or RGB) depending on the number of channels.  If a format does not support gray intensity values, then I'd argue that an RGB image is closer. Note that a grayscale image can perfectly be of class double and on the range [0 1], definitely not only an indexed of a gray map.
  3. Tiff is the de facto standard when such details matter.
  4. If the user really really wants an indexed image to be written, and it's dependent on it, then it should be specific and specify the colormap.
  5. Matlab documentation is not explicit on what should happen. It says "If A is of data type uint8, then imwrite outputs 8-bit values".  We already do this, we write 8-bit per channel. This is not semantics. Matlab documentation also calls RGB files 8-bit files "If A is a grayscale or RGB color image [...] scales the data by 255 before writing it to the file as 8-bit values" (it also calls them 24-bit images in other cases).


So I propose closing this as won't fix. If anyone is relaying on imwrite to write an indexed image, then it should be explicit and use:


imwrite (gray_image, gray (256), "foo.bmp");


Carnë Draug <carandraug>
Group Member
Tue 25 Aug 2015 01:12:47 PM UTC, comment #6: 

I thought I had already reported that I tested this in Matlab, but just in case, imwrite in Matlab creates an 8-bit-per-sample indexed grayscale image if the input array is uint8 and even if there is no colormap argument. Octave currently requires the colormap argument to create an indexed image. This is the incompatibility to fix in this bug report.

Mike Miller <mtmiller>
Group Member
Mon 24 Aug 2015 09:42:50 PM UTC, comment #5: 

Checking BMP format in Wikipedia (https://en.wikipedia.org/wiki/BMP_file_format) shows that
there us no option for an 8 bit image to be without color table,
unless it is stored as 24 bit image. So, there are two options to save eight bit image
1. As 24 bit image (RGB image with three identical bands)
2. As 8 bit image with color map.

What does Matlab do when there is an eight bit image, but there is
no specific request for a map?

Avinoam Kalma <avinoam>
Group Member
Mon 24 Aug 2015 06:49:25 PM UTC, comment #4: 

One correction to the results below.

imwrite (randi ([0 255], 100, 100, 3, "uint8"), "without-map-rgb.bmp")

correctly produces a 24bit RGB image.


imwrite (randi ([0 255], 100, 100, 3, "uint8"), "without-map-gray.bmp")

incorrectly generates a 24bit RGB image where all three channels seem to have the same information.

Ian Journeaux <ijourneaux>
Mon 24 Aug 2015 05:23:36 PM UTC, comment #3: 

I ran the following on my computer running win7 and Octave 4.1.0+.


imwrite (rand(100) > 0.6, "bool.bmp")
imwrite (randi ([0 255], 100, 100, "uint8"), gray (256), "with-gray-map.bmp")
imwrite (randi ([0 255], 100, 100, "uint8"), cubehelix (256), "with-color-map.bmp")
imwrite (randi ([0 255], 100, 100, 3, "uint8"), "without-map-rgb.bmp")
imwrite (randi ([0 255], 100, 100, "uint8"), "without-map-gray.bmp")


After installing GraphicsMagick, gm returns


Depth: 1 bits-per-pixel component
Depth: 8 bits-per-pixel component
Depth: 8 bits-per-pixel component
Depth: 8 bits-per-pixel component
Depth: 8 bits-per-pixel component


respectively
So things might look correct.

Unfortunately both Windows and ImageJ reports the last two Without-Map images as 24bit. The size of the image files are also different.
With-Map versions are 11kb while the Without-Map versions are 30kb.

 

Ian Journeaux <ijourneaux>
Mon 24 Aug 2015 02:59:55 PM UTC, comment #2: 

Matlab documentation on this is very much sparse.  My interpretation is that the 1, 8, and 24 bit format that they support are for boolean, indexed, and RGB images (8-bit per color channel) respectively.

Octave does check the type of the matrix being saved and supports saving boolean matrices as 1-bit images. I just tested it and GM seems to respect it:


$ octave
octave> imwrite (rand(100) > 0.6, "bool.bmp")
octave> exit
$ gm identify -verbose bool.bmp
Image: bool.bmp
  Format: BMP (Microsoft Windows bitmap image)
[...]
  Type: bilevel
  Depth: 1 bits-per-pixel component
  Channel Depths:
    Gray:     1 bits
[...]


An image with a colormap should be saved as an 8 bit indexed image.  In the following, it seems like both are saved as indexed images, the difference is that GM seems to identify that the colormap is gray but the image itself is indexed and 8 bit in both cases:


$ octave
octave> imwrite (randi ([0 255], 100, 100, "uint8"), gray (256), "with-gray-map.bmp")
octave> imwrite (randi ([0 255], 100, 100, "uint8"), cubehelix (256), "with-color-map.bmp")
octave> exit
$ gm identify -verbose with-gray-map.bmp
Image: with-gray-map.bmp
  Format: BMP (Microsoft Windows bitmap image)
  Geometry: 100x100
  Class: PseudoClass
  Type: grayscale
  Depth: 8 bits-per-pixel component
  Channel Depths:
    Gray:     8 bits
[...]
$ gm identify -verbose with-color-map.bmp
Image: with-color-map.bmp
  Format: BMP (Microsoft Windows bitmap image)
  Geometry: 100x100
  Class: PseudoClass
  Type: palette
  Depth: 8 bits-per-pixel component
  Channel Depths:
    Red:      8 bits
    Green:    8 bits
    Blue:     8 bits


Finally, without a colormap, I'm unsure what should happen for Matlab compatibility. I think the logical thing would be to save an 8-bit image for grayscale image and 24-bit (8-bit per channel) for RGB images which seems to be happening:


$ octave
octave> imwrite (randi ([0 255], 100, 100, 3, "uint8"), "without-map-rgb.bmp")
octave> imwrite (randi ([0 255], 100, 100, "uint8"), "without-map-gray.bmp")
octave> exit
$ gm identify -verbose without-map-rgb.bmp
Image: without-map-rgb.bmp
  Format: BMP (Microsoft Windows bitmap image)
  Geometry: 100x100
  Class: DirectClass
  Type: true color
  Depth: 8 bits-per-pixel component
  Channel Depths:
    Red:      8 bits
    Green:    8 bits
    Blue:     8 bits
[...]
$ gm identify -verbose without-map-gray.bmp
Image: without-map-gray.bmp
  Format: BMP (Microsoft Windows bitmap image)
  Geometry: 100x100
  Class: DirectClass
  Type: grayscale
  Depth: 8 bits-per-pixel component
  Channel Depths:
    Gray:     8 bits
[...]


Unfortunately, `gm identify` is not 100% reliable and I'm not very familiar with the BMP format either.

So, could you please be a bit more verbose on what exactly should be happening? 8-bit image means very little as you can see from above.

Carnë Draug <carandraug>
Group Member
Mon 17 Aug 2015 03:03:59 PM UTC, comment #1: 

According to https://www.mathworks.com/help/matlab/ref/imwrite.html, Matlab can write BMP in 1-bit, 8-bit, or 24-bit formats. I'm assuming this means if a logical matrix is passed in, then a 1-bit image is written with a black-white palette. And if a uint8 matrix is passed in, it is written in 8-bit indexed format with an 8-bit grayscale palette.

Determining the image format from the input matrix type is not something that Octave imwrite handles yet, I think.

Mike Miller <mtmiller>
Group Member
Mon 17 Aug 2015 01:58:33 PM UTC, original submission:  

With Octave 4.1.0+ MXE compiled locally with Default config, to successfully save an 8-bit image to a bmp file requires using

octave> img = uint8 (ones (100, 1) .* [0:255]);
octave> map = gray(256);
octave> imwrite (img, map, "foo-octave.bmp");

If I understand Matlab (maybe I am misinterpreting Matlab)

octave> img = uint8 (ones (100, 1) .* [0:255]);
octave> imwrite (img, "foo-octave.bmp");

should also work but, with Octave, still produces a 24-bit image.

Ian Journeaux <ijourneaux>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by avinoam (Posted a comment)
  • -email is unavailable- added by mtmiller (Posted a comment)
  • -email is unavailable- added by ijourneaux (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only group members can vote.

     

    Follow 4 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-08-25 carandraug SummaryWriting and 8-bit image to BMP produces a 24-bit bmp Writing 8-bit grayscale image to bmp file should write an indexed image
    2015-08-24 carandraug StatusConfirmed Need Info
    2015-08-17 mtmiller StatusNone Confirmed
        Operating SystemMicrosoft Windows Any

    Back to the top

    Powered by Savane 3.13-758e.
    Corresponding source code