/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/
   
#include "mrilib.h"

#define XYNUM    0
#define YXNUM    1
#define XDOTYNUM 2
#define YDOTXNUM 3

int main( int argc , char * argv[] )
{
   MRI_IMARR * imar, *inimar;
   MRI_IMAGE * im ;
   char prefix[240] = "cat" , fnam[256] ;
   char suffix[50] = "\0";
   char *scale_image = NULL;
   int iarg , ii,jj , nx,ny , nxim,nyim ;
   int nmode = XYNUM, nxin, nyin ;
   int gap = 0, ScaleInt=0, force_rgb_out = 0, matrix_size_from_scale = 0;
   byte  gap_col[3] = {255, 20, 128} ;
   MRI_IMAGE *imscl=NULL;
   int kkk, nscl=-1, resix=-1, resiy=-1, force_rgb_at_input=0, N_byte = 0, N_rgb = 0;
   float *scl=NULL;
   byte *scl3=NULL, *rgb=NULL;
   char name[100];
   void *ggg=NULL;
    
   if( argc < 4 ){
      printf("Usage: imcat [options] fname1 fname2 etc.\n"
             "Puts a set images into an image matrix (IM) \n"
             " montage of NX by NY images.\n"
             " The minimum set of input is N images (N >= 1).\n"
             " If need be, images are reused until the desired\n"
             " NX by NY size is achieved.\n"
             " \n"
             "OPTIONS:\n"
             " ++ Options for editing, coloring input images:\n"
             "  -scale_image SCALE_IMG: Multiply each image IM(i,j) in output\n"
             "                          image matrix IM by the color or intensity\n"
             "                          of the pixel (i,j) in SCALE_IMG.\n"
             "  -scale_intensity: Instead of multiplying by the color of \n"
             "                          pixel (i,j), use its intensity \n"
             "                          (average color)\n"
             "  -rgb_out: Force output to be in rgb, even if input is bytes.\n"
             "            This option is turned on automatically in certain cases.\n"
             "  -res_in RX RY: Set resolution of all input images to RX by RY pixels.\n"
             "                 Default is to make all input have the same\n"
             "                 resolution as the first image.\n"
             " ++ Options for output:\n"
             "  -prefix ppp = Prefix the output files with string 'ppp'\n"
             "  -matrix NX NY: Specify number of images in each row and column \n"
             "                 of IM at the same time. \n"
             "  -nx NX: Number of images in each row (3 for example below)\n"
             "  -ny NY: Number of images in each column (4 for example below)\n"
             "      Example: If 12 images appearing on the command line\n"
             "               are to be assembled into a 3x4 IM matrix they\n"
             "               would appear in this order:\n"
             "                 0  1  2\n"
             "                 3  4  5\n"
             "                 6  7  8\n"
             "                 9  10 11\n"
             "    NOTE: The program will try to guess if neither NX nor NY \n"
             "          are specified.\n"
             "  -matrix_from_scale: Set NX and NY to be the same as the \n"
             "                      SCALE_IMG's dimensions. (needs -scale_image)\n"
             "  -gap G: Put a line G pixels wide between images.\n"
             "  -gap_col R G B: Set color of line to R G B values.\n"
             "                  Values range between 0 and 255.\n"
             "\n"
             "Example 0 (assuming afni is in ~/abin directory):\n"
             "   Resizing an image:\n"
             "   imcat -prefix big -res_in 1024 1024 \\\n"
             "         ~/abin/face_zzzsunbrain.jpg \n"
             "   imcat -prefix small -res_in 64 64 \\\n"
             "         ~/abin/face_zzzsunbrain.jpg \n"
             "   aiv small.ppm big.ppm \n"
             "\n"
             "Example 1:\n"
             "   Stitching together images:\n"
             "    (Can be used to make very high resolution SUMA images.\n"
             "     Read about 'Ctrl+r' in SUMA's GUI help.)\n"
             "   imcat -prefix cat -matrix 14 12 \\\n"
             "         ~/abin/face_*.jpg\n"
             "   aiv cat.ppm\n"
             "\n"
             "Example 2 (assuming afni is in ~/abin directory):\n"
             "   imcat -prefix bigcat -scale_image ~/abin/face_rwcox.jpg \\\n"
             "         -matrix_from_scale -rgb_out -res_in 32 32 ~/abin/face_*.jpg \n"
             "   aiv   bigcat.ppm bigcat.ppm \n"
             "   Crop/Zoom in to see what was done. In practice, you want to use\n"
             "   a faster image viewer to examine the result. Zooming on such\n"
             "   a large image is not fast in aiv.\n"
             "   Be careful with this toy. Images get real big, real quick.\n"
             "\n"
             "You can look at the output image file with\n"
             "  afni -im ppp.ppm  [then open the Sagittal image window]\n"
             "\n"
            ) ;
      exit(0) ;
    }

   machdep() ;

    ScaleInt = 0;
    resix=-1;
    resiy=-1;
    force_rgb_out = 0;
    iarg = 1 ;
      nx = -1; ny = -1;
    while( iarg < argc && argv[iarg][0] == '-' ){

       if( strcmp(argv[iarg],"-matrix") == 0 ){
         if (iarg+2 > argc) {
            fprintf(stderr,"*** ERROR: Need two integers after -matrix\n");
            exit(1);
         }
         nx = (int) strtod( argv[++iarg] , NULL ) ;
         ny = (int) strtod( argv[++iarg] , NULL ) ;
          iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-res_in") == 0 ){
         if (iarg+2 > argc) {
            fprintf(stderr,"*** ERROR: Need two integers after -res_in\n");
            exit(1);
         }
         resix = (int) strtod( argv[++iarg] , NULL ) ;
         resiy = (int) strtod( argv[++iarg] , NULL ) ;
          iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-nx") == 0 ){
         if (iarg+1 > argc) {
            fprintf(stderr,"*** ERROR: Need an integer after -nx\n");
            exit(1);
         }
         nx = (int) strtod( argv[++iarg] , NULL ) ;
          iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-ny") == 0 ){
         if (iarg+1 > argc) {
            fprintf(stderr,"*** ERROR: Need an integer after -ny\n");
            exit(1);
         }
         ny = (int) strtod( argv[++iarg] , NULL ) ; 
          iarg++ ; continue ;
       }

       if( strcmp(argv[iarg],"-gap") == 0 ){
         if (iarg+1 > argc) {
            fprintf(stderr,"*** ERROR: Need an integer after -gap\n");
            exit(1);
         }
         gap = (int) strtod( argv[++iarg] , NULL ) ; 
          iarg++ ; continue ;
       }
       if( strcmp(argv[iarg],"-gap_col") == 0 ){
         if (iarg+2 > argc) {
            fprintf(stderr,"*** ERROR: Need three integers between 0 and 255 after -gap_col\n");
            exit(1);
         }
         gap_col[0] = (byte) strtod( argv[++iarg] , NULL ) ;
         gap_col[1] = (byte) strtod( argv[++iarg] , NULL ) ;
         gap_col[2] = (byte) strtod( argv[++iarg] , NULL ) ;
          iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-scale_image") == 0 ){
          if (iarg+1 > argc) {
            fprintf(stderr,"*** ERROR: Need an image after -scale_image\n");
            exit(1);
         }
          scale_image = argv[++iarg];
          iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-scale_intensity") == 0) {
         ScaleInt = 1;
         iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-rgb_out") == 0) {
         force_rgb_out = 1;
         iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-matrix_from_scale") == 0) {
         matrix_size_from_scale = 1;
         iarg++ ; continue ;
       }
       
       if( strcmp(argv[iarg],"-prefix") == 0 ){
          MCW_strncpy( prefix , argv[++iarg] , 240 ) ;
          iarg++ ; continue ;
       }

       fprintf(stderr,"*** ERROR: illegal option %s\n",argv[iarg]) ;
       exit(1) ;
    }
      
    if (scale_image) {
      if (!(imscl = mri_read_just_one(scale_image))) {
         fprintf(stderr,"*** Failed to read scale image.\n");
         exit(1);
      }else {
         rgb= MRI_BYTE_PTR(imscl);
         nscl = imscl->nx*imscl->ny;
         scl = (float *)malloc(sizeof(float)*nscl);
         scl3 = (byte *)malloc(sizeof(byte)*nscl*3);
         if (imscl->kind == MRI_rgb) {
            for (kkk=0; kkk<nscl; ++kkk) {
               scl[kkk] = (   (float)rgb[3*kkk  ] +
                              (float)rgb[3*kkk+1] +
                              (float)rgb[3*kkk+2] ) / 3.0;
               scl3[3*kkk  ] = rgb[3*kkk  ];
               scl3[3*kkk+1] = rgb[3*kkk+1];
               scl3[3*kkk+2] = rgb[3*kkk+2];
            }
        } else if (imscl->kind == MRI_byte) {
            for (kkk=0; kkk<nscl; ++kkk) {
               scl[kkk] = ((float)rgb[kkk  ]);
               scl3[3*kkk  ] = rgb[kkk];  /* inefficient, but makes life easy */
               scl3[3*kkk+1] = rgb[kkk];
               scl3[3*kkk+2] = rgb[kkk];
            } 
        } else {
         fprintf(stderr,"*** Scale image must be RGB or byte type.\n");
            exit(1);
        }
      }
   }
   
   if (matrix_size_from_scale) {
      if (imscl) {
         fprintf(stderr,"+++ Matrix size of %dx%d, based on scale image\n", imscl->nx, imscl->ny);
         nx = imscl->nx; ny = imscl->ny;
      } else {
         fprintf(stderr,"*** Want matrix size from scale image but no scale image found.\n");
         exit(1);
      }
   }
   /* allow from catwrapping */
   mri_Set_OK_catwrap();

   /* read all images */
   inimar = mri_read_resamp_many_files( argc-iarg , argv+iarg, resix, resiy ) ;
   if( inimar == NULL ){
      fprintf(stderr,"*** no input images read!\a\n") ;
      exit(1) ;
   } else if( inimar->num < 1 ){
      fprintf(stderr,"*** less than 1 input image read!\a\n") ;
      exit(1) ;
   }
   
   /* figure out the count */
   if (nx < 0 && ny < 0) {
      nx = ny = (int)sqrt((double)inimar->num+0.00001);
      if (nx*ny != inimar->num) {
         fprintf(stderr,"*** Can't guess at matrix layout for %d images.\n"
                        "    Use -nx, -ny or -matrix\n", inimar->num);
         exit(1);
      }
   } else if (nx < 0) {
      if (inimar->num % ny) {
         fprintf(stderr,"*** Have %d images, not divisible by %d!\n", inimar->num, ny);
         exit(1);
      }
      nx = inimar->num / ny;
   } else if (ny < 0) {
      if (inimar->num % nx) {
         fprintf(stderr,"*** Have %d images, not divisible by %d!\n", inimar->num, nx);
         exit(1);
      }
      ny = inimar->num / nx;
   }
   
   if( nx < 1 || ny < 1 ){
    fprintf(stderr,"*** ERROR: illegal values nx=%d ny=%d\n",nx,ny);
    exit(1) ;
   }
   if (nx*ny > inimar->num) {
      fprintf(stderr,"+++ Have %d images. Will wrap to fill %dx%d matrix\n", inimar->num, nx, ny);
   } else if (nx*ny < inimar->num) {
      fprintf(stderr,"+++ Have more images than needed to fill matrix.\n"
                     "    Will ignore the last %d images.\n", inimar->num- nx*ny);
   }
   
   nxin = IMAGE_IN_IMARR(inimar,0)->nx;
   nyin = IMAGE_IN_IMARR(inimar,0)->ny;
   
   fprintf(stdout, "+++ Arranging %d images (each %dx%d) into a %dx%d matrix.\n", inimar->num, nxin, nyin, nx, ny);

   force_rgb_at_input = 0;
   N_byte = 0; N_rgb = 0;
   if (gap) force_rgb_at_input = 1; /* must go rgb */
   else {
      /* if have multiple types, must also go rgb */
      for (kkk=0; kkk<inimar->num; ++kkk) {
         if (IMAGE_IN_IMARR(inimar,kkk)->kind != MRI_byte && IMAGE_IN_IMARR(inimar,kkk)->kind != MRI_rgb) {
            fprintf(stderr,"*** Unexpected image kind on input.\n"
                           "    Only byte and rgb (3byte) images allowed.\n");
            exit(1);
         } else if (IMAGE_IN_IMARR(inimar,kkk)->kind == MRI_byte) {
            ++N_byte;
         } else if (IMAGE_IN_IMARR(inimar,kkk)->kind == MRI_rgb) {
            ++N_rgb;
         } 
         if (N_byte && N_rgb) { force_rgb_at_input = 1; continue; }
      }
   }
   
   if (force_rgb_at_input) { /* make sure all images are rgb type */
      MRI_IMAGE *imin, *newim;
      int kkk, nmin;
      if (nx*ny < inimar->num) nmin = nx*ny;
      else nmin = inimar->num;
      /* must transform input to rgb here to allow for gap option */
      fprintf(stderr,"+++ Transforming all input to rgb for a good reason \n");
      for (kkk=0; kkk<nmin; ++kkk) {
         imin = IMAGE_IN_IMARR(inimar,kkk%inimar->num);
         if(imin->kind == MRI_byte) {
            newim = mri_3to_rgb( imin, imin, imin ) ;
            MRI_COPY_AUX(newim,imin) ; mri_free(imin);
            IMAGE_IN_IMARR(inimar,kkk%inimar->num) = newim;
         } else if (imin->kind != MRI_rgb) {
            fprintf(stderr,"*** Unexpected image kind.\n");
            exit(1);
         }
      }
   } 

   
   ggg = (void *)gap_col;
   
   if (!(im = mri_cat2D( nx , ny , gap , ggg , inimar ))) {
      fprintf(stderr,"*** ERROR: Failed to create image!\n");
      exit(1) ;
   }
   FREE_IMARR(inimar) ; inimar = NULL;

   if (force_rgb_out && im->kind == MRI_byte) {
      MRI_IMAGE *newim=NULL;
      fprintf(stderr,"+++ Forcing output to RGB\n");
      newim = mri_3to_rgb( im, im, im ) ;
      MRI_COPY_AUX(newim,im) ; mri_free(im);
      im = newim;
   }
   
   /* image scaling needed, not very efficient in implementation but mostly for fun */
   if (scale_image && ( im->kind == MRI_rgb || im->kind == MRI_byte) ) {
      MRI_IMARR *imtriple ; 
      MRI_IMAGE *rim, *gim, *bim, *tim, *imin, *newim;
      fprintf(stderr,"+++ Scaling by image\n");
      if (!imscl) {
         fprintf(stderr,"*** No scale image!!!.\n");
         exit(1);
      }else {
         rgb= MRI_BYTE_PTR(imscl);
         nscl = imscl->nx*imscl->ny;
         scl = (float *)malloc(sizeof(float)*nscl);
         scl3 = (byte *)malloc(sizeof(byte)*nscl*3);
         if (imscl->kind == MRI_rgb) {
            for (kkk=0; kkk<nscl; ++kkk) {
               scl[kkk] = (   (float)rgb[3*kkk  ] +
                              (float)rgb[3*kkk+1] +
                              (float)rgb[3*kkk+2] ) / 3.0;
               scl3[3*kkk  ] = rgb[3*kkk  ];
               scl3[3*kkk+1] = rgb[3*kkk+1];
               scl3[3*kkk+2] = rgb[3*kkk+2];
            }
        } else if (imscl->kind == MRI_byte) {
            for (kkk=0; kkk<nscl; ++kkk) {
               scl[kkk] = ((float)rgb[kkk  ]);
               scl3[3*kkk  ] = rgb[kkk];  /* inefficient, but makes life easy */
               scl3[3*kkk+1] = rgb[kkk];
               scl3[3*kkk+2] = rgb[kkk];
            } 
        } else {
         fprintf(stderr,"*** Scale image must be RGB or byte type.\n");
            exit(1);
        }
      }
      
      /* Now break the image again */
      if (!(inimar = mri_uncat2D(  nxin ,  nyin ,im ))) {
         fprintf(stderr,"*** Failed to cut up image\n");
         exit(1);
      }
      mri_free(im); im = NULL;
      
      for (kkk=0; kkk<inimar->num; ++kkk) {  
         imin = IMAGE_IN_IMARR(inimar,kkk);
         /*  sprintf(name,"prescaled.%d.ppm", kkk);
           mri_write(name,imin); */
         if(imin->kind == MRI_rgb) {
            imtriple = mri_rgb_to_3byte( imin ) ;
            if( imtriple == NULL ){
             fprintf(stderr,"*** mri_rgb_to_3byte fails!\n"); break;
            }
           rim = IMAGE_IN_IMARR(imtriple,0) ;
           gim = IMAGE_IN_IMARR(imtriple,1) ;
           bim = IMAGE_IN_IMARR(imtriple,2) ; FREE_IMARR(imtriple) ;
           /* scale image */
           if (ScaleInt) {
              /* fprintf(stderr,"Scaling image %d by %f\n", kkk, scl[kkk%nscl]); */
              tim = mri_to_byte_scl(0.0, scl[kkk%nscl], rim ); mri_free(rim); rim = tim;
              tim = mri_to_byte_scl(0.0, scl[kkk%nscl], gim ); mri_free(gim); gim = tim;
              tim = mri_to_byte_scl(0.0, scl[kkk%nscl], bim ); mri_free(bim); bim = tim;
           } else {
              /* fprintf(stderr,"Scaling image %d by [%.2f %.2f %.2f]\n", 
                              kkk,  (float)scl3[3*(kkk%nscl)  ],
                                    (float)scl3[3*(kkk%nscl)+1],
                                    (float)scl3[3*(kkk%nscl)+2]); */
              tim = mri_to_byte_scl(0.0, (float)scl3[3*(kkk%nscl)  ], rim ); mri_free(rim); rim = tim;
              tim = mri_to_byte_scl(0.0, (float)scl3[3*(kkk%nscl)+1], gim ); mri_free(gim); gim = tim;
              tim = mri_to_byte_scl(0.0, (float)scl3[3*(kkk%nscl)+1], bim ); mri_free(bim); bim = tim;
           }
           newim = mri_3to_rgb( rim, gim, bim ) ;
           mri_free(rim) ; mri_free(gim) ; mri_free(bim) ;
           MRI_COPY_AUX(newim,imin) ; mri_free(imin);
           IMAGE_IN_IMARR(inimar,kkk) = newim;
         } else if(imin->kind == MRI_byte) {
           /* scale image */
           /* fprintf(stderr,"Scaling byte image %d by %f\n", kkk, scl[kkk%nscl]); */ 
           newim = mri_to_byte_scl(0.0, (float)scl[kkk%nscl], imin ); 
           MRI_COPY_AUX(newim,imin) ; mri_free(imin);
           /* sprintf(name,"postscaled.%d.ppm", kkk);
           mri_write(name,newim); */
           IMAGE_IN_IMARR(inimar,kkk) = newim;
         } else {
            fprintf(stderr,"*** image %d is not rgb or byte. No scaling for you!\n", kkk%inimar->num);
         }
      }
      if (scl) free(scl); scl = NULL;
      if (scl3) free(scl3); scl3 = NULL;
      /* Now put it back together */
      /* fprintf(stderr,"Going to cat2D, again\n"); */
      if (!(im = mri_cat2D( nx , ny , gap , ggg , inimar ))) {
         fprintf(stderr,"*** ERROR: Failed to create image!\n");
         exit(1) ;
      }
      FREE_IMARR(inimar) ; inimar = NULL;

   }

   sprintf(fnam,"%s.ppm",prefix);
   fprintf(stderr,"+++ Writing image to %s\n", fnam);
   mri_write(fnam,im) ;
   
   mri_free(im); im = NULL;
   fprintf(stdout, "You can view image %s with:\n aiv %s \n", fnam, fnam);
    exit(0) ;
}


syntax highlighted by Code2HTML, v. 0.9.1