#python #image #python-imaging-library
#python #изображение #python-imaging-library
Вопрос:
Моя цель — нарисовать несколько полигонов на черном изображении таким образом, чтобы общий размер результирующего изображения был как можно меньше.
Итак, я прочитал статью в wiki об индексированных цветах (ссылка), и мне кажется, что это хороший выбор для меня (поскольку я должен поддерживать только черный цвет и 5 других, т. Е. всего 6 цветов), а png
формат изображения должен поддерживать режим «P» (т. Е. изображения палитры).
Вот почему я создал этот фрагмент кода, чтобы посмотреть, какой размер изображения я получу для 6 цветов и 1224x1024
изображения:
from PIL import Image, ImageDraw
# Create a black 1224x1024 image
img = Image.new('P', (1224, 1024))
img.putpalette([
0, 0, 0, # black background
236, 98, 98, # red color
236, 98, 97,
236, 98, 96,
236, 98, 95,
236, 98, 94,
])
draw = ImageDraw.Draw(img)
# Draw a random red polygon at the top left corner of the image
draw.polygon(xy=list(1,1,2,2,3,3,4,4), fill=1)
del draw
img.save('1.png', format='PNG')
Результирующий размер изображения составляет 768
байты, что кажется мне слишком большим.
Могу ли я что-нибудь исправить в своем коде, чтобы сделать размер результирующего изображения еще меньше?
Ответ №1:
768 байт не кажутся мне необоснованными для представления изображения с разрешением 1,2 мегапикселя. Вы могли бы попробовать запустить файл, созданный с помощью PIL, через pngcrush
вот так, чтобы посмотреть, сможет ли он сократить несколько байтов:
pngcrush input.png result.png
Если вы действительно хотите нарисовать только несколько однотонных полигонов на черном фоне, я бы посоветовал вам обратиться к векторному формату, такому как SVG, пример здесь, а не к растровому формату PNG и др..
Вы также можете использовать rsvg для рендеринга SVG-изображений в PNG, если вам нужно, но ни ваше приложение, ни причина, по которой вам нужны такие маленькие изображения, не ясны из вашего вопроса, поэтому я понятия не имею, подходит ли вам такой вариант.
Вот 300-байтовое SVG-изображение с черным фоном, 2 прямоугольниками и многоугольником в форме красной звезды вверху слева:
<svg width="1224" height="1024">
<rect width="100%" height="100%" fill="black"/>
<polygon points="9.9, 1.1, 3.3, 21.78, 19.8, 8.58, 0, 8.58, 16.5, 21.78" fill="red"/>
<rect x="100" y="200" width="100" height="400" fill="blue"/>
<rect x="800" y="280" width="100" height="200" fill="lime"/>
</svg>
Вы могли бы загрузить SVG в массив Numpy следующим образом:
#!/usr/bin/env python3
import cairosvg
import io
from PIL import Image
def svgRead(filename):
"""Load an SVG file and return image in Numpy array"""
# Make memory buffer
mem = io.BytesIO()
# Convert SVG to PNG in memory
cairosvg.svg2png(url=filename, write_to=mem)
# Convert PNG to Numpy array
return np.array(Image.open(mem))
# Read SVG file into Numpy array
res = svgRead('image.svg')
Если вы одержимы желанием сделать файлы еще меньше ценой снижения совместимости с другими средствами просмотра изображений, вы могли бы разработать свой собственный очень простой формат, несколько похожий на SVG, чтобы вы могли сохранить изображение, представленное в примере SVG, который я привел, в виде простого текстового файла:
b,1224,1024,#00000
r,100,200,100,400,#0000ff
r,800,280,100,200,#00ff00
p,9.9,1.1,3.3,21.78,19.8,8.58,0,8.58,16.5,21.78,#ff0000
И у меня получается 127 байт.
Вы можете просмотреть свои SVG-файлы, загрузив их в любой веб-браузер, или преобразовать их в PNG с помощью ImageMagick в терминале следующим образом:
magick input.svg result.png
Комментарии:
1. Отметьте, вам, вероятно, следует добавить -density XX перед SVG-файлом, чтобы пользователь мог решить, какого размера сделать PNG. Также обратите внимание, что существует 3 средства визуализации SVG, которые ImageMagick может использовать: MSVG / XML (внутренняя визуализация ImageMagick в формате SVG), RSVG delegate и Inkscape (в порядке повышения точности).
2. @fmw42 Фред, спасибо тебе за твой вклад и идеи, как всегда. Я согласен, что
-density
здесь полезно, но получение хорошего PNG на самом деле не было моей целью. Моей целью было использовать векторный формат, точка. Это более сжато / эффективно для простых сплошных полигонов на черном фоне, и размер файла, очевидно, является серьезной проблемой для операционной системы, если 94% сжатия недостаточно.3.
@Mark
Да, я бы согласился. Используйте векторный формат для меньшего размера файла. Я просто добавил примечание о растрировании для полноты картины.