数据压缩第五次实验:JPEG文件分析

一.实验目的

掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。

二.实验内容

1.JPEG编解码原理
在这里插入图片描述
*JPEG编码的过程如上图所示。解码是编码的逆过程。

三 .JPEG 的解码流程

3.1 读取文件
3.2 解析 Segment Marker
3.2.1 解析 SOI
3.2.2 解析 APP0
检查标识“JFIF”及版本 得到一些参数
3.2.3 解析 DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是 0 —— 3)
得到量化表内容(64 个数据)
3.2.4 解析 SOF0
得到每个 sample 的比特数、长宽、颜色分量数
得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表
序号(与 DQT 中序号对应)
3.2.5 解析 DHT
得到 Huffman 表的类型(AC、DC)、序号
依据数据重建 Huffman 表
3.2.6 解析 SOS
得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT
中序号对应)
3.3 依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8
宏块的个数
3.4 对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解
码)
3.4.1 对每个宏块进行 Huffman 解码,得到 DCT 系数
3.4.2 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
3.4.3 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
3.5 解析到 EOI,解码结束
3.6 将 Y、Cb、Cr 转化为需要的色彩空间并保存。

四.代码实现

步骤一:

逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
sprintf函数用于格式化写入字符串,然后将component[]内的所有yuv内容写入总体的yuv文件当中。

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);**
}

参数设置:
在这里插入图片描述

实验效果

原图:
在这里插入图片描述
输出的yuv文件:
在这里插入图片描述

步骤二:

目标一:理解程序设计的整体框架
读入文件名以及输出格式后来到convert_one_image函数:判断是否成功打开文件,获得文件长度,是否成功读入缓存,调用tinyjpeg.c函数获取文件大小以及component。
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_decode进行解码:3.4.1 对每个宏块进行 Huffman 解码,得到 DCT 系数,对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr。遇到 Segment Marker RST 时,清空之前的 DC DCT 系数。解析到 EOI,解码结束。
最后将 Y、Cb、Cr 转化为需要的色彩空间并保存。
目标二:理解三个结构体的设计目的
struct huffman_table目的:加快解码速度,迅速找到符号值

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

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
  unsigned int cid;检查
#endif
};

struct jdec_private用于定义各种指针解码数据流

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的作用主要是定点纠错,在trace=1时通过if trace{输出内容}end if可以输出所需参数,进一步理解解码过程。

目标四:以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

目标五:输出DC图像并统计其概率分布。
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#ifndef JPEGDECODE_H #define JPEGDECODE_H #include "global.h" #include "globalextern.h" typedef unsigned char BYTE; struct ImageComponentData { double value[3]; }; class MBitReader { public: BYTE* Data; int m_currentData; int m_currentDataIndex; int m_currentBitPosition; MBitReader(BYTE* data,int currentDataIndex) { Data=data; m_currentBitPosition=8; m_currentDataIndex=currentDataIndex; m_currentData=Data[m_currentDataIndex]; } public: int ReadNextBit() { if (m_currentBitPosition-1> m_currentBitPosition) & 0x01; } }; class MJpegDecode { public: struct _JFIFAPPOInfo { BYTE APP0[2]; /* 02h Application Use Marker */ BYTE Length[2]; /* 04h Length of APP0 Field */ BYTE Identifier[5]; /* 06h "JFIF" (zero terminated) Id String */ BYTE Version[2]; /* 0Bh JFIF Format Revision */ BYTE Units; /* 0Dh Units used for Resolution */ BYTE Xdensity[2]; /* 0Eh Horizontal Resolution */ BYTE Ydensity[2]; /* 10h Vertical Resolution */ BYTE XThumbnail; /* 12h Thumbnail Horizontal Pixel Count */ BYTE YThumbnail; /* 13h Thumbnail Vertical Pixel Count */ } JFIFAPPOINFO; struct _JFIFDQTInfo { BYTE DQT[2]; // 14h 量化表段标记 BYTE Length[2]; // 16h 量化表段长度 BYTE Identifier; // 18h 量化表ID BYTE QTData[64]; // 19h 量化表数据 } JFIFDQTINFO[2]; struct _JFIFSOFOInfo { BYTE SOFO[2]; // 9Eh 帧开始段标记 BYTE Length[2]; // A0h 帧开始段长度 BYTE BitCount; // A2h 样本精度bit位数 BYTE Height[2]; // A5h 图像像素宽度 BYTE Width[2]; // A3h 图像像素高度 BYTE ComponentsCount; // A7h 图像组件计数 BYTE YIdentifier; // A8h 亮度Y的ID号 BYTE YHVSamplingCoefficient; // A9h 亮度Y垂直和水平采样系数 BYTE YUsedDQTIdentifier; // AAh 亮度Y使用的量化表ID号 BYTE CbIdentifier; // ABh 色度Cb的ID号 BYTE CbHVSamplingCoefficient; // ACh 色度Cb垂直和水平采样系数 BYTE CbUsedDQTIdentifier; // ADh 色度Cb使用的量化表ID号 BYTE CrIdentifier; // AEh 色度Cr的ID号 BYTE CrHVSamplingCoefficient; // AFh 色度Cr垂直和水平采样系数 BYTE CrUsedDQTIdentifier; // B0h 色度Cr使用的量化表ID号 } JFIFSOFOINFO; struct _JFIFDRIInfo { BYTE DRI[2]; BYTE Length[2]; BYTE NMCUReset[2]; //每n个MCU块就有一个 RSTn 标记. } JFIFDRIINFO; struct _JFIFDHTInfo { BYTE DHT[2]; // B1h 哈夫曼表定义段标记 BYTE Length[2]; // B3h 哈夫曼表段长度 BYTE HTIdentifier; // B5h 哈夫曼表号 BYTE NBitsSymbolsCount[16]; // B6h (符号的二进制位长度为n)的符号个数 BYTE SymbolsTable[256]; // C6h 按递增次序代码长度排列的符号表 } JFIFDHTINFO[2][2]; struct _JFIFSOSInfo { BYTE SOS[2]; // 261h 扫描开始段标记 BYTE Length[2]; // 263h 扫描开始段长度 BYTE ComponentsCount; // 265h 扫描行内组件的数量 BYTE YIdentifier; // 266h 亮度Y的ID号 BYTE YHTTableID; // 267h 亮度Y使用的哈夫曼表ID号 BYTE CbIdentifier; // 268h 色度Cb的ID号 BYTE CbHTTableID; // 269h 色度Cb使用的哈夫曼表ID号 BYTE CrIdentifier; // 26Ah 色度Cr的ID号 BYTE CrHTTableID; // 26Bh 色度Cr使用的哈夫曼表ID号 BYTE Reserved[3]; // 26Ch 3个未知保留字节 } JFIFSOSINFO; private: struct HuffmanTable { int CodeOfFirstNLengthSymbol[17]; //长度为N的第一个码字的整数值 int NLengthToSymbolsTableIndex[16]; //查表得到第一个长度为N的符号位于符号表的索引 } HUFFMANTABLE[2][2]; public: int ReadJFIFInfo(const BYTE* const jfifData,int jfifDataSize); void DecodeData(int mcuStartIndex,BYTE* jfifData,int jfifDataSize,ImageComponentData*& targetBitmapData); void SetHuffmanTable(); void DecodeOneDUDC(MBitReader* myBitReader,double* DU,double& lastDC,int index1,int index2); void DecodeOneDUAC(MBitReader* myBitReader,double* DU,int index1,int index2); void DecodeOneMCU(MBitReader* myBitReader,int mcuXn,int mcuYn,int mcuWidth,int mcuHeight,double *DU,ImageComponentData* targetImage); void InverseQuantization(double* du,BYTE* quantizationTable); void InverseZigzag(double* sourceDU,double* targetDU); void IDCT(double* sourceDU,double* targetDU); void YCbCrToRGB(ImageComponentData* sourceImage,ImageComponentData* targetImage); public: int imageHeight; int imageWidth; int alignedImageWidth; int alignedImageHeight; HuffmanTable* HT; double DC[3]; int HSamplingCoefficient[3]; int VSamplingCoefficient[3]; int DQTID[3]; int nMCUReset; }; #endif // JPEGDECODE_H
#ifndef JPEGDECODE_H #define JPEGDECODE_H #include "globalextern.h" typedef unsigned char BYTE; struct ImageComponentData { double value[3]; }; class MBitReader { public: MBitReader(BYTE* data,int currentDataIndex) { Data=data; m_currentBitPosition=8; m_currentDataIndex=currentDataIndex; } BYTE* Data; int m_currentDataIndex; int m_currentBitPosition; public: int ReadNextBit() { --m_currentBitPosition; if (m_currentBitPosition<0) { m_currentBitPosition+=8; ++m_currentDataIndex; } //if (m_currentDataIndex>632) theUI->label1->setText("Error!"); return ((Data[m_currentDataIndex]>>m_currentBitPosition) & 0x01); } void GoPreviousBit() { ++m_currentBitPosition; if (m_currentBitPosition>7) { m_currentBitPosition-=8; --m_currentDataIndex; } } int GetCurrentByte() { return Data[m_currentDataIndex]; } }; class MJpegDecode { private: struct _JFIFAPPOInfo { BYTE APP0[2]; /* 02h Application Use Marker */ BYTE Length[2]; /* 04h Length of APP0 Field */ BYTE Identifier[5]; /* 06h "JFIF" (zero terminated) Id String */ BYTE Version[2]; /* 0Bh JFIF Format Revision */ BYTE Units; /* 0Dh Units used for Resolution */ BYTE Xdensity[2]; /* 0Eh Horizontal Resolution */ BYTE Ydensity[2]; /* 10h Vertical Resolution */ BYTE XThumbnail; /* 12h Thumbnail Horizontal Pixel Count */ BYTE YThumbnail; /* 13h Thumbnail Vertical Pixel Count */ } JFIFAPPOINFO; struct _JFIFDQTInfo { BYTE DQT[2]; // 14h 量化表段标记 BYTE Length[2]; // 16h 量化表段长度 BYTE Identifier; // 18h 量化表ID BYTE QTData[64]; // 19h 量化表数据 } JFIFDQTINFO[2]; struct _JFIFSOFOInfo { BYTE SOFO[2]; // 9Eh 帧开始段标记 BYTE Length[2]; // A0h 帧开始段长度 BYTE BitCount; // A2h 样本精度bit位数 BYTE Height[2]; // A5h 图像像素宽度 BYTE Width[2]; // A3h 图像像素高度 BYTE ComponentsCount; // A7h 图像组件计数 BYTE YIdentifier; // A8h 亮度Y的ID号 BYTE YHVSamplingCoefficient; // A9h 亮度Y垂直和水平采样系数 BYTE YUsedDQTIdentifier; // AAh 亮度Y使用的量化表ID号 BYTE CbIdentifier; // ABh 色度Cb的ID号 BYTE CbHVSamplingCoefficient; // ACh 色度Cb垂直和水平采样系数 BYTE CbUsedDQTIdentifier; // ADh 色度Cb使用的量化表ID号 BYTE CrIdentifier; // AEh 色度Cr的ID号 BYTE CrHVSamplingCoefficient; // AFh 色度Cr垂直和水平采样系数 BYTE CrUsedDQTIdentifier; // B0h 色度Cr使用的量化表ID号 } JFIFSOFOINFO; struct _JFIFDHTInfo { BYTE DHT[2]; // B1h 哈夫曼表定义段标记 BYTE Length[2]; // B3h 哈夫曼表段长度 BYTE HTIdentifier; // B5h 哈夫曼表号 BYTE NBitsSymbolsCount[16]; // B6h (符号的二进制位长度为n)的符号个数 BYTE SymbolsTable[256]; // C6h 按递增次序代码长度排列的符号表 } JFIFDHTINFO[4]; struct _JFIFSOSInfo { BYTE SOS[2]; // 261h 扫描开始段标记 BYTE Length[2]; // 263h 扫描开始段长度 BYTE ComponentsCount; // 265h 扫描行内组件的数量 BYTE YIdentifier; // 266h 亮度Y的ID号 BYTE YHTTableID; // 267h 亮度Y使用的哈夫曼表ID号 BYTE CbIdentifier; // 268h 色度Cb的ID号 BYTE CbHTTableID; // 269h 色度Cb使用的哈夫曼表ID号 BYTE CrIdentifier; // 26Ah 色度Cr的ID号 BYTE CrHTTableID; // 26Bh 色度Cr使用的哈夫曼表ID号 BYTE Reserved[3]; // 26Ch 3个未知保留字节 } JFIFSOSINFO; private: struct HuffmanTable { int CodeOfFirstNLengthSymbol[17]; //长度为N的第一个码字的整数值 int NLengthToSymbolsTableIndex[16]; //查表得到第一个长度为N的符号位于符号表的索引 } HUFFMANTABLE[4]; public: int ReadJFIFInfo(const BYTE* const jfifData,int jfifDataSize); void DecodeData(int mcuStartIndex,BYTE* jfifData,int jfifDataSize,ImageComponentData*& targetBitmapData); void SetHuffmanTable(); void DecodeOneDUDC(MBitReader* myBitReader,double* DU,double& lastDC,int HTID); void DecodeOneDUAC(MBitReader* myBitReader,double* DU,int HTID); void DecodeOneMCU(MBitReader* myBitReader,int mcuXn,int mcuYn,int mcuWidth,int mcuHeight,double *DU,ImageComponentData* targetImage); void InverseQuantization(double* du,BYTE* quantizationTable); void InverseZigzag(double* sourceDU,double* targetDU); void IDCT(double* sourceDU,double* targetDU); void YCbCrToRGB(ImageComponentData* sourceImage,ImageComponentData* targetImage); public: int imageHeight; int imageWidth; int alignedImageWidth; int alignedImageHeight; struct HuffmanTableID { int dc; int ac; } HTID[3]; double DC[3]; int HSamplingCoefficient[3]; int VSamplingCoefficient[3]; int DQTID[3]; }; #endif // JPEGDECODE_H
本课程详细、全面地介绍了 Qt 开发中的各个技术细节,并且额外赠送在嵌入式端编写Qt程序的技巧。整个课程涵盖知识点非常多,知识模块囊括 Qt-Core 组件、QWidgets、多媒体、网络、绘图、数据库,超过200个 C++ 类的分析和使用,学完之后将拥有 Qt 图形界面开发的非常坚实的功底。 每个知识点不仅仅会通过视频讲解清楚,并且会配以精心安排的实验和作业,用来保证学习过程中切实掌握核心技术和概念,通过实验来巩固,通过实验来检验,实验与作业的目的是发现问题,发现技术盲点,通过答疑和沟通夯实技术技能。注意:本套视频教程来源于线下的实体班级,因此视频中有少量场景对话和学生问答,对此比较介意的亲们谨慎购买。注意:本套视频教程包含大量课堂源码,包含对应每个知识点的精心编排的作业。由于CSDN官方规定在课程介绍中不能出现作者的联系方式,因此在这里无法直接给出QQ答疑号,视频中的源码、资料和作业文档链接统一在购买后从CSDN平台跟我沟通,我会及时回复跟进。注意:本套视频教程包含全套10套作业题,覆盖所有视频知识点,循序渐进,各个击破,作业总纲如下:下面是部分作业题目展示,每道题都有知识点说明,是检验学习效果的一大利器:(部分作业展示,为了防止盗图盗题对题干做了模糊处理)(部分作业展示,为了防止盗图盗题对题干做了模糊处理)(部分作业展示,为了防止盗图盗题对题干做了模糊处理)(部分作业展示,为了防止盗图盗题对题干做了模糊处理)(部分作业展示,为了防止盗图盗题对题干做了模糊处理)…… ……

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值