Advertisement
Guest User

Terrible Seam Carving

a guest
Jul 9th, 2017
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.81 KB | None | 0 0
  1. #define cimg_display 0
  2. #include "lib/CImg.h"
  3. #include "lib/slVector.H"
  4.  
  5. #include <math.h>   //For pow, abs, maybe a few other things
  6. using namespace cimg_library;
  7.  
  8. //Get the Lab values of a pixel. For points not on the 2d array, "wrap around". The algorithm below should document itself really.
  9. SlVector3 getPix ( int row, int col, int wid, int hei, int actWid, SlVector3 *image ) {
  10.    
  11.     //SWITCH TO RETURN 0.0 AT BOUNDARY IF RUSULTS UNFAVORABLE
  12.     //Get wrapped pixel value if it's not withing image bounds, for energy computation.
  13.     //Handle values above or to left of image
  14.     //while ( row < 0 ) row += hei;
  15.     //while ( col < 0 ) col += wid;
  16.     //Handle values below or to right of image
  17.     //row %= hei; col %= wid;
  18.    
  19.     //If beyond boundary, just return a 0 vector.
  20.     if ( row < 0 || row >= hei || col < 0 || col >= wid )
  21.        
  22.         return SlVector3 ( 0, 0, 0 );
  23.     return image[row*actWid + col];
  24. }
  25.  
  26. //Get bounded array value, that is, get valid array value for point in array, get twice highest possible value for point outside array.
  27. double getArBd ( int row, int col, int wid, int hei, double *en, double high ) {
  28.    
  29.     //Return something greater than any possible energy at boundaries, so that seams do not go across boundaries
  30.     if ( row < 0 || row >= hei || col < 0 || col >= wid )
  31.        
  32.         return high * 2;
  33.     return en[row*wid + col];
  34. }
  35.  
  36. //Shift a row/column in array by 1 starting from some point. This is the double array variant.
  37. void shift ( int rowStart, int colStart, int wid, int hei, int actWid, bool vert, double *array ) {
  38.    
  39.     for ( int ind = (vert?rowStart:colStart); ind < (vert?hei:wid) - 1; ind ++ )
  40.  
  41.         array[(vert?ind:rowStart)*actWid + (vert?colStart:ind)] = array[(vert?ind+1:rowStart)*actWid + (vert?colStart:ind+1)];
  42. }
  43.  
  44. //Shift a row/column in array by 1 starting from some point. This is the SlVector3 array variant.
  45. void shift ( int rowStart, int colStart, int wid, int hei, int actWid, bool vert, SlVector3 *array ) {
  46.    
  47.     for ( int ind = (vert?rowStart:colStart); ind < (vert?hei:wid) - 1; ind ++ )
  48.  
  49.         array[(vert?ind:rowStart)*actWid + (vert?colStart:ind)] = array[(vert?ind+1:rowStart)*actWid + (vert?colStart:ind+1)];
  50. }
  51.  
  52. //Compute the gradient at each pixel using Scharr operator
  53. double computeEnergy ( int row, int col, int wid, int hei, int actWid, SlVector3 *image ) {
  54.    
  55.     //If ranges are invalid, return early.
  56.     if ( row < 0 || row >= hei || col < 0 || col >= wid ) return 0.0;
  57.    
  58.     //Calculate the horizontal and vertical gradients of each pixel, using common Scharr operator.
  59.     SlVector3 difV = 3 * getPix( row-1, col-1, wid, hei, actWid, image )
  60.                    +10 * getPix( row-1, col  , wid, hei, actWid, image )
  61.                    + 3 * getPix( row-1, col+1, wid, hei, actWid, image )
  62.                    - 3 * getPix( row+1, col-1, wid, hei, actWid, image )
  63.                    -10 * getPix( row+1, col  , wid, hei, actWid, image )
  64.                    - 3 * getPix( row+1, col+1, wid, hei, actWid, image );
  65.    
  66.     SlVector3 difH = 3 * getPix( row-1, col-1, wid, hei, actWid, image )
  67.                    +10 * getPix( row  , col-1, wid, hei, actWid, image )
  68.                    + 3 * getPix( row+1, col-1, wid, hei, actWid, image )
  69.                    - 3 * getPix( row-1, col+1, wid, hei, actWid, image )
  70.                    -10 * getPix( row  , col+1, wid, hei, actWid, image )
  71.                    - 3 * getPix( row+1, col+1, wid, hei, actWid, image );
  72.  
  73.     //Return the sum of the magnitudes of the gradients
  74.     return sqrt ( sqrMag ( difV ) + sqrMag ( difH ) );
  75. }
  76.  
  77. //Get the minimum of three numbers.
  78. inline double minOfThree ( double a, double b, double c ) {
  79.    
  80.     return ( a < b ? ( a < c ? a : c ) : ( b < c ? b : c ) );
  81. }
  82.  
  83. int main(int argc, char *argv[]) {
  84.  
  85.     //Debug variables
  86.     bool debugWriteEnergy = false;
  87.     if ( argc == 6 )
  88.        
  89.         debugWriteEnergy = atoi( argv[5] );
  90.    
  91.     //Open the image proper and store the input and output dimensions and their differences
  92.     CImg<double> input(argv[1]);
  93.     CImg<double> lab = input.RGBtoLab();
  94.     int inHei = input.height();
  95.     int inWid = input.width();
  96.     int outHei = atoi( argv[4] );
  97.     int outWid = atoi( argv[3] );
  98.     int difHei = inHei - outHei;
  99.     int difWid = inWid - outWid;
  100.  
  101.     //Map all of lab's pixels to an SlVector3 array for easy pixel manipulation
  102.     SlVector3 *image = new SlVector3 [inWid * inHei];
  103.     for ( int row = 0; row < inHei; row++ )
  104.  
  105.         for ( int col = 0; col < inWid; col++ )
  106.  
  107.             for ( int iter = 0; iter < 3; iter ++ )
  108.            
  109.                 image[row*inWid + col][iter] = lab( col, row, iter );
  110.  
  111.     //Get the energy and max energy
  112.     double maxEnergy = 0.0;
  113.     double *energy = new double [inWid*inHei];
  114.     for ( int row = 0; row < inHei; row++ ) {
  115.  
  116.         for ( int col = 0; col < inWid; col++ ) {
  117.  
  118.             double en = computeEnergy ( row, col, inWid, inHei, inWid, image );
  119.             energy[row*inWid + col] = en;
  120.             maxEnergy = ( maxEnergy < en ? en : maxEnergy );
  121.         }
  122.     }
  123.    
  124.     if ( debugWriteEnergy ) {
  125.        
  126.         //Set all the image pixels based on their energy
  127.         for ( int row = 0; row < inHei; row++ ) {
  128.  
  129.             for ( int col = 0; col < inWid; col++ ) {
  130.  
  131.                 double set = pow ( energy[row*inWid + col] / maxEnergy, 0.25 );
  132.                 image[row*inWid + col][0] = (int) ( 100 * set );
  133.                 image[row*inWid + col][1] = 0;
  134.                 image[row*inWid + col][2] = 0;
  135.             }
  136.         }
  137.     } else {
  138.        
  139.         //Select a certain number of horizontal seams and remove them.
  140.         for ( int hSeams = 0; hSeams < difHei; hSeams++ ) {
  141.  
  142.             //Create and calculate the seam values
  143.             //Set the first column of seam values equal to the energies at those points.
  144.             double *seamMap = new double [inWid*(inHei-hSeams)];
  145.             for ( int row = 0; row < inHei-hSeams; row++ )
  146.                
  147.                 seamMap[row*inWid] = energy[row*inWid];
  148.  
  149.             //For every subsequent column, set each pixel's seam value equal to the sum of its energy and the least of all the left neighbor's energies.
  150.             for ( int col = 1; col < inWid ; col++ )
  151.  
  152.                 for ( int row = 0; row < inHei-hSeams; row++ )
  153.                    
  154.                     seamMap[row*inWid + col] = energy[row*inWid + col]
  155.                                  + minOfThree ( getArBd(row-1,col-1,inWid,inHei-hSeams,seamMap,maxEnergy*inWid),
  156.                                         getArBd(row  ,col-1,inWid,inHei-hSeams,seamMap,maxEnergy*inWid),
  157.                                         getArBd(row+1,col-1,inWid,inHei-hSeams,seamMap,maxEnergy*inWid));
  158.  
  159.             //Store each row index for every column, starting from last and working backward. This way we can easily get each pixel of the seam.
  160.             int *rowIndicies = new int [inWid];
  161.  
  162.             //Find lowest value seam ending by looking at ending columns and finding lowest cumulative seam.
  163.             rowIndicies[inWid-1] = 0;
  164.             double minSeam = getArBd ( 0, inWid-1, inWid, inHei-hSeams, seamMap, maxEnergy*inWid );
  165.             for ( int row = 1; row < inHei - hSeams; row++ ) {
  166.  
  167.                 double current = getArBd ( row, inWid-1, inWid, inHei-hSeams, seamMap, maxEnergy*inWid );
  168.                 if ( current < minSeam ) { minSeam = current; rowIndicies[inWid-1] = row; }
  169.             }
  170.            
  171.             //Work from ending to recover entire least important seam.
  172.             for ( int col = inWid - 2; col >= 0; col -- ) {
  173.  
  174.                 double a = getArBd ( rowIndicies[col+1]-1, col, inWid, inHei-hSeams, seamMap, maxEnergy*inWid );
  175.                 double b = getArBd ( rowIndicies[col+1]  , col, inWid, inHei-hSeams, seamMap, maxEnergy*inWid );
  176.                 double c = getArBd ( rowIndicies[col+1]+1, col, inWid, inHei-hSeams, seamMap, maxEnergy*inWid );
  177.                 double min = minOfThree ( a, b, c );
  178.                 if ( min == a )         rowIndicies[col] = rowIndicies[col+1]-1;
  179.                 else if ( min == b )    rowIndicies[col] = rowIndicies[col+1]  ;
  180.                 else                    rowIndicies[col] = rowIndicies[col+1]+1;
  181.             }
  182.  
  183.             //Call shift function on the seam point of each column of image and energy; get rid of that seam in the image and in energy map
  184.             for ( int col = 0; col < inWid; col ++ ) {
  185.  
  186.                 shift ( rowIndicies[col], col, inWid, inHei - hSeams, inWid, true, image );
  187.                 shift ( rowIndicies[col], col, inWid, inHei - hSeams, inWid, true, energy );
  188.             }
  189.  
  190.             //Recompute the energy at the locations where the seam was removed, and at the locations exactly below that
  191.             for ( int col = 0; col < inWid; col ++ ) {
  192.  
  193.                 for ( int dif = -2; dif <= 2; dif ++ ) {
  194.  
  195.                     int index = rowIndicies[col] + dif;
  196.                     if ( index >= 0 && index < (inHei-hSeams) )
  197.  
  198.                         energy[(index)*inWid + col] = computeEnergy ( index, col, inWid, inHei-hSeams-1, inWid, image );
  199.                 }
  200.             }
  201.             delete [] rowIndicies;
  202.             delete [] seamMap;
  203.         }
  204.  
  205.         //Select a certain number of horizontal seams and remove them...
  206.         for ( int vSeams = 0; vSeams < difWid; vSeams++ ) {
  207.  
  208.             //Create and calculate the seam values
  209.             //Set the first row of seam values equal to the energies at those points.
  210.             double *seamMap = new double [(inWid-vSeams)*outHei];
  211.             for ( int col = 0; col < inWid-vSeams; col++ )
  212.                
  213.                 seamMap[col] = energy[col];
  214.  
  215.             //For every subsequent row, set each pixel's seam value equal to the sum of its energy and the least of all the top neighbor's energies.
  216.             for ( int row = 1; row < outHei ; row++ )
  217.  
  218.                 for ( int col = 0; col < inWid-vSeams; col++ )
  219.                    
  220.                     seamMap[row*(inWid-vSeams) + col] = energy[row*inWid + col]
  221.                                                       + minOfThree ( getArBd(row-1,col-1,inWid-vSeams,outHei,seamMap,maxEnergy*outHei),
  222.                                                                      getArBd(row-1,col  ,inWid-vSeams,outHei,seamMap,maxEnergy*outHei),
  223.                                                                      getArBd(row-1,col+1,inWid-vSeams,outHei,seamMap,maxEnergy*outHei));
  224.  
  225.             //Store each column index for every row, starting from last and working backward. This way we can easily get each pixel of the seam.
  226.             int *colIndicies = new int [outHei];
  227.  
  228.             //Find lowest value seam ending by looking at ending columns and finding lowest cumulative seam.
  229.             colIndicies[outHei-1] = 0;
  230.             double minSeam = getArBd ( outHei-1, 0, inWid-vSeams, outHei, seamMap, maxEnergy*outHei );
  231.             for ( int col = 1; col < inWid-vSeams; col++ ) {
  232.  
  233.                 double current = getArBd ( outHei-1, col, inWid-vSeams, outHei, seamMap, maxEnergy*inWid );
  234.                 if ( current < minSeam ) { minSeam = current; colIndicies[outHei-1] = col; }
  235.             }
  236.            
  237.             //Work from ending to recover entire least important seam.
  238.             for ( int row = outHei - 2; row >= 0; row -- ) {
  239.  
  240.                 double a = getArBd ( row, colIndicies[row+1]-1, inWid-vSeams, outHei, seamMap, maxEnergy*outHei );
  241.                 double b = getArBd ( row, colIndicies[row+1]  , inWid-vSeams, outHei, seamMap, maxEnergy*outHei );
  242.                 double c = getArBd ( row, colIndicies[row+1]+1, inWid-vSeams, outHei, seamMap, maxEnergy*outHei );
  243.                 double min = minOfThree ( a, b, c );
  244.                 if ( min == a )         colIndicies[row] = colIndicies[row+1]-1;
  245.                 else if ( min == b )    colIndicies[row] = colIndicies[row+1]  ;
  246.                 else                    colIndicies[row] = colIndicies[row+1]+1;
  247.             }
  248.  
  249.             //Call shift function on the seam point of each column of image and energy; get rid of that seam in the image and in energy map
  250.             for ( int row = 0; row < outHei; row ++ ) {
  251.  
  252.                 shift ( row, colIndicies[row], inWid-vSeams, outHei, inWid, false, image );
  253.                 shift ( row, colIndicies[row], inWid-vSeams, outHei, inWid, false, energy );
  254.             }
  255.  
  256.             //Recompute the energy at the locations where the seam was removed, and at the locations exactly below that
  257.             for ( int row = 0; row < outHei; row ++ ) {
  258.  
  259.                 for ( int dif = -2; dif <= 2; dif ++ ) {
  260.  
  261.                     int index = colIndicies[row] + dif;
  262.                     if ( index >= 0 && index < (inWid-vSeams) )
  263.  
  264.                         energy[row*inWid + index] = computeEnergy ( row, index, inWid-vSeams-1, outHei, inWid, image );
  265.                 }
  266.             }
  267.             delete [] colIndicies;
  268.             delete [] seamMap;
  269.         }
  270.     }
  271.  
  272.     delete [] energy;
  273.    
  274.     //Open the output image and place all map all the processing array points to it.
  275.     CImg<double> output( outWid, outHei, input.depth(), input.spectrum(), 0 );
  276.     for ( int row = 0; row < outHei; row++ )
  277.  
  278.         for ( int col = 0; col < outWid; col++ )
  279.  
  280.             for ( int iter = 0; iter < 3; iter ++ )
  281.            
  282.                 output( col, row, iter ) = image[row*inWid + col][iter];
  283.    
  284.     //Save the image as the appropriate format, deallocate processing array and exit.
  285.     CImg<double> rgb = output.LabtoRGB();
  286.     if ( strstr( argv[2], "png" ) )         rgb.save_png( argv[2] );
  287.     else if ( strstr( argv[2], "jpg" ) )    rgb.save_jpeg( argv[2] );
  288.    
  289.     delete [] image;
  290.     return 0;
  291. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement