浮点像素值如何转换为整数值?

时间:2019-11-30 03:45:21

标签: opencv image-processing computer-vision python-imaging-library scikit-image

图像库(例如PIL,OpenCV等)如何将浮点值转换为整数像素值?

例如

import numpy as np
from PIL import Image

# Creates a random image and saves in a file
def get_random_img(m=0, s=1, fname='temp.png'):

    im =  m + s * np.random.randn(60, 60, 3) # For eg. min: -3.8947058634971179, max: 3.6822041760496904

    print(im[0, 0]) # for eg. array([ 0.36234732, 0.96987366, 0.08343])

    imp = Image.fromarray(im, 'RGB') #                         (*)

    print(np.array(imp)[0, 0]) # [140 , 74, 217] 

    imp.save(fname) 

    return im, imp

对于上述方法,注释中提供了一个示例(随机产生)。我的问题是:如何(*)(范围从-无穷大到正无穷大)转换为ndarray到0到25​​5之间的像素值?

我试图研究Pil.Image.fromarray方法,并最终在d.decode(data)方法内的行#798 Pil.Image.Image().frombytes处结束。我可以找到decode方法的实现,因此无法知道转换后进行了什么计算。

我最初的想法是,该方法可能使用数组中的 minimum (至0)和 maximum (至255)值,然后相应地映射所有其他值0和255。但是经过调查,我发现事实并非如此。而且,当数组的值在0到1之间或任何其他值范围内时,该如何处理?

2 个答案:

答案 0 :(得分:1)

某些库假定浮点像素值在0到1之间,并且在转换为8位无符号整数时会将其线性映射到0和255。其他一些将找到最小值和最大值并将其映射到0和255。如果要确定数据发生了什么,则应始终明确进行此转换。

通常,像素不需要是8位无符号整数。像素可以具有任何数字类型。通常,像素强度表示光的量或某种密度,但这并非总是如此。任何物理量都可以在2维或更多维中采样。因此,有意义值的范围取决于所成像的内容。负值通常也很有意义。

许多相机在将光强度转换为数字时具有8位精度。同样,显示器通常具有b位强度范围。这就是许多图像文件格式仅存储8位无符号整数数据的原因。但是,某些相机具有12位或更多,并且某些过程会以更高的精度导出像素数据,而这是人们不希望量化的。因此,诸如TIFF和ICS之类的格式将使您可以将图像保存为您可以想到的几乎任何数字格式。

答案 1 :(得分:1)

恐怕它在任何地方都没有像您希望的那样聪明!它只是将第一个浮点数的第一个字节解释为uint8,然后将第二个字节解释为另一个uint8 ...

from random import random, seed
import numpy as np
from PIL import Image

# Generate repeatable random data, so other folks get the same results
np.random.seed(42)

# Make a single RGB pixel
im =  np.random.randn(1, 1, 3)

# Print the floating point values - not that we are interested in them
print(im)                                                                                 
# OUTPUT: [[[ 0.49671415 -0.1382643   0.64768854]]]

# Save that pixel to a file so we can dump it
im.tofile('array.bin')

# Now make a PIL Image from it and print the uint8 RGB values
imp = Image.fromarray(im, 'RGB')
print(imp.getpixel((0,0)))                                                                
# OUTPUT: (124, 48, 169)

因此,PIL将我们的数据解释为RGB = 124/48/169

现在看看我们丢弃的十六进制。它的长度为24个字节,即3个float64(8字节)值,其中1个代表红色,一个代表绿色,一个代表蓝色,分别表示图像中的1个像素:

xxd array.bin

输出

00000000: 7c30 a928 2aca df3f 2a05 de05 a5b2 c1bf  |0.(*..?*.......
00000010: 685e 2450 ddb9 e43f                      h^$P...?

第一个字节(7c)变为124,第二个字节(30)变为48,第三个字节(a9)变为169。

TLDR; PIL仅将第一个浮点数的第一个字节作为第一个像素的红色uint8通道,然后将第一个浮点数的第二个字节作为绿色{{第一个像素的1}}通道和第一个浮点的第三个字节作为第一个像素的蓝色uint8通道。