SeamCarving相关介绍及MATLAB实现

SeamCarving是一款图像智能缩放的技术,他能在改变图像大小的同时,不影响图像的重要内容。

由于是小组合作,这里就只发布我做的第一部分,裁剪线的删除和增加部分,以及GUI部分不做叙述.

SeamCarving相关介绍及MATLAB实现_第1张图片

 如上,在这张图中,明显重要的内容就是飞鸟,而背景则不重要。传统的缩放技术,一是等比例缩放,如果纵向和横线缩放的比例不一致,就会导致图像严重变形;二是裁剪,那就会把边缘的飞鸟也裁剪掉。

示范图例如下:



SeamCarving相关介绍及MATLAB实现_第2张图片

从以上的对比图中可以看出seamcarving可以较好的保存图像的重要部分,并看起来不那么畸形。那么我们如何来实现呢?

1.计算能量图

 首先我们要知道,seamcarving是通过删除或插入水平或垂直的裁剪线来达到智能缩放的目的,这个裁剪线就是图像能量最小的通道。那么我们就要先得到图像的能量图再来根据能量图得到裁剪线。而能量图是由梯度图计算得来。图像某处的变化越大那么相应的梯度以及能量也就越大。

可由sobel算子与图像三个通道相乘来得到它的梯度能量图.

2.计算能量累加图

    能量累加图的由以下公式获得:

  M(i,j) =e(i,j)+min(M(i−1,j−1),M(i−1,j),M(i−1,j+1))
 (此公式摘自论文 Seam Carving for Content-Aware Image Resizing)
    在这里我们可以看到利用动态规划可以找到最优的裁剪线。第一步是将图像从第二行遍历到最后一行。计算累积最小能量。M(i,j)为该点的累积能量值,e(i,j)为该点由第一步计算得出的能量值.
3.寻找最小裁剪线
    由于能量累积图中我们是由前往后累加计算,第一行并未得到累加,那么找纵向裁剪线时,则应该从最后一行往前寻找裁剪线的点.
    找到最后一行能量最小的点,再根据坐标找前一行与之相邻的点中能量最小的点,如此递归,当贯穿了整个图像后,即得到了裁剪线.可将已经找到的裁剪线的坐标点做个标记,以防2次找线时有重叠,导致每行像素点不齐.
    而纵向裁剪线则可将图片转置,并重新计算能量累加图,同上找最佳的裁剪线,并记录坐标.
    若是缩小图片,可直接将找出的裁剪线删除,若是放大图片,以防只在同一个位置插入无用的像素,导致图像的重心偏移,可以在多条裁剪线(从小到大)的一个方向插入与裁剪线相同或相邻的像素点.

4.代码实现
.)))

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);

 

原图:

SeamCarving相关介绍及MATLAB实现_第3张图片

能量图:

SeamCarving相关介绍及MATLAB实现_第4张图片

能量累加图:

SeamCarving相关介绍及MATLAB实现_第5张图片

裁剪线的显示:

SeamCarving相关介绍及MATLAB实现_第6张图片






你可能感兴趣的:(机器视觉,seamcarving,matlab,数字图像)