Я получил 3 массива данных NumPy r,g,b, представленных в виде 2D-массива float64 (720x1024)
По сути, для каждой строки каждый канал представляет собой набор поплавков:
[[0.94984896 0.96076077 0.9599071 ... 0.80338298 0.80253267 0.80560301],
[0.94958899 0.95265769 0.95964721 ... 0.80312296 0.80619422 0.80534302],
[0.94932439 0.95239315 0.95938273 ... 0.80285821 0.80592952 0.80115679], ...,
[0.97768162 0.98087441 0.98014583 ... 0.87006474 0.87325722 0.87252819],
[0.97711028 0.98030547 0.9795793 ... 0.86948629 0.87268113 0.87587604],
[0.97653759 0.97581362 0.9711683 ... 0.8610633 0.86818208 0.87530095]]
Что я хотел бы сделать, так это сделать его каналом, который я могу использовать в cv2.merge((r,g,b))
Так что значения float64 для каждой строки умножаются на 255 и что-то, что принимает cv2.merge().
Я думаю, что NumPy может делать такие вещи, но я запутался, есть ли способ сделать это?
@QuangHoang, если есть вероятность, что ввод будет 1.0
, вы должны умножить на 255,99999 вместо 256, чтобы предотвратить ошибку переполнения.
или просто умножьте на 255 и используйте np.round
или np.clip
давайте рассмотрим ваши три массива как r
и g
и b
соответственно. В таком случае вам нужен следующий код:
Edit-1: на основе комментария Марка я предлагаю следующие изменения
r = (r*255).round(0).astype(np.uint8)
g = (g*255).round(0).astype(np.uint8)
b = (b*255).round(0).astype(np.uint8)
img = np.stack((r,g,b), axis=-1)
Edit-2: я могу ошибаться, но я не уверен, что умножение на 255,99999 лучше. Это приведет к тому, что многие другие значения пикселей будут сдвинуты до следующего значения, чем они были изначально или должны были быть, вот небольшой пример, который я мог бы придумать.
# random fraction values
a: [3.2000e-04 5.5111e-01 9.9999e-01 9.8850e-01]
a*255.99999: [8.19199968e-02 1.41084154e+02 2.55997430e+02 2.53055990e+02]
(a*255.99999).astype(np.uint8): [ 0 141 255 253]
a*255: [8.1600000e-02 1.4053305e+02 2.5499745e+02 2.5206750e+02]
(a*255).round(0).astype(np.uint8): [ 0 141 255 252]
Кроме того, я провел еще один небольшой эксперимент со всеми возможными значениями пикселей, и это удивительно говорит о том, что оба метода точны.
pixels = np.arange(256, dtype=np.float64)
pixels /= 255
print(sum((pixels*255.99999).astype(np.uint8) == np.arange(256)))
# output is 256
print(sum((pixels*255).round(0).astype(np.uint8) == np.arange(256)))
# output is 256
Я предпочитаю умножать на 255,9999 вместо 255, иначе 255 будет очень мало представлено в выводе.
Нет, округление не лучше. Теперь и 0, и 255 недостаточно представлены.
Можно ли как-то подтвердить на примере? @МаркРансом
если у вас есть критика этого ответа, вам нужно сделать шаг вперед и обосновать свои рассуждения. предложение использовать магическое число ставит вас в невыгодное положение.
@sai умножь на 256, тогда np.clip
. в принципе, диапазон [0..1) сопоставляется с [0..256) (коэффициент 256), и если есть значения 1,0, которые сопоставляются с 256, просто обрежьте их до 255. чтобы правильно ответить на этот вопрос, нам нужно было бы знать, как эти значения с плавающей запятой были сгенерированы в первую очередь.
@ChristophRackwitz Я полностью с вами согласен, я готов учиться и защищать то, что я считаю правильным. Я также согласен с необходимостью знать, как изначально генерируются фракционные ценности.
@MarkRansom Не могли бы вы прокомментировать мои выводы?
Преобразование в целочисленные значения требует преобразования диапазонов в строке действительных чисел. В первом случае, умножая на 255 и усекая, каждый из этих диапазонов имеет ширину 1/255, кроме последнего для 255, который имеет диапазон 0. При округлении большинство диапазонов по-прежнему имеют ширину 1/255, за исключением первый и последний, которые теперь оба 0,5/255.
Ваш недавно добавленный тестовый пример не говорит вам, что, по вашему мнению, он делает. Преобразование целых чисел туда и обратно намного проще, чем преобразование произвольных вещественных значений.
Попробуйте протестировать с помощью pixels = np.arange(1024, dtype=np.float64) / 1024
, и, возможно, станет понятно, о чем я говорю.
Что ж, в моем случае умножьте на 255 швов, мои поплавки находятся в диапазоне от 0 до 1. поэтому максимальное значение может быть 255, а минимальное 0, в opencvt есть некоторые другие значения и фильтры для контраста и яркости, которые в конечном итоге также будут использоваться.
@user219279 user219279 сравните шансы получить результат 255 с шансами получить результат 254.
я поменял на 255.999 визуально разницы для глаз не было.
просто
(arr * 256).astype('uint8')
?