背景
上篇博客埋了个坑,就是如何将ISP DMA
写到DDR的数据unpack
成可被7yuv等软件预览的RAW图。跟通用DMA这种源端
、目的端
均以字节对齐
访问不通,ISP DMA通常有一端是非字节对齐的,即流式(stream)访问
——不需要指定访问地址,而另一端是字节对齐的,即对齐(aligned)访问
,这就导致对齐端写入DDR的像素,是紧密排列
的,如果像素位宽
不是8的整数,则想要用7yuv等软件查看raw图内容,就需要unpack
操作——给紧密排列的像素的高位补零
,从而让其对齐到8bit或16bit甚至32bit。
思路
因为ISP DMA每行写到DDR的内存布局是一样的,因此问题可以简化成如何unpack一行像素,又因为ISP写入每行像素时是以beat
为单位的(通常就是AXI_data
总线的宽度),所以问题进一步简化成如何unpack一个beat内包含的像素,并保留好被beat截断的首尾像素残值,考虑到要拆分像素必然要用到移位操作,而PC机的移位只能作用于32位的int
或64位的long long
类型,但是AXI_data
的宽度是可以到128位甚至256位的,为了实现的简单(反正被beat截断和被int截断都要考虑如何恢复像素残值),将问题变换成如何unpack一个blk
内包含的像素,并保留好被blk
截断的首尾像素残值,这里blk
即block,表示用户选择的32位或64位整型,通常对应DDR里的4字节或8字节。
实现细节
关键结构体
图像宽高、像素位宽和总线位宽
为了支持任意宽度的像素,和任意宽度的总线位宽,我们定义下面的结构体,以存储此类全局信息
typedef struct _raw_info_s {
unsigned char pix_width;
unsigned char bus_width;
unsigned short img_width;
unsigned short img_height;
} raw_info_t;
像素截断和恢复信息
为了记录截断信息,定义下面的结构体
typedef struct _unpack_ctx_s {
unsigned short* dst_pix_buf;
unsigned short pix_num_unpacked;
unsigned short left_bits_of_last_blk;
unsigned short last_pix_val;
} unpack_ctx_t;
注意,像素恢复操作统一在blk开始进行,blk末尾不做处理,以避免重复。
通过移位操作分离单个像素
tmp = blk & ((1 << info->pix_width) - 1); // 提取blk内单个完整像素
ctx->dst_pix_buf[ctx->pix_num_unpacked++] = tmp; // 将提取出来的像素放到int16或int32
blk >>= info->pix_width; // 将已提取的完整像素通过移位操作丢弃,同时将下一个像素挪到blk的最低位
bits_left -= info->pix_width; // 将已提取的像素所占bit数 从 blk剩余bit数 里减去
通过截断上下文信息恢复被截断像素
if (ctx->left_bits_of_last_blk > 0) { // 上一个blk有残余像素
miss_bits_of_last_blk = info->pix_width - ctx->left_bits_of_last_blk; // 算出残余像素遗留在本blk的bit数
tmp = blk & ((1 << miss_bits_of_last_blk) - 1); // 获取残余像素在本blk的高位像素值,存在tmp的低位
tmp <<= ctx->left_bits_of_last_blk; // 将残余像素的高位值移到正确的位置
tmp |= ctx->last_pix_val; // 将残余像素的低位值放到tmp的低位,从而拼凑出完整的被截像素
ctx->dst_pix_buf[ctx->pix_num_unpacked++] = tmp; // 将被截像素放到int16或int32
blk >>= miss_bits_of_last_blk; // 将已提取的被截像素通过移位操作丢弃,同时将下一个像素挪到blk的最低位
bits_left -= miss_bits_of_last_blk; // 将已提取的被截像素所占bit数 从 blk剩余bit数 里减去
}
while (bits_left > info->pix_width) {
// 通过移位操作分离单个像素
}
if (bits_left > 0) { // 本blk也留下残余像素了-_-!
ctx->left_bits_of_last_blk = bits_left; // 将残余像素的bit数存到ctx
tmp = blk & ((1 << bits_left) - 1); // 将残余像素的低位像素值存到tmp
ctx->last_pix_val = tmp; // 将上一步的tmp存到ctx
}
总结
想获取完整可编译运行代码,访问此github地址,命令行使用方法:
rawpeck img_width img_height pixel_width bus_width path_of_packed_raw path_of_unpacked_raw