Saving 2D arrays as indexed color images

Hi,

please, is there a way to save a 2D array of integers as a .png image, where for each integer value in the array I associate a certain color in the output image?

I am looking for a solution for Indexed color - Wikipedia

Ideally, something like
imwrite(myArray, myMap, ‘myfile.png’)

Background of my application: I classify pixels in an image in a set of predefined categories and I would like to save the resulting image with a different color to represent each category. For now, what I was able to do in Julia was
save(File(format"PNG", “myfile.png”), colorview(Gray, myArray/nclasses)), this generates grayscale images

Please, do you have some hints?

Thank you in advance for your time,

Just index into your lookup table by myArray. Something like:

save(File(format"PNG", “myfile.png”), colortable[myArray])

Are you looking for 1-to-1 correspondence - or just something like a heatmap? https://docs.juliaplots.org/latest/generated/gr/#gr-ref28-1

that sounds promissing, thank you. I tried it, say I have nclasses = 15

julia> colortable = rand(RGB,nclasses,1)
julia> save(File(format"PNG", "myfile.png"), colortable[myArray])

and indeed it saved a RGB image, with tree channels R,G,B. However, I wonder if there is a way to save like a one channel image with a color palette associated, this would save space in the disk, correct?

thank you. I am looking for a 1-to-1 correspondence. I would like to save a image, probably what I need is something like in Indexed color - Wikipedia where I save a “gray scale” image with a color palette associated

That’s a matter of the compression involved in the file format. IIRC PNG already uses indexed color when advantageous.

1 Like

A quick test seemed to indicate on my system, it is not using a pallet (saved file with rand data had 554400 Bytes or on average 1.8 bytes/pixel for nclasses = 15 and 640x480).

I believe the problem is that when the backend is invoked (PNGFiles ? ImageMagick ?) in the background to generate the PNG it treats it as a full 24-bit image and doesn’t realise it is a 16 or 256 color image.
It is then compressing the result using spatial data relations, which depending on your use case might or might not be able to compress.

Saving as gif seemed to work, result in much smaller files (182709Bytes or average 0.59 bytes/pixel for nclasses = 15 and 640x480).

thank you for the replay.

What I am hopping is to be able to generate an image like https://i.postimg.cc/4xkQY8Z7/myfile2.png

Using an external software, it is easy to see that the above “RGB” image is only a “gray scale” (single band, dimensions X: 217 Y: 512 Bands: 1) image with a color palette associated. I am still not able to generate an image like this in Julia (by the way, neither read the image above as a gray scale and get the colors as a separate palette, but perhaps this is because of by limited Julia skills…)

Well, the closer I can arrive so far is to save an image with three channels (instead of the palette) as suggested by @mbauman’s post above. Any additional hits are deeply appreciated. Thank you!

How can you tell if an image has the format you want?

I created the image below using Gaston:

using Gaston
Z = [5 4 3 1 0; 2 2 0 0 1; 0 0 0 1 0; 0 1 2 4 3]
imagesc(Z)
printfigure(term="png",outputfile="test.png")

but I don’t know if it has a palette or not.
image

The distinction is simply one of compression. All color images are 2-d matrices of RGB pixels. That’s how Julia will represent them, regardless of their compression or filetype. How they are stored on the other hand, is another question. You can coerce png files to use a palette with an external tool like optipng. I’m not sure if there’s an easy way to ask ImageMagick to “try harder” to use a palette.

Note that the discourse forum software we use will optimize and even change image formats upon upload.

You’re right, using a palette will save space in simple images with few colors, but for things like photography they’re not efficient.

Good point about Discourse modifying the image.

But maybe that is the whole point of the matter: The moment it gets converted to a “unstructured/unconstrained” RBG format, you lose the information that it was actually a limited set of classes he wanted to represent. The backend you make a guess that your using a limited number of colors, but how would it know it just didn’t happen to work out that way.

The correct way to do it should be to supply the data and palette separately. I created a quick PR to achieve this, see:

It seems to work.
Ideally it should then be tied to FileIO, but I’m not sure how to do that.

2 Likes