Сохранить массив numpy как изображение с высокой точностью (16 бит) с scikit-image

Я работаю с двумерными массивами numpy с плавающей запятой, которые я бы хотел сохранить в greyscale.png файлах с высокой точностью (например, 16 бит). Я хотел бы сделать это, используя пакет scikit-image skimage.io, если это возможно.

Вот главное, что я пробовал:

import numpy as np
from skimage import io, exposure, img_as_uint, img_as_float

im = np.array([[1., 2.], [3., 4.]], dtype='float64')
im = exposure.rescale_intensity(im, out_range='float')
im = img_as_uint(im)
im

дает:

array([[    0, 21845],
       [43690, 65535]], dtype=uint16)

Сначала я попытался сохранить это как изображение, а затем перезагрузить с помощью библиотеки изображений Python:

# try with pil:
io.use_plugin('pil')
io.imsave('test_16bit.png', im)
im2 = io.imread('test_16bit.png')
im2

дает:

array([[  0,  85],
       [170, 255]], dtype=uint8)

Итак, где-то (в записи или чтении) я потерял точность. Затем я попытался использовать плагин matplotlib:

# try with matplotlib:
io.use_plugin('matplotlib')
io.imsave('test_16bit.png', im)
im3 = io.imread('test_16bit.png')
im3

дает мне 32-битный float:

array([[ 0.        ,  0.33333334],
       [ 0.66666669,  1.        ]], dtype=float32)

но я сомневаюсь, что это действительно 32-бит, учитывая, что я сохранил 16-битный uint для файла. Было бы здорово, если бы кто-то мог указать мне, где я ошибаюсь. Я хотел бы, чтобы это также распространялось на 3D-массивы (то есть сохраняло 16 бит на цветной канал, для 48 бит на изображение).

UPDATE:

Проблема заключается в imsave. Изображения имеют 8 бит на канал. Как можно использовать io.imsave для вывода изображения с высокой глубиной?

Ответ 1

Вы хотите использовать библиотеку freeimage для этого:

import numpy as np
from skimage import io, exposure, img_as_uint, img_as_float

io.use_plugin('freeimage')

im = np.array([[1., 2.], [3., 4.]], dtype='float64')
im = exposure.rescale_intensity(im, out_range='float')
im = img_as_uint(im)

io.imsave('test_16bit.png', im)
im2 = io.imread('test_16bit.png')

Результат:

[[    0 21845]
 [43690 65535]]

Что касается 3D-массивов, вам нужно правильно построить массив, а затем он будет работать:

# im = np.array([[1, 2.], [3., 4.]], dtype='float64')
im = np.linspace(0, 1., 300).reshape(10, 10, 3)
im = exposure.rescale_intensity(im, out_range='float')
im = img_as_uint(im)

io.imsave('test_16bit.png', im)
im2 = io.imread('test_16bit.png')

Обратите внимание, что прочитанное изображение перевернуто, поэтому что-то вроде np.fliplr(np.flipud(im2)) приведет к оригинальной форме.