Преобразование изображения PIL в изображение VIPS

Я работаю над некоторыми большими гистологическими изображениями, используя библиотеку изображений Vips. Вместе с изображением у меня есть массив с координатами. Я хочу создать двоичную маску, которая маскирует часть изображения внутри многоугольника, созданного координатами. Сначала я попытался сделать это с помощью функции отрисовки vips, но это очень неэффективно и занимает много времени (в моем реальном коде изображения имеют размер около 100000 x 100000 пикселей, а массив полигонов очень большой).

Затем я попытался создать двоичную маску с помощью PIL, и это прекрасно работает. Моя проблема состоит в том, чтобы преобразовать изображение PIL в изображение vips. Они оба должны быть VIP-изображениями, чтобы иметь возможность использовать команду умножения. Я также хочу писать и читать из памяти, так как считаю, что это быстрее, чем запись на диск.

В команде im_PIL.save(memory_area,'TIFF') я должен указать и формат изображения, но, поскольку я создаю новый образ, я не уверен, что здесь указать.

Команда Vips.Image.new_from_memory(..) возвращает: TypeError: constructor returned NULL

from gi.overrides import Vips
from PIL import Image, ImageDraw
import io

# Load the image into a Vips-image
im_vips = Vips.Image.new_from_file('images/image.tif')

# Coordinates for my mask
polygon_array = [(368, 116), (247, 174), (329, 222), (475, 129), (368, 116)]

# Making a new PIL image of only 1's
im_PIL = Image.new('L', (im_vips.width, im_vips.height), 1)

# Draw polygon to the PIL image filling the polygon area with 0's
ImageDraw.Draw(im_PIL).polygon(polygon_array, outline=1, fill=0)

# Write the PIL image to memory ??
memory_area = io.BytesIO()
im_PIL.save(memory_area,'TIFF')
memory_area.seek(0)

# Read the PIL image from memory into a Vips-image
im_mask_from_memory = Vips.Image.new_from_memory(memory_area.getvalue(), im_vips.width, im_vips.height, im_vips.bands, im_vips.format)

# Close the memory buffer ?
memory_area.close()

# Apply the mask with the image
im_finished = im_vips.multiply(im_mask_from_memory)

# Save image
im_finished.tiffsave('mask.tif')

person Rune    schedule 21.02.2017    source источник


Ответы (1)


Вы сохраняете из PIL в формате TIFF, но затем используете конструктор vips new_from_memory, который ожидает простой массив C значений пикселей.

Самое простое решение — использовать вместо этого new_from_buffer, что загрузит изображение в каком-то формате, обнюхивая формат из строки. Измените среднюю часть вашей программы следующим образом:

# Write the PIL image to memory in TIFF format
memory_area = io.BytesIO()
im_PIL.save(memory_area,'TIFF')
image_str = memory_area.getvalue()

# Read the PIL image from memory into a Vips-image
im_mask_from_memory = Vips.Image.new_from_buffer(image_str, "")

И это должно работать.

Операция vips multiply на двух 8-битных изображениях uchar создаст 16-битное изображение uchar, которое будет выглядеть очень темным, поскольку числовой диапазон будет от 0 до 255. Вы можете либо снова преобразовать его в uchar (добавить .cast("uchar") к умножьте строку) перед сохранением или используйте 255 вместо 1 для маски PIL.

Вы также можете переместить образ из PIL в VIPS как простой массив байтов. Это может быть немного быстрее.

Вы правы, операции draw в vips плохо работают с очень большими изображениями в Python. Не сложно написать в вип штуку, чтобы из набора точек сделать изображение маски любого размера (достаточно комбинировать множество && и < с обычным правилом намотки), но с помощью PIL конечно проще.

Вы также можете рассмотреть возможность использования полимаски в виде изображения SVG. libvips может эффективно загружать очень большие изображения SVG (он отображает разделы по запросу), поэтому вы просто увеличиваете его до любого размера, необходимого для ваших растровых изображений.

person jcupitt    schedule 23.02.2017
comment
Спасибо за ваш ответ, изменение на new_from_buffer исправило это для меня. А также спасибо за дополнительную информацию о добавлении приведения к функции умножения. Сначала я попробую это решение, а затем рассмотрю другие ваши предложения, если это необходимо. - person Rune; 27.02.2017