在Python中存储不与字节对齐而不填充的数据(NumPy)

5w9g7ksd  于 2023-10-19  发布在  Python
关注(0)|答案(1)|浏览(87)

我有图像数据,是用10或12位整数表示,我想保存此数据到磁盘,而不写不必要的6或4个零的填充时,使用16位整数来表示它们。所有这些都是在Python中实现的。
更具体地说,如果我有两个12位整数存储为16位整数,它们将占用32位空间,但数据本身只覆盖24位。如果我能够将它们存储为3个8位整数,就不会浪费填充空间。唯一需要的是记住原始输入数组的形状(图像分辨率)和位深,以便能够恢复原始数据。
数据源使用NumPy nd-arraysnp.int16 dtype,最高有效位设置为零作为填充。数据的目的地是一个HDF 5文件,该文件使用h5py模块进行操作,该模块也使用NumPy nd-arrays作为底层数据类型。因此,我认为最好是用NumPy本身来完成操作,而不需要任何额外的转换为不同数据类型的开销。但任何建议的解决方案都是受欢迎的。
我无法找到任何解决这个问题的方法,至少是相当有效的。使用简单的位屏蔽操作来以某种方式划分和合并的数字可能是不可行的,因为它将花费更多的CPU时间比它将保存磁盘写入时间。这个假设来自于这样的期望,即这个操作需要在没有任何向量化/其他可能的优化的情况下逐个元素地完成。
因此,一些优化的功能可能是必要的,但我还没有找到任何。我正在处理每秒数百兆字节的数据(视频),因此这种优化真的会使我的SSD更容易管理存储数据。

ttcibm8c

ttcibm8c1#

我不知道这是否有效,或者你在寻找什么。但是你可以简单地展平数组,然后做一些基本的移位/掩码(或者*16+)。创建一个np.uint8数组,你可以用任何你知道的方法来存储数组。或者重新创建“16位表示12位数组”
这当然不是很有效率。但至少它是向量化的(至少不是“元素方面的”,如果你的意思是“在纯Python迭代中为每个元素执行纯Python操作”的话。我的意思是,它仍然是元素明智的,但在numpy迭代。我不明白它怎么可能不是元素明智的:你必须至少读取所有元素一次才能做任何事情)
这样

def storable8from12(arr):
   tmp32=arr.reshape(-1,2).astype(np.uint32)
   return ((tmp32[:,0]<<12)+tmp32[:,1]).view(np.uint8).reshape(-1,4)[:,:3].reshape(len(arr),-1)

一些临时的阵列甚至比你想减少的还要大。但不是循环。我认为这是为了存储,而不是直接在内存中,你想减少大小。
然后反过来

def unpack12bitsFrom8(st):
   tmp32=np.pad(st.reshape(-1,3), ((0,0),(0,1))).view(np.uint32)
   odd=(tmp32&0xfff000)>>12
   even=tmp32&0xfff
   return np.hstack([odd,even]).reshape(len(st),-1).astype(np.uint16)

所以,再一次,不确定这是你所期望的。因为你显然已经想到了按位操作(<<&等等)。但我不明白为什么你说它不是“矢量化的”(在这个意义上,我们通常给予这个词在numpyfor循环在numpy中完成)
请注意,这里我假设行的大小是这样的,边界中没有例外。因此,在处理的情况下“16携带12位-> 8位-> 16位携带12位”的情况下,这意味着行的长度是偶数。否则,很明显,通过填充每行的最后一个像素,它仍然是可能的。或者甚至在整个图像上做这个。但是,您可能需要存储形状(在我的情况下,我不需要太多,因为我假设W×H“16携带12”图像转换为1.5W×H 8位图像)。
我还没谈到10比特的案子。如果你不想浪费2比特,而像处理12比特的情况一样处理它,那么你需要按10和8的最接近公倍数的数据包分组。这将是40比特。所以大致相同的技术,但有点痛苦(你需要64位临时整数, Package 4个数字,而不是2个,成一个。在返回的路上,你需要4列,而不仅仅是奇数和偶数,来hstack)。这意味着W分辨率必须是4的倍数。或者,如果你不能假设W×H 10 bits被存储为(1.25W)×H 8 bits,那么你将需要一些填充和存储形状。

相关问题