Как я могу инициализировать RGBA cv :: Mat из байтового массива? - PullRequest
0 голосов
/ 29 мая 2019

Я отправляю изображения по сети (из Python) и хочу создать OpenCV Mat s из них на принимающей стороне (в C ++).

Они созданы так:

image = self.camera.capture_image()   # np.array of dtype np.uint8
h, w, c = image.shape   # 4 channels
image = np.transpose(image, (2, 0, 1)) # transpose because channels come first in OpenCV (?)
image = np.ascontiguousarray(image, dtype='>B')  # big-endian bytes
bytess = image.tobytes(order='C')

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

bytess[channel*height*width + i*wwidth + j] == image[channel, i, j]

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

Теперь, с другой стороны, я пытаюсь сделать это:

char* pixel_data = … // retrieve array of bytes from message
// assume height, width and channels are known
const int sizes[3] = {channels, width, height};
const size_t steps[3] = {(size_t)height * (size_t)width, (size_t)height};
cv::Mat image(3, sizes, CV_8UC1, pixel_data, steps);

Итак, я создаю Матрицу с тремя измерениями, где тип элемента равен byte. Я не уверен, что правильно определяю steps, но думаю, что он соответствует документации 1018 *.

Но выполнение этого просто вылетает с

error: (-5:Bad argument) Unknown array type in function 'cvarrToMat'

Как правильно сериализовать изображение RGBA (или BGRA для OpenCV) в байтовый буфер и создать из него cv::Mat с помощью API C ++?

1 Ответ

0 голосов
/ 29 мая 2019

У меня есть одно решение, которое обходит проблему.Эта строка здесь:

cv::Mat image(3, sizes, CV_8UC1, pixel_data, steps);

делает предположение, что я могу передавать размеры трех измерений с отдельными байтами, но я не мог сделать эту работу.

Вместо использования другого конструктора

cv::Mat image(height, width CV_8UC4, pixel_data);

Я могу обрабатывать изображение как двумерное, но с векторным типом данных (размер элемента 4 байта вместо скалярных байтов)).Если указатель pixel_data находится в правильной компоновке, это работает.

Корректная компоновка на самом деле явно не задокументирована, но может быть получена из одного из официальных руководств

enter image description here

Таким образом, данные хранятся таким образом, что одна строка идет после другой, и каждый элемент строки разбивается на n_channels элементов.Использование типа данных, такого как CV_8UC4, заставляет матрицу считывать 4 байта в каждой позиции в массиве необработанных данных и перемещать указатель на 4 байта.

Так что в этом случае мне просто нужно переставить массив numpy в соответствующую последовательность: добавить строки вместе, но чередовать каналы.Я сделал это так, но надеюсь, что есть способ без зацикливания.

def array_to_cv_bytes(ary):
    assert ary.ndim == 3, 'Array must have 3 dimensions'
    h, w, c = ary.shape
    ary = ary[..., (2, 1, 0, 3)]
    output = np.empty(h * c * w, dtype=np.uint8)
    for channeld_idx in range(c):
        output[channeld_idx::c] = ary[..., channeld_idx].reshape(h*w)
    return output.tobytes(order='C')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...