SeamCarving是一款图像智能缩放的技术,他能在改变图像大小的同时,不影响图像的重要内容。
由于是小组合作,这里就只发布我做的第一部分,裁剪线的删除和增加部分,以及GUI部分不做叙述.
如上,在这张图中,明显重要的内容就是飞鸟,而背景则不重要。传统的缩放技术,一是等比例缩放,如果纵向和横线缩放的比例不一致,就会导致图像严重变形;二是裁剪,那就会把边缘的飞鸟也裁剪掉。
示范图例如下:
从以上的对比图中可以看出seamcarving可以较好的保存图像的重要部分,并看起来不那么畸形。那么我们如何来实现呢?
1.计算能量图
首先我们要知道,seamcarving是通过删除或插入水平或垂直的裁剪线来达到智能缩放的目的,这个裁剪线就是图像能量最小的通道。那么我们就要先得到图像的能量图再来根据能量图得到裁剪线。而能量图是由梯度图计算得来。图像某处的变化越大那么相应的梯度以及能量也就越大。
可由sobel算子与图像三个通道相乘来得到它的梯度能量图.
2.计算能量累加图
能量累加图的由以下公式获得:
function seam(pic,n,n2)%pic是要裁剪的图片,n是纵向裁剪线的条数,n2是横向裁剪线的条数
%UNTITLED 此处显示有关此函数的摘要
% seam carving赵若妃组:谢沛蓉第一部分
%pic=imread('timg2.jpg');
%n=1;
%n2=2;
%将图像分RGB三个通道做处理
%pic=imread('timg2.jpg');
picR = pic(:,:,1);
picG = pic(:,:,2);
picB = pic(:,:,3);
%将图像内容变成可做运算的值
picRI=double(picR);
picGI=double(picG);
picBI=double(picB);
%原图像的宽和高
[H,W]=size(picRI);
%求R,G,B三个通道上的梯度图
after_picr=zeros(H,W);
after_picg=zeros(H,W);
after_picb=zeros(H,W);
gradient=zeros(H,W);
%定义sobel算子
sobelx=[-1,-2,-1;0,0,0;1,2,1;];
sobely=[-1,0,1;-2,0,2;-1,0,1;];
%将sobel算子与原图的三个通道图的子模块分别做点乘,得到三个通道的梯度图
for i=2:H-1
for j=2:W-1
subR=picRI(i-1:i+1,j-1:j+1);
tempR1=subR.*sobelx;
tempR2=subR.*sobely;
x1=sum(sum(tempR1));
y1=sum(sum(tempR2));
after_picr(i,j)=sqrt(x1*x1+y1*y1);
subG=picGI(i-1:i+1,j-1:j+1);
tempG1=subG.*sobelx;
tempG2=subG.*sobely;
x2=sum(sum(tempG1));
y2=sum(sum(tempG2));
after_picg(i,j)=sqrt(x2*x2+y2*y2);
subB=picBI(i-1:i+1,j-1:j+1);
tempB1=subB.*sobelx;
tempB2=subB.*sobely;
x3=sum(sum(tempB1));
y3=sum(sum(tempB2));
after_picb(i,j)=sqrt(x3*x3+y3*y3);
end
end
%以picR,picG,picB三个图像边缘的原值来填充三个梯度图的边缘.
for i=1:H
after_picr(i,1)=picRI(i,1);
after_picr(i,W)=picRI(i,W);
after_picg(i,1)=picGI(i,1);
after_picg(i,W)=picGI(i,W);
after_picb(i,1)=picBI(i,1);
after_picb(i,W)=picBI(i,W);
end
for i=1:W
after_picr(1,i)=picRI(1,i);
after_picr(H,i)=picRI(H,i);
after_picg(1,i)=picGI(1,i);
after_picg(H,i)=picGI(H,i);
after_picb(1,i)=picBI(1,i);
after_picb(H,i)=picBI(H,i);
end
%将三个通道的梯度图相加,得到原图像的梯度图
for i=1:H
for j=1:W
gradient(i,j)=after_picr(i,j)+after_picg(i,j)+after_picb(i,j);
end
end
%定义能量累加图
energy=zeros(H,W);
%能量矩阵的副本
energy2=zeros(H,W);
%对能量累加图第一行的填充
for i=1:W
energy(1,i)=gradient(1,i);
energy2(1,i)=energy(1,i);
end
%根据公式求其能量累加图
for i=2:H
for j=1:W
if(j==1||j==W)
if j==1
energy(i,1)=gradient(i,1)+min(energy(i-1,1),energy(i-1,2));
energy2(i,1)=energy(i,1);
else
energy(i,W)=gradient(i,W)+min(energy(i-1,W),energy(i-1,W-1));
energy2(i,W)=energy(i,W);
end
else
tempEn=min(energy(i-1,j-1),energy(i-1,j));
energy(i,j)=gradient(i,j)+min(tempEn,energy(i-1,j+1));
energy2(i,j)=energy(i,j);
end
end
end
%对能量图的归一化,方便显示
energy1=mapminmax(energy,0,255);
%定义一个转置的梯度矩阵
gradientTrans=zeros(W,H);
for i=1:W
for j=1:H
gradientTrans(i,j)=gradient(j,i);
end
end
%定义一个转置的能量矩阵
energyTrans=zeros(W,H);
%转置的能量矩阵的副本
energyTrans2=zeros(W,H);
%对转置能量累加图第一行的填充
for i=1:H
energyTrans(1,i)=gradientTrans(1,i);
energyTrans2(1,i)=energyTrans(1,i);
end
%根据公式求转置梯度矩阵的能量累加图
for i=2:W
for j=1:H
if(j==1||j==H)%当其在边缘时,取上一行两个能量中较小的一个
if j==1
energyTrans(i,1)=gradientTrans(i,1)+min(energyTrans(i-1,1),energyTrans(i-1,2));
energyTrans2(i,1)=energyTrans(i,1);
else
energyTrans(i,H)=gradientTrans(i,H)+min(energyTrans(i-1,H),energyTrans(i-1,H-1));
energyTrans2(i,H)=energyTrans(i,H);
end
else
tempEn=min(energyTrans(i-1,j-1),energyTrans(i-1,j));
energyTrans(i,j)=gradientTrans(i,j)+min(tempEn,energyTrans(i-1,j+1));
energyTrans2(i,j)= energyTrans(i,j);
end
end
end
%对转置能量图的归一化,方便显示
energyTrans1=mapminmax(energyTrans,0,255);
%选取能量图最后一行能量最小的一点,并记录坐标,纵向裁剪线
indexVer=zeros(H,n);%要修改的所有能量线的最后一点的集合
last=energy2(H,:);
temp=zeros(1,3);
lastMax=max(last)+1;
%找出要修改的能量线的最后一点的集合
for i=1:n
[~,lastMin]=min(last);
last(lastMin)=lastMax;
indexVer(H,i)=lastMin;
end
maxEnergy=max(max(energy2))+1;
for i=1:n
for j=H-1:-1:1
index=indexVer(j+1,i);%裁剪线上一点的坐标
if(index==1)%考虑边缘情况
temp(1)=maxEnergy;
temp(2)=energy2(j,index);
temp(3)=energy2(j,index+1);
end
if(index==W)%考虑边缘情况
temp(1)=energy2(j,index-1);
temp(2)=energy2(j,index);
temp(3)=maxEnergy;%当某一点已在最小裁剪线中,在副本里将其定义为最大值,下一条裁剪线则不会重叠
end
if(index~=W&&index~=1)
temp(1)=energy2(j,index-1);
temp(2)=energy2(j,index);
temp(3)=energy2(j,index+1);
end
if(min(temp)==temp(1))
indexVer(j,i)=index-1;
energy2(j,index-1)=maxEnergy;
end
if(min(temp)==temp(2))
indexVer(j,i)=index;
energy2(j,index)=maxEnergy;
end
if(min(temp)==temp(3))
indexVer(j,i)=index+1;
energy2(j,index+1)=maxEnergy;
end
end
end
%选取转置能量图最后一行能量最小的一点,并记录坐标,横向
indexHor=zeros(W,n2);%要删减的能量线的最后一点的集合
lastTrans=energyTrans2(W,:);
temp2=zeros(1,3);
lastTransMax=max(lastTrans)+1;
%找出要删减的能量线的最后一点的集合
for i=1:n2
[~,lastTransMin]=min(lastTrans);
lastTrans(lastTransMin)=lastTransMax;
indexHor(W,i)=lastTransMin;
end
maxEnergyTrans=max(max(energyTrans2))+1;
for i=1:n2
for j=W-1:-1:1
indexHor(j+1,i)
index=indexHor(j+1,i);
if(index==1)
temp2(1)=maxEnergyTrans;
temp2(2)=energyTrans2(j,index);
temp2(3)=energyTrans2(j,index+1);
end
if(index==H)
temp2(1)=energyTrans2(j,index-1);
temp2(2)=energyTrans2(j,index);
temp2(3)=maxEnergyTrans;
end
if((index~=H)&&(index~=1))
temp2(1)=energyTrans2(j,index-1);
temp2(2)=energyTrans2(j,index);
temp2(3)=energyTrans2(j,index+1);
end
if(min(temp2)==temp2(1))
indexHor(j,i)=index-1;
energyTrans2(j,index-1)=maxEnergyTrans;
end
if(min(temp2)==temp2(2))
indexHor(j,i)=index;
energyTrans2(j,index)=maxEnergyTrans;
end
if(min(temp2)==temp2(3))
indexHor(j,i)=index+1;
energyTrans2(j,index+1)=maxEnergyTrans;
end
end
end
%显示裁剪线
for i=1:H
for j=1:n
picR(i,indexVer(i,j))=255;
picG(i,indexVer(i,j))=0;
picB(i,indexVer(i,j))=0;
end
end
for i=1:W
for j=1:n2
picR(indexHor(i,j),i)=255;
picG(indexHor(i,j),i)=0;
picB(indexHor(i,j),i)=0;
end
end
pic2(:,:,1)=picR;
pic2(:,:,2)=picG;
pic2(:,:,3)=picB;
figure(1);
gradient=uint8(gradient);
imshow(gradient);
figure(2);
energy1=uint8(energy1);
imshow(energy1);
figure(3);
gradientTrans=uint8(gradientTrans);
imshow(gradientTrans);
figure(4);
energyTrans1=uint8(energyTrans1);
imshow(energyTrans1);
figure(5);
lastimg=uint8(pic2);
imshow(lastimg);
原图:
能量图:
能量累加图:
裁剪线的显示: