基于接缝裁剪的图像压缩

题目出自《算法导论》P234,T15-8

给定一副彩色图像,它由一个mxn的像素数组A[1..m,1..n]构成,每个像素是一个红绿蓝(RGB)亮度的三元组。假定我们希望轻度压缩这幅图像。具体地,我们希望从每一行中删除一个像素,使得图像变窄一个像素。但为了避免影响视觉效果,我们要求相邻两行中删除的像素必须位于同一列或相邻列。也就是说,删除的像素构成从顶端行到底端行的一条“接缝”(seam),相邻像素均在垂直或对角线方向上相邻。

        a.证明:可能的接缝数量是m的指数函数,假定n>1.

        第1行有n种可能选取像素点方式,第2到m行中每行有2-3种(可能选中A[i][j-1],A[i][j],A[i][j+1]. (j=1 or j=n时,是2种可能)),所以总共有至少大于n*2^(m-1).

        b 假定现在对每个像素A[i,j]我们都已计算出一个实型的“破坏度”d[i,j],表示删除像素A[i,j]对图像可视效果的破坏程度。直观地,一个像素的破坏度越低,它与相邻像素的相似度越高。再假定一条接缝的破坏度定义为包含的响度的破坏度之和。设计算法,寻找破坏度最低的接缝。分析算法的时间复杂度。

求破坏度最低的接缝很简单,c[i,j]记录接缝走到当前像素的最低破坏度。从第一行开始,c[i,j]只有当前像素的破坏度,直接赋值即可;第i行,每个像素的上一个像素来源一共有三个,左上、正上和右上方,每次计算c[i,j]时,需要去三种情况中破坏度最低的情况然后加上当前像素的破坏度,就是接缝走到当前像素的最低破坏度。递推式为c[i][j]=d[i][j]+min{c[i-1][j-1],c[i-1][j],c[i-1][j+1]}.

难的是怎样求这个路径。

有两种方法,

方法一算法如下:

1. 很容易确定在第m行(第0行的d[i][j]数据全为0)中c[m][j](1=<j<=n)的最小值,记录下此时的j值(其实这时已经初步锁定了路线了)。

2. 根据这个j值向上查找,在第m-1行,比较c[m-1][j-1],c[m-1][j],c[m-1][j+1]的大小(因为c[m][j]只可能由这三种情况其中的一种而得到)。记录此时三个值中最小值的j值(实际上进一步缩小了路线范围)。

3. 根据这个j值向上查找,在第m-2行,比较c[m-2][j-1],c[m-2][j],c[m-2][j+1]的大小(因为c[m-1][j]只可能由这三种情况其中的一种而得到)。记录此时三个值中最小值的j值。

4.  ...

      ...

直到i=1为止。


在这里有一个求解三个值中最小值的j值的小技巧。

void SC_SEQUENCE(int **c,int i,int j)//i行j列
{//输出一条接缝
    int T;
    if (i==0)
       return;  //递归出口
    else
	{
	    if (j==n)
	    {
		T=c[i][j]>c[i][j-1]?j-1:j;
	    }
	    else if (j==1)
	    {
		T=c[i][j]>c[i][j+1]?j+1:j;
	    }
	    else
	    {
		if (c[i][j]>c[i][j-1])
		{
		     T=j-1;      //记住这种小技巧,省得比较来比较去乱七八糟<span style="font-family: Arial;">的</span>
		     if (c[i][j-1]>c[i][j+1])
		     {
			T=j+1;
		      }
		}
		else
		{
		      T=j;
		      if (c[i][j]>c[i][j+1])
		      {
			 T=j+1;
		       }
		 }
	     }
	}
	OP_SEQUENCE(c,i-1,T);
	cout<<"第"<<i<<"行"<<"第"<<T<<"列像素点->";  //用递归来逆向输出
}

方法二:

动归输出普遍采用的方法,递归法,可以比较最大公共子序列的输出和活动选择问题的动归法的输出,可以得出一般结论。

r[i][j]记录当前结点来自于上一行的那个像素(用-1,0,1分别表示左上,正上和右上方向),和c[i][j]是同时被确定的。

代码如下:

调用 print(m,t,r);  //t是最后一行c[m][j](1=<j<=n)的最小值的j值。

void print(int i,int j,int r[m+1][n+1])    
{    
        if(i=0)
           return ;    
        if(r[i][j]==0) 
        {   
           print(i-1,j,r);
           printf("%d ",j);
         }   
        else if(r[i][j]==-1)     
        
        {   
           print(i-1,j-1,r); //说明 print(i,j,r) 是由print(i-1,j-1,r)进化而来
           printf("%d ",j);
         }     
        else    
        {   
           print(i-1,j+1,r);
           printf("%d ",j);
         }   
}    


参考资料:

http://blog.csdn.net/z84616995z/article/details/38562467

http://blog.csdn.net/yuan3683084/article/details/35994157

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值