目录
问题描述
在计算机中常用像素点灰度值序列{P1,P2, … ,Pn}表示图像。其中整数Pi,1<=i<=n,表示像素点i的灰度值。通常灰度值的范围是0~255,一个像素需要8位表示。
实际情况下,若一个像素灰度值小于8,则只需要3位来表示;灰度值小于16,则只需要4位表示……由此可以看出,如果用定长8位来表示灰度图中的每一个像素,就会造成很大的空间浪费。图像压缩问题要求确定像素序列{P1,P2,…Pn}的最优分段,使得以此分段所需要的存储空间最小。
另外,如果图像压缩的操作对象是一个彩色图像,那么需要将其先转换为灰度图,计算原始大小再进行DP压缩;并且,计算压缩比后要将图像恢复。
最优子结构性质
设l[i],b[i],1<=i<=m是{p1,p1,……pn}的一个最优分段,则l[1],b[1]是{p1,……,pl[1]}的一个最优分段,且l[i],b[i],2<=i<=m是{pl[1]+1,……,pn}的一个最优分段。即图像压缩问题满足最优子结构性质。
递归计算最优值
设s[i],1<=i<=n是像素序列{p1,p1,……pi}的最优分段所需的存储位数,由最优子结构性质可得:
其中, 。
核心代码如下:
void Compress(int n, int p[], int s[], int l[], int b[]) //令s[i]为前i个段最优合并的存储位数
{
int Lmax = 256, header = 11;
s[0] = 0;
for(int i=1; i<=n; i++) //i表示前几段
{
b[i] = length(p[i]); //计算像素点p需要的存储位数
int bmax = b[i];
cout<<i<<"bmax: "<<bmax<<endl;
s[i] = s[i-1] + bmax;
l[i] = 1;
for(int j=2; j<=i && j<=Lmax; j++) //递推关系:s[i]= min(1<=j<=i)(lsum(i-j+1, i)<=256) {s[i-j]+ lsum(i-j+1,i)*bmax(i-j+1,i) } + 11
{
if(bmax < b[i-j+1])
bmax = b[i-j+1];
if(s[i] > s[i-j] + j*bmax) //因为一开始所有序列并没有分段,所以可以看作每一段就是一个数,故lsum(i-j+1, i) = j;
{
s[i] = s[i-j] + j*bmax;
l[i] = j; //最优断开位置,l[i]表示前i段的最优分段方案中应该是在i-j处断开 比如l[5] = 3,这表示前五段的最优分段应该是(5-3=2)处断开,s[5] = s[2] + 3*bmax
//即 12 | 345,以此类推,得到l[n];之后构造最优解时再由l[n]向前回溯
}
}
s[i] += header;
}
}
void Traceback(int n, int &m, int s[], int l[])
{
if(n == 0) return;
Traceback(n-l[n], m, s, l);
s[m++] = n-l[n]; //重新为s[]数组赋值,用来存储分段位置
}