基于FFT2替代DCT2的Matlab JPEG压缩:二进制导出难题
Matlab中FFT2替代DCT2实现JPEG压缩的二进制文件写入解决方案
问题背景
我正尝试在Matlab中用FFT2替代DCT2实现JPEG压缩,并导出二进制文件供解码使用,目前在二进制文件转换环节遇到较大困难。
已实现的压缩/解码流程
压缩步骤
- 将RGB图像转换为Y、Cb、Cr三通道
- 对每个通道执行以下操作:
- 若为Cb或Cr通道,进行下采样
- 将通道分割为8×8不重叠块
- 对每个块执行:
- 应用2D FFT,输出8×8频率表
- 量化:与量化表逐元素相除后取整,Y通道与Cb、Cr通道使用不同量化表
- 之字形(ZigZag)遍历系数,提升游程编码效率
- 游程编码(Run Length Encoding),输出包含计数矩阵与符号矩阵的元胞数组
- 霍夫曼(Huffman)编码,分别编码计数矩阵与符号矩阵
- 生成自定义二进制文件格式并导出
解码步骤
- 读取二进制文件
- Huffman解码
- 逆游程编码
- 逆ZigZag遍历
- 乘以量化表
- 逆2D FFT
- 重构8×8块
二进制文件需求
需包含:Y、Cb/Cr量化表各一张,Huffman表/字典,实际图像数据。Huffman表通过扫描所有块生成字典后编码所有块。
核心难题
作为Matlab新手,不熟悉二进制文件写入操作。使用Matlab内置Huffman函数时,输出为元胞数组矩阵:第一列为符号,第二列为二进制码(以整数数组形式存储)。不知道如何将这些二进制码直接以二进制值写入文件,而非被当作十进制处理。
解决方案:二进制码的序列化与写入
Matlab中要将整数数组形式的二进制码转为实际的二进制位写入文件,需要先将零散的二进制位拼接成完整的字节(8位一组),再写入文件。以下是具体实现步骤:
1. 收集所有Huffman编码的二进制位
假设你的Huffman编码结果存储在huffman_codes元胞数组中,每个元素是一个整数数组(比如[1,0,1,1]表示二进制1011)。首先把所有二进制位拼接成一个大的逻辑数组:
% 收集所有二进制位 all_bits = []; for i = 1:size(huffman_codes, 1) all_bits = [all_bits, huffman_codes{i, 2}]; end
2. 补位对齐到8位字节
因为文件是以字节为单位写入的,若总位数不是8的倍数,需要补0到最近的8的倍数:
bit_count = length(all_bits); pad_bits = mod(8 - mod(bit_count, 8), 8); if pad_bits > 0 all_bits = [all_bits, zeros(1, pad_bits)]; end
3. 将二进制位转换为字节数组
把每8位一组转为十进制整数(0-255),这就是要写入的字节数据:
% 按8位分组 byte_groups = reshape(all_bits, 8, [])'; % 转换为十进制字节(高位在前) byte_array = bi2de(byte_groups, 'left-msb');
4. 写入二进制文件
使用fwrite函数,指定'uint8'格式确保按字节写入,同时按约定顺序写入量化表、Huffman字典、补位信息和图像数据:
% 打开二进制文件(写入模式) fid = fopen('compressed_image.bin', 'wb'); if fid == -1 error('无法打开文件进行写入'); end % 写入Y通道量化表(8x8矩阵,假设元素为0-255整数) fwrite(fid, y_quant_table, 'uint8'); % 写入Cb/Cr通道量化表 fwrite(fid, cbcr_quant_table, 'uint8'); % 写入Huffman字典:先写符号数量,再逐个写入符号、码长、码 symbol_count = size(huffman_dict, 1); fwrite(fid, symbol_count, 'uint16'); % 用uint16存储符号数量,支持最多65535个符号 for i = 1:symbol_count fwrite(fid, huffman_dict{i, 1}, 'int16'); % 符号可能为负数,用int16存储 code_length = length(huffman_dict{i, 2}); fwrite(fid, code_length, 'uint8'); % 存储二进制码的长度 fwrite(fid, huffman_dict{i, 2}, 'uint8'); % 存储二进制码的每一位 end % 写入补位数量(解码时需用此去除多余的补位0) fwrite(fid, pad_bits, 'uint8'); % 写入图像数据字节数组 fwrite(fid, byte_array, 'uint8'); % 关闭文件 fclose(fid);
5. 解码时的对应处理
读取文件时需严格按照写入顺序还原数据:
fid = fopen('compressed_image.bin', 'rb'); if fid == -1 error('无法打开文件进行读取'); end % 读取量化表 y_quant_table = fread(fid, [8,8], 'uint8'); cbcr_quant_table = fread(fid, [8,8], 'uint8'); % 读取Huffman字典 symbol_count = fread(fid, 1, 'uint16'); huffman_dict = cell(symbol_count, 2); for i = 1:symbol_count huffman_dict{i, 1} = fread(fid, 1, 'int16'); code_length = fread(fid, 1, 'uint8'); huffman_dict{i, 2} = fread(fid, code_length, 'uint8'); end % 读取补位数量 pad_bits = fread(fid, 1, 'uint8'); % 读取图像字节数组 byte_array = fread(fid, inf, 'uint8'); fclose(fid); % 将字节数组转回二进制位 byte_groups = de2bi(byte_array, 8, 'left-msb'); all_bits = byte_groups(:)'; % 去除补位的0 if pad_bits > 0 all_bits = all_bits(1:end-pad_bits); end % 后续执行Huffman解码、逆游程编码等操作
注意事项
- 若量化表元素超出0-255范围,需调整量化表或使用
uint16等更大的存储格式。 - 符号若为负数,需选择合适的整数类型存储(如
int16),避免溢出。 - Huffman字典的序列化方式可根据需求调整,只要保证解码时能正确还原即可。
内容的提问来源于stack exchange,提问作者Linn Htet Aung
相关产品推荐
相关产品推荐

