图像压缩问题
问题描述
给定一张灰度图,其像素为长度为\(n\)的灰度值序列:{\(p_1,p_2,\cdots,p_n\)},其中\(p_i \in [0:1:255]\)可表示为8位二进制数。现使用一种变位压缩方式对图像进行压缩,具体压缩过程如下:
将{\(p_1,p_2,\cdots,p_n\)}分割成为\(m\)段:\(S_1,S_2,\cdots,S_m\);
\(l[i]\)为\(S_i\)段的像素数,要求\(l[i]\leq 256\);
\(h_i\)为\(S_i\)段中最大像素灰度值对应的二进制位数,则有$$h_i=\left \lceil\log\left(\max\limits_{p_k \in s_i}{p_k}+1\right)\right\rceil$$
\(b[i]\)为\(S_i\)段中所有像素的灰度值二进制表示的最小位数,则有$$h_i \leq b[i] \leq 8$$
每个分段\(S_i\)的段头都有11位
\(b[i]\leq 8\)的二进制表示:3位
\(l[i]\leq 256\)的二进制表示:8位
\(S_i\)段的二进制总位数(占用空间):\(11+b[i]\times l[i]\);
可以看出,不同的分段方案\(T=\{S_1,S_2,\cdots,S_j\}\)导致不同的变位压缩结果,即占用不同大小的总空间。现需要确定空间占用最小的分段方案,即$$\min\limits_{T}\left{\sum_{i=1}^{j}(b[i]\times l[i]+11)\right}$$
约束条件
图像压缩问题中约束条件是:\(l[i]\leq 256\),即每个段中的像素数不超过256个。也就是说只要不违背这个约束条件的所有解均是可行解。
目标函数
图像压缩问题是最小化问题,其目标函数是:各个分段占用空间之和,即
算法设计
子问题边界参数化
在该问题中,我们将问题的左侧边界固定,右侧边界进行参数化,所有子问题可建模为:像素序列\(P_i=\{p_1,p_2,\cdots,p_i\}, i=1,2,\cdots,n\)。
递推方程设计
设\(s[i]\)是像素序列\(P_i=\{p_1,p_2,\cdots,p_i\},i=1,2,\cdots,n\)的最优分段所需存储的位数,则递推关系设计如下:
其中,\(b_{max}(i-j+1,i)=\left\lceil \log \left ( \max\limits_{p_k \in S_m}p_k + 1 \right )\right\rceil \leq 8\)。
算法的伪代码描述
function Compress(n, p, l, s, b)
lmax ← 256;header ← 11;s[0] ← 0
for i = 1 → n do
b[i] ← length(p[i])
bmax ← b[i]
s[i] ← s[i − 1] + bmax
l[i] ← 1
for j = 2 → min i, lmax do
if bmax < b[i − j + 1] then
bmax ← b[i − j + 1]
if s[i] > s[i − j] + j ∗ bmax then
s[i] ← s[i − j] + j ∗ bmax
l[i] ← j
s[i] ← s[i] + header
return s, b, l
end function
function Traceback(n, i, s, l)
i ← 0
if n == 0 then
return
Traceback(n − l[n], i, s, l)
s[i + +] ← n − l[n]
end function
算法时空效率估计
(1)估计算法Compress的时间复杂度,试给出详细过程。
Compress只需\(O(n)\),由于在算法中j的次数不超过256次,故对每一个确定的i可在\(O(1)\)时间内完成,因此时间复杂度为\(O(n)\).
(2)估计算法Traceback的时间复杂度,试给出详细过程。
由于数组\(l[i],b[i]\)记录了最优分段所需的信息,最优分段的最后一段的段长度和像素位数分别存在\(l[n],b[n]\)中,其前一段的段长度和像素位数存储于\(l[n-l[n]]\)和\(b[n-l[n]]\)中,依次类推,在\(O(n)\)时间内构造最优解。
编码实现
#include<iostream>
#include<vector>
using namespace std;
//灰度值二进制位数
int Length(int i){
int k = 1;
i = i/2;
while(i > 0){
k ++;
i = i/2;
}
return k;
}
//迭代备忘录实现动态规划
void Compress(int n,vector<int> &p,vector<int> &s,vector<int> &l,vector<int> &b){
int lmax = 256;
int header = 11;//分段首部
s[0] = 0;
int bmax;
for(int i = 1;i <= n;i ++){
b[i] = Length(p[i]);
bmax = b[i];
s[i] = s[i-1] + bmax;
l[i] = 1;
int k = 0;
if(i > lmax){
k = lmax;
}else{
k = i;
}//k取lmax和 i的较小值
for(int j = 2;j <= k;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] = s[i] + header;//加上首部
}
}
//递归追踪解
void Traceback(int n,int& i,vector<int> &s,vector<int> &l){
if(n == 0){
return;
}
Traceback(n-l[n],i,s,l);
s[i++] = n-l[n];//用s表记录分段位置
}
int main(){
int n;
cin >> n;
vector<int> s,b,l,p;
for(int i = 0;i <= n;i ++){
s.push_back(0);
b.push_back(0);
l.push_back(0);
p.push_back(0);
}
for(int i = 1;i <= n;i ++){
int p1;
cin >> p1;
p[i] = p1;
}
Compress(n,p,s,l,b);
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;
}
return 0;
}
结果展示
结束语
若结局非你所愿,请在尘埃定前奋力一搏
作者:花城
本文来自博客园,作者:{花城1122},转载请注明原文链接:https://www.cnblogs.com/huacheng1122/p/15011355.html