动态规划之‘图像压缩’

问题描述:图像压缩的问题我们是这样理解的:大家都知道计算机的图像是用灰度值序列来表示的{P1,P2......Pn},其中Pi表示像素点i的灰度值。而通常灰度值的范围是0~255,因此需要8位二进制数来表示一个像素。这个时候大家应该有了一些小的疑问:我能不能用更少的位数来表示灰度值?(因为有的灰度值并没有达到255这么大)所以我们引入了图像压缩算法来解决这个问题。

不过在引入问题之前,我要在这里介绍一些算法设计的知识——我们要将灰度值序列分组,而每一组中所有的数就有可能是<255的,所以我们就不需要用8位数字去表示像素大小了,但是分组会带来一个新的问题:我如何表示当前组中像素的个数和像素的位数呢(因为不是八位,所以要有一个数据来记录真正的位数)?这里我们引入两个固定位数的值来表示,①我们用3位数字来表示当前组的每一位像素的的位数②我们引入8来表示当前组中像素点的个数(header=3+8=11) 因为我们在这里规定了一组中最多存储-->0~255个数字,而一个灰度值最多有8位(2^3),所以我们可以用即3位数字来表示当前组的像素位数(注意这里都是二进制)

 

算法设计:知道了这些之后剩下的问题就很好解决了。下面我们看一个例子:

{6, 5, 7,5, 245, 180, 28,28,19, 22, 25,20}这是一组灰度值序列。我们按照默认的解体方法来看----一共12个数字,所以12*8=96位来表示。

而下面我们将其进行分组:

这里我们将他们分为三组:

第一组4个数,最大是7所以用3位表示;

第二组2个数,最大是245所以用8位表示;

第三组6个数,最大是28所以用5位表示;

这个时候,我们最后得到了最后的位数结果为:4*3+2*8+6*5+11*3=91。是不是优化了??

那我们算法应该怎么做来找最优的值呢??

 

既然是DP问题,所以我们肯定需要数组来记录每一步的最优值。这里我们用S[n]来记录第i个数字的最优处理方式得到的最优解。l[n]中来记录第当前第i个数所在组中有多少个数。而b[n]中存的数为当前组的像素位数。

下面我写出来具体的递归过程-->

例题:  求像素序列4,6,5,7,129,138,1的最优分段。

在解体过程中,我们知道在我们求s[3]的时候,我们是分三种情况----

①前三个数为一组,这个时候我需要的存储位数是3(位数)*3(每一组中数的个数)+11(每分一组所必须的固定位数)

②s[1]为单独一组,剩下的两个数字为一组,此时我所需要的空间为s[1]+2*3+11

③前两个数字为一组,最后一个数为一组。此时我们要用s[2](前面已经计算出的最优值)+3*1+11

 

然后比较三个数的大小,取最小的那一种分组情况,然后记下l[3]=3(当前最优分组中是三个数在同一组中),b[3]=3(每一个像素所用的存储位数)。

 

递归到最后得到最优解为  58.

【之后我们考虑我们的分组情况。这个时候我们就要用到l这个矩阵了。

 l= [1 2 3 4 1 2 3] 在这里我们先从最后一个看,它是3,所以向前推三个数字到了4,在向前推四个数,所以我们得到了这里要分两组 即 l= [1 2 3 4 | 1 2 3] 。】

附上代码:

#include<stdio.h>
#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,4,6,5,7,129,138,1};//图像灰度数组 下标从1开始计数
	int s[N],l[N],b[N];
 
	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;
		l[i] = 1;
 
		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)
{
	int k=1;
	i = i/2;
	while(i>0)
	{
		k++;
		i=i/2;
	}
	return k;
}
 
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]];
		b[j] = b[s[j]];
	}
	for(int j=1; j<=m; j++)
	{
		cout<<"段长度:"<<l[j]<<",所需存储位数:"<<b[j]<<endl;
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值