基于接缝裁剪的图像压缩 算法导论

给定一副彩色图像,它由一个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]}。

参考链接:http://www.ithao123.cn/content-6605545.html

// 15-8.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>
#include<time.h>
#define ROW 10
#define COL 10
#define Left -1
#define Center 0
#define Right 1
#define INF 65536
using namespace std;

int c[ROW + 1][COL + 2];
int r[ROW + 1][COL + 1];
int maxj;
void carving(int d[][COL + 1])
{
	//给第0,1行赋值
	for (int j = 0; j <= COL; j++)
	{
		c[0][j] = 0;
		r[0][j] = Center;
		c[1][j] = d[1][j];
		r[1][j] = Center;
	}

	//给边界0,COL赋值做哨兵,由此在下面求C[i][j]时不用检查j是否为0或COL;
	for (int i = 0; i <= ROW; i++)
	{
		c[i][0] = INF;
		c[i][COL + 1] = INF;
	}

	//根据递归式求c[i][j],从第二行开始
	for(int i=2;i<=ROW;i++)
		for (int j = 1; j <= COL; j++)
		{
			int temp = INF;
			for (int k = j - 1; k <= j + 1; k++)
			{
				if (c[i - 1][k] < temp)
				{
					temp = c[i - 1][k];
					r[i][j] = k - j;
				}
			}
			c[i][j] = temp+d[i][j];
		}

	//求最后一行最小的值就是最小的裁剪代价
	int temp = INF;
	for (int j = 1; j <= COL; j++)
		if (c[ROW][j] < temp)
		{
			temp = c[ROW][j];
			maxj = j;
		}

	//打印出c[i][j]
	cout << "c[i][j]:\n" << "最小的切缝代价为" << temp << endl;
	for (int i = 1; i <= ROW; i++) {
		for (int j = 1; j <= COL; j++)
			cout << c[i][j] << ‘\t‘;
		cout << endl;
	}
}

//因为从最后一行回推出路径,因此用递归来打印
void display(int row,int maxj)
{
	switch (r[row][maxj])
	{
	   case -1:display(row - 1, maxj - 1); cout << "\\" << endl; break;
	   case 0: display(row - 1, maxj); cout << "||" << endl; break;
	   case  1:display(row - 1, maxj + 1); cout << "//" << endl; break;
	}
}

int main()
{
	//用5-15随机数初始化d[i][j];
	srand((int)time(0));   //产生随机数种子
	int d[ROW + 1][COL + 1];
	for (int i = 0; i <= ROW; i++)
		for (int j = 0; j <= COL; j++)
			d[i][j] = 5 + rand() % 10;
	cout << "d[i][j]:\n";
	for (int i = 1; i <= ROW; i++) {
		for (int j = 1; j <= COL; j++)
			cout << d[i][j] << ‘\t‘;
		cout << endl;
	}
	cout << "----------------------------------------------------------------------------" << endl;

    carving(d);
	cout << "----------------------------------------------------------------------------" << endl;
	display(ROW, maxj);
	while (1);
    return 0;

}

  结果如下图所示:

时间: 2024-10-10 06:21:13

基于接缝裁剪的图像压缩 算法导论的相关文章

动态规划之基于接缝裁剪的图像压缩

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

算法导论第十五章动态规划

概述: 1.动态规划是通过组合子问题的解而解决原问题的. 2.动态规划适用于子问题不是独立的情况,也就是各子问题的包含公共的子子问题. 3.动态规划对每个子问题只求解一次,将其结果保存在一张表中. 4.动态规划的设计步骤:a.描述最优解的结构b.递归定义最优解的值c.按自底向上的方式计算最优觖的值d.由计算出的结构构造一个最优解 15.1钢条切割 钢条切割问题:给定定长的钢条和价格表,求切割方案,使得收益最大.如果n英寸的钢条的价格足够大,则不需要切割. 代码如下: //朴素递归求解钢条切割收益

算法导论学习之插入排序+合并排序

最近准备花时间把算法导论详细的看一遍,强化一下算法和数据结构的基础,将一些总结性的东西写到博客上去. 一.插入排序 算法思想:如果一个数组A,从A[1–n-1]都是有序的,然后我们将A[n]插入到A[1–n-1]的某个合适的位置上去那么就可以保证A[1–n]都是有序的.这就是插入排序的思想:具体实现的时候我们将数组的第一个元素看出有序,然后从第二个元素开始按照上面的步骤进行插入操作,直到插入最后一个元素,然后整个数组都是有序的了. 时间复杂度分析:代码中有两重for循环,很容易看出时间复杂度是n

算法导论5:基数排序 2016.1.5

今天的这个比较神奇,是一个线性复杂度的排序算法O(n),算法导论在这一部分先证明了比较排序的复杂度下界是nlgn,所以基数排序不是基于比较的排序. 其实这种比较方法我们应该都接触过.假设输入的数都是三位以下的数(当然其他位数也可以,类比一下,这里就假设是三位数.两位数.一位数),那么只需要大致3n的复杂度就可以排好序.过程是这样: 先设置辅助空间t[0..9][n] 然后扫第一遍n个数,个位是几就放在t[几]那一行.然后扫一遍t数组,按顺序放回原数组中 然后扫第二遍n个数,十位是几就放在t[几]

算法导论学习之快排+各种排序算法时间复杂度总结

快排是一种最常用的排序算法,因为其平均的时间复杂度是nlgn,并且其中的常数因子比较小. 一.快速排序 快排和合并排序一样都是基于分治的排序算法;快排的分治如下: 分解:对区间A[p,r]进行分解,返回q,使得A[p–q-1]都不大于A[q] A[q+1,r]都大于A[q]; 求解:对上面得到的区间继续递归进行快排 合并:因为快排是原地排序,所以不需要特别的合并 从上可以看出最重要的就是分解函数,其按关键值将数组划分成3部分,其具体实现的过程见代码注释. 我们一般取数组的最后一个元素作为划分比较

《算法导论》 — Chapter 7 高速排序

序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均性能相当好.期望的执行时间为O(nlgn).且O(nlgn)中隐含的常数因子非常小.另外它还能够进行就地排序在虚拟环境中也能非常好的工作. GitHub chapter 7 程序代码下载 原理 高速排序也和合并排序一样,基于分治法,分为分解.解决.合并三个步骤. 分解:数组array[low-hig

堆排序与优先队列&mdash;&mdash;算法导论(7)

1. 预备知识 (1) 基本概念     如图,(二叉)堆一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组A包括两个属性:A.length给出了数组的长度:A.heap-size表示有多少个堆元素保存在该数组中(因为A中可能只有部分位置存放的是堆的有效元素).     由于堆的这种特殊的结构,我们可以很容易根据一个结点的下标i计算出它的父节点.左孩子.右孩子的下标.计算公式如下: parent(i) =

算法导论之插入排序和归并排序

一.创建我们的测试工程 因为我们只理解相应算法,没有什么用户图形,也就用不到UI了,在这儿使用Xcode创建一个基于Mac开发的控制台工程即可,整个工程很简单,一个main函数一个排序类,如下所示. 在Sort类中我们写了关于排序的一些类方法,然后在main函数中进行调用. 二.插入排序 插入排序顾名思义,就是把无序的元素插入到有序的元素当中.<算法导论>中举了一个特为形象的例子,插入排序就如同你在打扑克时摸牌一样,手里的牌是有序的,而你刚摸得牌是是随机的,需要你插入到已经排好序的扑克牌中,这

算法导论——lec 11 动态规划及应用

和分治法一样,动态规划也是通过组合子问题的解而解决整个问题的.分治法是指将问题划分为一个一个独立的子问题,递归地求解各个子问题然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题不是相互独立的情况,即各个子问题包含公共的子子问题.在这种情况下,如果用分治法会多做许多不必要的工作,重复求解相同的子子问题.而动态规划将每个子问题的解求解的结果放在一张表中,避免了重复求解. 一. 动态规划介绍 1. 动态规划方法介绍: 动态规划主要应用于最优化问题, 而这些问题通常有很多可行解,而我们希