简体   繁体   中英

Save 2 channels of pixels of an image in png format

According to scipy documentation it is possible to save single channel of an image like so;

>>> x = np.zeros((255, 255))
>>> x = np.zeros((255, 255), dtype=np.uint8)
>>> x[:] = np.arange(255)
>>> imsave('/tmp/gradient.png', x)

And 3 channels of an image like so:

>>> rgb = np.zeros((255, 255, 3), dtype=np.uint8)
>>> rgb[..., 0] = np.arange(255)
>>> rgb[..., 1] = 55
>>> rgb[..., 2] = 1 - np.arange(255)
>>> imsave('/tmp/rgb_gradient.png', rgb)

However is it possible to save only 2 channels of an image as png? I have tried to mimic the above code with only 2 channels, I am greeted with the following error:

ValueError: 'arr' does not have a suitable array shape for any mode.

It's not entirely clear to me what you want to achieve or what kind of channels you want to store.

According to the PNG color options table here , there is only one option: using the Grayscale + alpha-channel. Of course you can store your data like that, but :

  • this implies a bit-depth of 16 or 32; maybe not what you want
  • libraries will by default interpret it as Grayscale + alpha-channel

TIFF is actually much more flexible, and seems to support arbritrary channels, but according to this libtiff does not support 2-channels. (maybe outdated information; not sure).

So it probably depends on what you really want to do. If you just want to store 2 channels (because you want to save bytes and you don't need more channels), while every step in your pipeline can be tuned to interpret it as you like, i recommend just storing 3 channels, where one is empty (eg all zeros). PNGs filters will be able to compress it efficiently. Of course, when reading/using these later, you need to modify your code to interpret it as done when saving.

If this is just for some kind of task involving python's scientific-stack only, you can just store your array using numpy's savez_compressed . (remark: there will be general-purpose compression; but no filter-usage like x+1 - x -> pixels: 0, 1, 2, 3 hard to compress; but using delta-filter: 0, 1, 1, 1 are easy to compress; meaning: compression efficiency compared to PNG depends on your images)

The PNG file format supports images with two channels with a bit depth of 16 or 32 bits (per channel). PNG interprets such a file as a grayscale image with an alpha (ie transparency) channel. If you are, in fact, trying to create a grayscale image with an alpha channel, then this answer might help you. If you are trying to use the PNG format to just store an array, then I recommend using a different format. See @sascha's answer for suggestions.

The numpngw module that I wrote ( source code on github ) can write PNG files in this format. Just pass in an array with type np.uint16 or np.uint32 with shape (m, n, 2) .

For example,

import numpy as np
from numpngw import write_png


# Create `img` with a black outer border, a white inner border and
# a transparent square in the middle.
img = np.full((64, 64, 2), fill_value=2**16-1, dtype=np.uint16)
img[16:-16, 16:-16, 1] = 0
img[:8, :, 0] = 0
img[-8:, :, 0] = 0
img[:, :8, 0] = 0
img[:, -8:, 0] = 0

write_png('foo.png', img)

You might have to experiment a bit to read this data back into an array with the same shape and data type. For example, the default behavior of Pillow when reading this file is to return an 8 bit array with shape (m, n, 4); that is, Pillow converts the data to the RGBA format.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM