Преобразование 8-битных изображений Numpy в 16/32-битные изображения перед cvtColor() в opencv

Я проводил некоторые тесты, основываясь на своем понимании, прочитав документацию, но наткнулся на это.

Источник: NumPy конвертирует 8-битное изображение в 16/32-битное.

 i = cv2.imread(imgNameIn, cv2.CV_LOAD_IMAGE_COLOR) # Need to be sure to have a 8-bit input
 img = np.array(i, dtype=np.uint16) # This line only change the type, not values
 img *= 256 # Now we get the good values in 16 bit format

Но из того, что я прочитал здесь http://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html

Чтобы уменьшить масштаб с 32 бит до 8 бит, нужно разделить на 255 вместо 256, упомянутых выше, которые я получил из другого поста? Этот код ниже - это то, с чем я пытаюсь поиграть.

Был бы признателен, если бы кто-нибудь мог проверить, правильно ли я сделал масштабирование. Я задаю этот вопрос главным образом для того, чтобы подтвердить, является ли предполагаемое значение приведения значением 255 или 256?


person SacreD    schedule 25.08.2017    source источник
comment
Всегда 255. Наибольшее значение, которое должно занимать плавающее изображение (для отображения и использования в функциях, которые его насыщают), равно 1. Таким образом, чтобы уменьшить масштаб до uint8, вы умножаете наибольшее значение float (1) на 255, чтобы получить наибольшее значение uint8 (255). И аналогично, самое большое число uint8 равно 255, поэтому, чтобы получить наибольшее значение float, вы должны разделить на 255, чтобы вернуться к 1. Просто как. С другой стороны, пожалуйста, используйте лучшие имена переменных. И утомитесь присваивать изображения другим изображениям; это может ссылаться на одно и то же в памяти. Используйте .copy() для массивов numpy для создания новых копий.   -  person alkasm    schedule 28.08.2017
comment
@AlexanderReynolds Я обновил пост. Могу я узнать, что вы имеете в виду под новыми копиями? Идея моего кода состоит в том, чтобы проверить, будет ли преобразование 8-битного изображения в float32 перед cvtColor() для предотвращения потери информации. Моя основная цель - сохранить информацию как можно более точной, поскольку я не мог сохранять изображения с плавающей запятой с помощью cv2.imwrite, мне нужно каким-то образом снова уменьшить его до 8 бит (без потери информации), чтобы сохранить.   -  person SacreD    schedule 29.08.2017


Ответы (1)


Я должен не согласиться с ответом на вопрос, на который вы ссылаетесь. Формат uint8 может представлять 256 значений целых чисел, то есть целых чисел от 0 до 255 (2^8 - 1). 256 не может быть представлено как uint8. uint16 будет варьироваться от 0 до (2^16 - 1) = 65535.

Поэтому, если вам нужны 16-битные изображения с 65535 цветовыми уровнями и если значения интенсивности изображений находятся в диапазоне [0, 1] при чтении изображения и в формате float, вам необходимо умножить значения на 65535 и на , а затем чтобы преобразовать массив в uint16.

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

Чтобы вернуться к 8-битному, т. е. 256 цветовым уровням, вам нужно умножить все значения на 255 / 65535.

То же самое справедливо и для 32-битных изображений с 2^32 = 4,294,967,296 возможными значениями интенсивности, т. е. значениями в диапазоне [0, 4294967295]. Когда это число становится большим, вы скорее будете работать с 32-битными изображениями в формате с плавающей запятой, где значения содержатся в диапазоне [0, 1].

PS: я нашел ваш код довольно запутанным и не вдавался в подробности. Понимание того, что 8/16/32/64-битный формат является представлением количества возможных уровней интенсивности в изображении, является ключом, который должен позволить вам понять преобразования, которые вы делаете с изображениями.

person Eskapp    schedule 25.08.2017
comment
Результат операции умножения или деления всегда является числом с плавающей запятой. Нет, это не так; (np.uint16(5)*255).dtype => dtype('int64') и (np.uint16(5)*np.uint16(255)).dtype => dtype('uint16') например. Только деление всегда возвращает float в Python. - person alkasm; 28.08.2017
comment
@AlexanderReynolds Ой, я пишу слишком быстро. Вы, конечно, правы в этом вопросе! Я исправил и добавил больше контекста. Это просто ошибка, которую я слишком много раз видел при работе с изображениями: приведение целочисленного типа, выполнение некоторых операций, а затем недоумение, почему следующая библиотечная функция, которую вы вызываете, не работает, хотя вы преобразовали значения пикселя в целое число (которое тем временем стало плавающим). ). - person Eskapp; 28.08.2017
comment
Абсолютно, просто хотел дать вам возможность отредактировать этот хороший ответ! - person alkasm; 29.08.2017
comment
@Eskapp Спасибо за ваш хорошо объясненный и подробный ответ, теперь я обновил код (подробно) и также опубликовал результат. Я заметил, что что-то может пойти не так с преобразованием? поскольку показанный результат float32 отличается от uint16 и uint8. - person SacreD; 29.08.2017
comment
@Eskapp Я все еще не мог понять этот бит. Хорошей практикой является приведение типа вашего конечного результата к самому последнему шагу выполняемых вами операций. В основном это происходит по двум причинам: - Если вы выполняете деление или умножение с плавающей запятой, результат вернет число с плавающей запятой, и вам нужно будет снова изменить тип, который я сделал, и это дает мне TypeError: ufunc 'true_divide' output (typecode 'd ') не может быть приведен к предоставленному выходному параметру (код типа 'B') в соответствии с правилом приведения ''same_kind'' - person SacreD; 29.08.2017
comment
@SacreD, какую версию Python вы используете? Я предположил Python3, но если вы используете Python2, оператор деления работает по-другому. Я собираюсь просмотреть ваш код, как только у меня будет время :) - person Eskapp; 29.08.2017
comment
@Eskapp я использую Python 3 - person SacreD; 29.08.2017
comment
@SacredD Вы получили ошибку, написав im /= 255, верно? Это не работает, потому что с левой стороны im представляет собой пустой массив. Когда я делаю im = im / 255, это работает, даже если im равно uint8. Я проверю, как выглядят результаты позже. - person Eskapp; 29.08.2017
comment
@Eskapp Извините, если я запутал вас, для первого преобразования я фактически получил его работу с im /= 255 ИЛИ im *= 1./255 ИЛИ im = im / 255, просто я говорю, что мне нужно преобразовать тип данных только для float32 мне разрешено продолжить операцию без каких-либо сообщений об ошибках, как уже упоминалось. - person SacreD; 29.08.2017
comment
@Eskapp, но для преобразования из uint16 в uint8 после cvtColor я могу заставить его работать, только используя im = im*(255./65535) вместо im *= (255./65535). Я получу сообщение об ошибке TypeError: Cannot cast ufuncmultiple output from dtype('float64') to dtype('uint16') with rule casting 'same_kind' . Я подозреваю, что это как-то связано с поведением upcasting - person SacreD; 29.08.2017
comment
@Eskapp Спасибо за подробное объяснение проверки вывода, которую я с тех пор переместил в новый пост stackoverflow.com/questions/46052359/ - person SacreD; 05.09.2017
comment
хорошо, отлично, я думал попросить вас переместить его в новый вопрос, чтобы сохранить правило, 1 сообщение, 1 вопрос. Был слишком занят, чтобы смотреть на это. Удачи в решении вашего вопроса :) - person Eskapp; 05.09.2017
comment
@Eskapp До сих пор не получил ответа :(..надеюсь, у вас будет время взглянуть на него в ближайшие несколько дней. - person SacreD; 05.09.2017