【CSP试题回顾】202212-3-JPEG 解码

CSP-202212-3-JPEG 解码

关键点:Z字扫描

在JPEG压缩中,Z字形扫描是一种将8x8块的数据按照Z字形(或之字形)顺序重新排列的过程。这样做的目的是为了将相似的数据(尤其是零值)放置在一起,从而提高后续压缩步骤的效率。

1.方法一:任意大小矩阵的Z字扫描

zScan函数实现了Z字形扫描的过程。它按照特定的顺序遍历8x8块,这个顺序开始于左上角,然后沿着块的对角线向上移动,达到边界后转向并沿着另一条对角线向下移动,依此类推。在每一步中,该函数检查是否还有待填充的系数(来自数组 m),如果有,则将当前系数填入对应的位置;如果没有更多的系数,则在当前位置填充零。该过程的关键在于管理遍历的方向和处理边界情况——例如,当扫描达到矩阵的上边界或右边界时,需要正确改变方向

void zScan(vector<vector<int>>& M, vector<int>& m) {
    int size = 8; // 矩阵大小为8x8
    vector<int> result;
    int i = 0, j = 0, mi = 0;
    bool up = true; // 用于标识当前移动方向,初始为向上

    for (int k = 0; k < size * size; ++k) {
        if (mi < m.size())
        {
            M[i][j] = m[mi];
            mi++;
        }
        else M[i][j] = 0;

        if (up) { // 如果当前方向是向上
            if (j == size - 1) { // 如果触及右边界
                ++i; // 向下移动
                up = false; // 改变方向
            }
            else if (i == 0) { // 如果触及上边界
                ++j; // 向右移动
                up = false; // 改变方向
            }
            else { // 否则,继续向上和向右移动
                --i;
                ++j;
            }
        }
        else { // 如果当前方向是向下
            if (i == size - 1) { // 如果触及底边界
                ++j; // 向右移动
                up = true; // 改变方向
            }
            else if (j == 0) { // 如果触及左边界
                ++i; // 向下移动
                up = true; // 改变方向
            }
            else { // 否则,继续向下和向左移动
                ++i;
                --j;
            }
        }
    }
}

2.方法二:固定大小矩阵下标驱动的Z字形扫描

另一种实现Z字形扫描的方法是使用一个预定义的索引矩阵(如你所示的 idx 矩阵)。这个矩阵直接定义了从Z字形位置到矩阵线性位置的映射。在这种方法中,你不需要通过逐步遍历来确定每个元素的Z字形顺序,而是直接使用索引矩阵来查找每个元素在Z字形顺序中的位置。

int idx[8][8] = {
        {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}
    };
  
void zScanByIndex(vector<vector<int>>& M, vector<int>& m) {
    for (int i = 0; i < 8; i++) { // 填充M矩阵,使用idx矩阵中的值作为m向量的索引
        for (int j = 0; j < 8; j++) {
            if (idx[i][j] < m.size()) { // 检查idx[i][j]是否在m向量的大小范围内
                M[i][j] = m[idx[i][j]];
            } else {
                M[i][j] = 0; // 如果索引超出了m的大小,就将M的当前元素设置为0
            }
        }
    }
}

解题思路

这段代码回答了JPEG压缩的关键步骤:Z字形扫描、量化和离散余弦变换。这个过程有助于理解JPEG压缩如何减少图像数据的大小,同时保留了相对较高的图像质量。代码中每一步都对应JPEG压缩的一部分。

1.初始化和数据输入

代码开始时,创建了几个二维数组来存储量化矩阵 Q、初始块 M、变换后的块 M1,以及其他变量 nT

  • vector<vector<int>>Q(8, vector<int>(8)): 8x8的量化矩阵。
  • vector<vector<int>>M(8, vector<int>(8)): 存储初始的8x8块,或是在Z字扫描后存储系数。
  • vector<vector<double>>M1(8, vector<double>(8)): 存储离散余弦变换(DCT)后的数据。

main 函数中读取这些值,其中 Q 是量化矩阵,n 是Z字形扫描输入系数的数量,T 是用户选择的压缩步骤标识。

2.Z字形扫描

zScan 函数将一维数组 m (这是按Z字形顺序排列的系数)填入8x8块 M 中。这模仿了JPEG编码过程中将量化后的DCT系数按Z字形排列的过程,这一步骤有助于后续的编码效率,因为它通常会将许多零值放置在块的末尾。

3.量化

multiply 函数通过将8x8的DCT系数块 M 与量化矩阵 Q 逐元素相乘,来实现量化过程。在实际JPEG压缩中,这会降低图像的质量,但同时大大减少所需的数据量。

4.离散余弦变换(DCT)

discreteCos 函数计算了DCT的逆过程,将量化后的系数转换回像素值。这里使用的是二维DCT公式,通常用于JPEG的解码过程。函数计算了8x8块中每个位置的值,根据DCT的公式,使用cosine函数根据频率变化的系数。

5.最终阶段和显示结果

main函数的最后部分,根据用户选择的处理阶段(通过变量 T 控制),程序可以仅执行Z字形扫描、执行扫描和量化或执行全部步骤(包括DCT)。最终,display函数用于输出处理后的8x8块,显示压缩过程中的中间或最终结果。

完整代码

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

vector<vector<int>>Q(8, vector<int>(8));
vector<vector<int>>M(8, vector<int>(8));
vector<vector<double>>M1(8, vector<double>(8));
int n, T;

// Z字扫描如果大小是固定的,可以直接存下标矩阵
void zScan(vector<vector<int>>& M, vector<int>& m) {
    int size = 8; // 矩阵大小为8x8
    vector<int> result;
    int i = 0, j = 0, mi = 0;
    bool up = true; // 用于标识当前移动方向,初始为向上

    for (int k = 0; k < size * size; ++k) {
        if (mi < m.size())
        {
            M[i][j] = m[mi];
            mi++;
        }
        else M[i][j] = 0;

        if (up) { // 如果当前方向是向上
            if (j == size - 1) { // 如果触及右边界
                ++i; // 向下移动
                up = false; // 改变方向
            }
            else if (i == 0) { // 如果触及上边界
                ++j; // 向右移动
                up = false; // 改变方向
            }
            else { // 否则,继续向上和向右移动
                --i;
                ++j;
            }
        }
        else { // 如果当前方向是向下
            if (i == size - 1) { // 如果触及底边界
                ++j; // 向右移动
                up = true; // 改变方向
            }
            else if (j == 0) { // 如果触及左边界
                ++i; // 向下移动
                up = true; // 改变方向
            }
            else { // 否则,继续向下和向左移动
                ++i;
                --j;
            }
        }
    }
}

void multiply(vector<vector<int>>& M, vector<vector<int>>& Q) {
    for (size_t i = 0; i < 8; i++)
    {
        for (size_t j = 0; j < 8; j++)
        {
            M[i][j] *= Q[i][j];
        }
    }
}

double pai = acos(-1);

double alpha(int u) {
    if (u == 0)return sqrt(0.5);
    return 1;
}

double discreteCos(vector<vector<int>>& M, int i, int j) {
    double MM = 0;
    for (size_t u = 0; u < 8.0; u++) {
        for (size_t v = 0; v < 8.0; v++) {
            MM += alpha(u) * alpha(v) * M[u][v] * cos((pai / 8.0) * (i + 0.5) * u) * cos((pai / 8.0) * (j + 0.5) * v);
        }
    }
    MM = MM / 4;
    return MM; 
}

void display(vector<vector<int>>& M) {
    for (auto& it : M) {
        for (auto& jt : it) {
            cout << jt << " ";
        }
        cout << endl;
    }
}

int main() {
    for (auto& it : Q) {
        for (auto& jt : it) {
            cin >> jt;
        }
    }
    cin >> n >> T;
    vector<int>m(n);
    for (size_t i = 0; i < n; i++)
    {
        cin >> m[i];
    }

    zScan(M, m);
    if (T == 0)
    {
        display(M);
        return 0;
    }

    multiply(M, Q);
    if (T == 1)
    {
        display(M);
        return 0;
    }

    for (size_t i = 0; i < M.size(); i++)
    {
        for (size_t j = 0; j < M[i].size(); j++)
        {
            M1[i][j] = discreteCos(M, i, j);
        }
    }

    for (size_t i = 0; i < M.size(); i++)
    {
        for (size_t j = 0; j < M[i].size(); j++)
        {
            M[i][j] = round(M1[i][j] + 128);
            if (M[i][j] > 255) M[i][j] = 255;
            if (M[i][j] < 0) M[i][j] = 0;
        }
    }

    display(M);
    return 0;
}

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows 安装cuda 11.2过程记录

参考&#xff1a; https://blog.csdn.net/m0_45447650/article/details/123704930 https://zhuanlan.zhihu.com/p/99880204?from_voters_pagetrue 在显卡驱动被正确安装的前提下&#xff0c;在命令行里输入nvidia-smi.exe 下载CUDA Toolkit: https://developer.nvidia.com/…

C++ Qt开发:QTcpSocket网络通信组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QTcpSocket组件实现基于TCP的网络通信…

Flutter-自定义图片3D画廊

效果 需求 3D画廊效果 设计内容 StackGestureDetectorTransformPositioned数学三角函数 代码实现 具体代码大概300行 import dart:math;import package:flutter/material.dart; import package:flutter_xy/widgets/xy_app_bar.dart;import ../../r.dart;class ImageSwitc…

[errcode] => 47003 [errmsg] => argument invalid! data.thing2.value invalid

[errcode] > 47003 [errmsg] > argument invalid! data.thing2.value invalid rid: 65f79ad9-09ea6af5-285a03af 检查了好大一圈&#xff0c;经过测试&#xff0c;原因是公众号模板消息接口中的字段不能超过20个汉字&#xff0c;包括标点符号。 虽然接口文档中参数说明…

基于深度学习的口罩人脸识别研究进展

MTCNN模型训练输入的所有图像都是正样本&#xff08;戴口罩的照片&#xff09;&#xff0c;没有负样本作为模型输入。在后续的识别任务模块中&#xff0c;导入MTCNN模型检测结果&#xff0c;对特征点进行编码比较进行识别。 基于MTCNN的口罩人脸识别框架可分为四个阶段&#xf…

数据结构试卷第九套

1.时间复杂度 2.树&#xff0c;森林&#xff0c;二叉树的转换 2.1树转二叉树 给所有的兄弟节点之间加一条连线&#xff1b;去线&#xff0c;只保留当前根节点与第一个叶子节点的连线&#xff0c;删除它与其他节点之间的连线&#xff1b;然后根据左孩子右兄弟进行调整&#xf…

C#装箱和拆箱

一&#xff0c;装箱 装箱是指将值类型转化为引用类型。 代码如下&#xff1a; 装箱的内部过程 当值类型需要被装箱为引用类型时&#xff0c;CLR&#xff08;Common Language Runtime&#xff09;会为值类型分配内存&#xff0c;在堆上创建一个新的对象。值类型的数据会被复…

Adobe Illustrator 2024 v28.3 (macOS, Windows) - 矢量绘图

Adobe Illustrator 2024 v28.3 (macOS, Windows) - 矢量绘图 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、Lightroom Classic、Media Encoder、Photoshop、Premiere Pro、Adobe XD 请访…

【机器学习】详细解析Sklearn中的StandardScaler---原理、应用、源码与注意事项

【机器学习】详细解析Sklearn中的StandardScaler—原理、应用、源码与注意事项 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x…

QT UI窗口常见操作

MainWidget::MainWidget(QWidget *parent): QWidget(parent), ui(new Ui::MainWidget) {ui->setupUi(this);// 设置主窗口背景颜色QPalette plt;plt.setColor(QPalette::Window,QColor(180,220,130));this->setPalette(plt);// 禁止窗口最大化按钮setWindowFlags(windowF…

基于多源遥感图像多级协同融合的舰船识别算法

源自&#xff1a;电子工程与电子技术 作者&#xff1a;张亚丽 冯伟 全英汇 邢孟道 “人工智能技术与咨询” 发布 摘 要 针对极化合成孔径雷达(polarimetric synthetic aperture radar, PolSAR)图像存在斑点噪声严重、可视性差、直接影响目标识别精度的问题, 提出一种基…

SpringBoot实现邮件发送

一.准备 引入starter <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId> </dependency>二.邮件发送需要的配置 因为各大邮件都有其对应安全系统&#xff0c;不是项目中想用就…

【晴问算法】入门篇—贪心算法—区间不相交问题

题目描述 给定n个开区间&#xff0c;从中选择尽可能多的开区间&#xff0c;使得这些开区间两两没有交集。 输入描述 输出描述 输出一个整数&#xff0c;表示最多选择的开区间个数。 样例1输入 4 1 3 2 4 3 5 6 7 输出 3 解释 最多选择(1,3)、(3,5)、(6,7)三个区间&#xff0c;它…

【IEDM2023】背势垒电荷运动诱导GaN HEMT随时间的非稳态击穿

分享一篇2023年IEDM上GaN HEMT&#xff08;高电子迁移率晶体管&#xff09;的研究论文&#xff0c;标题为“Charge Movement in Back Barrier Induced Time-Dependent On-State Breakdown of GaN HEMT”。论文讨论了在GaN HEMT中&#xff0c;由于背栅&#xff08;Back Barrier&…

【数据库】数据库基本知识

1.数据库的四个基本概念 1.1 数据&#xff1a;描述事务的符号记录 1.2 数据库&#xff1a;概括的说&#xff0c;数据库数据具有永久存储、有组织的、可共享的大量数据的集合&#xff0c;数据库中的数据按一定的数据模型组织、描述和储存&#xff0c;具有较小的冗余度、较高的…

ubuntu16.04上pycharm卡住关不了

在使用pycharm的过程中&#xff0c;突然卡住&#xff0c;黑屏&#xff0c;手动界面关闭失败&#xff0c;可尝试以下方法解决。 输入以下命令&#xff0c;查看所有和pycharm有关的进程 ps -ef | grep pycharm得到以下结果 根据相应的PID&#xff0c;输入以下命令&#xff0c;强…

Java基础入门day16

day16 回顾二分查找 思路&#xff1a; 对于一个已经排好序的数组&#xff0c;在该数组中查找指定的元素&#xff0c;将要查找的元素与排好序之后的数组中的中间数值进行比对 如果一致&#xff0c;则直接返回&#xff0c;一次性可以得到要查找元素的下标 如果要查找的元素比中间…

Perl下载器:一步步教你抓取Amazon网站数据

引言&#xff1a;掌握数据&#xff0c;掌握未来 在这个信息爆炸的时代&#xff0c;数据就是新石油。但如何有效地获取和利用这些数据呢&#xff1f;爬虫技术是关键。今天&#xff0c;我们将深入探讨如何使用Perl语言编写一个下载器&#xff0c;以Amazon网站为例&#xff0c;教…

.Net使用ElasticSearch

文章目录 前言主体内容一.Kibana中ElasticSearch的基础操作1.GET&#xff08;查询&#xff09;1.POST&#xff08;新增&#xff09;1.PUT&#xff08;修改&#xff09;1.DELET&#xff08;删除&#xff09; 二.在.Net中&#xff0c;对ElasticSearch进行基础操作1.DotNet连接Ela…

【爬虫逆向】Python逆向采集猫眼电影票房数据

进行数据抓包&#xff0c;因为这个网站有数据加密 !pip install jsonpathCollecting jsonpathDownloading jsonpath-0.82.2.tar.gz (10 kB)Preparing metadata (setup.py) ... done Building wheels for collected packages: jsonpathBuilding wheel for jsonpath (setup.py) .…