ccfcsp202212-3 JPEG 解码

试题编号:202212-3
试题名称:JPEG 解码
时间限制:1.0s
内存限制:512.0MB
问题描述:

问题背景

四年一度的世界杯即将画上尾声。在本次的世界杯比赛中,视频助理裁判(Video Assistant Referee, VAR)的应用可谓是大放异彩。VAR 使用视频回放技术帮助主裁判作出正确判罚决定。西西艾弗岛足球联赛的赛场上也引入了一套 VAR 设备。作为技术供应商的技术主管小C,需要存储和编码 VAR 产生的图像数据。小 C 分析比较发现,JPEG 编码算法可以达到较好的压缩效果,并且质量损失是可以接受的。因此,小 C 决定使用 JPEG 编码算法来存储和传输图像数据。JPEG 是一种常用的图片有损压缩算法,它的压缩率高,但是压缩后的图片质量下降较多。JPEG 图片的压缩率一般在 10:1 到 20:1 之间,一般用于存储照片等图片质量要求不高的场景。

为了简化问题,我们以灰度图片为例,介绍 JPEG 编码算法的过程。一张灰度图片,可以被视为由多个像素点组成。每个像素点对应一个 0 到 255 之间的数值,用于表示像素点的亮度。JPEG 编码算法将图片分割为 8×8 的小块,每个小块被称作一个最小编码单元。对每个小块进行如下的计算:

  1. 将每个像素点的数值减去 128,使得每个像素点的数值都在 -128 到 127 之间。
  2. 将每个小块的像素点排成一个 8×8 的矩阵,并对矩阵进行离散余弦变换(DCT)。进行离散余弦变换后,仍然得到一个 8×8 的矩阵,矩阵中的每个元素都是实数,并且所得矩阵的左上方的数字的绝对值较大,右下方的数字的绝对值较小,甚至接近 0。
  3. 对矩阵进行量化操作。量化操作是指将矩阵中的每个元素都除以一个数字,并取整数。量化操作的目的是为了减少矩阵中的数据,从而减少编码后的文件大小。量化操作的数字越大,矩阵中的数据就越少,但是压缩后的图片质量也会越差。
  4. 对矩阵进行 Z 字形扫描。Z 字形扫描是指从左上角开始,沿着 Z 字形的路径扫描矩阵中的元素,将扫描到的元素依次排成一个数组,由于 Z 字形扫描的路径是从左上角到右下角,数组结尾处可能存在着连续的 0,为了节省空间,可以不存储这些连续的 0。得到的数据被称为扫描数据。

最后,将得到的各个小块的扫描数据采用哈夫曼编码进行压缩,并置于必要的数据结构中,就能得到一张 JPEG 图片了。

样例输入:

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
26
2
-26 -3 0 -3 -2 -6 2 -4 1 -3 1 1 5 1 2 -1 1 -1 2 0 0 0 0 0 -1 -1

样例输出:

62 65 57 60 72 63 60 82
57 55 56 82 108 87 62 71
58 50 60 111 148 114 67 65
65 55 66 120 155 114 68 70
70 63 67 101 122 88 60 78
71 71 64 70 80 62 56 81
75 82 67 54 63 65 66 83
81 94 75 54 68 81 81 87

样例说明

本组样例即为题目描述中的样例。

子任务

对于 20% 的数据,有 𝑇=0;

对于 40% 的数据,有 𝑇=0 或 1;

对于 100% 的数据,有 𝑇∈{0,1,2},且 𝑛∈[0,64],并且量化矩阵中的各个元素 𝑞𝑖,𝑗 满足 0<𝑞𝑖,𝑗<256,扫描序列中的各个元素 𝑚𝑖 满足 −256<𝑚𝑖<256。

提示

在 C/C++ 语言中,可以通过包含 math.h(C 语言)或 cmath(C++ 语言)来使用数学函数。 𝜋 的值可以通过表达式 acos(-1) 获得。

在 Python 语言中,可以通过 from math import pi 引入 𝜋。

在 Java 语言中,可以使用 Math.PI 来获取 𝜋 的值.

import java.util.*;
public class Main {
    static int[][] arr;
    static int[][] liang;

    public Main(int[][] at) {
        arr=at;
        liang=new int[][]{
                {1,2,6,7,15,16,28,29},
                {3,5,8,14,17,27,30,43},
                {4,9,13,18,26,31,42,44},
                {10,12,19,25,32,41,45,54},
                {11,20,24,33,40,46,53,55},
                {21,23,34,39,47,52,56,61},
                {22,35,38,48,51,57,60,62},
                {36,37,49,50,58,59,63,64}};

    }
    public static void getliang(String l) {
        String[] input=l.split(" ");
        for(int i=0;i<8;i++){
            for(int j=0;j<8;j++){
                int temp=liang[i][j];
                if(temp>input.length){
                    liang[i][j]=0;
                }else{
                    liang[i][j]=Integer.parseInt(input[temp-1]);
                }
            }
        }
    }
    public static void display(){
        for(int i=0;i<8;i++){
            for(int j=0;j<7;j++){
                System.out.print(liang[i][j]+" ");
            }
            System.out.println(liang[i][7]);
        }
    }
    public static void cheng(){
        for(int i=0;i<8;i++){
            for(int j=0;j<8;j++){
                int temp=liang[i][j];
                liang[i][j]=temp*arr[i][j];
            }
        }
    }
    public static double geta(int a){
        if(a==0){
            return Math.sqrt((double) 1/2);
        }else{
            return 1;
        }
    }
    public static void change(){
        double[][] newliang=new double[8][8];
        for(int i=0;i<8;i++){
            for(int j=0;j<8;j++){
                double num=0;
                for(int u=0;u<8;u++){
                    for(int v=0;v<8;v++){
                        double t1=geta(u);
                        double t2=geta(v);
                        double t3=Math.cos(Math.PI/8*(i+ (double) 1 /2)*u);
                        double t4=Math.cos(Math.PI/8*(j+ (double) 1 /2)*v);
                        num+=t1*t2*liang[u][v]*t3*t4;
                    }
                }
                newliang[i][j]=(float)1/4*num;
            }
        }
        for(int i=0;i<8;i++){
            for(int j=0;j<8;j++){
                liang[i][j]= Math.round((float) newliang[i][j]+128);
                if(liang[i][j]>255){
                    liang[i][j]=255;
                }
                if(liang[i][j]<0){
                    liang[i][j]=0;
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int[][] at=new int[8][8];
        for(int i=0;i<8;i++){
            String[] input=in.nextLine().split(" ");
            for(int j=0;j<8;j++){
                at[i][j]=Integer.parseInt(input[j]);
            }
        }
        int num=in.nextInt();
        int t=in.nextInt();
        in.nextLine();
        String liang=in.nextLine();
        Main ts=new Main(at);
        getliang(liang);
        if(t==0){
            display();
        }else if(t==1){
            ts.cheng();
            display();
        }else if(t==2){
            ts.cheng();
            change();
            display();
        }
    }
}

  • 48
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值