图形压缩算法设计与分析(c++)--动态规划

图形压缩算法设计与分析(c++)

下面是几个讲解的比较好的视频

北大公开课,屈婉玲教授

东北大学,郭楠老师1

东北大学,郭楠老师2

上面的这几个视频对学习图像压缩都有很大的帮助,建议看一看。

分割线--------------------------------------------

思路参考,这篇不错

我的代码与课本代码的区别在于求最优解,我实在想不明白为什么后面每个分割段中像素所占最大位数分割结束位置像素所占位数,不应该是这一段中的最大值吗。所以我没有用课本上的代码,参考屈婉玲教授的。

下方代码是求最优解的代码,大家应该能看明白是如何求每一段的长度,每段所占像最大值是用了一个循环,从这一段开始位置遍历到结束位置,最大值既是我们要的。

要注意的是,课本代码中的数组b在不同函数中功能不同,在compress是存每一个像素所占位数,在求最优值时是存的某段最大位数。

// 构造最优解    下面参考的是北大屈婉玲教授的伪码,更容易理解,链接在下面
// https://www.bilibili.com/video/BV1Ls411W7PB?p=44
void TraceBack( int n, int *l, int *b ) { // n,l,b与上方同义
	int j = 1; //正在追踪的段数,从后往前,最后面是第一段
	int c[100];  //存划分段的的长度
	int w[100];  //存划分段元素所需最大位数

	while( n ) {
		c[j] = l[n];  //从后往前追踪的第j段长度
		w[j] = b[n];  //从后往前追踪的第j段元素最大位数

		//求位数的方法屈教授没讲,自己写的,写的不好多多包涵
		//遍历每一个段所有元素,取最大值
		for( int i = n - l[n] + 1; i <= n; i++ ) {
			if( w[j] < b[i] ) {
				w[j] = b[i];
			}

		}

		n = n - l[n]; // n更新为第j段之前序列长度
		j++;
	}

	cout << "将原序列划分成 " << j - 1 << " 段\n";

	for( int i = j - 1; i >= 1; i-- ) {
		cout << "段长度:" << c[i] << ",所需存储位数:" << w[i] << endl;
	}
}

完整代码1(自己写的求最优解部分):

#include 
using namespace std;

#define N 100

//求灰度值是val的像素所需要的位数,即多少二进制位
int length( int val ) {
    int count = 1;
    val /= 2;

    while( val > 0 ) {
        count++;
        val /= 2;
    }

    return count;
}

//计算最优值
// l[i]存的的从1到i的序列中某个位置i-j+1隔开到最后i这一段的最优长度l[i]
// b[i]应该要存相应段的最大像素位数,但实际存的是每个像素的所占位数
//变量含义  n:序列p中元素个数  p:灰度值序列  s:存从1到i最少存储位数
void Compress( int n, int *p, int *s, int *l, int *b ) {
    int bb[N];
    int Lmax = 255;  //像素序列最大长度
    int header = 11; //每一段的头部信息3(元素最多用8位二进制数表示)+8(段最大长度255)=11
    s[0] = 0;

    //子问题的后边界i
    for( int i = 1; i <= n; i++ ) {
        //计算像素点p需要的存储位数
        b[i] = length( p[i] );
        int bMax = b[i];            //后面段中元素所占位数最大值
        s[i] = s[i - 1] + 1 * bMax; //只有一个元素,所以*1
        l[i] = 1;

        // j为最后一段中元素个数,j取i和255中的最小值
        //可看作从i往前逐渐扩大i到i-1,i再到i-2,i-1,i...
        for( int j = 2; j <= i && j <= Lmax; j++ ) {

            //元素逐渐向前更新,可能会出现比bMax更大的,需要更新
            if( bMax < b[i - j + 1] ) {
                bMax = b[i - j + 1];
            }

            //找到更好的分段  j为段中元素个数
            if( s[i] > s[i - j] + j * bMax ) {
                s[i] = s[i - j] + j * bMax;
                l[i] = j;

            }
        }

        //加上每一段的头部信息3(元素最多用8位二进制数表示)+8(段最大长度255)
        s[i] += header;
    }

    cout << "图像压缩后的最小空间:" << s[n] << endl;
}

// 构造最优解    下面参考的是北大屈婉玲教授的伪码,更容易理解,链接在下面
// https://www.bilibili.com/video/BV1Ls411W7PB?p=44
void TraceBack( int n, int *l, int *b ) { // n,l,b与上方同义
    int j = 1; //正在追踪的段数,从后往前,最后面是第一段
    int c[100];  //存划分段的的长度
    int w[100];  //存划分段元素所需最大位数

    while( n ) {
        c[j] = l[n];  //从后往前追踪的第j段长度
        w[j] = b[n];  //从后往前追踪的第j段元素最大位数

        //求位数的方法屈教授没讲,自己写的,写的不好多多包涵
        //遍历每一个段所有元素,取最大值
        for( int i = n - l[n] + 1; i <= n; i++ ) {
            if( w[j] < b[i] ) {
                w[j] = b[i];
            }

        }

        n = n - l[n]; // n更新为第j段之前序列长度
        j++;
    }

    cout << "将原序列划分成 " << j - 1 << " 段\n";

    for( int i = j - 1; i >= 1; i-- ) {
        cout << "段长度:" << c[i] << ",所需存储位数:" << w[i] << endl;
    }
}

int main() {
    int p[N] = {0} ; //图像灰度数组{p1,p2,p3,p4,p5,p6...pn} 下标从1开始计数,p0是0
    int s[N] = {0}, l[N] = {0}, b[N] = {0};	//s存最优值,l存最优解
    int n;
    cout << "请输入灰度值序列元素个数(最多99):";
    cin >> n;
    cout << "请输入灰度值序列元素:";

    for( int i = 1; i <= n; i++ ) {
        cin >> p[i];
    }

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

    for( int i = 1; i < n + 1; i++ ) {
        cout << p[i] << " ";
    }

    cout << endl;

    Compress( n, p, s, l, b );
    TraceBack( n, l, b );
    system( "pause" );
    return 0;
}

//例1: 15 5 37 244 20 50
//例2: 10 12 15 255 1 2

完整代码2(完全参考课本代码)

#include 
using namespace std;

#define N 100

//求灰度值是val的像素所需要的位数,即多少二进制位
int length( int val ) {
    int count = 1;
    val /= 2;

    while( val > 0 ) {
        count++;
        val /= 2;
    }

    return count;
}

//计算最优值
// l[i]存的的从1到i的序列中某个位置i-j+1隔开到最后i这一段的最优长度l[i]
// b[i]应该要存相应段的最大像素位数,但实际存的是每个像素的所占位数
//变量含义  n:序列p中元素个数  p:灰度值序列  s:存从1到i最少存储位数
void Compress( int n, int *p, int *s, int *l, int *b ) {
    int bb[N];
    int Lmax = 255;  //像素序列最大长度
    int header = 11; //每一段的头部信息3(元素最多用8位二进制数表示)+8(段最大长度255)=11
    s[0] = 0;

    //子问题的后边界i
    for( int i = 1; i <= n; i++ ) {
        //计算像素点p需要的存储位数
        b[i] = length( p[i] );
        int bMax = b[i];            //后面段中元素所占位数最大值
        s[i] = s[i - 1] + 1 * bMax; //只有一个元素,所以*1
        l[i] = 1;

        // j为最后一段中元素个数,j取i和255中的最小值
        //可看作从i往前逐渐扩大i到i-1,i再到i-2,i-1,i...
        for( int j = 2; j <= i && j <= Lmax; j++ ) {

            //元素逐渐向前更新,可能会出现比bMax更大的,需要更新
            if( bMax < b[i - j + 1] ) {
                bMax = b[i - j + 1];
            }

            //找到更好的分段  j为段中元素个数
            if( s[i] > s[i - j] + j * bMax ) {
                s[i] = s[i - j] + j * bMax;
                l[i] = j;

            }
        }

        //加上每一段的头部信息3(元素最多用8位二进制数表示)+8(段最大长度255)
        s[i] += header;
    }

    cout << "图像压缩后的最小空间:" << s[n] << endl;
}

//求每一段的分割点
void Traceback( int n, int& i, int s[], int l[] ) {
    if( n == 0 )
        return;

    Traceback( n - l[n], i, s, l ); //p1,p2,p3,...,p(n-l[n])像素序列,最后一段有l[n]个像素
    s[i++] = n - l[n]; //重新为数组赋值,用来存储分段位置,最终i为共分了多少段
}

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[0]:第一段从哪分,s[1]:第二段从哪分...,s[m-1]第m段从哪分
    s[m] = n;//此时m为总段数,设s[m]=n,分割位置为第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;
    }
}


int main() {
    int p[N] = {0} ; //图像灰度数组{p1,p2,p3,p4,p5,p6...pn} 下标从1开始计数,p0是0
    int s[N] = {0}, l[N] = {0}, b[N] = {0};	//s存最优值,l存最优解
    int n;
    cout << "请输入灰度值序列元素个数(最多99):";
    cin >> n;
    cout << "请输入灰度值序列元素:";

    for( int i = 1; i <= n; i++ ) {
        cin >> p[i];
    }

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

    for( int i = 1; i < n + 1; i++ ) {
        cout << p[i] << " ";
    }

    cout << endl;

    Compress( n, p, s, l, b );
    Output( s, l, b, n );
    system( "pause" );
    return 0;
}

//例1: 15 5 37 244 20 50
//例2: 10 12 15 255 1 2

你可能感兴趣的:(银行家算法,c++,动态规划,信息压缩)