在loadjpeg中找到usage()提示,
命令行参数设置为 (可选模式 )<文件名 ><输出文件格式>< 输出文件名>
之后修改write_yuv即可,原函数单独输出了y,u,v三个分量
添加代码后如下
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F;
char temp[1024];
snprintf(temp, 1024, "%s.Y", filename);
F = fopen(temp, "wb");
fwrite(components[0], width, height, F);
fclose(F);
snprintf(temp, 1024, "%s.U", filename);
F = fopen(temp, "wb");
fwrite(components[1], width*height/4, 1, F);
fclose(F);
snprintf(temp, 1024, "%s.V", filename);
F = fopen(temp, "wb");
fwrite(components[2], width*height/4, 1, F);
fclose(F);
snprintf(temp, 1024, "%s.YUV", filename);
F = fopen(temp, "wb");
fwrite(components[0], width* height, 1,F);
fwrite(components[1], width * height / 4, 1, F);
fwrite(components[2], width * height / 4, 1, F);
fclose(F);
}
loadjpeg通过命令行参数读取文件信息。
进入convert_one_image.通过一系列判断是否成功打开文件,获得文件长度,是否成功读入缓存。
进入tinyjpeg_parse_header函数,判断文件开头是是否满足jpg条件后,获得SOI后数据地址,文件长度信息。
调用parse_JFIF函数,分析拆解每个文件块进行分析。
解析DQT块进入parse_DQT,通过 build_quantization_table建立量化表,只支持小于4张量化表,每个表64个数据。
解析DHT块进入parse_DHT,通过 build_huffman_table建立哈夫曼表。
解析 SOS调用parse_SOS,解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT中序号对应)。
数据块解析完成后,先调用tinyjpeg_get_size得到jpg图像的长宽。
最后调用 tinyjpeg_decode进行解码:
对每个宏块进行 Huffman 解码,得到 DCT 系数
对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr 遇到
Segment Marker RST 时,清空之前的 DC DCT 系数
设计目的是加速解码速度。
struct huffman_table
{
/* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
通过HUFFMAN_HASH_NBITS 可以直接找到符号值
* if the symbol is <0, then we need to look into the tree table
* 如果找到的符号值小于0,则需要查哈夫曼树*/
short int lookup[HUFFMAN_HASH_SIZE];
/* code size: give the number of bits of a symbol is encoded
code size是符号加密后比特数 */
unsigned char code_size[HUFFMAN_HASH_SIZE];
/* some place to store value that is not encoded in the lookup table
* FIXME: Calculate if 256 value is enough to store all values
*/
uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
//慢速表
};
struct component
{
unsigned int Hfactor;//水平采样因子
unsigned int Vfactor;//垂直采样因子
float *Q_table; /* Pointer to the quantisation table to use所用量化表 */
struct huffman_table *AC_table;//交流哈夫曼表
struct huffman_table *DC_table;//直流哈夫曼表
short int previous_DC; /* Previous DC coefficient 前一个直流的系数*/
short int DCT[64]; /* DCT coef */
#if SANITY_CHECK//debug用的
unsigned int cid;
#endif
};
该结构体储存了解码过程中所用的大部分信息,为解码过程中的数据提供了指针定位和衔接。
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];
unsigned int width, height; /* Size of the image */
unsigned int flags;
/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;
const unsigned char *stream; /* Pointer to the current stream */
unsigned int reservoir, nbits_in_reservoir;
struct component component_infos[COMPONENTS];
float Q_tables[COMPONENTS][64]; /* quantization tables */
struct huffman_table HTDC[HUFFMAN_TABLES]; /* DC huffman tables */
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */
int default_huffman_table_initialized;
int restart_interval;
int restarts_to_go; /* MCUs left in this restart interval */
int last_rst_marker_seen; /* Rst marker is incremented each time */
/* Temp space used after the IDCT to store each components */
uint8_t Y[64*4], Cr[64], Cb[64];
jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];
};
trace如其名字一样是起追踪纠错作用,只要trace为1即可使用下列语句在程序某一进程中输出到指定的文件。可以输出参数debug,也可以在步骤中经常输出trace理解整个解码流程。
#if TRACE
输出内容
#endif
直接在trace文件里输出,用上述的if 语句即可。
要求输出所有量化矩阵,则在解析量化矩阵时输出即可,调用prase_DQT时就输出一张表。
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
int qi;
float *table;
const unsigned char *dqt_block_end;
#if TRACE
fprintf(p_trace,"> DQT marker\n");
fflush(p_trace);
#endif
dqt_block_end = stream + be16_to_cpu(stream);
stream += 2; /* Skip length */
while (stream < dqt_block_end)
{
qi = *stream++;
#if SANITY_CHECK
if (qi>>4)
snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
if (qi>4)
snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif
table = priv->Q_tables[qi];
build_quantization_table(table, stream);
stream += 64;
}
#if TRACE
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 8; ++j) {
fprintf(p_trace, "%f ", table[i * 8 + j]);
}
fprintf(p_trace, "\n");
}
fprintf(p_trace,"< DQT marker\n");
fflush(p_trace);
#endif
return 0;
}
每一个宏块计算一次DC与AC值,修改代码如下
for (y=0; y < priv->height/ystride_by_mcu; y++)
{
//trace("Decoding row %d\n", y);
priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
for (x=0; x < priv->width; x+=xstride_by_mcu)
{
int count = y * (priv->width / xstride_by_mcu) + (x / xstride_by_mcu);
if (count < priv->width * priv->height / 64) {
DCYUV[count] = priv->component_infos[0].DCT[0];
ACYUV[count] = priv->component_infos[0].DCT[1];
}
decode_MCU(priv);
...
}
}
#if TRACE
fprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);
fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);
fflush(p_trace);
//
int DC_max = DCYUV[0];
int AC_max = ACYUV[0];
int DC_min = DCYUV[0];
int AC_min = ACYUV[0];
for (int i = 0; i< priv->width * priv->height / 64; i++)
{
DC_max = (DC_max < DCYUV[i]) ? DCYUV[i] : DC_max;
AC_max = (AC_max < ACYUV[i]) ? ACYUV[i] : AC_max;
DC_min = (DC_min < DCYUV[i]) ? DC_min : DCYUV[i];
AC_min = (AC_min < ACYUV[i]) ? AC_min : ACYUV[i];
printf("%d ", i);
}//求最大最小值为归一化准备
// 归一化处理
for (int i = 0; i < priv->width * priv->height / 64; i++)
{
DC_op[i] = (unsigned char)(255 * (DCYUV[i] - DC_min) / (DC_max - DC_min));
AC_op[i] = (unsigned char)(255 * (ACYUV[i] - AC_min) / (AC_max - AC_min));
}
fwrite(DC_op, 1, priv->width * priv->height / 64, DC_file);
fwrite(AC_op, 1, priv->width * priv->height / 64, AC_file);
unsigned char* uvbuf = (unsigned char*)malloc(sizeof(unsigned char)*priv->width * priv->height / 32);
for (int i = 0; i < priv->width * priv->height / 32; i++) {
uvbuf[i] = 128;
}
fwrite( uvbuf, 1, priv->width * priv->height / 32, DC_file);
fwrite(uvbuf, 1, priv->width * priv->height / 32, AC_file);
#endif