Какой способ использовать для преобразования изображения, прочитанного с помощью cv2, в байты? tostring () или io.BytesIO? - PullRequest
0 голосов
/ 03 августа 2020

Я хочу преобразовать массив A в байты, есть два способа:

img_file = "/home/test.jpg"
A = cv2.imread(img_file)
im_bytes = A.tostring()

или

img_file = "/home/test.jpg"
A = cv2.imread(img_file)
pil_im = Image.fromarray(A)
b = io.BytesIO()
pil_im.save(b, 'jpeg')
im_bytes = b.getvalue()

Не понимаю их различий. И результат A.tostring() получит меньше памяти. Кто-нибудь может это объяснить?

1 Ответ

2 голосов
/ 03 августа 2020

Когда вы это сделаете:

import cv2

A = cv2.imread("image.jpg")
im_bytes = A.tostring()

A будет массивом Numpy всех пикселей изображения. Таким образом, если изображение имеет размер 1024x1024 и RGB, A будет массивом Numpy из shape(1024,1024,3), который непосредственно содержит все пиксели RGB в массиве типа np.uint8. Затем, когда вы запустите на нем tobytes()/tostring(), он будет иметь размер 3 МБ необработанных данных пикселей без размеров, без даты и без сжатия. Если вы передадите его кому-то другому через сокет или сохраните его в файле, приемник не узнает, какой это формат: 1024x1024 RGB, 1048576x3 в оттенках серого или 384x2048 RGBA.

С другой стороны, когда вы сделайте следующее:

import cv2
from PIL import Image
import io

A = cv2.imread("image.jpg")
pil_im = Image.fromarray(A)
b = io.BytesIO()
pil_im.save(b, 'jpeg')

b теперь будет JPEG-сжатой версией вашего изображения с потерями, которая содержит то же самое, как если бы вы записали его в файл JPEG. Таким образом, он содержит дату, которую вы написали, размеры изображения, цветовое пространство и таблицы, необходимые для его распаковки, а также сжатые данные пикселей. b - это, по сути, JPEG в ОЗУ.

Есть и другие проблемы с вашим вторым примером.

Во-первых, и что наиболее важно, нет никакого смысла распаковывать изображение JPEG в массив Numpy пикселей, а затем повторно сжимать его в JPEG. Если вам нужны данные в кодировке JPEG, вы также можете прочитать их без кодирования прямо из файла:

with open('image.jpg', 'rb') as f: 
    image = f.read()

Во-вторых, если по какой-то неизвестной причине вы действительно хотите декодировать JPEG в массив Numpy сырых пикселей, а затем перекодировать его обратно в JPEG, вы могли бы также использовать OpenCV, а не вводить совершенно новую зависимость от PIL:

A = cv2.imread("image.jpg")
_, JPEG = cv2.imencode('.jpeg', A)

В-третьих, если вы настаиваете на ненужном декодировании и перекодировании в JPEG, а также при введении PIL в качестве зависимости вы столкнетесь с еще одной проблемой. Поскольку вы читаете изображение с помощью OpenCV, вы получите его в порядке BGR. PIL хранит изображения в порядке RGB. Итак, если вы создадите изображение PIL из массива OpenCV Numpy, вы получите поменять местами красный и синий каналы, и все ваши цвета будут неправильными.

...