16 位整数写回文件有符号和无符号要分开 clamp
把一批 16 位采样的数据读进来做了点预处理(去直流、去背景这种减法操作),再写回原来的二进制文件给别的专业软件打开。读进来处理的时候一切正常,写回去打开一看,有的文件好好的,有的直接满屏噪点、糊成一团。同样的代码、同样的处理,结果天差地别,卡了好一阵。
根子是 16 位整数到底按有符号还是无符号解读,不是统一的,取决于文件格式。
同样 16 位,有的围绕 0 有的围绕 32768
这批数据两种格式:一种是无符号 16 位(unsigned),数据的载波零点在 32768,数值在 065535 之间抖;另一种是有符号 16 位(signed),围绕 0 抖,范围是 −3276832767。同样是两个字节 00 80,按无符号读是 32768,按有符号读(小端)是 −32768,完全是两码事。
读的时候按各自格式来,没问题。问题出在写回。
减法预处理之后有了负值
去直流、去背景这类操作本质是减法:估一个背景再从原数据里减掉。减完之后,数据就从「围绕 32768」或者「围绕 0」变成了「围绕 0」,而且必然出现负值(原来低于背景的地方)。
写回有符号格式(围绕 0)的那种文件,很直接——数据本来就围绕 0,减完还是围绕 0,按有符号 clamp 到 [−32768, 32767] 写进去就行,啥额外操作都不用加。
但写回无符号格式(围绕 32768)的那种文件就出事了:减法之后围绕 0、带负值,直接当无符号写回去,那些负值会被解释成 65536 附近的超大数,落在一个本该是中灰的位置上全是亮点,整张图就满屏噪点了。
写回无符号得先平移回它的基准
所以写回无符号格式之前,得先把数据从「围绕 0」平移回「围绕 32768」——加一个全图统一的直流基准(大概就是整图的均值,约等于 32768),把负值整体抬进 0~65535 的范围,再 clamp:
if target_unsigned: |
有个反着的坑得强调一下:写回有符号格式绝对不能也傻乎乎加个 +32768 的基准。因为数据本来围绕 0,加完 32768 之后落到 32768 附近,再被 (short) 强转,超过 32767 的部分直接溢回 −32768,数据当场写坏。也就是说 clamp 方式必须按目标格式二选一,不能图省事统一一套。
还有一个细节:那个直流基准必须是全图统一的一个值,不能用每一道各自的均值。用各道均值的话,等于把每道都单独拉回自己的 32768,结果是保留了各道之间的直流差——这恰恰是去直流、去背景想抹掉的东西,效果会差很远。这一条是走过弯路之后才纠正过来的:一开始图精确用了各道均值,结果处理完的图还不如不平移干净,换成全图统一直流基准才对。
说到底,这事的核心就一句:16 位数据的有符号、无符号不是读的时候定死就完了,写回的时候还得按目标格式再判一次。同样是「减完带负值」的中间结果,写回有符号和无符号要走的 clamp 路径完全不同,搞混了就是满屏噪点和数据写坏的区别。
