Кодирование изображения Numpy Array в тип изображения (.png и т. Д.) Для использования его с API GCloud Vision - без OpenCV - PullRequest
1 голос
/ 12 июня 2019

После того, как я решил не использовать OpenCV , потому что я использую только одну его функцию, я пытался заменить функцию cv2.imencode() чем-то другим. Цель состоит в том, чтобы преобразовать 2D Numpy Array в формат изображения (например, .png), чтобы отправить его в GCloud Vision API .

Это то, что я использовал до сих пор :

content = cv2.imencode('.png', image)[1].tostring()
image = vision.types.Image(content=content)

И теперь я хочу добиться того же, не используя OpenCV.

Вещи, которые я нашел до сих пор:

  • Vision API требуется base64 кодированные данные
  • Imencode возвращает закодированные байты для определенного типа изображения

Я думаю, что стоит отметить, что мой массивный массив представляет собой двоичное изображение только с двумя измерениями, и в API будут использоваться целые функции, поэтому следует избегать сохранения png на диске и его перезагрузки.

1 Ответ

1 голос
/ 12 июня 2019

Автор PNG на чистом Python

Если вы настаиваете на использовании более или менее чистого Python, следующая функция из ответа идейника на этот вопрос полезна.

def write_png(buf, width, height):
    """ buf: must be bytes or a bytearray in Python3.x,
        a regular string in Python2.x.
    """
    import zlib, struct

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b''.join(
        b'\x00' + buf[span:span + width_byte_4]
        for span in range((height - 1) * width_byte_4, -1, - width_byte_4)
    )

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return (struct.pack("!I", len(data)) +
                chunk_head +
                struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))

    return b''.join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])

Записать массив Numpy в формат байта PNG, закодировать как base64

Чтобы представить изображение в градациях серого как изображение RGBA, мы сгруппируем матрицу в 4 канала и установим альфа-канал.(Предположим, что ваш двумерный массив numpy называется img).Мы также переворачиваем массив NumPy по вертикали из-за способа работы координат PNG.

import base64
img_rgba = np.flipud(np.stack((img,)*4, axis=-1)) # flip y-axis
img_rgba[:, :, -1] = 255 # set alpha channel (png uses byte-order)
data = write_png(bytearray(img_rgba), img_rgba.shape[1], img_rgba.shape[0])
data_enc = base64.b64encode(data)

Проверка правильности кодирования

Наконец, чтобы убедиться, что кодирование работает, мы декодируем base64строка и записать вывод на диск как «test_out.png».Убедитесь, что это то же изображение, с которого вы начали.

with open("test_out.png", "wb") as fb:
   fb.write(base64.decodestring(data_enc))

Альтернатива: просто используйте PIL

Однако я предполагаю, что вы используете какую-то библиотеку для фактического чтения ваших изображений впервое место?(Если вы их не генерируете).Большинство библиотек для чтения изображений поддерживают такие вещи.Предположим, что вы используете PIL, вы также можете попробовать следующий фрагмент ( из этого ответа ).Он просто сохраняет файл в памяти, а не на диске, и использует его для генерации строки base64.

in_mem_file = io.BytesIO()
img.save(in_mem_file, format = "PNG")
# reset file pointer to start
in_mem_file.seek(0)
img_bytes = in_mem_file.read()

base64_encoded_result_bytes = base64.b64encode(img_bytes)
base64_encoded_result_str = base64_encoded_result_bytes.decode('ascii')
...