我正在尝试使用Python 3阅读包含图像(视频)的12位二进制文件。
要读取类似的文件但以16位编码,以下内容非常有效:
import numpy as np
images = np.memmap(filename_video, dtype=np.uint16, mode='r', shape=(nb_frames, height, width))
其中filename_video是可以从另一个文件读取的视频的文件和nb_frames,高度和宽度特征。通过'工作得很好'我的意思是快:在我的计算机上读取140帧的640x256视频大约需要1毫秒。
据我所知,当文件以12位编码时我不能使用它,因为没有uint12类型。所以我要做的是读取一个12位文件并将其存储在一个16位的uint数组中。以下摘自(Python: reading 12 bit packed binary image),有效:
with open(filename_video, 'rb') as f:
data=f.read()
images=np.zeros(int(2*len(data)/3),dtype=np.uint16)
ii=0
for jj in range(0,int(len(data))-2,3):
a=bitstring.Bits(bytes=data[jj:jj+3],length=24)
images[ii],images[ii+1] = a.unpack('uint:12,uint:12')
ii=ii+2
images = np.reshape(images,(nb_frames,height,width))
然而,这非常慢:读取640x256视频时,我的机器只有5帧需要大约11.5秒。理想情况下,我希望能够像使用memmap读取8位或16位文件一样有效地读取12位文件。或者至少慢10 ^ 5倍。我怎么能加快速度呢?
这是一个文件示例: http://s000.tinyupload.com/index.php?file_id=26973488795334213426 (nb_frames = 5,高度= 256,宽度= 640)。
答案 0 :(得分:6)
我的实现与@ max9111提出的实现略有不同,该实现不需要调用unpackbits
。
它直接通过将中间字节切成两半并使用numpy的二进制运算从三个连续的uint12
中创建两个uint8
值。在下文中,假设data_chunks
是一个二进制字符串,其中包含任意数目的12位整数的信息(因此,其长度必须为3的倍数)。
def read_uint12(data_chunk):
data = np.frombuffer(data_chunk, dtype=np.uint8)
fst_uint8, mid_uint8, lst_uint8 = np.reshape(data, (data.shape[0] // 3, 3)).astype(np.uint16).T
fst_uint12 = (fst_uint8 << 4) + (mid_uint8 >> 4)
snd_uint12 = ((mid_uint8 % 16) << 8) + lst_uint8
return np.reshape(np.concatenate((fst_uint12[:, None], snd_uint12[:, None]), axis=1), 2 * fst_uint12.shape[0])
我以其他实现作为基准,这种方法在〜5 Mb输入下被证明快了约4倍:
read_uint12_unpackbits
每个循环65.5 ms±1.11 ms(平均±标准偏差,共运行7次,每个循环10个循环)
read_uint12
每个循环14 ms±513 µs(平均±标准偏差,共运行7次,每个循环100个循环)
答案 1 :(得分:3)
修改强>
在@Cyril Gaudefroy回答的帮助下,我使用Numba
创建了一个编译解决方案。
import numba as nb
import numpy as np
@nb.njit(nb.uint16[::1](nb.uint8[::1]),fastmath=True,parallel=True)
def nb_read_uint12(data_chunk):
"""data_chunk is a contigous 1D array of uint8 data)
eg.data_chunk = np.frombuffer(data_chunk, dtype=np.uint8)"""
#ensure that the data_chunk has the right length
assert np.mod(data_chunk.shape[0],3)==0
out=np.empty(data_chunk.shape[0]//3*2,dtype=np.uint16)
for i in nb.prange(data_chunk.shape[0]//3):
fst_uint8=np.uint16(data_chunk[i*3])
mid_uint8=np.uint16(data_chunk[i*3+1])
lst_uint8=np.uint16(data_chunk[i*3+2])
out[i*2] = (fst_uint8 << 4) + (mid_uint8 >> 4)
out[i*2+1] = ((mid_uint8 % 16) << 8) + lst_uint8
return out
<强>计时强>
num_Frames=10
data_chunk=np.random.randint(low=0,high=255,size=np.int(640*256*1.5*num_Frames),dtype=np.uint8)
Cyril Gaudefroy(numpy only): 11ms ->225MB/s
Numba version: 1.1ms ->2.25GB/s
以前的版本(不推荐)
如果Numba不是一个选项,请参阅@Cyril Gaudefroys回答。
当我读到这个问题时,我认为必须有一个简单的答案,但我失败了。不过我写了一个简单(但很难看)的代码,比你的例子快300倍,在我的笔记本电脑上实现了大约25 MB / s(Core i5 3210M)。
def read_uint12(filename_video,nb_frames,height,width):
data=np.fromfile(filename_video, dtype=np.uint8)
data=np.unpackbits(data)
data=data.reshape((data.shape[0]/12,12))
images=np.zeros(data_2.shape[0],dtype=np.uint16)
for i in xrange(0,12):
images+=2**i*data[:,11-i]
images = np.reshape(images,(nb_frames,height,width))
return images
答案 2 :(得分:2)
发现@cyrilgaudefroy答案很有用。但是,最初,它不适用于我的12位压缩二进制图像数据。发现这种特殊情况下的包装有些不同。 “中间”字节包含最低有效半字节。三元组的字节1和3是十二个字节中的最高8位。因此将@cyrilgaudefroy答案修改为:
def read_uint12(data_chunk):
data = np.frombuffer(data_chunk, dtype=np.uint8)
fst_uint8, mid_uint8, lst_uint8 = np.reshape(data, (data.shape[0] // 3, 3)).astype(np.uint16).T
fst_uint12 = (fst_uint8 << 4) + (mid_uint8 >> 4)
snd_uint12 = (lst_uint8 << 4) + (np.bitwise_and(15, mid_uint8))
return np.reshape(np.concatenate((fst_uint12[:, None], snd_uint12[:, None]), axis=1), 2 * fst_uint12.shape[0])
答案 3 :(得分:0)
这是另一种变化。我的数据格式是:
第一个uint12:第二个uint8的最低有效4位中的最高有效位+第一个uint8的最低有效8位中的
第二个uint12:第三个uint8的最高有效8位+第二个uint8的最高有效4位的最低有效4
相应的代码是:
def read_uint12(data_chunk):
data = np.frombuffer(data_chunk, dtype=np.uint8)
fst_uint8, mid_uint8, lst_uint8 = numpy.reshape(data, (data.shape[0] // 3, 3)).astype(numpy.uint16).T
fst_uint12 = ((mid_uint8 & 0x0F) << 8) | fst_uint8
snd_uint12 = (lst_uint8 << 4) | ((mid_uint8 & 0xF0) >> 4)
return numpy.reshape(numpy.concatenate((fst_uint12[:, None], snd_uint12[:, None]), axis=1), 2 * fst_uint12.shape[0])