正点原子stm32教程提到过jpeg解码库libjpeg,但是没有提到jpeg编码,我也好奇jpeg编码怎么实现,用代码怎么生成jpeg文件的。所以最近学习了jpeg编码,在这里做记录。
参考文章
jpeg图片格式详解 https://blog.csdn.net/yun_hen/article/details/78135122
这篇文章比较详细的介绍了jpeg的文件结构以及每个段的内容的解析。
但是jpeg使用的游程编码和哈夫曼编码描述很少。
jpeg编码原理 https://zhuanlan.zhihu.com/p/600252083
jpeg中的范式哈夫曼编码 https://zhuanlan.zhihu.com/p/72044095
这两篇文章进行了补充。
但是依旧没有实际的代码实现jpeg编码。
下面这位给出了C语言的代码,
learn_jpeg https://github.com/xnvi/learn-jpeg-encode
使用devcpp编译代码,有字节对齐的问题,我修改后代码如下,
代码思路是从bmp文件读取RGB数据,然后进行jpeg压缩,对应的讲解和测试图片在作者的仓库都存在,clone仓库测试即可。
我阅读代码后,发现作者使用了YUV444的格式进行jpeg压缩,那压缩效果肯定不行,所以我又改成YUV420的数据格式,然后进行jpeg压缩。对比压缩后的文件大小,很明显。
test.bmp是原始的RGB的图片,
output_master.jpg是原作者的master分支代码的结果,使用YUV444进行jpeg编码。
output_YUV420.jpg是我修改后使用YUV420进行jpeg压缩的结果。
output_win11.jpg是使用win11自带的画图软件生成的jpeg图片,显示压缩率更高了。
使用YUV420进行jpeg压缩更符合我们学习jpeg压缩原理的目的,所以如下是我修改后使用YUV420压缩图片的代码。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>#define DCT_SIZE 8
#define HUFF_TREE_DC_LEN 16
#define HUFF_TREE_AC_LEN 256
#define INPUT_BMP_FILE "test.bmp"
#define OUTPUT_JPEG_FILE "output_YUV420.jpeg"FILE *fp_jpeg = NULL;#pragma pack(push) //保存对齐状态
#pragma pack(1)//设定为4字节对齐
// 位图文件头typedef struct BMP_FILE_HEAD
{uint16_t bfType; // 文件类型,必须是0x424D,也就是字符BMuint32_t bfSize; // 文件大小,包含头uint16_t bfReserved1; // 保留字uint16_t bfReserved2; // 保留字uint32_t bfOffBits; // 文件头到实际的图像数据的偏移字节数
}BMP_FILE_HEAD;// 位图信息头
typedef struct BMP_INFO_HEAD
{uint32_t biSize; // 这个结构体的长度,为40字节int32_t biWidth; // 图像的宽度int32_t biHeight; // 图像的长度uint16_t biPlanes; // 必须是1uint16_t biBitCount; // 表示颜色时要用到的位数,常用的值为 1(黑白二色图),4(16 色图),8(256 色),24(真彩色图)(新的.bmp 格式支持 32 位色,这里不做讨论)uint32_t biCompression; // 指定位图是否压缩,有效的值为 BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定义好的常量,暂时只考虑BI_RGB不压缩的情况)uint32_t biSizeImage; // 指定实际的位图数据占用的字节数int32_t biXPelsPerMeter; // 指定目标设备的水平分辨率int32_t biYPelsPerMeter; // 指定目标设备的垂直分辨率int32_t biClrUsed; // 指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为 2 的 biBitCount 次方int32_t biClrImportant; // 指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
}BMP_INFO_HEAD;
#pragma pack(pop)//恢复对齐状态const uint8_t default_luma_table[] =
{16, 11, 10, 16, 24, 40, 51, 61,12, 12, 14, 19, 26, 58, 60, 55,14, 13, 16, 24, 40, 57, 69, 56,14, 17, 22, 29, 51, 87, 80, 62,18, 22, 37, 56, 68, 109, 103, 77,24, 35, 55, 64, 81, 104, 113, 92,49, 64, 78, 87, 103, 121, 120, 101,72, 92, 95, 98, 112, 100, 103, 99,
};const uint8_t default_chroma_table[] =
{17, 18, 24, 47, 99, 99, 99, 99,18, 21, 26, 66, 99, 99, 99, 99,24, 26, 56, 99, 99, 99, 99, 99,47, 66, 99, 99, 99, 99, 99, 99,99, 99, 99, 99, 99, 99, 99, 99,99, 99, 99, 99, 99, 99, 99, 99,99, 99, 99, 99, 99, 99, 99, 99,99, 99, 99, 99, 99, 99, 99, 99,
};const uint8_t zigzag_table[] =
{0, 1, 5, 6, 14, 15, 27, 28,2, 4, 7, 13, 16, 26, 29, 42,3, 8, 12, 17, 25, 30, 41, 43,9, 11, 18, 24, 31, 40, 44, 53,10, 19, 23, 32, 39, 45, 52, 54,20, 22, 33, 38, 46, 51, 55, 60,21, 34, 37, 47, 50, 56, 59, 61,35, 36, 48, 49, 57, 58, 62, 63,
};const uint8_t default_ht_luma_dc_len[16] =
{0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0
};const uint8_t default_ht_luma_dc[12] =
{0,1,2,3,4,5,6,7,8,9,10,11
};const uint8_t default_ht_chroma_dc_len[16] =
{0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0
};const uint8_t default_ht_chroma_dc[12] =
{0,1,2,3,4,5,6,7,8,9,10,11
};const uint8_t default_ht_luma_ac_len[16] =
{0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d
};const uint8_t default_ht_luma_ac[162] =
{0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,0xF9, 0xFA
};const uint8_t default_ht_chroma_ac_len[16] =
{0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77
};const uint8_t default_ht_chroma_ac[162] =
{0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,0xF9, 0xFA
};typedef struct
{uint8_t raw;uint8_t code_length;uint16_t code_word;
} coefficients;#pragma pack(1)//设定为4字节对齐
// 参考JFIF标准 ITU-T T.871
typedef struct
{uint16_t len;uint8_t identifier[5];uint16_t version;uint8_t units;uint16_t Hdensity;uint16_t Vdensity;uint8_t HthumbnailA;uint8_t VthumbnailA;
} jfif_header;typedef struct
{uint8_t id; // 颜色分量IDuint8_t sampling_factors; // 水平/垂直采样因子,高4位水平采样因子,低4位垂直采样因子uint8_t qt_table_id; // 量化表的ID
} color_component;typedef struct
{uint8_t precision; // 图像的采样精度,常用8位uint16_t height; // 图像高度uint16_t width; // 图像宽度uint8_t component_num; // 角色分量数,YUV一共3个分量color_component component[3]; // 颜色分量
} frame_header;#pragma pack()//设定为4字节对齐int32_t read_bmp_to_rgb888(char *path, uint8_t **data, int32_t *width, int32_t *height);
void rgb888_dump_bmp(char *path, uint8_t *data, int32_t width, int32_t height);
void rgb888_dump_ppm(char *path, uint8_t *data, int32_t width, int32_t height);int32_t read_block(const uint8_t *rgb, uint8_t *block, int32_t w, int32_t h, int32_t x, int32_t y);
// int32_t block_rgb_to_yuv444(const uint8_t *block, uint8_t *y, uint8_t *u, uint8_t *v);
// int32_t block_dct(const uint8_t *in, double *out);
int32_t block_rgb_to_yuv444(const uint8_t *block, int8_t *y, int8_t *u, int8_t *v);
int32_t block_dct(const int8_t *in, double *out);
int32_t block_quantization(const uint8_t *table, const double *in, int32_t *out);
int32_t block_zigzag(int32_t *in, int32_t *out);
int32_t block_encode(int32_t *in, int32_t *last_dc, coefficients *dc, coefficients *ac);
int32_t huffman_encode();
int32_t make_qt_table(int32_t qt, const uint8_t *in, uint8_t *out);
int32_t make_huffman_tree(uint8_t *code_length, uint8_t *raw_val, int32_t out_len, coefficients *out);double ck(int32_t k);
int32_t get_val_bit_and_code(int32_t value, uint16_t *bit_num, uint16_t *code);uint16_t sw16(uint16_t dat);
int32_t jpeg_write_file(uint8_t *data, int32_t len);
int32_t jpeg_write_bits(uint32_t data, int32_t len, int32_t flush);
int32_t jpeg_write_u8(uint8_t data);
int32_t jpeg_write_u16(uint16_t data);
int32_t jpeg_write_u32(uint32_t data);int32_t jpeg_write_file(uint8_t *data, int32_t len)
{return fwrite(data, 1, len, fp_jpeg);
}int32_t jpeg_write_bits(uint32_t data, int32_t len, int32_t flush)
{static uint32_t bit_ptr = 0; // 与平时阅读习惯相反,最高位计为0,最低位计为31static uint32_t bitbuf = 0x00000000;uint8_t w = 0x00;bitbuf |= data << (32 - bit_ptr - len);bit_ptr += len;while (bit_ptr >= 8){w = (uint8_t)((bitbuf & 0xFF000000) >> 24);jpeg_write_u8(w);if (w == 0xFF){jpeg_write_u8(0x00);}bitbuf <<= 8;bit_ptr -= 8;}if (flush){w = (uint8_t)((bitbuf & 0xFF000000) >> 24);jpeg_write_u8(w);}return 0;
}int32_t jpeg_write_u8(uint8_t data)
{uint8_t wd = data;return fwrite(&wd, 1, sizeof(uint8_t), fp_jpeg);
}int32_t jpeg_write_u16(uint16_t data)
{uint16_t wd = data;return fwrite(&wd, 1, sizeof(uint16_t), fp_jpeg);
}int32_t jpeg_write_u32(uint32_t data)
{uint32_t wd = data;return fwrite(&wd, 1, sizeof(uint32_t), fp_jpeg);
}uint16_t sw16(uint16_t dat)
{uint16_t lo = (dat & 0x00ff);uint16_t hi = ((dat & 0xff00) >> 8);return (uint16_t)((lo << 8) | hi);
}double ck(int32_t k)
{if (k == 0){return sqrt(1.0 / DCT_SIZE);}else{return sqrt(2.0 / DCT_SIZE);}
}// int32_t block_dct(const uint8_t *in, double *out)
int32_t block_dct(const int8_t *in, double *out)
{int32_t i, j, n, m;double sum = 0.0;for (m = 0; m < DCT_SIZE; m++){for (n = 0; n < DCT_SIZE; n++){for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){// sum += in[i][j] * cos((2 * j + 1) * n * M_PI / (2 * DCT_SIZE)) * cos((2 * i + 1) * m * M_PI / (2 * DCT_SIZE));sum += in[i * DCT_SIZE + j] * cos((2 * j + 1) * n * M_PI / (2 * DCT_SIZE)) * cos((2 * i + 1) * m * M_PI / (2 * DCT_SIZE));}}// out[m][n] = sum * ck(m) * ck(n);out[m * DCT_SIZE + n] = sum * ck(m) * ck(n);sum = 0.0;}}return 0;
}int32_t read_block(const uint8_t *rgb, uint8_t *block, int32_t w, int32_t h, int32_t x, int32_t y)
{// 长宽不足8可以填充,也可以复制边缘像素int32_t dx, dy;int32_t rgb_offset, block_offset;for (dy = 0; dy < DCT_SIZE * 2; dy++){for (dx = 0; dx < DCT_SIZE * 2; dx++){rgb_offset = (((y + dy) * w) + (x + dx)) * 3;block_offset = (dy * DCT_SIZE * 2 + dx) * 3;// printf("b %d \n", block_offset);if (x + dx >= w){block[block_offset + 0] = block[block_offset - 3 + 0];block[block_offset + 1] = block[block_offset - 3 + 1];block[block_offset + 2] = block[block_offset - 3 + 2];continue;}if (y + dy >= h){block[block_offset + 0] = block[block_offset - 3 * DCT_SIZE + 0];block[block_offset + 1] = block[block_offset - 3 * DCT_SIZE + 1];block[block_offset + 2] = block[block_offset - 3 * DCT_SIZE + 2];continue;}block[block_offset + 0] = rgb[rgb_offset + 0];block[block_offset + 1] = rgb[rgb_offset + 1];block[block_offset + 2] = rgb[rgb_offset + 2];}}return 0;
}// YUV有BT601、BT709、BT2020
// jpeg 使用的yuv公式 https://en.wikipedia.org/wiki/YCbCr
// 参考JFIF标准 ITU-T T.871
int32_t block_rgb_to_yuv444(const uint8_t *block, int8_t *y, int8_t *u, int8_t *v)
{uint8_t r, g, b;int32_t dx, dy, index;int ori_x, ori_y; //像素点在入参的block中的位置int32_t rgb_offset, block_offset, YU_offset;int mcu_offset[] = {0, 64*1, 64*2, 64*3}; int y_offset[] = {0, 0, 8, 8};int x_offset[] = {0, 8, 0, 8};int new_Y_offset = 0, new_uv_offset = 0; //在输出的数组中,YUV的offsetfloat luma, cb, cr;for(index = 0; index < 4; index++){for (dy = 0; dy < DCT_SIZE; dy++){for (dx = 0; dx < DCT_SIZE; dx++){ori_y = dy + y_offset[index];ori_x = dx + x_offset[index];block_offset = (ori_y * DCT_SIZE * 2 + ori_x);new_Y_offset = (dy * DCT_SIZE + dx);new_uv_offset = ori_y / 2 * DCT_SIZE + ori_x / 2;r = block[block_offset * 3 + 0];g = block[block_offset * 3 + 1];b = block[block_offset * 3 + 2];// luma = 0.299f * r + 0.587f * g + 0.114f * b;// cb = -0.1687f * r - 0.3313f * g + 0.5f * b + 128.0f;// cr = 0.5f * r - 0.4187f * g - 0.0813f * b + 128.0f;// y[block_offset] = (uint8_t)luma;// u[block_offset] = (uint8_t)cb;// v[block_offset] = (uint8_t)cr;luma = 0.299f * r + 0.587f * g + 0.114f * b - 128;cb = -0.1687f * r - 0.3313f * g + 0.5f * b;cr = 0.5f * r - 0.4187f * g - 0.0813f * b;y[new_Y_offset + mcu_offset[index]] = (int8_t)round(luma);u[new_uv_offset] = (int8_t)round(cb);v[new_uv_offset] = (int8_t)round(cr);}}}return 0;
}int32_t make_qt_table(int32_t qt, const uint8_t *in, uint8_t *out)
{int32_t dx, dy;int32_t offset;float alpha;float tmp;if (qt < 50){alpha = 50.0f / qt;}else{alpha = 2.0f - qt / 50.0f;}for (dy = 0; dy < DCT_SIZE; dy++){for (dx = 0; dx < DCT_SIZE; dx++){offset = dy * DCT_SIZE + dx;tmp = in[offset] * alpha;tmp = tmp < 1 ? 1 : tmp;tmp = tmp > 255 ? 255 : tmp;out[offset] = (uint8_t)tmp;}}return 0;
}int32_t make_huffman_tree(uint8_t *code_length, uint8_t *raw_val, int32_t out_len, coefficients *out)
{int32_t i, j;uint32_t code = 0;int32_t code_bits = 1;int32_t count = 0;for(i = 0; i < 16; i++){for (j = 0; j < code_length[i]; j++){// 生成// out[count].code_word = code;// out[count].code_length = code_bits;// out[count].raw = raw_val[count];// 生成并排序out[raw_val[count]].raw = raw_val[count];out[raw_val[count]].code_word = code;out[raw_val[count]].code_length = code_bits;// printf("num %d, raw %#02x, code %#02x, len %d \n", count, raw_val[count], code, code_bits);count += 1;code += 1;}code_bits += 1;code <<= 1;}// for (i = 0; i < out_len; i++)// {// printf("num %d, raw %#02x, code %#02x, len %d \n", i, out[i].raw, out[i].code_word, out[i].code_length);// }return 0;
}int32_t block_quantization(const uint8_t *table, const double *in, int32_t *out)
{int32_t dx, dy;int32_t offset;for (dy = 0; dy < DCT_SIZE; dy++){for (dx = 0; dx < DCT_SIZE; dx++){offset = dy * DCT_SIZE + dx;out[offset] = round(in[offset] / table[offset]);}}return 0;
}int32_t block_zigzag(int32_t *in, int32_t *out)
{int32_t i;for (i = 0; i < DCT_SIZE * DCT_SIZE; i++){// out[i] = in[zigzag_table[i]];out[zigzag_table[i]] = in[i];}
}int32_t block_encode(int32_t *in, int32_t *last_dc, coefficients *dc, coefficients *ac)
{int32_t i;int32_t dc_delta = 0;uint16_t bit_num, code;int32_t last_not_zero_cnt = 0;int32_t zero_cnt = 0;uint8_t run_size = 0;// 直流dc_delta = in[0] - *last_dc;*last_dc = in[0];if (dc_delta != 0){get_val_bit_and_code(dc_delta, &bit_num, &code);jpeg_write_bits(dc[bit_num].code_word, dc[bit_num].code_length, 0);jpeg_write_bits(code, bit_num, 0);}else{jpeg_write_bits(dc[0].code_word, dc[0].code_length, 0);}// 交流for (i = 63; i > 0; i--){if (in[i] != 0){last_not_zero_cnt = i;break;}}for (i = 1; i <= last_not_zero_cnt; i++){zero_cnt = 0;while (in[i] == 0){zero_cnt++;i++;if (zero_cnt == 16){jpeg_write_bits(ac[0xF0].code_word, ac[0xF0].code_length, 0);zero_cnt = 0;}}get_val_bit_and_code(in[i], &bit_num, &code);run_size = zero_cnt << 4 | bit_num;jpeg_write_bits(ac[run_size].code_word, ac[run_size].code_length, 0);jpeg_write_bits(code, bit_num, 0);}if (last_not_zero_cnt != 63){jpeg_write_bits(ac[0].code_word, ac[0].code_length, 0);}
}int32_t get_val_bit_and_code(int32_t value, uint16_t *bit_num, uint16_t *code)
{// 计算方法:正数不变,负数则计算它绝对值的反码// 位数为这个数的绝对值的位数// 优化方法,利用补码特性int32_t abs_val = value;if ( value < 0 ){abs_val = -abs_val;value -= 1;}*bit_num = 1;while (abs_val >>= 1){*bit_num += 1;}*code = (uint16_t)(value & ((1 << *bit_num) - 1));#if 0// 原始方法int32_t tmp_val = value;int32_t abs_val = value;if (value < 0){abs_val = -value;tmp_val = ~abs_val;}*bit_num = 1;while (abs_val >>= 1){*bit_num += 1;}*code = (uint16_t)(tmp_val & ((1 << *bit_num) - 1));
#endifreturn 0;
}int32_t read_bmp_to_rgb888(char *path, uint8_t **data, int32_t *width, int32_t *height)
{FILE *img_fp = NULL;BMP_FILE_HEAD BFH;BMP_INFO_HEAD BIH;int32_t file_size = 0;int32_t ret = 0;int32_t i = 0, j = 0;int32_t h = 0, v = 0;uint32_t offset = 0;uint32_t line_size = 0;uint8_t *bmp_data = NULL;uint8_t *rgb888_data = NULL;img_fp = fopen(path, "rb");if (img_fp == NULL){printf("can not open %s\n", path);*data = NULL;*width = 0;*height = 0;return 1;}fseek(img_fp, 0, SEEK_END);file_size = ftell(img_fp);fseek(img_fp, 0, SEEK_SET);if (file_size < 54){printf("file %s size error\n", path);*data = NULL;*width = 0;*height = 0;goto end;}fseek(img_fp, 0, SEEK_SET);fread(&BFH, sizeof(BFH), 1, img_fp); // 读取BMP文件头fread(&BIH, sizeof(BIH), 1, img_fp); // 读取BMP信息头,40字节,直接用结构体读printf("sizeof(BFH) = %d\n", sizeof(BFH));printf("sizeof(BIH) = %d\n", sizeof(BIH));printf("\nBMP file head\n");printf("bfType = %x\n", BFH.bfType);printf("bfSize = %d\n", BFH.bfSize);printf("bfReserved1 = %d\n", BFH.bfReserved1);printf("bfReserved2 = %d\n", BFH.bfReserved2);printf("bfOffBits = %d\n", BFH.bfOffBits);printf("\nBMP info head\n");printf("biSize = %d\n", BIH.biSize);printf("biWidth = %d\n", BIH.biWidth);printf("biHeight = %d\n", BIH.biHeight);printf("biPlanes = %d\n", BIH.biPlanes);printf("biBitCount = %d\n", BIH.biBitCount);printf("biCompression = %d\n", BIH.biCompression);printf("biSizeImage = %d\n", BIH.biSizeImage);printf("biXPelsPerMeter = %d\n", BIH.biXPelsPerMeter);printf("biYPelsPerMeter = %d\n", BIH.biYPelsPerMeter);printf("biClrUsed = %d\n", BIH.biClrUsed);printf("biClrImportant = %d\n", BIH.biClrImportant);// if((BFH.bfType != 0x424D) || (BIH.biClrImportant != 0))if((BFH.bfType != 0x4D42)){printf("\nnot bmp file\n");goto end;}if (BIH.biBitCount != 24 || ((BIH.biClrImportant != 0) && (BIH.biClrImportant != 16777216))){printf("\nnot 24 bit bmp file\n");goto end;}bmp_data = (unsigned char *)malloc(BIH.biSizeImage);if (bmp_data == NULL){printf("malloc bmp_buf error\n");*data = NULL;*width = 0;*height = 0;goto end;}fseek(img_fp, BFH.bfOffBits, SEEK_SET);// ret = fread(bmp_data, BIH.biSizeImage, 1, img_fp);// if (ret != 1) {ret = fread(bmp_data, 1, BIH.biSizeImage, img_fp);if (ret != BIH.biSizeImage){printf("read bmp file error\n");*data = NULL;*width = 0;*height = 0;goto end;}fclose(img_fp);rgb888_data = (unsigned char *)malloc(BIH.biWidth * BIH.biHeight * 3);if (rgb888_data == NULL){printf("malloc rgb_buf error\n");*data = NULL;*width = 0;*height = 0;return 1;}h = BIH.biWidth;v = BIH.biHeight;line_size = ((h * 3 + 3) >> 2) << 2; // 行4字节对齐for (i = 0; i < v; i++){for (j = 0; j < h; j++){offset = (v - i - 1) * line_size + j * 3;rgb888_data[i * h * 3 + j * 3] = bmp_data[offset + 2];rgb888_data[i * h * 3 + j * 3 + 1] = bmp_data[offset + 1];rgb888_data[i * h * 3 + j * 3 + 2] = bmp_data[offset];}}*width = BIH.biWidth;*height = BIH.biHeight;*data = rgb888_data;free(bmp_data);end:fclose(img_fp);return 1;
}void rgb888_dump_bmp(char *path, uint8_t *data, int32_t width, int32_t height)
{BMP_FILE_HEAD bfh;BMP_INFO_HEAD bih;FILE *fp;uint8_t line_buf[2048 * 3];int32_t line_size;int32_t i, j;if (width > 2048){printf("width larger than 2048\n");return;}fp = fopen(path, "wb");if(fp == NULL){printf("dump file %s error \n", path);return;}memset(&bfh, 0, sizeof(BMP_FILE_HEAD));memset(&bih, 0, sizeof(BMP_INFO_HEAD));memset(line_buf, 0, sizeof(line_buf));line_size = ((width * 3 + 3) >> 2) << 2;bfh.bfType = 0x4D42;bfh.bfSize = 54 + line_size * height;bfh.bfOffBits = 54;bih.biSize = 40;bih.biWidth = width;bih.biHeight = height;bih.biPlanes = 1;bih.biBitCount = 24;bih.biCompression = 0;bih.biSizeImage = line_size * height;bih.biXPelsPerMeter = 4724;bih.biYPelsPerMeter = 4724;bih.biClrUsed = 0;bih.biClrImportant = 0;fwrite(&bfh, sizeof(BMP_FILE_HEAD), 1, fp);fwrite(&bih, sizeof(BMP_INFO_HEAD), 1, fp);for(i = 0; i < height; i++){for (j = 0; j < width; j++){line_buf[j * 3] = data[(height - i - 1) * width * 3 + j * 3 + 2];line_buf[j * 3 + 1] = data[(height - i - 1) * width * 3 + j * 3 + 1];line_buf[j * 3 + 2] = data[(height - i - 1) * width * 3 + j * 3];}fwrite(line_buf, 1, line_size, fp);}fclose(fp);
}void rgb888_dump_ppm(char *path, uint8_t *data, int32_t width, int32_t height)
{FILE *fp;char ppm_head[128];int32_t ppm_head_len;fp = fopen(path, "wb");if(fp == NULL){printf("dump file %s error \n", path);return;}memset(ppm_head, 0, sizeof(ppm_head));sprintf(ppm_head, "P6 %d %d 255 ", width, height);ppm_head_len = strlen(ppm_head);fwrite(ppm_head, 1, ppm_head_len, fp);fwrite(data, 1, height * width * 3, fp);fclose(fp);
}void print_block_double(double *in)
{int32_t i, j;printf("\n");for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){printf("%8.3f ", in[i * DCT_SIZE + j]);}printf("\n");}printf("\n");
}void print_block_u8(uint8_t *in)
{int32_t i, j;printf("\n");for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){printf("%3d ", in[i * DCT_SIZE + j]);}printf("\n");}printf("\n");
}void print_block_i8(int8_t *in)
{int32_t i, j;printf("\n");for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){printf("%3d ", in[i * DCT_SIZE + j]);}printf("\n");}printf("\n");
}void print_block_i32(int32_t *in)
{int32_t i, j;printf("\n");for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){printf("%3d ", in[i * DCT_SIZE + j]);}printf("\n");}printf("\n");
}void print_block_u8s3(uint8_t *in)
{int32_t i, j;printf("\n");for (i = 0; i < DCT_SIZE; i++){for (j = 0; j < DCT_SIZE; j++){printf("%3d ", in[(i * DCT_SIZE + j) * 3 + 0]);printf("%3d ", in[(i * DCT_SIZE + j) * 3 + 1]);printf("%3d ", in[(i * DCT_SIZE + j) * 3 + 2]);}printf("\n");}printf("\n");
}int main(int argc, const char **argv)
{int32_t w = 0, h = 0;int32_t x = 0, y = 0;int32_t last_y = 0, last_u = 0, last_v = 0;int32_t qt = 100;int32_t i = 0;uint8_t *rgb_data;uint8_t rgb_block[DCT_SIZE * DCT_SIZE * 3 * 4]; // 16x16的MCU// uint8_t y_block[DCT_SIZE * DCT_SIZE];// uint8_t u_block[DCT_SIZE * DCT_SIZE];// uint8_t v_block[DCT_SIZE * DCT_SIZE];int8_t y_block[DCT_SIZE * DCT_SIZE * 4];int8_t u_block[DCT_SIZE * DCT_SIZE];int8_t v_block[DCT_SIZE * DCT_SIZE];double dct_block[DCT_SIZE * DCT_SIZE];int32_t qt_block[DCT_SIZE * DCT_SIZE];int32_t zigzag_block[DCT_SIZE * DCT_SIZE];uint8_t luma_table[DCT_SIZE * DCT_SIZE];uint8_t chroma_table[DCT_SIZE * DCT_SIZE];uint8_t qt_table_tmp[DCT_SIZE * DCT_SIZE];jfif_header jfif;frame_header frame;coefficients huff_tree_luma_dc[HUFF_TREE_DC_LEN];coefficients huff_tree_chroma_dc[HUFF_TREE_DC_LEN];coefficients huff_tree_luma_ac[HUFF_TREE_AC_LEN];coefficients huff_tree_chroma_ac[HUFF_TREE_AC_LEN];memset(huff_tree_luma_dc, 0, sizeof(huff_tree_luma_dc));memset(huff_tree_chroma_dc, 0, sizeof(huff_tree_chroma_dc));memset(huff_tree_luma_ac, 0, sizeof(huff_tree_luma_ac));memset(huff_tree_chroma_ac, 0, sizeof(huff_tree_chroma_ac));// 生成huffman编码树make_huffman_tree((uint8_t *)default_ht_luma_dc_len, (uint8_t *)default_ht_luma_dc, HUFF_TREE_DC_LEN, huff_tree_luma_dc);make_huffman_tree((uint8_t *)default_ht_chroma_dc_len, (uint8_t *)default_ht_chroma_dc, HUFF_TREE_DC_LEN, huff_tree_chroma_dc);make_huffman_tree((uint8_t *)default_ht_luma_ac_len, (uint8_t *)default_ht_luma_ac, HUFF_TREE_AC_LEN, huff_tree_luma_ac);make_huffman_tree((uint8_t *)default_ht_chroma_ac_len, (uint8_t *)default_ht_chroma_ac, HUFF_TREE_AC_LEN, huff_tree_chroma_ac);// 生成量化参数表make_qt_table(qt, default_luma_table, luma_table);make_qt_table(qt, default_chroma_table, chroma_table);// print_block_u8(luma_table);// print_block_u8(chroma_table);read_bmp_to_rgb888(INPUT_BMP_FILE, &rgb_data, &w, &h);// rgb888_dump_ppm("out.ppm", rgb_data, w, h);fp_jpeg = fopen(OUTPUT_JPEG_FILE, "wb");// 写文件头jpeg_write_u16(sw16(0xFFD8));// 写JFIFjfif.len = sw16(sizeof(jfif_header));strcpy(jfif.identifier, "JFIF");jfif.version = sw16(0x0102);jfif.units = 0x01; // 0x00-unspecified 0x01-dots_per_inch 0x02-dots_per_cmjfif.Hdensity = sw16(96);jfif.Vdensity = sw16(96);jfif.HthumbnailA = sw16(0);jfif.VthumbnailA = sw16(0);jpeg_write_u16(sw16(0xFFE0));printf("sizeof(jfif_header) = %d\n", sizeof(jfif_header));jpeg_write_file((uint8_t *)&jfif, sizeof(jfif_header));// 写量化表// 亮度量化表jpeg_write_u16(sw16(0xFFDB));jpeg_write_u16(sw16(2+1+64));jpeg_write_u8(0x00); // AAAABBBB(bin), AAAA = 精度(0:8位,1:16位) BBBB = 量化表ID(最多4个)for (i = 0; i < DCT_SIZE * DCT_SIZE; i++){qt_table_tmp[zigzag_table[i]] = luma_table[i];}jpeg_write_file(qt_table_tmp, sizeof(qt_table_tmp));// 色度量化表jpeg_write_u16(sw16(0xFFDB));jpeg_write_u16(sw16(2+1+64));jpeg_write_u8(0x01); // AAAABBBB(bin), AAAA = 精度(0:8位,1:16位) BBBB = 量化表ID(最多4个)for (i = 0; i < DCT_SIZE * DCT_SIZE; i++){qt_table_tmp[zigzag_table[i]] = chroma_table[i];}jpeg_write_file(qt_table_tmp, sizeof(qt_table_tmp));// 写图像开始标记jpeg_write_u16(sw16(0xFFC0));jpeg_write_u16(sw16(2+sizeof(frame_header)));frame.precision = 8;frame.height = sw16(h);frame.width = sw16(w);frame.component_num = 3;// 这里采用最简单的YUV444格式,所以这三个分量的采样因子都是0x11// 假设采用YUV420格式,这三个分量的采样因子分别是0x22,0x11,0x11// 亮度分量frame.component[0].id = 1;frame.component[0].sampling_factors = 0x22;frame.component[0].qt_table_id = 0;// 色度分量frame.component[1].id = 2;frame.component[1].sampling_factors = 0x11;frame.component[1].qt_table_id = 1;// 色度分量frame.component[2].id = 3;frame.component[2].sampling_factors = 0x11;frame.component[2].qt_table_id = 1;jpeg_write_file((uint8_t *)&frame, sizeof(frame_header));// 写huffman表,AC、DC表的ID分别从0开始累加// 亮度DC表jpeg_write_u16(sw16(0xFFC4));jpeg_write_u16(sw16(2+1+16+sizeof(default_ht_luma_dc)));jpeg_write_u8(0x00); // AAAABBBB(bin), AAAA = 类型(0:DC,1:AC) BBBB = 表IDjpeg_write_file((uint8_t *)default_ht_luma_dc_len, sizeof(default_ht_luma_dc_len));jpeg_write_file((uint8_t *)default_ht_luma_dc, sizeof(default_ht_luma_dc));// 亮度AC表jpeg_write_u16(sw16(0xFFC4));jpeg_write_u16(sw16(2+1+16+sizeof(default_ht_luma_ac)));jpeg_write_u8(0x10); // AAAABBBB(bin), AAAA = 类型(0:DC,1:AC) BBBB = 表IDjpeg_write_file((uint8_t *)default_ht_luma_ac_len, sizeof(default_ht_luma_ac_len));jpeg_write_file((uint8_t *)default_ht_luma_ac, sizeof(default_ht_luma_ac));// 色度DC表jpeg_write_u16(sw16(0xFFC4));jpeg_write_u16(sw16(2+1+16+sizeof(default_ht_chroma_dc)));jpeg_write_u8(0x01); // AAAABBBB(bin), AAAA = 类型(0:DC,1:AC) BBBB = 表IDjpeg_write_file((uint8_t *)default_ht_chroma_dc_len, sizeof(default_ht_chroma_dc_len));jpeg_write_file((uint8_t *)default_ht_chroma_dc, sizeof(default_ht_chroma_dc));// 色度AC表jpeg_write_u16(sw16(0xFFC4));jpeg_write_u16(sw16(2+1+16+sizeof(default_ht_chroma_ac)));jpeg_write_u8(0x11); // AAAABBBB(bin), AAAA = 类型(0:DC,1:AC) BBBB = 表IDjpeg_write_file((uint8_t *)default_ht_chroma_ac_len, sizeof(default_ht_chroma_ac_len));jpeg_write_file((uint8_t *)default_ht_chroma_ac, sizeof(default_ht_chroma_ac));// 写扫描开始标记jpeg_write_u16(sw16(0xFFDA));jpeg_write_u16(sw16(2+1+3*2+3));jpeg_write_u8(0x03); // 颜色分量数目 1-灰度图 3-YUV 4-CMYK// 颜色分量信息,有几种颜色就要几次jpeg_write_u8(0x01); // 颜色分量IDjpeg_write_u8(0x00); // 高4位直流分量huffman表ID,低4位交流分量huffman表IDjpeg_write_u8(0x02); // 颜色分量IDjpeg_write_u8(0x11); // 高4位直流分量huffman表ID,低4位交流分量huffman表IDjpeg_write_u8(0x03); // 颜色分量IDjpeg_write_u8(0x11); // 高4位直流分量huffman表ID,低4位交流分量huffman表IDjpeg_write_u8(0x00);// 谱选择开始:1个字节,固定值0x00jpeg_write_u8(0x3F);// 谱选择结束:1个字节,固定值0x3Fjpeg_write_u8(0x00);// 谱选择:1个字节,固定值0x00// 测试用,打印某一块的数据// if (0)// {// read_block(rgb_data, rgb_block, w, h, 0, 0);// print_block_u8s3(rgb_block);// block_rgb_to_yuv444(rgb_block, y_block, u_block, v_block);// print_block_i8(y_block);// print_block_i8(u_block);// print_block_i8(v_block);// block_dct(y_block, dct_block);// block_quantization(luma_table, dct_block, qt_block);// block_zigzag(qt_block, zigzag_block);// print_block_double(dct_block);// print_block_i32(qt_block);// print_block_i32(zigzag_block);// block_dct(u_block, dct_block);// block_quantization(chroma_table, dct_block, qt_block);// block_zigzag(qt_block, zigzag_block);// print_block_u8(u_block);// print_block_double(dct_block);// print_block_i32(zigzag_block);// block_dct(v_block, dct_block);// block_quantization(chroma_table, dct_block, qt_block);// block_zigzag(qt_block, zigzag_block);// print_block_u8(v_block);// print_block_double(dct_block);// print_block_i32(zigzag_block);// }int index = 0;// 写入真正的图像数据/* 一次处理16x16的图像块,使用YUV420作为数据源,那么会存在4个8x8Y分量矩阵,1个U分量矩阵,1个V分量矩阵他们的排列顺序是左上Y,右上Y,左下Y,右下Y,U分量,Y分量*/for(y = 0; y < h; y += DCT_SIZE * 2){for(x = 0; x < w; x += DCT_SIZE * 2){// printf("\ncoordinate %d %d\n", x, y);read_block(rgb_data, rgb_block, w, h, x, y);// print_block_u8s3(rgb_block);block_rgb_to_yuv444(rgb_block, y_block, u_block, v_block);// printf("yuv block\n");// print_block_i8(y_block);// print_block_i8(u_block);// print_block_i8(v_block);// printf("y block\n");for(index = 0 ; index < 4; index++){block_dct(y_block + 64 * index, dct_block);// print_block_double(dct_block);block_quantization(luma_table, dct_block, qt_block);// print_block_i32(qt_block);block_zigzag(qt_block, zigzag_block);// print_block_i32(zigzag_block);block_encode(zigzag_block, &last_y, huff_tree_luma_dc, huff_tree_luma_ac);}// printf("u block\n");block_dct(u_block, dct_block);// print_block_double(dct_block);block_quantization(chroma_table, dct_block, qt_block);// print_block_i32(qt_block);block_zigzag(qt_block, zigzag_block);// print_block_i32(zigzag_block);block_encode(zigzag_block, &last_u, huff_tree_chroma_dc, huff_tree_chroma_ac);// printf("v block\n");block_dct(v_block, dct_block);// print_block_double(dct_block);block_quantization(chroma_table, dct_block, qt_block);// print_block_i32(qt_block);block_zigzag(qt_block, zigzag_block);// print_block_i32(zigzag_block);block_encode(zigzag_block, &last_v, huff_tree_chroma_dc, huff_tree_chroma_ac);}printf("process:y[%d]/h[%d] = %f per\n", y, h, (float)y/h);}// 清除缓存jpeg_write_bits(0, 0, 1);// 写入结束标记jpeg_write_u16(sw16(0xFFD9));free(rgb_data);fclose(fp_jpeg);return 0;
}