动态规划算法实现图像压缩问题

问题描述:

给定图像的灰度值序列 其中为第i个像素的灰度值,采用变位压缩存储格式,如何对P进行分段,以使得存储P占用的二进制位数达到最少?

问题要求

图像压缩问题就是要我们找出一个最优的分段方案,使得总存储空间最小。

 问题分析

一幅图像的由很多个像素点构成,像素点越多分辨率越高,像素的灰度值范围为0~255,也就是需要8bit来存储一个像素的灰度值信息。一幅由n*m像素点构成的图像,所需存储空间大小为:n*m*8bit=8nmbit。然而,正常情况下,一幅图像的某一范围内像素点的灰度值是很接近的,表现为一幅图片某一区域颜色相近。

实现思路

子问题的划分边界问题: 设像素序列x1,x2,x3。先把它分为两段,尾段和前段。我们假设尾端是没有分割的。并且前段是已经被分割成了最优解。则我们只需要遍历尾段的长度,就可以得到整个序列的最优解。也就是

(1)假设最后一段只有一个元素x3,用x3的长度+一个段头+min(x1,x2)

(2)假设尾段(X2,x3),加上min(x1);

(1)(2)两个结果比较,就可以拿到最小值,而min(x1,x2)就是子问题

所以递推公式是dp[i] = dp[i - j] + j x log(max(Pi-j+1,…,Pi)) + 11

初始化:

l[i]:i段像素总个数;

b[i]:每个像素占的比特空间;

bmax一段中占用比特空间最多的像素占用的空间;

s[i]:每个段占用的总比特空间

约束条件:

每段像素个数l[i]<=256;

i段占用空间:b[i]*l[i]+11

状态表示:

l[i]表示以P[i](包含)结尾的最优分段所需的bit数(最少)

状态转移:

l[i] =P[i-k]+b[i-k+1,i]+11 , 0<=k<=i

b[i-k+1,i]为求i-k+1到i这个序列每个像素灰度值需要的的bit数

其中,k<=256(图像压缩的限制)

l[i] =P[i-k]+b[i-k+1,i]+11 , 0<=k<=min(i,256)

以下两个实例帮助理解

 

完整代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#include<fstream>

using namespace std;
const int N = 1e5 + 10;

int P[N];//存储每个像素的灰度值

int length(int i)//计算每个像素的灰度值占的bit
{
	int k = 1;
	i = i / 2;
	while (i > 0) {
		k++;
		i = i / 2;
	}
	return k;
}

void compress(int* p, int n, int* s, int* I, int* b)
{
	int header = 11;//一个段需要3位来存储每个像素点需要几个比特来存储,8位来存储这个像素段有多少个像素点。
	int Imax = 256;
	s[0] = 0;//定义s[0]
	for (int i = 1; i <= n; i++) {
		b[i] = length(p[i]);
		int bmax = b[i];//bmax是最后一段中占用比特最多的像素需要几个比特的空间来存储,它初始化为最后一个点需要的空间
		s[i] = s[i - 1] + bmax;//s[i]记录前段,即1-i像素所需的最小空间,它初始化为前s[i-1]加上最后一段(只有一个点组成)
		I[i] = 1;
		for (int k = 2; k <= i && k <= Imax; k++)
		{//k是最后一段大小,注意i受约束条件限制<=256
			if (bmax < b[i - k + 1])//更新每段像素点占的最大比特空间
				bmax = b[i - k + 1];
			if (s[i] > s[i - k] + k * bmax)
			{
				s[i] = s[i - k] + k * bmax;
				I[i] = k;
			}
		}
		s[i] += header;//最后加上段头的占用的空间
	}
}
int main() {

	ifstream OpenFile("input.txt");
	int n;
	while (OpenFile >> n) {//用打开的文件流对象来读取输入数据,输入总的像素点个数
		cout << n << endl;
		for (int i = 1; i <= n; i++) {//将每个像素的灰度值依次存放到P数组中
			OpenFile >> P[i];
			cout << P[i] << "\t";
		}
	}
	cout << endl;
	OpenFile.close(); //关闭文件流对象

	int* s = new int[n + 1]();
	int* I = new int[n + 1]();
	int* b = new int[n + 1]();
	compress(P, n, s, I, b);
	int t = n;
	vector<int>v;
	while (t) {
		v.push_back(I[t]);//将长度信息用vector存储
		t = t - I[t];
	}
	reverse(v.begin(), v.end());//倒置
	printf("最少需要:%dbit\n", s[n]);
	for (int i = 0, t = 1; i < v.size(); i++) {//输出分段信息
		for (int j = 0; j < v[i]; j++)
			printf("%d ", P[t++]);
		printf("长%d,每个像素需要%dbit\n", I[t - 1], b[t-1]);
		
	}
	return 0;
}

 文件输入("input.txt")

 运行结果

 

时间复杂度分析:

因为我们规定一段的像素个数最多是256个,所以k<=256。虽然看是两层循环,但是内层循环是一个常数级,因为最大只能到256;而外层循环是n个。所以时间复杂度就是O(n)。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值