JPEG格式研究——(4)反量化、逆ZigZag变化和IDCT变换
反量化
反量化其实很简单,将霍夫曼解码出来的数据乘上对应的量化表就好了
通过当前色度选择出SOF中的Component,其中的Tqi指出了这一色度所需的量化表id

Component的结构如下:
| 名称 | 长度(bit) | 备注 |
|---|---|---|
| Ci | 8 | Compoenent的id |
| Hi | 4 | 水平缩放因子 |
| Vi | 4 | 垂直缩放因子 |
| Tqi | 8 | 对应的量化表id |
然后就可以根据量化表id找到量化表,将其中每一个元素与霍夫曼解码的结果相乘就OK了
// 就是简单粗暴的直接相乘
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 8; ++j) {
result[i][j] = input[i][j] * dqt[i][j];
}
}
逆ZigZag变换
逆ZigZag变换实际上和ZigZag变换做的是同样的操作,直接按Z字形重新排序就好
参考代码
// 直接查表大法
const int zigzag_table[8*8][2] = {
{0, 0}, {0, 1}, {1, 0}, {2, 0}, {1, 1}, {0, 2}, {0, 3}, {1, 2},
{2, 1}, {3, 0}, {4, 0}, {3, 1}, {2, 2}, {1, 3}, {0, 4}, {0, 5},
{1, 4}, {2, 3}, {3, 2}, {4, 1}, {5, 0}, {6, 0}, {5, 1}, {4, 2},
{3, 3}, {2, 4}, {1, 5}, {0, 6}, {0, 7}, {1, 6}, {2, 5}, {3, 4},
{4, 3}, {5, 2}, {6, 1}, {7, 0}, {7, 1}, {6, 2}, {5, 3}, {4, 4},
{3, 5}, {2, 6}, {1, 7}, {2, 7}, {3, 6}, {4, 5}, {5, 4}, {6, 3},
{7, 2}, {7, 3}, {6, 4}, {5, 5}, {4, 6}, {3, 7}, {4, 7}, {5, 6},
{6, 5}, {7, 4}, {7, 5}, {6, 6}, {5, 7}, {6, 7}, {7, 6}, {7, 7}
};
void zigzag_transform(int input[8][8], int output[8][8]) {
for (int i = 0; i < 8*8; ++i) {
int row = zigzag_table[i][0];
int col = zigzag_table[i][1];
output[row][col] = input[i/8][i%8];
}
}
IDCT变换
最后再进行IDCT2D,IDCT变换就是DCT变换的逆变换,IDCT2D就是横着变换一次再竖着变换一次。这个更多是数学上的东西我就不多说了,怕误人子弟
话不多说,上代码:
pub struct DCT {
pub idct2d_data: [[[[f32; 8]; 8]; 8]; 8],
}
fn cc(i: usize, j: usize) -> f32 {
if i == 0 && j == 0 {
return 1.0 / 2.0;
} else if i == 0 || j == 0 {
return 1.0 / (2.0 as f32).sqrt();
} else {
return 1.0;
}
}
impl DCT {
// 先提前算好一部分
pub fn new() -> DCT {
let mut tmp: [[[[f32; 8]; 8]; 8]; 8] = Default::default();
for i in 0..8 {
for j in 0..8 {
for x in 0..8 {
let i_cos = ((2 * i + 1) as f32 * PI / 16.0 * x as f32).cos();
for y in 0..8 {
let j_cos = ((2 * j + 1) as f32 * PI / 16.0 * y as f32).cos();
tmp[i][j][x][y] = cc(x, y) * i_cos * j_cos / 4.0;
}
}
}
}
DCT { idct2d_data: tmp }
}
pub fn idct2d(&self, data: [[f32; 8]; 8]) -> [[f32; 8]; 8] {
let mut tmp: [[f32; 8]; 8] = Default::default();
for i in 0..8 {
for j in 0..8 {
tmp[i][j] = {
let mut tmp = 0.0;
for x in 0..8 {
for y in 0..8 {
tmp += self.idct2d_data[i][j][x][y] * data[x][y];
}
}
tmp
};
}
}
tmp
}
// 后面还有SSE和AVX加速的代码就不放了,直接看源代码就好了
}
尾声
到这里JPEG解码的部分就全部结束了。需要注意的是,这里解码出来的数据颜色格式并不是可以直接输出到屏幕的RGB888(其实允许这种格式,但很少见,比较常见的是YCbCr格式),还要根据需要处理。
提醒一下,解码出来的是分割出来的8x8的块,还要再拼起来
参考资料
博客园博客:JPEG解码——(4)霍夫曼解码 - OnlyTime_唯有时光 - 博客园 (cnblogs.com)
JPEG标准:Microsoft Word - T081E.DOC (w3.org)
一个Rust写的JPEG解码器:MROS/jpeg_tutorial: 跟我寫 JPEG 解碼器 (Write a JPEG decoder with me) (github.com)
友情链接
我学习过程中写的JPEG图片查看器:Ryan1202/my-tiny-jpeg-viewer: A Tiny Jpeg Viewer (github.com)
JPEG格式研究——(4)反量化、逆ZigZag变化和IDCT变换的更多相关文章
- JPEG解码——(5)反量化和逆ZigZag变换
本篇是该系列的第五篇,承接上篇huffman解码,介绍接下来的两个步骤--反量化和逆zigzag变换,即IDCT前的两个步骤. 需要说明的是,这两个步骤可以颠倒,本人的实现是,先反量化,再逆ZigZa ...
- 图像JPEG格式介绍
1 JPG格式介绍 JPEG (Joint PhotographicExperts GROUP)是由国际标准组织和国际电话电报咨询委员会为静态图像所建立的第一个国际数字图像压缩标准,也是至今一直在使用 ...
- 嵌入式Linux基于framebuffer的jpeg格式本地LCD屏显示
在基于Linux的视频监控采集系统中,摄像头采集到的一帧视频图像数据一般都是经过硬件自动压缩成jpeg格式的,然后再保存到摄像头设备的缓冲区.如果要把采集到的jpeg格式显示在本地LCD屏上,由于我们 ...
- JPEG格式 介绍
JPEG格式 jpeg是有损压缩格式, 将像素信息用jpeg保存成文件再读取出来,其中某些像素值会有少许变化.在保存时有个质量参数可在[0,100]之间选择,参数越大图片就越保真,但图片的体积也就越大 ...
- 让阿里云的Centos,PHP组件 ImageMagick支持png和jpeg格式
我们在Centos安装ImageMagick教程中讲述了如何安装ImageMagick,安装完毕之后发现程序并不支持png和jpeg格式的图片,但是这两种图片又是我们平时所常见的,所以我们还要进一步地 ...
- ecshop图片上传JPEG格式失败问题
在根目录下找到includes文件目录,在其目录中找到cls_image.php打开并找到: $allow_file_types = '|GIF|JPG|JEPG|PNG|BMP|SWF|'; 此处J ...
- 超图不支持JPEG格式的WMTS服务
就目前面而言,超图不支持JPEG格式的WMTS服务,只支持PNG格式的. <本篇完>
- 一般源码安装添加的GD库 是不支持 jpeg 格式的图片的
一般源码安装添加的GD库 是不支持 jpeg 格式的图片的,只支持如下格式 GD Support enabled GD Version bundled (2.0.34 compatible) GIF ...
- linux下将jpg,jpeg格式转为PDF
1.安装imagemagick(用其中的convert)和gthumb sudo apt-get install imagemagick gthumb 2.将tiff图片转换为png或jpeg ...
- Call to undefined function imagecreatefromjpeg() 让GD支持JPEG格式的图片扩展
安装扩展支持jpeg格式: 第一步:首先下载文件: 版本v8: wget http://www.ijg.org/files/jpegsrc.v8b.tar.gz 版本v9: wget http://w ...
随机推荐
- R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024
大规模图像-文本预训练模型实现了零样本分类,并在不同数据分布下提供了一致的准确性.然而,这些模型在下游任务中通常需要微调优化,这会降低对于超出分布范围的数据的泛化能力,并需要大量的计算资源.论文提出新 ...
- C++创建与调用dll动态链接库(MinGW64 Dev-C++)
本文使用的是dev-c++,如果涉及到VC++中不一样的操作,也会适当进行区分. 项目一:创建DLL 1.创建一个DLL类型的项目,当前命名为dlltest,并选择合适的路径进行保存. 2.在生成的 ...
- 如何使用ChatGPT自带插件
OpenAI的插件将ChatGPT连接到第三方应用程序.这些插件使ChatGPT能够与开发者定义的API进行交互,增强ChatGPT的能力,并使其能够执行广泛的操作.插件使ChatGPT能够做如下事情 ...
- Angular 18+ 高级教程 – Component 组件 の Dynamic Component 动态组件
前言 Angular 是 MVVM 框架. MVVM 的宗旨是 "不要直接操作 DOM". 为了这个 "不要直接操作 DOM",我们已经讲了 2 篇文章了. 第 ...
- QT原理与源码分析之QT对象类型QObject源码中的间接的设计思想
这一篇文章介绍QT框架中QT对象类型QObject类型的源代码在设计上的一个比较优秀的设计思想. QObject类型定义 QObject 直接来看QObject的源代码.为了表达更简洁更直观,这里省略 ...
- javaScript遍历对象总结
定义对象: const obj = { name: 'zlx', id: 245, age: 25, gender: '男', } 1. 使用 for... in..循环遍历 对象的属性有2种方式可以 ...
- javascript 中 0.1 + 0.2 === 0.3 是否正确 ?
不正确 ,因为 js 是 动态 .弱类型 ,即时编译的语言 :js中的小数都是浮点型 ,比如 0.1 实际上可能是 0.11111111... 0.2 可能是 0.2 22222..... 所以 0. ...
- Nuxt3+PM2集群模式启动及勘误
起因 之前写过一篇 Nuxt3 的文章,Nuxt3 环境变量配置,用到了 PM2,但是里面的一些配置存在问题,最近有空又验证了一下,这里做一个勘误. 问题 PM2 的启动配置中有一项是exec_mod ...
- KubeSphere Namespace 数据删除事故分析与解决全记录
作者:宇轩辞白,运维研发工程师,目前专注于云原生.Kubernetes.容器.Linux.运维自动化等领域. 前言 2023 年 7 月 23 日在项目上线前夕,K8s 生产环境出现故障,经过紧急修复 ...
- KubeSphere 3.2.1 正式发布,多项功能优化来袭!
KubeSphere 从诞生第一天起就 100% 开源,并以社区的方式成长,在各位社区小伙伴的共同努力下,KubeSphere 迅速走向全球,成为全世界最受欢迎的开源容器平台之一. 经过 3 年的发展 ...