Как отделить две битовые карты размером 8 и 16 от одного PNG

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

Сканировать изображение

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

Предварительный просмотр отсканированного изображения в Matlab

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

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

Правильно разделенные изображения интенсивности и глубины с использованием подхода @ DanMašek


person Lars Lort    schedule 25.03.2020    source источник
comment
Я бы сказал им, что вы ищете нового поставщика сканера, который действительно позволит вам понять данные, предоставляемые сканером.   -  person Jonathon Reinhart    schedule 25.03.2020
comment
@JonathonReinhart Я согласен, но я считаю, что они продают этот сканер в основном для довольно простых приложений, которые не нуждаются в значительной интеграции с другими процессами. Парень из службы поддержки приятный и услужливый, но они не мешают клиентам обращаться с имиджем. Университет купил сканер, и они могут совершенно не заботиться о том, на какие продукты они тратят свои деньги.   -  person Lars Lort    schedule 25.03.2020
comment
Этот вопрос, вероятно, не по теме для Stack Overflow, но я чувствую вашу боль. Отмечу, что предоставленное вами изображение однозначно странное. В моем браузере он выглядит иначе, чем на вашем снимке экрана Matlab и в галерее изображений моего телефона. Я бы попытался посмотреть на это в другом программном обеспечении для просмотра изображений, таком как GIMP, или в инструменте, который выгружает информацию о слоях PNG и т. Д., Чтобы попытаться понять, что они делают.   -  person Jonathon Reinhart    schedule 25.03.2020
comment
С портала поддержки: это нормально. Изображение .png TriSpector разделено на две части: 8-битное изображение интенсивности вверху и 16-битное изображение высот внизу. Поскольку изображение .png имеет размер 8 бит, для карты высот требуется два пикселя в изображении png для каждого реального пикселя. Таким образом, он становится вдвое больше и выглядит испорченным. Если вы хотите посмотреть изображение, загрузите его с помощью эмулятора TriSpector. | Есть еще кое-что, но позвольте мне сначала закончить обед :)   -  person Dan Mašek    schedule 25.03.2020
comment
Вот простой скрипт Python, демонстрирующий, как отделить два компонента от PNG: pastebin.com/Nw7zmPhy   -  person Dan Mašek    schedule 25.03.2020
comment
Голосование за повторное открытие этого вопроса - ИМХО, постановка проблемы завершена, это просто особенность несколько непонятной части оборудования. У меня подготовлен иллюстрированный ответ (на основе официальной информации от SICK) вместе с доказательством концепции на Python (см. Выше - нет доступной Matlab).   -  person Dan Mašek    schedule 25.03.2020
comment
@ DanMašek Я согласен, вы предоставили отличное решение и сформулировали, что моя проблема больше связана с разделением двух битовых карт размером 8 и 16 из одного PNG. Если я могу отметить ваш вопрос как ответ, то сделаю это. Я здесь явно новичок, так что дайте мне знать, что я могу сделать. Спасибо!   -  person Lars Lort    schedule 25.03.2020
comment
Спасибо, Ларс. Запрос на повторное открытие пришел, так что ответ есть. Рад, что смог помочь - раньше не использовал этот датчик, но я работаю с устройствами LMS и Ranger уже более десяти лет. Если у вас еще нет доступа к сайту поддержки, возможно, стоит его получить - есть дополнительные ссылки, которых вы не найдете на общедоступном сайте, а также форумы, которые часто посещают сотрудники SICK (хотя я не знаю Пока удалось извлечь из них много полезной информации, ТБХ).   -  person Dan Mašek    schedule 26.03.2020


Ответы (1)


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


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

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

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

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

Макет исходного PNG и способ разбить его на два компонента

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

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

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

Иллюстрация разделения карты высот на подкомпоненты


Пример скрипта на 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)

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

Пример извлеченного изображения яркости

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

Пример уменьшенного изображения карты высот

person Dan Mašek    schedule 26.03.2020