Как отделить двухбитные карты размером 8 и 16 от одного PNG - PullRequest
2 голосов
/ 25 марта 2020

У меня есть изображения с глубинного лазерного сканера SICK Trispector. Формат изображения PNG. SICK называет это Trispector 2.5D PNG. Изображения содержат как данные отражения, так и данные глубины в соответствии с документацией SICK. Но SICK не будет предоставлять информацию о том, как использовать эти данные без использования программного обеспечения их или партнеров. По сути, мне нужны данные о глубине. Данные об отражении могут быть полезны, но не обязательны. Полученное изображение получилось моно chrome. Кажется, у него есть данные об отражении в верхней части изображения и перекрывающиеся данные о высоте в нижней части. Отсканированный объект - это ящик с пивными бутылками с bottle крышками. Вы можете увидеть пример здесь:

Scan image

Я пытался открыть изображение во многих различных средствах просмотра изображений и искал информацию о 2.5D, но это не похоже на это. В режиме предварительного просмотра изображений Matlab я получаю одну сторону данных о высоте по отдельности, но я не знаю, как использовать эту информацию. См. Следующее изображение из предварительного просмотра Matlab:

Matlab preview of a scan image

Кто-нибудь знает, как экстраполировать данные высоты из изображения, подобного этому? Возможно, кто-то раньше работал со сканерами SICK SOPAS или SICK и понимает этот формат "2.5D PNG", который SICK называет. Было бы неплохо использовать решение OpenCV.

Редактировать: Как отмечает @ DanMašek, проблема заключается в том, чтобы отделить два изображения с разной битовой глубиной от одного PNG. Он дает дополнительное понимание проблемы и отличное решение OpenCV для разделения изображений интенсивности и глубины на 8- и 16-битные соответственно:

Correctly separated intensity and depth images using @DanMašek's approach

1 Ответ

1 голос
/ 26 марта 2020

Примечание. Эта информация основана на FAQ по SICK TriSpector , который находится на форумах поддержки SICK (недоступен для общего доступа, но вы можете запросить доступ).


Изображения PNG сгенерированный SICK TriSpector хранит конкатенацию двух пиксельных буферов:

  • 8-битное изображение с интенсивностью
  • 16-битное (с прямым порядком байтов) изображение карты высот

Полученное PNG-изображение имеет ту же ширину, что и каждый компонент, и в три раза больше высоты (поскольку PNG является 8-битным, и у нас есть 3 байта для каждой позиции пикселя).

Давайте рассмотрим Простой пример, где компоненты имеют 3 строки и 4 столбца. Данные, хранящиеся в PNG, будут иметь следующую структуру:

Layout of the source PNG and way to split it into the two components

Первый шаг, как показано выше, состоит в том, чтобы разделить изображение на два компонента. , PNG содержит 9 строк, треть из которых - 3 строки, поэтому строки 0-2 содержат интенсивность, а остальные - карту высот. Изображение интенсивности можно использовать напрямую, карта высот нуждается в дальнейшей обработке.

Если мы используем архитектуру с прямым порядком байтов и не заботимся о переносимости, мы можем воспользоваться макетом в памяти и просто переосмыслить пиксельный буфер в виде 16-битных целых чисел без знака (в Python numpy.ndarray.view, в C ++ создайте новый Mat обертывание буфера).

Более гибкий, хотя и более медленный метод заключается в объединить части вручную. Измените массив таким образом, чтобы он содержал правильное количество строк, а затем разбейте его на основе нечетного или четного номера столбца (пропустите индексирование в Python). Преобразуйте каждый вложенный массив в 16-битные целые числа без знака и, наконец, объедините их по формуле LSB + 256 * HSB.

Illustration of splitting the height-map into sub-components


Пример сценария в Python:

import cv2
import numpy as np

img = cv2.imread('combined.png', cv2.IMREAD_GRAYSCALE)
height, width = img.shape

# Determine the actual height of the component images
real_height = height/3

# Extract the top (intensity) part
intensity_img = img[:real_height,...]

# Extract the botton (heightmap) part
# Since this has two values per pixel, also reshape it to have the correct number of rows
heightmap_raw = img[real_height:,...].reshape(real_height,-1)

# The heightmap is 16 bit, two subsequent 8 bit pixels need to be combined
# ABCD -> A+256*B, C+256*D

# Quick but non-portable (needs little-endian architecture)
heightmap_img = heightmap_raw.view(np.uint16)

# Slower but portable
heightmap_a = heightmap_raw[...,::2].astype(np.uint16)
heightmap_b = heightmap_raw[...,1::2].astype(np.uint16)
heightmap_img = heightmap_a + 256 * heightmap_b

# Note: intensity is np.uint8, heightmap is np.uint16

cv2.imwrite('intensity.png', intensity_img)
cv2.imwrite('heightmap.png', heightmap_img)

Извлеченное изображение интенсивности:

Example extracted intensity image

Извлеченное изображение карты высот (обратите внимание, что оригинал данные были уменьшены в 256 раз при сохранении этого для иллюстрации, потеряв много деталей):

Example downscaled heightmap image

...