Python cairo сохранение фигуры в виде np-массива дает странные результаты

#python #numpy #matplotlib #cairo

#питон #numpy #matplotlib #cairo

Вопрос:

Попробуйте запустить мой пример:

 import numpy as np
import cairo
import matplotlib.pyplot as plt

img_size = 28
size = 0.8
color = (255, 0, 0)
thickness = 2

data = np.zeros((img_size, img_size, 3), dtype=np.uint8)
surface = cairo.ImageSurface.create_for_data(
  data, cairo.FORMAT_RGB16_565, img_size, img_size
)
cr = cairo.Context(surface)

# fill with solid white
cr.set_source_rgb(1, 1, 1)
cr.paint()


size = img_size * size - thickness
cr.rectangle((img_size - size) / 2, (img_size - size) / 2, size, size)
cr.set_line_width(thickness)
cr.set_source_rgb(*color)
cr.stroke()

surface.write_to_png("shape.png")
plt.imshow(data)
plt.savefig("shape_from_np.png")
 

shape.png выглядит следующим образом:

введите описание изображения здесь

shape_from_np.png выглядит следующим образом:

введите описание изображения здесь

Я подумал, может быть, я пропустил формат или каналы / форму, но если я распечатаю красный слой, например data[:,:,0] , он также показывает странные значения 0 в нижней половине, и я не уверен, почему.

Ответ №1:

В документации, которую я нашел ImageSurface.create_from_png(...) , я использовал your 'shape.png' , чтобы проверить, какие значения он будет использовать для изображения PNG.

 import cairo

surface = cairo.ImageSurface.create_from_png('shape.png')

print('format:', surface.get_format())
print('width :', surface.get_width())
print('height:', surface.get_height())
print('stride:', surface.get_stride())
print('stride/width:', surface.get_stride()/surface.get_width())
 

и это дает

 format: 1
width : 28
height: 28
stride: 112
stride/width: 4.0
 

Я нашел это format: 1 средство cairo.Format.RGB24 и в документе cairo.Формат, который я обнаружил, что для него нужен 32bit пиксель, что означает 4 байты в

 data = np.zeros((img_size, img_size, 4), dtype=np.uint8)
 

и то же 4 самое дает мне stride/width ( stride/28 )


Используя эти значения, я могу создать правильное изображение, но BGR вместо RGB

введите описание изображения здесь

Так что он все еще нуждается в некоторых изменениях.


Но cv2 использует BGR изображения и сохраняет их правильно без изменений.

 import cv2
cv2.imwrite('cv2.png', data)
 

 import numpy as np
import cairo
import matplotlib.pyplot as plt

img_size = 28
size = 0.8
color = (255, 0, 0)
thickness = 2

data = np.zeros((img_size, img_size, 4), dtype=np.uint8)

surface = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_RGB24, img_size, img_size)
cr = cairo.Context(surface)

# fill with solid white
cr.set_source_rgb(1, 1, 1)
cr.paint()

size = int(img_size * size) - thickness
cr.rectangle((img_size - size) / 2, (img_size - size) / 2, size, size)
cr.set_line_width(thickness)
cr.set_source_rgb(*color)
cr.stroke()

plt.imshow(data)
plt.savefig("shape_from_np.png")

surface.write_to_png("shape.png")

import cv2
cv2.imwrite('cv2.png', data)
 

Документ: ImageSurface, Формат

Комментарии:

1. Я вижу, что с RGB16_565 последняя ось имеет размер 2. RGB24 имеет размер 4. Я бы ожидал, что последняя ось будет иметь размер 3, по одному для красного, зеленого и синего соответственно. В данном случае они не это имеют в виду?

2. Я присмотрелся повнимательнее, и последний (4-й) элемент на последней оси, похоже, просто заполнитель. Итак, если мы удалим это и перевернем значения, это сработает : np.flip(data[:,:,:3], -1) .

3. даже в документации для RGB24 вы можете прочитать "pixel is a 32-bit quantity, with the upper 8 bits unused." , что я думаю, что он может использовать один метод для RGB24 и ARGB32 который использует это место для альфа-канала.