图像压缩问题

图像压缩问题的bilibil讲解

1.问题引入

首先,图像是由像素组合成的,每个像素都有灰度值,灰度值是体现像素的颜色的。灰度值从0~255,灰度值占用的位数就是像素占用的位数。我们要存储一个图像就要存储它的所有像素。现在的问题是我们如何存储,使得存储空间最小?

思路:我们需要将图像分成m段,每段中有L[t]个像素,每个要占用b[t]个比特位,每个段都有一个端头,段头是固定的需要占用11位。因此总共需要如下存储空间。

一段像素个数最多有256个,这是题目要求的已知条件。一个像素要最少要占用多少位哩?设第t个像素的灰度值为n,b[t](每个像素占用的位数)要考虑到2种情况,第一种,n是2的整数次方,这时b[t]就是logn;第二种情况,a<logn<b(a,b为整数),这时要保证能表示灰度值,要加一再向上取整。综上,我们要满足这两种情况的并集,b[t]=log(n+1)向上取整,注意不能是log(n)+1向上取整,因为如果(n=2的x次方-1)时,这两种值就不一样了。例如,灰度值为1,我们需要一位来存储。再如,n=7,我们需要3位来存储。第t段最大的像素所占位数为max pk,得到如下图所示:

一个例子:

对于这个问题就转变成了如何分段问题。如果要用暴力求解法,我们要遍历多少种分段方法哩?

假设一共有n个像素,那么就有n-1个分割点,每个分割点可以选择分或不分,一共有2的n-1次方种分段方法

显然,比较浪费时间。

2.用动态规划算法可以减少时间复杂度,那么动态归划算法怎么划分子问题呢?

我们只用一个变量i来表示第i个像素的位置,找到第i个位置的最优解,每次增加一个像素,最终变成规模为n的像素问题。

例子:

上面例子种S[1]=4+11=15

S[2]的最优解在这两种划分方法中(1)分2段:S[1]+4+11=30  (2)分1段:S[0]+4*2+11=19

S[3]的最优解在这三种划分方法中(1)S[1]+4*2+11=34  (2)S[2]+4+11=34 (3)S[0]+4*3+11=23

S[4]的最优解在这四种划分方法中(1)S[3]+8+11=42 (2)S[2]+8*2+11 (3)S[1]+8*3+11 (4)S[0]+8*4+11

依次类推,S[6]的最优解在如上图所示这六种划分算法中。

(1)S[5]+1*2+11 (2)S[4]+2*2+11 (3) S[3]+3*8+11 (4)S[2]+4*8+11

(5)S[1]+5*8+11 (6) S[0]+6*8+11

红色部分对应代码

s[i] = s[i - 1] + bmax;//整个数组从1到i-1的最优解加i单独一段

  s[i] += header;

黄色部分对应代码

//在s[i]+j*bmax中找最好的存储方法
        for (int j = 2; j <= i && j <= Lmax; j++)
        {
            if (bmax < b[i - j + 1])//下标前移,找最大的像素
            {
                bmax = b[i - j + 1];
            }

            if (s[i] > s[i - j] + j * bmax)
            {
                s[i] = s[i - j] + j * bmax;
                l[i] = j;
            }
        }
        s[i] += header;

其中,L[i]记录S[i]的划分段数。

3.接下来,我们看看递推方程怎么表示

j-i+1i个像素中占用位数最大的像素记为b[i-j+1,i]

S[i]表示从第一个像素到第i个像素的最小存储总位数。

4.伪代码实现

s[i]来记录前i个数字的最优处理方式得到的最优解,即最小存储位数
l[i]来记录当前第i个数所在段中有多少个数,
b[i]中存放前i个像素点的最后一段中最大像素位数。

5.代码实现

#include <iostream>   
using namespace std;

const int N = 8;

int length(int i);
void Compress(int n, int p[], int s[], int l[], int b[]);
void Tracebace(int n, int& i, int s[], int l[]);
void Output(int s[], int l[], int b[], int n);

int main()
{
    int p[] = { 0,10,12,15,255,1,2,0 };//图像灰度数组 下标从1开始计数  
    int s[N], l[N], b[N];
/**s[i]来记录前i个数字的最优处理方式得到的最优解,即最小存储位数
l[i]来记录当前第i个数所在段中有多少个数,
b[i]中存放前i个像素点的最后一段中最大像素位数。**/

    cout << "图像的灰度序列为:" << endl;

    for (int i = 1; i < N; i++) //输出原灰度序列 
    {
        cout << p[i] << " ";
    }
    cout << endl;

    Compress(N - 1, p, s, l, b);
    Output(s, l, b, N - 1);
    return 0;
}

void Compress(int n, int p[], int s[], int l[], int b[])
{
    int Lmax = 256, header = 11;
    s[0] = 0;
    for (int i = 1; i <= n; i++)
    {
        b[i] = length(p[i]); //计算像素点p需要的存储位数  
        int bmax = b[i];
        s[i] = s[i - 1] + bmax;//整个数组从1到i-1的最优解加i单独一段
        l[i] = 1;
//在s[i]+j*bmax中找最好的存储方法
        for (int j = 2; j <= i && j <= Lmax; j++)
        {
            if (bmax < b[i - j + 1])//下标前移,找最大的像素
            {
                bmax = b[i - j + 1];
            }

            if (s[i] > s[i - j] + j * bmax)
            {
                s[i] = s[i - j] + j * bmax;
                l[i] = j;
            }
        }
        s[i] += header;
    }
}
/***求数组中的元素用多少位二进制表示***/
int length(int i) //i表示p数组中元素的值 
{
    int k = 1;
    i = i / 2;
    while (i > 0)
    {
        k++;
        i = i / 2;
    }
    return k;
}
/**s[i]来记录前i个数字的最优处理方式得到的最优解,即最小存储位数
l[i]来记录当前第i个数所在段中有多少个数,
b[i]中存放前i个像素点的最后一段中最大像素位数。**/
/*递归找寻分段的位置*/
void Traceback(int n, int& i, int s[], int l[])
{
    if (n == 0)
        return;
    Traceback(n - l[n], i, s, l);
    s[i++] = n - l[n];//重新为s[]数组赋值,用来存储分段位置  
}

void Output(int s[], int l[], int b[], int n)
{
    //在输出s[n]存储位数后,s[]数组则被重新赋值,用来存储分段的位置  
    cout << "图像压缩后的最小空间为:" << s[n] << endl;
    int m = 0;
    Traceback(n, m, s, l);
    s[m] = n;
    cout << "将原灰度序列分成" << m << "段序列段" << endl;
    for (int j = 1; j <= m; j++)
    {
        l[j] = l[s[j]];//在下标为s[j]的地方划分
        b[j] = b[s[j]];
    }
    for (int j = 1; j <= m; j++)
    {
        cout << "段长度:" << l[j] << ",所需存储位数:" << b[j] << endl;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值