jpeg编码学习

正点原子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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340830.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法004:盛水最多的容器

这道题比较简单&#xff0c;使用双指针。 要求的是最大面积&#xff0c;对于一个水桶&#xff08;水杯来说&#xff09;&#xff0c;面积的算法是固定的&#xff0c;就是底乘以高。 在这个题中&#xff0c;我们把左边的位置设为left&#xff0c;右边的位置设为right&#xff…

Python语法详解module1(变量、数据类型)

目录 一、变量1. 变量的概念2. 创建变量3. 变量的修改4. 变量的命名 二、数据类型1. Python中的数据类型2. 整型&#xff08;int&#xff09;3. 浮点型&#xff08;float&#xff09;4. 布尔型&#xff08;bool&#xff09;5. 字符串&#xff08;str&#xff09;6.复数&#xf…

企业微信接入系列-上传临时素材

企业微信接入系列-上传临时素材 文档介绍上传临时素材写在最后 文档介绍 创建企业群发的文档地址&#xff1a;https://developer.work.weixin.qq.com/document/path/92135&#xff0c;在创建企业群发消息或者群发群消息接口中涉及到上传临时素材的操作&#xff0c;具体文档地址…

OJ题目【栈和队列】

题目导入 栈&#xff1a; 题目一&#xff1a;有效的括号题目二&#xff1a;用栈实现队列 队列 题目&#xff1a;实现循环队列 栈 题目一 有效的括号 题目要求 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘…

信号稳定,性能卓越!德思特礁鲨系列MiMo天线正式发布!

作者介绍 礁鲨系列天线&#xff0c;以其独特的外观设计和强大的性能&#xff0c;成为德思特Panorama智能天线家族的最新成员。这款天线不仅稳定提供5G、WIFI和GNSS信号&#xff0c;更能在各类复杂环境中展现出卓越的性能。它的设计灵感来源于海洋中的礁鲨&#xff0c;象征着力量…

Julia编程11:变量作用域 Scope of Variables

There are two main types of scopes in Julia, global* scope* and local* scope*. Julia有全局变量作用域和局部变量作用域&#xff0c;函数或者一些结构体、循环体如for等是否内部是局部环境可以参照下表。 ConstructScope typeAllowed withinmodule, baremoduleglobalglo…

边缘计算网关:企业数字化转型的重要支撑-天拓四方

在数字化浪潮席卷全球的今天&#xff0c;企业对于数据处理和传输的需求日益增强。然而&#xff0c;传统的数据处理模式往往依赖于中心化的数据中心&#xff0c;这种方式在处理大量数据时存在延迟高、成本高、安全性差等问题。数据量的激增和实时性要求的提高&#xff0c;使得传…

视频汇聚EasyCVR平台视图库GA/T 1400协议与GB/T 28181协议的区别

在公安和公共安全领域&#xff0c;视频图像信息的应用日益广泛&#xff0c;尤其是在监控、安防和应急指挥等方面。为了实现视频信息的有效传输、接收和处理&#xff0c;GA/T 1400和GB/T 28181这两个协议被广泛应用。虽然两者都服务于视频信息处理的目的&#xff0c;但它们在实际…

智能合约引领:探索Web3的商业革新之路

随着区块链技术的迅速发展&#xff0c;智能合约作为其重要应用之一&#xff0c;正在逐步改变着商业世界的格局。Web3作为下一代互联网的代表&#xff0c;正引领着智能合约在商业领域的广泛应用和创新。本文将深入探讨智能合约在Web3中的作用&#xff0c;以及智能合约如何引领着…

Vue总结

介绍 Vue 是一套前端框架&#xff0c;免除原生IavaScript中的DOM操作&#xff0c;简化书写。基于MVVM(Model-View-ViewModel)思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上。框架:是一个半成品软件&#xff0c;是一套可重用的、通用的、软件基础代…

免费,Scratch蓝桥杯比赛历年真题--第15届蓝桥杯STEMA真题-2024年3月份(含答案解析和代码)

第15届蓝桥杯STEMA真题-2024年3月份 一、单选题 答案&#xff1a;D 解析&#xff1a;y坐标正值表示上&#xff0c;负值表示下&#xff0c;故答案为D。 答案&#xff1a;C 解析&#xff1a;18<25为真&#xff0c;或关系表示一真即为真&#xff0c;故答案为C。 答案&#xff…

Excel 将分组头信息填入组内明细行

Excel由多个纵向的分组表组成&#xff0c;组之间由空白行隔开&#xff0c;每组第1、2行的第2格是分组表头&#xff0c;第3行是列头&#xff0c;第1列和第6列数据是空白的&#xff1a; ABCDEF1ATLANTIC SPIRIT2Looe3VesselSpeciesSizeKgDateLocation4POLLACK22.523/04/20245POL…

音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析

这一节&#xff0c;我们学习常用的音频的格式 AAC&#xff0c;重点是掌握 AAC的传输格式 ADTS 头部的信息&#xff0c;目的是 &#xff1a; 当音频数据有问题的时候&#xff0c;如果是AAC的编码&#xff0c;在分析 头部信息的时候能够根据头部信息 判断问题是否出现在 头部。 A…

京东笔试-校招

2022京东数据分析笔试&#xff08;0821&#xff09; 一、选择题&#xff1a;30道 1.解决数据不平衡的方法主要有&#xff08;pca&#xff1f;&#xff09; 2.等频&#xff08;等宽&#xff09;划分问题 3.参数估计&#xff1a;矩估计与极大似然估计的用法&#xff0c;问题分…

python之生成xmind

今天为啥要说这个呢&#xff0c;因为前几天做接口测试&#xff0c;还要写测试用例&#xff0c;我觉得麻烦&#xff0c;所以我就用了python里面xmind的插件。自动生成了测试用例&#xff0c;数据来源是json。 &#x1f366; 第一步安装 pip install xmind &#x1f366; 第二…

测绘GIS和遥感领域比较好的公众号有哪些

测绘GIS和遥感领域&#xff0c;微信公众号作为信息传播和知识分享的重要渠道&#xff0c;为从业者提供了一个快速获取行业动态、技术进展和职业发展机会的平台。分享一些在测绘GIS和遥感领域表现突出的公众号推荐&#xff1a; 1. 慧天地&#xff1a;慧天地是一个知名的测绘公众…

纯js仿淘宝多图片封面图插件模板/带视频,带放大镜,带前后端完整代码PHP

功能预览,他依赖jq插件,请自已引入 类似这样 <script type"text/javascript" src"/Application/Admin/Static/js/jquery-2.0.3.min.js"></script>一,前端模板代码 <!--多图功能--><style> charset "utf-8"; .wrap_imgs…

【设计模式深度剖析】【1】【行为型】【模板方法模式】| 以烹饪过程为例加深理解

&#x1f448;️上一篇:结构型设计模式对比 文章目录 模板方法模式定义英文原话直译如何理解呢&#xff1f; 2个角色类图代码示例 应用优点缺点使用场景 示例解析&#xff1a;以烹饪过程为例类图代码示例 模板方法模式 模板方法模式&#xff08;Template Method Pattern&…

MySQL 关键特性一:插入缓冲、双写缓冲

前言 ​ 本文主要介绍 mysql 的几大特性之几&#xff0c;如&#xff1a;双写缓冲和插入缓存。 双写缓冲 基本概念 ​ 双写缓冲&#xff08;doublewrite buffer&#xff09;是MySQL/InnoDB中用于支持原子页面更新的一种机制。在传统的数据库系统中&#xff0c;为了保证数据的…

计网ppt标黄知识点整理第(4)章节——谢希仁版本、期末复习自用

路由器&#xff1a;查找转发表&#xff0c;转发分组。 IP网的意义&#xff1a;当互联网上的主机进行通信时&#xff0c;就好像在一个网络上通信一样&#xff0c;看不见互连的各具体的网络异构细节。如果在这种覆盖全球的 IP 网的上层使用 TCP 协议&#xff0c;那么就…