/******************************************************************************
MODULE:  object_cloud_shadow_match

PURPOSE: Identify the final shadow pixels by doing a geometric cloud and shadow
         matching which ends in with the maximum cloud and shadow similarity

RETURN: 0 on success
        -1 on error

HISTORY:
Date        Programmer       Reason
--------    ---------------  -------------------------------------
3/15/2013   Song Guo         Original Development

NOTES: 
******************************************************************************/
int object_cloud_shadow_match
(
    Input_t *input,
    float ptm,
    float t_templ,
    float t_temph,
    int cldpix,
    int sdpix,
    unsigned char **cloud_mask,
    unsigned char **shadow_mask,
    unsigned char **snow_mask,
    unsigned char **water_mask,
    unsigned char **final_mask,
    bool verbose       
)
{
    char errstr[MAX_STR_LEN];
    int nrows = input->size.l;
    int ncols = input->size.s;
    int row;
    int col = 0;
    float sun_ele;
    float sun_ele_rad;
    float sun_tazi;
    float sun_tazi_rad;
    int sub_size = 30;
    int status;

    /* Dynamic memory allocation */
    unsigned char **cloud_cal;
    unsigned char **shadow_cal;
    unsigned char **boundary_test;

    cloud_cal = (unsigned char **)ias_misc_allocate_2d_array(input->size.l, 
                 input->size.s, sizeof(unsigned char)); 
    shadow_cal = (unsigned char **)ias_misc_allocate_2d_array(
                 input->size.l, input->size.s, sizeof(unsigned char)); 
    boundary_test = (unsigned char **)ias_misc_allocate_2d_array(
                 input->size.l, input->size.s, sizeof(unsigned char)); 
    if (cloud_cal == NULL || shadow_cal == NULL || boundary_test == NULL)
    {
        sprintf (errstr, "Allocating mask memory");
        ERROR (errstr, "cloud/shadow match");
    }

    /* Read in potential mask ... */
    /* Solar elevation angle */
    sun_ele = 90 - input->meta.sun_zen;
    sun_ele_rad = (PI / 180.0) * sun_ele;
    /* Solar azimuth angle */
    sun_tazi = input->meta.sun_az - 90;
    sun_tazi_rad = (PI / 180.0) * sun_tazi;

    int cloud_counter = 0;
    int boundary_counter = 0;
    float revised_ptm;
    for (row = 0; row < nrows; row++)
    {
        for (col = 0; col < ncols; col++)
        {
            if (cloud_mask[row][col] == 1)
                cloud_counter++;
 
            /* Boundary layer includes both cloud_mask equals 0 and 1 */
            if (cloud_mask[row][col] < 255)     
            {
                boundary_test[row][col] = 1;
                boundary_counter++;
            }
            else
                boundary_test[row][col] = 0;
        }
    }

     /* Revised percent of cloud on the scene after plcloud */
     revised_ptm = (float)cloud_counter / (float)boundary_counter;

     if (verbose)
     {
         printf("cloud_counter, boundary_counter = %d, %d\n", cloud_counter, 
                boundary_counter);
         printf("Revised percent of cloud = %f\n", revised_ptm);
     }

     /* cloud covers more than 90% of the scene
         => no match => rest are definite shadows */
     if (ptm <= 0.1 || revised_ptm >= 0.90)
     {
         for (row = 0; row < nrows; row++)
         {
             for (col = 0; col < ncols; col++)
             {
                 /* No Shadow Match due to too much cloud (>90 percent) */
                 if (cloud_mask[row][col] == 1) 
                     cloud_cal[row][col] = 1;
                 else
                     shadow_cal[row][col] = 1;
             }
         }
     }
     else
     {
         if (verbose)
             printf("Shadow Match in processing\n");

         /* define constants */
         float t_similar=0.30;
         float t_buffer=0.95; /* threshold for matching buffering */
         int num_cldoj=9; /* minimum matched cloud object (pixels) */
         int num_pix=8; /* number of inward pixes (240m) for cloud base 
                          temperature */
         float a, b, c, omiga_par, omiga_per;

         if (verbose)
         {
             printf("Set cloud similarity = %.3f\n", t_similar);
             printf("Set matching buffer = %.3f\n", t_buffer);
             printf("Shadow match for cloud object >= %d pixels\n", num_cldoj);
         }
         int i_step;
         i_step=rint(2.0*(float)sub_size*tan(sun_ele_rad)); 
                                            /* move 2 pixel at a time */
         /* Get moving direction, the idea is to get the corner rows/cols */
         int x_ul = 0;
         int y_ul = 0;
         int x_lr = 0;
         int y_lr = 0;
         int x_ll = 0;
         int y_ll = 0;
         int x_ur = 0;
         int y_ur = 0;
         for (row = 0; row < nrows; row++)
         {
             for (col = 0; col < ncols; col++)
             {
                 if (boundary_test[row][col] == 1)
                 {
                     y_ul = row;
                     x_ul = col;
                     goto next1;
                 }
             }
        }

        next1:        
        for (col = ncols - 1; col >= 0; col--)
        {
            for (row = 0; row < nrows; row++)
            {
                if (boundary_test[row][col] == 1)
                {
                    y_ur = row;
                    x_ur = col;
                    goto next2;
                }
            }
        }

        next2:
        for (col = 0; col < ncols; col++)
        {
            for (row = nrows - 1; row >= 0; row--)
            {
                if (boundary_test[row][col] == 1)
                {
                    y_ll = row;
                    x_ll = col;
                    goto next3;
                }
            }
        }

        next3:
        for (row = nrows - 1; row >= 0; row--)
        {
            for (col = ncols - 1; col >= 0; col--)
            {
                if (boundary_test[row][col] == 1)
                {
                    y_lr = row;
                    x_lr = col;
                    goto next4;
                }
            }
        }

        next4:
        /* get view angle geometry */
        viewgeo(x_ul,y_ul,x_ur,y_ur,x_ll,y_ll,x_lr,y_lr, &a, &b, &c, 
            &omiga_par, &omiga_per);

        /* Allocate memory for segment cloud portion */
        int *obj_num;
        obj_num = (int *)calloc(MAX_CLOUD_TYPE, sizeof(int));
        cloud_node **cloud;
        cloud = (cloud_node **)ias_misc_allocate_2d_array(nrows, 
               ncols, sizeof(cloud_node)); 
        int **cloud_first_node;
        cloud_first_node = (int **)ias_misc_allocate_2d_array(2, 
               MAX_CLOUD_TYPE, sizeof(int)); 
        if (obj_num == NULL || cloud == NULL || cloud_first_node == NULL)
        {
            sprintf (errstr, "Allocating memory");
            ERROR (errstr, "cloud/shadow match");
            return -1;        
        }

        /* Initialize the cloud nodes */
        for (row = 0; row < nrows; row++)
        {
           for (col = 0; col <ncols; col++)
           {
               cloud[row][col].value = 0;
               cloud[row][col].row = 0;
               cloud[row][col].col = 0;
               cloud[row][col].parent = &cloud[row][col];
               cloud[row][col].child = &cloud[row][col];
           }
        }

        /* Labeling the cloud pixels */
        label(cloud_mask, nrows, ncols, cloud, obj_num, cloud_first_node);

        /* The cloud pixels are not counted as cloud pixels if the total number 
           of cloud pixels is less than 9 within a cloud cluster */
        int num;
        int counter = 0;
        for (num = 1; num <= num_clouds; num++)
        {
           if (obj_num[num] <= MIN_CLOUD_OBJ)
               obj_num[num] = 0;
           else
               counter++;
        }

        if (verbose)
            printf("Num of real clouds = %d\n", counter);

        /* Cloud_cal pixels are cloud_mask pixels with < 9 pixels removed */
        for (row = 0; row < nrows; row++)
        {
           for (col = 0; col <ncols; col++)
           {
              if ((cloud_mask[row][col] == 1) && (boundary_test[row][col] != 0)
                   && (obj_num[cloud[row][col].value] != 0))
                  cloud_cal[row][col] = cloud_mask[row][col];
              else
                  cloud_cal[row][col] = 0;
          }
        }

        /* Need to read out whole image brightness temperature for band 6 */
        int16 **temp;
        temp = (int16 **)ias_misc_allocate_2d_array(input->size.l, 
                 input->size.s, sizeof(int16)); 
        if (!temp)
        {
             sprintf (errstr, "Allocating temp memory");
             ERROR (errstr, "cloud/shadow match");
        }

        /* Read out thermal band in 2d */
        for (row = 0; row < nrows; row++)
        {
	    if (!GetInputThermLine(input, row))
            {
	        sprintf (errstr, "Reading input thermal data for line %d", row);
	        ERROR (errstr,"cloud/shadow match");
	    }
            memcpy(&temp[row][0], &input->therm_buf[0], input->size.s * 
                   sizeof(int16));
        }

        /* Use iteration to get the optimal move distance, Calulate the 
           moving cloud shadow */
        int cloud_type;
        int **xy_type;
        int **tmp_xy_type;
        float **tmp_xys;
        int **orin_xys;
        int16 *temp_obj; 
        int16 temp_obj_max = 0;
        int16 temp_obj_min = 0;
        int index;
        float r_obj;
        float pct_obj;
        float t_obj;
        float rate_elapse=6.5;
        float rate_dlapse=9.8;
        int max_cl_height; /* Max cloud base height (m) */
        int min_cl_height; /* Min cloud base height (m) */
        int max_height;
        int min_height;
        float record_thresh;
        float *record_h;
        int base_h;
        float *h;
        float i_xy;
        int out_all;
        int match_all;
        int total_all;
        float thresh_match;
        int i;

        for (cloud_type = 1; cloud_type <= num_clouds; cloud_type++)
        {
            if (obj_num[cloud_type] == 0)
                continue;
            else
            {
                /* Note: matlab array index starts with 1 and C starts with 0, 
                         array(3,1) in matlab is equal to array[2][0] in C */
                min_cl_height = 200;
                max_cl_height = 12000;
                xy_type = (int **)ias_misc_allocate_2d_array(2, 
                           obj_num[cloud_type], sizeof(int)); 
                tmp_xy_type = (int **)ias_misc_allocate_2d_array(2, 
                     obj_num[cloud_type], sizeof(int)); 
                /* corrected for view angle xys */
                tmp_xys = (float **)ias_misc_allocate_2d_array(2, 
                          obj_num[cloud_type], sizeof(float)); 
                /* record the original xys */
                orin_xys = (int **)ias_misc_allocate_2d_array(2, 
                      obj_num[cloud_type], sizeof(int)); 
                if (xy_type == NULL || tmp_xy_type == NULL || tmp_xys == NULL 
                    || orin_xys == NULL)
                {
                    sprintf (errstr, "Allocating cloud memory");
                    ERROR (errstr, "cloud/shadow match");
                }

                /* Temperature of the cloud object */
                temp_obj = malloc(obj_num[cloud_type] * sizeof(int16));
                if (temp_obj == NULL)
                {
                    sprintf (errstr, "Allocating temp_obj memory");
                    ERROR (errstr, "cloud/shadow match");
                }
     
                temp_obj_max = 0;
                temp_obj_min = 0;
                index = 0;
                cloud_node *node;
                node = &cloud[cloud_first_node[0][cloud_type]]
                       [cloud_first_node[1][cloud_type]];
                while (node->child != node)
                {
                    temp_obj[index] = temp[node->row][node->col];
                    if (temp_obj[index] > temp_obj_max)
                    temp_obj_max = temp_obj[index];
                    if (temp_obj[index] < temp_obj_min)
                    temp_obj_min = temp_obj[index];
                    orin_xys[0][index] = node->col;
                    orin_xys[1][index] = node->row;
                    index++;  
                    node = node->child;         
                }
                temp_obj[index] = temp[node->row][node->col];
                if (temp_obj[index] > temp_obj_max)
                    temp_obj_max = temp_obj[index];
                if (temp_obj[index] < temp_obj_min)
                    temp_obj_min = temp_obj[index];
                orin_xys[0][index] = node->col;
                orin_xys[1][index] = node->row;
                index++;  
                obj_num[cloud_type] = index;

                /* the base temperature for cloud
                   assume object is round r_obj is radium of object */
                r_obj=sqrt((float)obj_num[cloud_type]/PI);
                /* number of inward pixes for correct temperature */
                pct_obj=((r_obj-(float)num_pix)*(r_obj-(float)num_pix)) /
                          (r_obj * r_obj);
                if ((pct_obj-1.0) >= MINSIGMA)
                    pct_obj = 1.0;/* pct of edge pixel should be less than 1 */

                prctile(temp_obj, obj_num[cloud_type],temp_obj_min,
                        temp_obj_max, 100.0 * pct_obj, &t_obj);

                /* refine cloud height range (m) */
                min_height = (int)rint(10.0*(t_templ-t_obj)/rate_dlapse);
                max_height = (int)rint(10.0*(t_temph-t_obj));
                if (min_cl_height < min_height)
                    min_cl_height = min_height;
                if (max_cl_height > max_height)
                    max_cl_height = max_height;
                /* put the edge of the cloud the same value as t_obj */
                for (i = 0; i < obj_num[cloud_type]; i++)
                {
                    if (temp_obj[i] >rint(t_obj))
                         temp_obj[i]=rint(t_obj);
                }

                /* Allocate memory for h and record_h*/
                h = malloc(obj_num[cloud_type] * sizeof(float));
                record_h = calloc(obj_num[cloud_type], sizeof(float));
                if (h == NULL || record_h == NULL)
                {
                    sprintf (errstr, "Allocating h memory");
                    ERROR (errstr, "cloud/shadow match");
                }

                /* initialize height and similarity info */
                record_thresh=0.0;
                for (base_h = min_cl_height; base_h <= max_cl_height; 
                              base_h+=i_step)
                {
                    for (i = 0; i < obj_num[cloud_type]; i++)
                    {
                        h[i]=(10.0*(t_obj-(float)temp_obj[i])) / 
                              rate_elapse+(float)base_h;
                    }
      
                   /* Get the true postion of the cloud
                      calculate cloud DEM with initial base height */
                   mat_truecloud(orin_xys[0], orin_xys[1], obj_num[cloud_type],
                                 h, a, b, c, omiga_par, omiga_per, tmp_xys[0], 
                                 tmp_xys[1]);

                   out_all = 0;
                   match_all = 0;
                   total_all = 0;
                   for (i = 0; i < obj_num[cloud_type]; i++)
                   {
                       i_xy=h[i]/((float)sub_size*tan(sun_ele_rad));
                       if ((input->meta.sun_az - 180.0) < MINSIGMA)
                       {
                            xy_type[1][i] = 
                                rint(tmp_xys[0][i]-i_xy*cos(sun_tazi_rad)); 
                            xy_type[0][i] = 
                                rint(tmp_xys[1][i]-i_xy*sin(sun_tazi_rad)); 
                       }
                       else
                       {
                            xy_type[1][i] =
                                rint(tmp_xys[0][i]+i_xy*cos(sun_tazi_rad)); 
                            xy_type[0][i] = 
                                rint(tmp_xys[1][i]+i_xy*sin(sun_tazi_rad)); 
                       }

                       /* the id that is out of the image */
                       if (xy_type[0][i] < 0 || xy_type[0][i] >= nrows 
                          || xy_type[1][i] < 0 || xy_type[1][i] >= ncols)
                          out_all++;
                       else
                       {
                           if (boundary_test[xy_type[0][i]][xy_type[1][i]] == 0
                               || (cloud[xy_type[0][i]][xy_type[1][i]].value !=
                               cloud_type &&
                               (cloud_mask[xy_type[0][i]][xy_type[1][i]]>0 ||
                               shadow_mask[xy_type[0][i]][xy_type[1][i]] == 1)))
                               match_all++;
                           if (cloud[xy_type[0][i]][xy_type[1][i]].value != 
                               cloud_type)
                               total_all++; 
                        }
                   }
                   match_all += out_all;
                   total_all+=out_all;

                   thresh_match=(float)match_all/(float)total_all;
                   if (((thresh_match - t_buffer*record_thresh) >= MINSIGMA)
                       && (base_h < max_cl_height-i_step) && ((record_thresh
                           - 0.95)<MINSIGMA))
                   {
                       if ((thresh_match - record_thresh) > MINSIGMA)
                       {
                           record_thresh=thresh_match;
                           for (i = 0; i < obj_num[cloud_type]; i++)
                               record_h[i] = h[i];
                       }
                   }
                   else if ((record_thresh - t_similar) > MINSIGMA)
                   {
                       float i_vir;
                       for (i = 0; i < obj_num[cloud_type]; i++)
                       {
                           i_vir = record_h[i] / 
                                   ((float)sub_size*tan(sun_ele_rad));
                           if ((input->meta.sun_az - 180.0) < MINSIGMA)
                           {
                               tmp_xy_type[1][i]=rint(tmp_xys[0][i]-
                                     i_vir*cos(sun_tazi_rad)); 
                               tmp_xy_type[0][i]=rint(tmp_xys[1][i]-
                                     i_vir*sin(sun_tazi_rad)); 
                           }
                           else
                           {
                               tmp_xy_type[1][i]=rint(tmp_xys[0][i]+
                               i_vir*cos(sun_tazi_rad)); 
                               tmp_xy_type[0][i]=rint(tmp_xys[1][i]+
                               i_vir*sin(sun_tazi_rad)); 
                           }

                           /* put data within range */
                           if (tmp_xy_type[0][i]<0)
                               tmp_xy_type[0][i]=0;
                           if (tmp_xy_type[0][i]>=nrows)
                               tmp_xy_type[0][i]=nrows-1;
                           if (tmp_xy_type[1][i]<0)
                               tmp_xy_type[1][i]=0;
                           if (tmp_xy_type[1][i]>=ncols)
                               tmp_xy_type[1][i]=ncols-1;
                           shadow_cal[tmp_xy_type[0][i]][tmp_xy_type[1][i]] = 1;
                       }
                       break;
                   }
                   else
                   {
                       record_thresh=0.0;
                       continue;
                   }
               }
               free(h);
               free(record_h);
               /* Free all the memory */
               status = ias_misc_free_2d_array((void **)xy_type);
               status = ias_misc_free_2d_array((void **)tmp_xys);
               status = ias_misc_free_2d_array((void **)orin_xys);
               free(temp_obj);
            }
       }      
       free(obj_num); 
       status = ias_misc_free_2d_array((void **)temp);
       status = ias_misc_free_2d_array((void **)cloud);

       IplImage* src = cvCreateImage(cvSize(ncols, nrows), IPL_DEPTH_8U, 1);
       IplImage* dst = cvCreateImage(cvSize(ncols, nrows), IPL_DEPTH_8U, 1);
       if (!src || !dst)
       {
            sprintf (errstr, "Creating images\n");
            ERROR (errstr, "cloud/shadow match");
       }
 
       int dilation_type = 1;
       IplConvKernel* element = cvCreateStructuringElementEx( 
              2*cldpix + 1, 2*cldpix + 1, cldpix, cldpix, dilation_type, 0);
 
       for (row = 0; row < nrows; row++)
       {
           for (col = 0; col < ncols; col++)
           {
               src->imageData[row*ncols+col] = cloud_cal[row][col];
           }
       }

       cvDilate(src, dst, element, 1);
       for (row = 0; row < nrows; row++)
       {
           for (col = 0; col < ncols; col++)
           {
               cloud_cal[row][col] = dst->imageData[row*ncols+col];
           }
       }

       element = cvCreateStructuringElementEx( 
              2*sdpix + 1, 2*sdpix + 1, sdpix, sdpix, dilation_type, 0);

       for (row = 0; row < nrows; row++)
       {
           for (col = 0; col < ncols; col++)
           {
               src->imageData[row*ncols+col] = shadow_cal[row][col];
           }
       }

       cvDilate(src, dst, element, 1);
       for (row = 0; row < nrows; row++)
       {
           for (col = 0; col < ncols; col++)
           {
               shadow_cal[row][col] = dst->imageData[row*ncols+col];
           }
       }

       for (row = 0; row < nrows; row++)
       {
           for (col = 0; col < ncols; col++)
           {
               src->imageData[row*ncols+col] = snow_mask[row][col];
           }
        }

        cvDilate(src, dst, element, 1);
        for (row = 0; row < nrows; row++)
        {
            for (col = 0; col < ncols; col++)
            {
                snow_mask[row][col] = dst->imageData[row*ncols+col];
            }
         }

         /* Release image memory */
         cvReleaseImage(&src);
         cvReleaseImage(&dst);
     }

     /* Use cloud mask as the final output mask */  
     int cloud_count = 0;    
     int shadow_count = 0;    
     for (row = 0; row < nrows; row++)
     {
         for (col = 0; col < ncols; col++)
         {
             if (boundary_test[row][col] ==0)
                 final_mask[row][col] = 255;
             else if (cloud_cal[row][col] == 1)
             {
	         final_mask[row][col] = 4;
                 cloud_count++;
             }
             else if (shadow_cal[row][col] == 1)
             {
	         final_mask[row][col] = 2;
                 shadow_count++;
             }
             else if (snow_mask[row][col] == 1)
                 final_mask[row][col] = 3;
             else if (water_mask[row][col] == 1)
	         final_mask[row][col] = 1;
             else 
                 final_mask[row][col] = 0;
         }
      }

      /* Release the memory */
      status = ias_misc_free_2d_array((void **)cloud_cal);
      status = ias_misc_free_2d_array((void **)shadow_cal);
      status = ias_misc_free_2d_array(( void **)boundary_test);

      if (verbose)
      {
          printf("cloud_count, shadow_count, boundary_counter = %d,%d,%d\n",
              cloud_count, shadow_count, boundary_counter);

          /* record cloud and cloud shadow percent; */
          float cloud_shadow_percent;
          cloud_shadow_percent = (float)(cloud_count + shadow_count)
                          / (float)boundary_counter;
          printf("The cloud and shadow percentage is %f\n", cloud_shadow_percent);
      }
  
      return 0;
}
/******************************************************************************
MODULE:  potential_cloud_shadow_snow_mask

PURPOSE: Identify the cloud pixels, snow pixels, water pixels, clear land 
         pixels, and potential shadow pixels

RETURN: SUCCESS
        FAILURE

HISTORY:
Date        Programmer       Reason
--------    ---------------  -------------------------------------
3/15/2013   Song Guo         Original Development

NOTES: 
1. Thermal buffer is expected to be in degrees Celsius with a factor applied
   of 100.  Many values which compare to the thermal buffer in this code are
   hardcoded and assume degrees celsius * 100.
******************************************************************************/
int potential_cloud_shadow_snow_mask
(
    Input_t * input,            /*I: input structure */
    float cloud_prob_threshold, /*I: cloud probability threshold */
    float *ptm,                 /*O: percent of clear-sky pixels */
    float *t_templ,             /*O: percentile of low background temp */
    float *t_temph,             /*O: percentile of high background temp */
    unsigned char **pixel_mask, /*I/O: pixel mask */
    bool verbose                /*I: value to indicate if intermediate
                                     messages be printed */
)
{
    char errstr[MAX_STR_LEN];   /* error string */
    int nrows = input->size.l;  /* number of rows */
    int ncols = input->size.s;  /* number of columns */
    int ib = 0;                 /* band index */
    int row = 0;                /* row index */
    int col = 0;                /* column index */
    int mask_counter = 0;       /* mask counter */
    int clear_pixel_counter = 0;        /* clear sky pixel counter */
    int clear_land_pixel_counter = 0;   /* clear land pixel counter */
    float ndvi, ndsi;           /* NDVI and NDSI values */
    int16 *f_temp = NULL;       /* clear land temperature */
    int16 *f_wtemp = NULL;      /* clear water temperature */
    float visi_mean;            /* mean of visible bands */
    float whiteness = 0.0;      /* whiteness value */
    float hot;                  /* hot value for hot test */
    float lndptm;               /* clear land pixel percentage */
    float l_pt;                 /* low percentile threshold */
    float h_pt;                 /* high percentile threshold */
    float t_wtemp;              /* high percentile water temperature */
    float **wfinal_prob = NULL; /* final water pixel probabilty value */
    float **final_prob = NULL;  /* final land pixel probability value */
    float wtemp_prob;           /* water temperature probability value */
    int t_bright;               /* brightness test value for water */
    float brightness_prob;      /* brightness probability value */
    int t_buffer;               /* temperature test buffer */
    float temp_l;               /* difference of low/high tempearture
                                   percentiles */
    float temp_prob;            /* temperature probability */
    float vari_prob;            /* probability from NDVI, NDSI, and whiteness */
    float max_value;            /* maximum value */
    float *prob = NULL;         /* probability value */
    float *wprob = NULL;        /* probability value */
    float clr_mask = 0.0;       /* clear sky pixel threshold */
    float wclr_mask = 0.0;      /* water pixel threshold */
    int16 *nir = NULL;          /* near infrared band (band 4) data */
    int16 *swir = NULL;         /* short wavelength infrared (band 5) data */
    float backg_b4;             /* background band 4 value */
    float backg_b5;             /* background band 5 value */
    int16 shadow_prob;          /* shadow probability */
    int status;                 /* return value */
    int satu_bv;                /* sum of saturated bands 1, 2, 3 value */
    unsigned char mask;         /* mask used for 1 pixel */

    /* Dynamic memory allocation */
    unsigned char **clear_mask = NULL;

    clear_mask = (unsigned char **) allocate_2d_array (input->size.l,
                                                       input->size.s,
                                                       sizeof (unsigned char));
    if (clear_mask == NULL)
        RETURN_ERROR ("Allocating mask memory", "pcloud", FAILURE);

    if (verbose)
        printf ("The first pass\n");

    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
        if (verbose)
        {
            /* Print status on every 1000 lines */
            if (!(row % 1000))
            {
                printf ("Processing line %d\r", row);
                fflush (stdout);
            }
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine (input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                         "band %d", row, ib);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
        }

        /* For the thermal band */
        /* Read the input thermal band -- data is read into input->therm_buf */
        if (!GetInputThermLine (input, row))
        {
            sprintf (errstr, "Reading input thermal data for line %d", row);
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        for (col = 0; col < ncols; col++)
        {
            int ib;
            for (ib = 0; ib < BI_REFL_BAND_COUNT; ib++)
            {
                if (input->buf[ib][col] == input->meta.satu_value_ref[ib])
                    input->buf[ib][col] = input->meta.satu_value_max[ib];
            }
            if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                input->therm_buf[col] = input->meta.therm_satu_value_max;

            /* process non-fill pixels only
               Due to a problem with the input LPGS data, the thermal band
               may have values less than -9999 after scaling so exclude those
               as well */
            if (input->therm_buf[col] <= -9999
                || input->buf[BI_BLUE][col] == -9999
                || input->buf[BI_GREEN][col] == -9999
                || input->buf[BI_RED][col] == -9999
                || input->buf[BI_NIR][col] == -9999
                || input->buf[BI_SWIR_1][col] == -9999
                || input->buf[BI_SWIR_2][col] == -9999)
            {
                mask = 0;
            }
            else
            {
                mask = 1;
                mask_counter++;
            }

            if ((input->buf[BI_RED][col] + input->buf[BI_NIR][col]) != 0
                && mask == 1)
            {
                ndvi = (float) (input->buf[BI_NIR][col]
                                - input->buf[BI_RED][col])
                       / (float) (input->buf[BI_NIR][col]
                                  + input->buf[BI_RED][col]);
            }
            else
                ndvi = 0.01;

            if ((input->buf[BI_GREEN][col] + input->buf[BI_SWIR_1][col]) != 0
                && mask == 1)
            {
                ndsi = (float) (input->buf[BI_GREEN][col]
                                - input->buf[BI_SWIR_1][col])
                       / (float) (input->buf[BI_GREEN][col]
                                  + input->buf[BI_SWIR_1][col]);
            }
            else
                ndsi = 0.01;

            /* Basic cloud test, equation 1 */
            if (((ndsi - 0.8) < MINSIGMA)
                && ((ndvi - 0.8) < MINSIGMA)
                && (input->buf[BI_SWIR_2][col] > 300)
                && (input->therm_buf[col] < 2700))
            {
                pixel_mask[row][col] |= 1 << CLOUD_BIT;
            }
            else
                pixel_mask[row][col] &= ~(1 << CLOUD_BIT);

            /* It takes every snow pixels including snow pixel under thin 
               clouds or icy clouds, equation 20 */
            if (((ndsi - 0.15) > MINSIGMA)
                && (input->therm_buf[col] < 1000)
                && (input->buf[BI_NIR][col] > 1100)
                && (input->buf[BI_GREEN][col] > 1000))
            {
                pixel_mask[row][col] |= 1 << SNOW_BIT;
            }
            else
                pixel_mask[row][col] &= ~(1 << SNOW_BIT);

            /* Zhe's water test (works over thin cloud), equation 5 */
            if (((((ndvi - 0.01) < MINSIGMA)
                  && (input->buf[BI_NIR][col] < 1100))
                 || (((ndvi - 0.1) < MINSIGMA)
                     && (ndvi > MINSIGMA)
                     && (input->buf[BI_NIR][col] < 500)))
                && (mask == 1))
            {
                pixel_mask[row][col] |= 1 << WATER_BIT;
            }
            else
                pixel_mask[row][col] &= ~(1 << WATER_BIT);
            if (mask == 0)
                pixel_mask[row][col] |= 1 << FILL_BIT;

            /* visible bands flatness (sum(abs)/mean < 0.6 => brigt and dark 
               cloud), equation 2 */
            if ((pixel_mask[row][col] & (1 << CLOUD_BIT)) && mask == 1)
            {
                visi_mean = (float) (input->buf[BI_BLUE][col]
                                     + input->buf[BI_GREEN][col]
                                     + input->buf[BI_RED][col]) / 3.0;
                if (visi_mean != 0)
                {
                    whiteness =
                        ((fabs ((float) input->buf[BI_BLUE][col] - visi_mean)
                          + fabs ((float) input->buf[BI_GREEN][col] - visi_mean)
                          + fabs ((float) input->buf[BI_RED][col]
                                  - visi_mean))) / visi_mean;
                }
                else
                {
                    /* Just put a large value to remove them from cloud pixel
                       identification */
                    whiteness = 100.0;
                }
            }

            /* Update cloud_mask,  if one visible band is saturated,
               whiteness = 0, due to data type conversion, pixel value
               difference of 1 is possible */
            if ((input->buf[BI_BLUE][col]
                 >= (input->meta.satu_value_max[BI_BLUE] - 1))
                ||
                (input->buf[BI_GREEN][col]
                 >= (input->meta.satu_value_max[BI_GREEN] - 1))
                ||
                (input->buf[BI_RED][col]
                 >= (input->meta.satu_value_max[BI_RED] - 1)))
            {
                whiteness = 0.0;
                satu_bv = 1;
            }
            else
            {
                satu_bv = 0;
            }

            if ((pixel_mask[row][col] & (1 << CLOUD_BIT)) &&
                (whiteness - 0.7) < MINSIGMA)
                pixel_mask[row][col] |= 1 << CLOUD_BIT;
            else
                pixel_mask[row][col] &= ~(1 << CLOUD_BIT);

            /* Haze test, equation 3 */
            hot =
                (float) input->buf[BI_BLUE][col] -
                0.5 * (float) input->buf[BI_RED][col] - 800.0;
            if ((pixel_mask[row][col] & (1 << CLOUD_BIT))
                && (hot > MINSIGMA || satu_bv == 1))
                pixel_mask[row][col] |= 1 << CLOUD_BIT;
            else
                pixel_mask[row][col] &= ~(1 << CLOUD_BIT);

            /* Ratio 4/5 > 0.75 test, equation 4 */
            if ((pixel_mask[row][col] & (1 << CLOUD_BIT)) &&
                input->buf[BI_SWIR_1][col] != 0)
            {
                if ((float) input->buf[BI_NIR][col] /
                    (float) (input->buf[BI_SWIR_1][col]) - 0.75 > MINSIGMA)
                    pixel_mask[row][col] |= 1 << CLOUD_BIT;
                else
                    pixel_mask[row][col] &= ~(1 << CLOUD_BIT);
            }
            else
                pixel_mask[row][col] &= ~(1 << CLOUD_BIT);

            /* Test whether use thermal band or not */
            if ((!(pixel_mask[row][col] & (1 << CLOUD_BIT))) && mask == 1)
            {
                clear_mask[row][col] |= 1 << CLEAR_BIT;
                clear_pixel_counter++;
            }
            else
                clear_mask[row][col] &= ~(1 << CLEAR_BIT);

            if ((!(pixel_mask[row][col] & (1 << WATER_BIT))) &&
                clear_mask[row][col] & (1 << CLEAR_BIT))
            {
                clear_mask[row][col] |= 1 << CLEAR_LAND_BIT;
                clear_land_pixel_counter++;
                clear_mask[row][col] &= ~(1 << CLEAR_WATER_BIT);
            }
            else if ((pixel_mask[row][col] & (1 << WATER_BIT)) &&
                     clear_mask[row][col] & (1 << CLEAR_BIT))
            {
                clear_mask[row][col] |= 1 << CLEAR_WATER_BIT;
                clear_mask[row][col] &= ~(1 << CLEAR_LAND_BIT);
            }
            else
            {
                clear_mask[row][col] &= ~(1 << CLEAR_WATER_BIT);
                clear_mask[row][col] &= ~(1 << CLEAR_LAND_BIT);
            }
        }
    }
    printf ("\n");

    *ptm = 100. * ((float) clear_pixel_counter / (float) mask_counter);
    lndptm = 100. * ((float) clear_land_pixel_counter / (float) mask_counter);

    if (verbose)
    {
        printf ("clear_pixels, clear_land_pixels, mask_counter = %d,%d,%d\n",
                clear_pixel_counter, clear_land_pixel_counter, mask_counter);
        printf ("*ptm, lndptm=%f,%f\n", *ptm, lndptm);
    }

    if ((*ptm - 0.1) <= MINSIGMA) /* No thermal test is needed, all clouds */
    {
        *t_templ = -1.0;
        *t_temph = -1.0;
        for (row = 0; row < nrows; row++)
        {
            for (col = 0; col < ncols; col++)
            {
                /* All cloud */
                if (!(pixel_mask[row][col] & (1 << CLOUD_BIT)))
                    pixel_mask[row][col] |= 1 << SHADOW_BIT;
                else
                    pixel_mask[row][col] &= ~(1 << SHADOW_BIT);
            }
        }
    }
    else
    {
        f_temp = malloc (input->size.l * input->size.s * sizeof (int16));
        f_wtemp = malloc (input->size.l * input->size.s * sizeof (int16));
        if (f_temp == NULL || f_wtemp == NULL)
        {
            sprintf (errstr, "Allocating temp memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The second pass\n");

        int16 f_temp_max = SHRT_MIN;
        int16 f_temp_min = SHRT_MAX;
        int16 f_wtemp_max = SHRT_MIN;
        int16 f_wtemp_min = SHRT_MAX;
        int index = 0;
        int index2 = 0;
        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For the thermal band, read the input thermal band  */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            for (col = 0; col < ncols; col++)
            {
                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                    input->therm_buf[col] = input->meta.therm_satu_value_max;

                if ((lndptm - 0.1) >= MINSIGMA)
                {
                    /* get clear land temperature */
                    if (clear_mask[row][col] & (1 << CLEAR_LAND_BIT))
                    {
                        f_temp[index] = input->therm_buf[col];
                        if (f_temp_max < f_temp[index])
                            f_temp_max = f_temp[index];
                        if (f_temp_min > f_temp[index])
                            f_temp_min = f_temp[index];
                        index++;
                    }
                }
                else
                {
                    /* get clear water temperature */
                    if (clear_mask[row][col] & (1 << CLEAR_BIT))
                    {
                        f_temp[index] = input->therm_buf[col];
                        if (f_temp_max < f_temp[index])
                            f_temp_max = f_temp[index];
                        if (f_temp_min > f_temp[index])
                            f_temp_min = f_temp[index];
                        index++;
                    }
                }
                /* Equation 7 */
                if (clear_mask[row][col] & (1 << CLEAR_WATER_BIT))
                {
                    f_wtemp[index2] = input->therm_buf[col];
                    if (f_wtemp[index2] > f_wtemp_max)
                        f_wtemp_max = f_wtemp[index2];
                    if (f_wtemp[index2] < f_wtemp_min)
                        f_wtemp_min = f_wtemp[index2];
                    index2++;
                }
            }
        }
        printf ("\n");

        /* Set maximum and minimum values to zero if no clear land/water 
           pixels */
        if (f_temp_min == SHRT_MAX)
            f_temp_min = 0;
        if (f_temp_max == SHRT_MIN)
            f_temp_max = 0;
        if (f_wtemp_min == SHRT_MAX)
            f_wtemp_min = 0;
        if (f_wtemp_max == SHRT_MIN)
            f_wtemp_max = 0;

        /* Tempearture for snow test */
        l_pt = 0.175;
        h_pt = 1.0 - l_pt;
        /* 0.175 percentile background temperature (low) */
        status = prctile (f_temp, index, f_temp_min, f_temp_max, 100.0 * l_pt,
                          t_templ);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        /* 0.825 percentile background temperature (high) */
        status = prctile (f_temp, index, f_temp_min, f_temp_max, 100.0 * h_pt,
                          t_temph);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = prctile (f_wtemp, index2, f_wtemp_min, f_wtemp_max,
                          100.0 * h_pt, &t_wtemp);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Temperature test */
        t_buffer = 4 * 100;
        *t_templ -= (float) t_buffer;
        *t_temph += (float) t_buffer;
        temp_l = *t_temph - *t_templ;

        /* Relase f_temp memory */
        free (f_wtemp);
        free (f_temp);
        f_wtemp = NULL;
        f_temp = NULL;

        wfinal_prob = (float **) allocate_2d_array (input->size.l,
                                                    input->size.s,
                                                    sizeof (float));
        final_prob =
            (float **) allocate_2d_array (input->size.l, input->size.s,
                                          sizeof (float));
        if (wfinal_prob == NULL || final_prob == NULL)
        {
            sprintf (errstr, "Allocating prob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The third pass\n");

        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            /* Loop through each line in the image */
            for (col = 0; col < ncols; col++)
            {
                for (ib = 0; ib < BI_REFL_BAND_COUNT - 1; ib++)
                {
                    if (input->buf[ib][col] == input->meta.satu_value_ref[ib])
                        input->buf[ib][col] = input->meta.satu_value_max[ib];
                }
                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                    input->therm_buf[col] = input->meta.therm_satu_value_max;

                if (pixel_mask[row][col] & (1 << WATER_BIT))
                {
                    /* Get cloud prob over water */
                    /* Temperature test over water */
                    wtemp_prob = (t_wtemp - (float) input->therm_buf[col]) /
                        400.0;
                    if (wtemp_prob < MINSIGMA)
                        wtemp_prob = 0.0;

                    /* Brightness test (over water) */
                    t_bright = 1100;
                    brightness_prob = (float) input->buf[BI_SWIR_1][col] /
                        (float) t_bright;
                    if ((brightness_prob - 1.0) > MINSIGMA)
                        brightness_prob = 1.0;
                    if (brightness_prob < MINSIGMA)
                        brightness_prob = 0.0;

                    /*Final prob mask (water), cloud over water probability */
                    wfinal_prob[row][col] =
                        100.0 * wtemp_prob * brightness_prob;
                    final_prob[row][col] = 0.0;
                }
                else
                {
                    temp_prob =
                        (*t_temph - (float) input->therm_buf[col]) / temp_l;
                    /* Temperature can have prob > 1 */
                    if (temp_prob < MINSIGMA)
                        temp_prob = 0.0;

                    /* label the non-fill pixels
                       Due to a problem with the input LPGS data, the thermal
                       band may have values less than -9999 after scaling so
                       exclude those as well */
                    if (input->therm_buf[col] <= -9999
                        || input->buf[BI_BLUE][col] == -9999
                        || input->buf[BI_GREEN][col] == -9999
                        || input->buf[BI_RED][col] == -9999
                        || input->buf[BI_NIR][col] == -9999
                        || input->buf[BI_SWIR_1][col] == -9999
                        || input->buf[BI_SWIR_2][col] == -9999)
                    {
                        mask = 0;
                    }
                    else
                        mask = 1;

                    if ((input->buf[BI_RED][col]
                         + input->buf[BI_NIR][col]) != 0
                        && mask == 1)
                    {
                        ndvi = (float) (input->buf[BI_NIR][col]
                                        - input->buf[BI_RED][col])
                               / (float) (input->buf[BI_NIR][col]
                                          + input->buf[BI_RED][col]);
                    }
                    else
                        ndvi = 0.01;

                    if ((input->buf[BI_GREEN][col]
                         + input->buf[BI_SWIR_1][col]) != 0
                        && mask == 1)
                    {
                        ndsi = (float) (input->buf[BI_GREEN][col]
                                        - input->buf[BI_SWIR_1][col])
                               / (float) (input->buf[BI_GREEN][col]
                                          + input->buf[BI_SWIR_1][col]);
                    }
                    else
                        ndsi = 0.01;

                    /* NDVI and NDSI should not be negative */
                    if (ndsi < MINSIGMA)
                        ndsi = 0.0;
                    if (ndvi < MINSIGMA)
                        ndvi = 0.0;

                    visi_mean = (input->buf[BI_BLUE][col]
                                 + input->buf[BI_GREEN][col]
                                 + input->buf[BI_RED][col]) / 3.0;
                    if (visi_mean != 0)
                    {
                        whiteness =
                            ((fabs ((float) input->buf[BI_BLUE][col]
                                    - visi_mean)
                              + fabs ((float) input->buf[BI_GREEN][col]
                                      - visi_mean)
                              + fabs ((float) input->buf[BI_RED][col]
                                      - visi_mean))) / visi_mean;
                    }
                    else
                        whiteness = 0.0;

                    /* If one visible band is saturated, whiteness = 0 */
                    if ((input->buf[BI_BLUE][col]
                         >= (input->meta.satu_value_max[BI_BLUE] - 1))
                        ||
                        (input->buf[BI_GREEN][col]
                         >= (input->meta.satu_value_max[BI_GREEN] - 1))
                        ||
                        (input->buf[BI_RED][col]
                         >= (input->meta.satu_value_max[BI_RED] - 1)))
                    {
                        whiteness = 0.0;
                    }

                    /* Vari_prob=1-max(max(abs(NDSI),abs(NDVI)),whiteness); */
                    if ((ndsi - ndvi) > MINSIGMA)
                        max_value = ndsi;
                    else
                        max_value = ndvi;
                    if ((whiteness - max_value) > MINSIGMA)
                        max_value = whiteness;
                    vari_prob = 1.0 - max_value;

                    /*Final prob mask (land) */
                    final_prob[row][col] = 100.0 * (temp_prob * vari_prob);
                    wfinal_prob[row][col] = 0.0;
                }
            }
        }
        printf ("\n");

        /* Allocate memory for prob */
        prob = malloc (input->size.l * input->size.s * sizeof (float));
        if (prob == NULL)
        {
            sprintf (errstr, "Allocating prob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        float prob_max = 0.0;
        float prob_min = 0.0;
        int index3 = 0;
        for (row = 0; row < nrows; row++)
        {
            for (col = 0; col < ncols; col++)
            {
                if (clear_mask[row][col] & (1 << CLEAR_LAND_BIT))
                {
                    prob[index3] = final_prob[row][col];
                    if ((prob[index3] - prob_max) > MINSIGMA)
                        prob_max = prob[index3];
                    if ((prob_min - prob[index3]) > MINSIGMA)
                        prob_min = prob[index3];
                    index3++;
                }
            }
        }

        /* Dynamic threshold for land */
        if (index3 != 0)
        {
            status = prctile2 (prob, index3, prob_min, prob_max, 100.0 * h_pt,
                               &clr_mask);
            if (status != SUCCESS)
            {
                sprintf (errstr, "Error calling prctile2 routine");
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
        }
        else
        {
            clr_mask = 27.5; /* no clear land pixel, make clr_mask double of
                                cloud_prob_threshold */
        }
        clr_mask += cloud_prob_threshold;

        /* Relase memory for prob */
        free (prob);
        prob = NULL;

        /* Allocate memory for wprob */
        wprob = malloc (input->size.l * input->size.s * sizeof (float));
        if (wprob == NULL)
        {
            sprintf (errstr, "Allocating wprob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        float wprob_max = 0.0;
        float wprob_min = 0.0;
        int index4 = 0;
        for (row = 0; row < nrows; row++)
        {
            for (col = 0; col < ncols; col++)
            {
                if (clear_mask[row][col] & (1 << CLEAR_WATER_BIT))
                {
                    wprob[index4] = wfinal_prob[row][col];
                    if ((wprob[index4] - wprob_max) > MINSIGMA)
                        wprob_max = wprob[index4];
                    if ((wprob_min - wprob[index4]) > MINSIGMA)
                        wprob_min = wprob[index4];
                    index4++;
                }
            }
        }

        /* Dynamic threshold for water */
        if (index4 != 0)
        {
            status =
                prctile2 (wprob, index4, wprob_min, wprob_max, 100.0 * h_pt,
                          &wclr_mask);
            if (status != SUCCESS)
            {
                sprintf (errstr, "Error calling prctile2 routine");
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
        }
        else
        {
            wclr_mask = 27.5; /* no clear water pixel, make wclr_mask 27.5 */
        }
        wclr_mask += cloud_prob_threshold;

        /* Relase memory for wprob */
        free (wprob);
        wprob = NULL;

        if (verbose)
        {
            printf ("pcloud probability threshold (land) = %.2f\n", clr_mask);
            printf ("pcloud probability threshold (water) = %.2f\n",
                    wclr_mask);

            printf ("The fourth pass\n");
        }

        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            for (col = 0; col < ncols; col++)
            {
                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                    input->therm_buf[col] = input->meta.therm_satu_value_max;

                if (((pixel_mask[row][col] & (1 << CLOUD_BIT)) &&
                     final_prob[row][col] > clr_mask &&
                     (!(pixel_mask[row][col] & (1 << WATER_BIT)))) ||
                    ((pixel_mask[row][col] & (1 << CLOUD_BIT)) &&
                     wfinal_prob[row][col] > wclr_mask &&
                     (pixel_mask[row][col] & (1 << WATER_BIT)))
                    || (input->therm_buf[col] < *t_templ + t_buffer - 3500))
                    pixel_mask[row][col] |= 1 << CLOUD_BIT;
                else
                    pixel_mask[row][col] &= ~(1 << CLOUD_BIT);
            }
        }
        printf ("\n");

        /* Free the memory */
        status = free_2d_array ((void **) wfinal_prob);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Freeing memory: wfinal_prob\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = free_2d_array ((void **) final_prob);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Freeing memory: final_prob\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Band 4 &5 flood fill */
        nir = calloc (input->size.l * input->size.s, sizeof (int16));
        swir = calloc (input->size.l * input->size.s, sizeof (int16));
        if (nir == NULL || swir == NULL)
        {
            sprintf (errstr, "Allocating nir and swir memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Open the intermediate file for writing */
        FILE *fd1;
        FILE *fd2;
        fd1 = fopen ("b4.bin", "wb");
        if (fd1 == NULL)
        {
            sprintf (errstr, "Opening file: b4.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        fd2 = fopen ("b5.bin", "wb");
        if (fd2 == NULL)
        {
            sprintf (errstr, "Opening file: b5.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The fifth pass\n");

        int16 nir_max = 0;
        int16 nir_min = 0;
        int16 swir_max = 0;
        int16 swir_min = 0;
        int idx = 0;
        int idx2 = 0;
        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            for (col = 0; col < ncols; col++)
            {
                if (input->buf[BI_NIR][col]
                    == input->meta.satu_value_ref[BI_NIR])
                {
                    input->buf[BI_NIR][col] =
                        input->meta.satu_value_max[BI_NIR];
                }
                if (input->buf[BI_SWIR_1][col]
                    == input->meta.satu_value_ref[BI_SWIR_1])
                {
                    input->buf[BI_SWIR_1][col] =
                        input->meta.satu_value_max[BI_SWIR_1];
                }

                if (clear_mask[row][col] & (1 << CLEAR_LAND_BIT))
                {
                    nir[idx] = input->buf[BI_NIR][col];
                    if (nir[idx] > nir_max)
                        nir_max = nir[idx];
                    if (nir[idx] < nir_min)
                        nir_min = nir[idx];
                    idx++;
                }

                if (clear_mask[row][col] & (1 << CLEAR_LAND_BIT))
                {
                    swir[idx2] = input->buf[BI_SWIR_1][col];
                    if (swir[idx2] > swir_max)
                        swir_max = swir[idx2];
                    if (swir[idx2] < swir_min)
                        swir_min = swir[idx2];
                    idx2++;
                }
            }

            /* Write out the intermediate file */
            status = fwrite (&input->buf[BI_NIR][0], sizeof (int16),
                             input->size.s, fd1);
            if (status != input->size.s)
            {
                sprintf (errstr, "Writing file: b4.bin\n");
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
            status = fwrite (&input->buf[BI_SWIR_1][0], sizeof (int16),
                             input->size.s, fd2);
            if (status != input->size.s)
            {
                sprintf (errstr, "Writing file: b5.bin\n");
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
        }
        printf ("\n");

        /* Close the intermediate file */
        status = fclose (fd1);
        if (status)
        {
            sprintf (errstr, "Closing file: b4.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = fclose (fd2);
        if (status)
        {
            sprintf (errstr, "Closing file: b5.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Estimating background (land) Band 4 Ref */
        status =
            prctile (nir, idx + 1, nir_min, nir_max, 100.0 * l_pt, &backg_b4);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Calling prctile function\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status =
            prctile (swir, idx2 + 1, swir_min, swir_max, 100.0 * l_pt,
                     &backg_b5);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Calling prctile function\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Release the memory */
        free (nir);
        free (swir);
        nir = NULL;
        swir = NULL;

        /* Write out the intermediate values */
        fd1 = fopen ("b4_b5.txt", "w");
        if (fd1 == NULL)
        {
            sprintf (errstr, "Opening file: b4_b5.txt\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Write out the intermediate file */
        fprintf (fd1, "%f\n", backg_b4);
        fprintf (fd1, "%f\n", backg_b5);
        fprintf (fd1, "%d\n", input->size.l);
        fprintf (fd1, "%d\n", input->size.s);

        /* Close the intermediate file */
        status = fclose (fd1);
        if (status)
        {
            sprintf (errstr, "Closing file: b4_b5.txt\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Call the fill minima routine to do image fill */
        status = system ("run_fillminima.py");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Running run_fillminima.py routine\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Open the intermediate file for reading */
        fd1 = fopen ("filled_b4.bin", "rb");
        if (fd1 == NULL)
        {
            sprintf (errstr, "Opening file: filled_b4.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        fd2 = fopen ("filled_b5.bin", "rb");
        if (fd2 == NULL)
        {
            sprintf (errstr, "Opening file: filled_b5.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* May need allocate two memory for new band 4 and 5 */
        int16 *new_nir;
        int16 *new_swir;

        new_nir = (int16 *) malloc (input->size.s * sizeof (int16));
        new_swir = (int16 *) malloc (input->size.s * sizeof (int16));
        if (new_nir == NULL || new_swir == NULL)
        {
            sprintf (errstr, "Allocating new_nir/new_swir memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The sixth pass\n");

        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            /* Read out the intermediate file */
            fread (&new_nir[0], sizeof (int16), input->size.s, fd1);
            fread (&new_swir[0], sizeof (int16), input->size.s, fd2);

            for (col = 0; col < ncols; col++)
            {
                if (input->buf[BI_NIR][col]
                    == input->meta.satu_value_ref[BI_NIR])
                {
                    input->buf[BI_NIR][col] =
                        input->meta.satu_value_max[BI_NIR];
                }
                if (input->buf[BI_SWIR_1][col]
                    == input->meta.satu_value_ref[BI_SWIR_1])
                {
                    input->buf[BI_SWIR_1][col] =
                        input->meta.satu_value_max[BI_SWIR_1];
                }

                /* process non-fill pixels only
                   Due to a problem with the input LPGS data, the thermal
                   band may have values less than -9999 after scaling so
                   exclude those as well */
                if (input->therm_buf[col] <= -9999
                    || input->buf[BI_BLUE][col] == -9999
                    || input->buf[BI_GREEN][col] == -9999
                    || input->buf[BI_RED][col] == -9999
                    || input->buf[BI_NIR][col] == -9999
                    || input->buf[BI_SWIR_1][col] == -9999
                    || input->buf[BI_SWIR_2][col] == -9999)
                {
                    mask = 0;
                }
                else
                    mask = 1;

                if (mask == 1)
                {
                    new_nir[col] -= input->buf[BI_NIR][col];
                    new_swir[col] -= input->buf[BI_SWIR_1][col];

                    if (new_nir[col] < new_swir[col])
                        shadow_prob = new_nir[col];
                    else
                        shadow_prob = new_swir[col];

                    if (shadow_prob > 200)
                        pixel_mask[row][col] |= 1 << SHADOW_BIT;
                    else
                        pixel_mask[row][col] &= ~(1 << SHADOW_BIT);
                }
                else
                {
                    pixel_mask[row][col] |= 1 << FILL_BIT;
                    pixel_mask[row][col] &= ~(1 << CLOUD_BIT);
                    pixel_mask[row][col] &= ~(1 << SHADOW_BIT);
                    pixel_mask[row][col] &= ~(1 << WATER_BIT);
                    pixel_mask[row][col] &= ~(1 << SNOW_BIT);
                }

                /* refine Water mask (no confusion water/cloud) */
                if ((pixel_mask[row][col] & (1 << WATER_BIT)) &&
                    (pixel_mask[row][col] & (1 << CLOUD_BIT)))
                    pixel_mask[row][col] &= ~(1 << WATER_BIT);
            }
        }
        printf ("\n");

        /* Release the memory */
        free (new_nir);
        free (new_swir);
        new_nir = NULL;
        new_swir = NULL;

        /* Close the intermediate file */
        status = fclose (fd1);
        if (status)
        {
            sprintf (errstr, "Closing file: filled_b4.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = fclose (fd2);
        if (status)
        {
            sprintf (errstr, "Closing file: filled_b5.bin\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Remove the intermediate files */
        status = system ("rm b4_b5.txt");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Removing b4_b5.txt file\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = system ("rm b4.bin");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Removing b4.bin file\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = system ("rm b5.bin");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Removing b5.bin file\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = system ("rm filled_b4.bin");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Removing filled_b4.bin file\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = system ("rm filled_b5.bin");
        if (status != SUCCESS)
        {
            sprintf (errstr, "Removing filled_b5.bin file\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
    }

    status = free_2d_array ((void **) clear_mask);
    if (status != SUCCESS)
    {
        sprintf (errstr, "Freeing memory: clear_mask\n");
        RETURN_ERROR (errstr, "pcloud", FAILURE);
    }

    return SUCCESS;
}
bool potential_cloud_shadow_snow_mask
(
 Input_t *input,
 float cloud_prob_threshold,
 float *ptm,
 float *t_templ,
 float *t_temph,
 unsigned char **cloud_mask,
 unsigned char **shadow_mask,
 unsigned char **snow_mask,
 unsigned char **water_mask,
 unsigned char **final_mask
)
{
    char errstr[MAX_STR_LEN];
    int nrows = input->size.l;
    int ncols = input->size.s;
    int ib = 0;
    int row =0; 
    int col = 0;
    int mask_counter = 0;
    int clear_pixel_counter = 0;
    int clear_land_pixel_counter = 0;
    float ndvi, ndsi;
    int16 *f_temp = NULL;
    int16 *f_wtemp = NULL;
    float visi_mean;
    float whiteness = 0;
    float hot;
    float lndptm;
    float l_pt;
    float h_pt;
    int mask;
    float t_wtemp;
    float **wfinal_prob;
    float **final_prob;
    float wtemp_prob;
    int t_bright;
    float brightness_prob;
    int t_buffer;
    float temp_l;
    float temp_prob;
    float vari_prob;
    float max_value;
    float *prob = NULL;
    float clr_mask;
    float wclr_mask;
    int16 *nir = NULL;
    int16 *swir = NULL;
    float backg_b4;
    float backg_b5;
    float shadow_prob;
    int status;

    /* Dynamic memory allocation */
    unsigned char **clear_mask;
    unsigned char **clear_land_mask;

    clear_mask = (unsigned char **)ias_misc_allocate_2d_array(input->size.l, 
                 input->size.s, sizeof(unsigned char)); 
    clear_land_mask = (unsigned char **)ias_misc_allocate_2d_array(
                 input->size.l, input->size.s, sizeof(unsigned char)); 
    if (clear_mask == NULL || clear_land_mask ==NULL)
    {
        sprintf (errstr, "Allocating mask memory");
        ERROR (errstr, "pcloud");
    }
    
    printf("The first pass\n");

    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
        /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
           printf ("Processing line %d\r",row);
           fflush (stdout);
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine(input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                    "band %d", row, ib);
                ERROR (errstr, "pcloud");
            }
        }

	/* For the thermal band */
	/* Read the input thermal band -- data is read into input->therm_buf */
	if (!GetInputThermLine(input, row))
        {
	  sprintf (errstr, "Reading input thermal data for line %d", row);
	  ERROR (errstr, "pcloud");
	}

   for (col = 0; col < ncols; col++)
    {
     if ((input->buf[2][col] +  input->buf[3][col]) != 0)
      ndvi = (input->buf[3][col] -  input->buf[2][col]) / 
       (input->buf[3][col] +  input->buf[2][col]);
     else
      ndvi = 0.01;

     if ((input->buf[1][col] +  input->buf[4][col]) != 0)
      ndsi = (input->buf[1][col] -  input->buf[4][col]) / 
       (input->buf[1][col] +  input->buf[4][col]);
     else
      ndsi = 0.01;

     /* process non-fill pixels only */
     if (input->therm_buf[col] > -9999)
       {
	 mask = 1;
	 mask_counter++;
       }
     else
       mask = 0;

     /* Basic cloud test */
     if ((ndsi < 0.8) && (ndvi < 0.8) && 
         (input->buf[5][col] > 300) && (input->therm_buf[col] < 2700))
         cloud_mask[row][col] = 1;
     else
         cloud_mask[row][col] = 0;

     /* It takes every snow pixels including snow pixel under thin clouds or 
        icy clouds */
     if ((ndsi > 0.15) && (input->therm_buf[col] < 380) && 
         (input->buf[3][col] > 1100) && (input->buf[1][col] > 1000))
         snow_mask[row][col] = 1;
     else
         snow_mask[row][col] = 0;

     /* Zhe's water test (works over thin cloud) */
     if (((ndvi < 0.01) && (input->buf[3][col] < 1100)) || 
         ((ndvi < 0.1) && (ndvi > 0.0) && 
          (input->buf[3][col] < 500)))
         water_mask[row][col] = 1;
     else 
         water_mask[row][col] = 0;
     if (mask == 0)
         water_mask[row][col] = 255;

     /* visible bands flatness (sum(abs)/mean < 0.6 => brigt and dark cloud) */
     visi_mean = (input->buf[0][col] + input->buf[1][col] +
                  input->buf[2][col]) / 3.0;
     whiteness = ((abs(input->buf[0][col] - visi_mean) + 
      abs(input->buf[1][col] - visi_mean) +
                   abs(input->buf[2][col] - visi_mean)))/ visi_mean;

     /* Update cloud_mask,  if one visible band is saturated, whiteness = 0 */
     if (input->buf[0][col] > input->meta.therm_satu_value_ref ||
	 input->buf[1][col] > input->meta.therm_satu_value_ref ||
	 input->buf[2][col] > input->meta.therm_satu_value_ref)
      whiteness = 0;
     if (cloud_mask[row][col] == 1 && whiteness < 0.7)
      cloud_mask[row][col] = 1; 
     else
      cloud_mask[row][col] = 0;

     /* Haze test */
     hot = input->buf[0][col] - 0.5 *input->buf[2][col] - 800;
     if (cloud_mask[row][col] == 1 && (hot > 0.0 || abs(whiteness) < MINSIGMA))
       cloud_mask[row][col] = 1;
     else
       cloud_mask[row][col] = 0;

     /* Ratio 4/5 > 0.75 test */
     if (cloud_mask[row][col] == 1 && (input->buf[3][col] / 
         input->buf[4][col] > 0.75))
       cloud_mask[row][col] = 1;
     else
       cloud_mask[row][col] = 0;
     
     /* Test whether use thermal band or not */
     if (cloud_mask[row][col] == 0 && mask == 1)
       {
	 clear_mask[row][col] = 1;
	 clear_pixel_counter++;

       }
     else
         clear_mask[row][col] = 0;

     if (water_mask[row][col] != 1 && clear_mask[row][col] == 1)
       {
	 clear_land_mask[row][col] = 1;
         clear_land_pixel_counter++;
       }
     else
       clear_land_mask[row][col] = 0;
    }
}

    *ptm = 100. * ((float)clear_pixel_counter / (float)mask_counter);
    lndptm = 100. * ((float)clear_land_pixel_counter / (float)mask_counter);

    if (*ptm <= 0.1)
     majority_filter(cloud_mask, nrows, ncols);
    else
    {
        f_temp = malloc(input->size.l * input->size.s * sizeof(int16));
	f_wtemp = malloc(input->size.l * input->size.s * sizeof(int16));
	if (f_temp == NULL   || f_wtemp == NULL)
	{
	     sprintf (errstr, "Allocating temp memory");
	     ERROR (errstr, "pcloud");
	}
    }
     
     printf("The second pass\n");
     int16 f_temp_max = 0;
     int16 f_temp_min = 0;
     int16 f_wtemp_max = 0;
     int16 f_wtemp_min = 0;
     int index = 0;
     int index2 = 0;
    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
        /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
	     printf ("Processing line %d\r", row);
	     fflush (stdout);
	   }
	 
	 /* For each of the image bands */
	 for (ib = 0; ib < input->nband; ib++)
	   {
	     /* Read each input reflective band -- data is read into
		input->buf[ib] */
	     if (!GetInputLine(input, ib, row))
	       {
		 sprintf (errstr, "Reading input image data for line %d, "
			  "band %d", row, ib);
		 ERROR (errstr, "pcloud");
	       }
	   }

	 /* For the thermal band */
	 /* Read the input thermal band -- data is read into input->therm_buf */
	 if (!GetInputThermLine(input, row))
	   {
	     sprintf (errstr, "Reading input thermal data for line %d", row);
	     ERROR (errstr, "pcloud");
	   }

	 for (col =0; col < ncols; col++)
	   {
	     if (*ptm <= 0.1) /* No thermal test, 
                                meaningless for snow detection */
	     {
		 /* All cloud */
		 if (cloud_mask[row][col] != 1)
		   shadow_mask[row][col] = 1;
                 else
		   shadow_mask[row][col] = 0;
#if 0
                 /* Tempoarary outpouts */
                 if (water_mask[row][col] == 1)
                     final_mask[row][col] = 1;
                 if (shadow_mask[row][col] == 1)
                     final_mask[row][col] = 2;
                 if (cloud_mask[row][col] == 1)
	             final_mask[row][col] = 4;
                 if (input->therm_buf[col] = -9999)
                     final_mask[row][col] = 255;
#endif
	     }
	     else
	     {		 
		 if (lndptm >= 0.1)
		 {
                     /* get clear land temperature */
		     if (clear_land_mask[row][col] == 1 && 
                         input->therm_buf[col] != -9999)
	             {
			 f_temp[index] = input->therm_buf[col];
                         if (f_temp_max < f_temp[index])
                            f_temp_max = f_temp[index];
                         if (f_temp_min > f_temp[index])
                            f_temp_min = f_temp[index];
			 index++;
	             }
	         }
		 else
	         {
		     /*get clear water temperature */
		     if (clear_mask[row][col] == 1 &&  
                         input->therm_buf[col] != -9999)
	             {
			 f_temp[index] = input->therm_buf[col];
                         if (f_temp_max < f_temp[index])
                            f_temp_max = f_temp[index];
                         if (f_temp_min > f_temp[index])
                            f_temp_min = f_temp[index];
			 index++;
	             }
	         }
		 if (water_mask[row][col] == 1 && input->therm_buf[col] <= 300
                     && input->therm_buf[col] != -9999)
	         {
		     f_wtemp[index2] = input->therm_buf[col];
                     if (f_wtemp[index2] > f_wtemp_max)
                        f_wtemp_max = f_wtemp[index2];
                     if (f_wtemp[index2] < f_wtemp_max)
                        f_wtemp_min = f_wtemp[index2];
                     index2++;
	         }
	     }
         }
    }

   printf("Clear sky pixel percentage in this scene = %.2f\n", *ptm);

   if (*ptm <= 0.1)
   {
      *t_templ = -1.0;
      *t_temph = -1.0;
      return 0;
   }
   else
   {
     /* Tempearture for snow test */
     l_pt = 0.175;
     h_pt = 1 - l_pt;

     printf("====%d,%d,%d,%d\n",f_wtemp_max,f_wtemp_min,f_temp_max,f_temp_min);

#if 0
     prctile(f_wtemp, index2 + 1, 100*h_pt, &t_wtemp);
     /* 0.175 percentile background temperature (low) */
     prctile(f_temp, index + 1, 100*l_pt, t_templ);
     /* 0.825 percentile background temperature (high) */
     prctile (f_temp, index + 1, 100*h_pt, t_temph);
#endif
     t_wtemp = h_pt * (float)(f_wtemp_max-f_wtemp_min) + (float)f_wtemp_min;
     *t_templ = l_pt * (float)(f_temp_max-f_temp_min) + (float)f_temp_min;
     *t_temph = h_pt * (float)(f_temp_max-f_temp_min) + (float)f_temp_min; 
     int f_temp_length;
     int f_wtemp_length;

     f_temp_length = f_temp_max - f_temp_min + 1;
     f_wtemp_length = f_wtemp_max - f_wtemp_min + 1;
#if 0
     prctile(f_wtemp, index2 + 1, f_wtemp_length, 100*h_pt, &t_wtemp);
     /* 0.175 percentile background temperature (low) */
     prctile(f_temp, index + 1, f_temp_length, 100*l_pt, t_templ);
     /* 0.825 percentile background temperature (high) */
     prctile (f_temp, index + 1, f_temp_length, 100*h_pt, t_temph);
#endif
     printf("index, index2 = %d,%d\n",index,index2);

     /* Temperature test */
     t_buffer = 4*100;    
     *t_templ -= t_buffer;
     *t_temph += t_buffer;
     temp_l=*t_temph-*t_templ;

     printf("t_wtemp,t_templ,t_temph = %f,%f,%f\n",t_wtemp,*t_templ,*t_temph);

     /* Relase f_temp memory */
     free(f_wtemp);
     free(f_temp);

     wfinal_prob = (float **)ias_misc_allocate_2d_array(input->size.l, 
                 input->size.s, sizeof(float)); 
     final_prob = (float **)ias_misc_allocate_2d_array(input->size.l, 
                 input->size.s, sizeof(float)); 
     if (wfinal_prob == NULL   ||  final_prob == NULL)
     {
	 sprintf (errstr, "Allocating prob memory");
	 ERROR (errstr, "pcloud");
     }

     printf("The third pass\n");
     /* Loop through each line in the image */
     for (row = 0; row < nrows; row++)
     {
        /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
           printf ("Processing line %d\r",row);
           fflush (stdout);
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine(input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                    "band %d", row, ib);
                ERROR (errstr, "pcloud");
            }
        }

	/* For the thermal band */
	/* Read the input thermal band -- data is read into input->therm_buf */
	if (!GetInputThermLine(input, row))
        {
          sprintf (errstr, "Reading input thermal data for line %d", row);
          ERROR (errstr, "pcloud");
	}

	for (col = 0; col <ncols; col++)
	{
	    /* Get cloud prob over water */
	    /* Temperature test over water */
	    wtemp_prob = (t_wtemp - input->therm_buf[col]) / 400.0;
	    
	    /* Brightness test (over water) */
	    t_bright = 1100;
	    brightness_prob = input->buf[4][col] / t_bright;
	    if (brightness_prob > 1)
	      brightness_prob = 1;
	    
	    /*Final prob mask (water), cloud over water probability */
	    wfinal_prob[row][col] =100 * wtemp_prob * brightness_prob;
	    
            temp_prob=(*t_temph-input->therm_buf[col]) / temp_l;
	    /* Temperature can have prob > 1 */
	    if (temp_prob < 0)
	      temp_prob = 0;
	    
            if ((input->buf[2][col] +  input->buf[3][col]) != 0)
                ndvi = (input->buf[3][col] -  input->buf[2][col]) / 
                   (input->buf[3][col] +  input->buf[2][col]);
            else
                ndvi = 0.01;

            if ((input->buf[1][col] +  input->buf[4][col]) != 0)
                ndsi = (input->buf[1][col] -  input->buf[4][col]) / 
                    (input->buf[1][col] +  input->buf[4][col]);
            else
                ndsi = 0.01;
	    
	    /* NDVI and NDSI should not be negative */
	    if (input->buf[2][col] >= input->meta.therm_satu_value_ref 
                && ndsi < 0)
	      ndsi = 0;
	    if (input->buf[3][col] >= input->meta.therm_satu_value_ref 
                && ndvi < 0)
	      ndvi = 0;
	    
	    /*      Vari_prob=1-max(max(abs(NDSI),abs(NDVI)),whiteness); */
	    if (abs(ndsi) > abs(ndvi))
	      max_value = abs(ndsi);
	    else
	      max_value = abs(ndvi);
	    if (whiteness > max_value)
	      max_value = whiteness;
	    vari_prob = 1 - max_value;
	    
	    /*Final prob mask (land) */
	    final_prob[row][col] = 100 * (temp_prob * vari_prob);
        }
     }

     prob = malloc(input->size.l * input->size.s * sizeof(float));
     if(prob == NULL)
     {
          sprintf (errstr, "Allocating prob memory");
	  ERROR (errstr, "pcloud");
     }

     float prob_max = 0.0;
     float prob_min = 0.0;
     int index3 = 0;
     for (row = 0; row < nrows; row++)
     {
	for (col = 0; col <ncols; col++)
	{	    
	    if (clear_land_mask[row][col] == 1);
	    {
		prob[index3] = final_prob[row][col];
                if ((prob[index3] - prob_max) > MINSIGMA)
                    prob_max = prob[index3];
                if ((prob_min - prob[index3]) > MINSIGMA)
                    prob_min = prob[index3];
		index3++;
	    }
        }
    }

    /*Dynamic threshold for land */
    //    prctile2(prob, index3+1, 100*h_pt, &clr_mask);
    printf("index3,prob_max,prob_min =%d, %f, %f\n",index3,prob_max,prob_min);
    clr_mask = h_pt * (prob_max - prob_min) + prob_min;
    printf("clr_mask =%d, %f\n",index3,clr_mask);
#if 0
    prctile2(prob, index3+1, 100*h_pt, &clr_mask);
    printf("clr_mask = %f\n",clr_mask);
#endif
    clr_mask += cloud_prob_threshold;

    printf("clr_mask = %f\n",clr_mask);
	    
    /* Relase memory for prob */
    free(prob);
	    
    /* Fixed threshold for water */
    wclr_mask = 50.0;
	    
    printf("pcloud probability threshold (land) = %.2f\n", clr_mask);
    
    printf("The fourth pass\n");
    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
        /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
           printf ("Processing line %d\r",row);
           fflush (stdout);
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine(input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                    "band %d", row, ib);
                ERROR (errstr, "pcloud");
            }
        }

	/* For the thermal band */
	/* Read the input thermal band -- data is read into input->therm_buf */
	if (!GetInputThermLine(input, row))
        {
	  sprintf (errstr, "Reading input thermal data for line %d", row);
	  ERROR (errstr, "pcloud");
	}

        for (col =0; col < ncols; col++)
        {
           if ((cloud_mask[row][col] == 1 && final_prob[row][col] > clr_mask &&
              water_mask[row][col] == 0) || (cloud_mask[row][col] == 1 && 
              wfinal_prob[row][col] > wclr_mask && water_mask[row][col] == 1) 
              || (final_prob[row][col] > 99 && water_mask[row][col] == 0) ||
              (input->therm_buf[col] < *t_templ - 3500))
                 cloud_mask[row][col] = 1;
        }
    }

    /* Free the memory */
    status = ias_misc_free_2d_array((void **)wfinal_prob);
    status = ias_misc_free_2d_array((void **)final_prob);

    /* Band 4 flood fill */
    nir = malloc(input->size.l * input->size.s * sizeof(int16)); 
    swir = malloc(input->size.l * input->size.s * sizeof(int16)); 
    if (nir == NULL   || swir == NULL)
    {
        sprintf(errstr, "Allocating nir and swir memory");
        ERROR (errstr, "pcloud");
    }
      
    int16 nir_max = 0;
    int16 swir_max = 0;
    index = 0;
    index2 = 0;
    for (row = 0; row < nrows; row++)
    {
      for (col = 0; col < ncols; col++)
      {
        if (clear_land_mask[row][col] == 1)
	{
	  nir[index] = input->buf[3][col];
          if (nir[index] > nir_max)
           nir_max = nir[index];
	  index++;
	}
      
        if (clear_land_mask[row][col] == 1)
	{
	  nir[index2] = input->buf[4][col];
          if (swir[index2] > swir_max)
           swir_max = swir[index2];
	  index2++;
	}
      }
    }

    status = ias_misc_free_2d_array((void **)clear_mask);
    status = ias_misc_free_2d_array((void **)clear_land_mask);

    /* Improve cloud mask by majority filtering */
    majority_filter(cloud_mask, nrows, ncols);

    /* Estimating background (land) Band 4 Ref */
    backg_b4 = l_pt * nir_max;
    backg_b5 = h_pt * swir_max;
    /* Release the memory */
    free(nir);
    free(swir);
	
    /* May need allocate two memory for new band 4 and 5 after imfill 
       (flood filling), also may need read in whole scene of bands 4 and 5
       for flood filling purpose */
    int16 **new_nir;
    int16 **new_swir;   

    new_nir = (int16 **)ias_misc_allocate_2d_array(input->size.l, 
              input->size.s, sizeof(int16)); 
    new_swir = (int16 **)ias_misc_allocate_2d_array(input->size.l, 
              input->size.s, sizeof(int16)); 
    if (wfinal_prob == NULL  || final_prob == NULL)
    {
        sprintf (errstr, "Allocating prob memory");
        ERROR (errstr, "pcloud");
    }

    printf("The fifth pass\n");

    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
       /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
           printf ("Processing line %d\r",row);
           fflush (stdout);
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine(input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                    "band %d", row, ib);
                ERROR (errstr, "pcloud");
            }
        }

	/* For the thermal band */
	/* Read the input thermal band -- data is read into input->therm_buf */
	if (!GetInputThermLine(input, row))
        {
	  sprintf (errstr, "Reading input thermal data for line %d", row);
	  ERROR (errstr, "pcloud");
	}

        for (col = 0; col < ncols; col++)
        {
           if (input->therm_buf[col] == -9999)
           {
               new_nir[row][col] = backg_b4;
	       new_swir[row][col] = backg_b5;
           }
           else
           {
	       new_nir[row][col] = input->buf[3][col];
	       new_swir[row][col] = input->buf[4][col];
           }
        }
    }

    /* TODO: Fill in regional minimum band 4 ref*/
    IplImage* img = cvCreateImage(cvSize(ncols, nrows), IPL_DEPTH_8U, 1);
    if (!img)
    {
        sprintf (errstr, "Reading input image data for line %d, "
                 "band %d", row, ib);
        ERROR (errstr, "pcloud");
    }
    //     cvSet2D(img, ncols, nrows, new_nir);
    for (row = 0; row < nrows; row++)
    {
       for (col = 0; col < ncols; col++)
       {
           img->imageData[row*ncols+col] = new_nir[row][col];
           // cvSet2D(img, col, row, new_nir[row][col]);
       }
    }
    CvPoint seed_point = cvPoint(3,3);
    CvScalar color = CV_RGB(1,0,0);

    cvFloodFill(img, seed_point, color, cvScalarAll(5.0), cvScalarAll(5.0), 
                NULL, 4, NULL );

    // cvGet2D(img, ncols, nrows, new_nir);
    for (row = 0; row < nrows; row++)
    {
       for (col = 0; col < ncols; col++)
       {
           new_nir[row][col] = img->imageData[row*ncols+col];
           // new_nir[row][col] = cvGet2D(img, col, row);          
       }
    }

    /* Release image memory */
    cvReleaseImage(&img);

    /* TODO: Fill in regional minimum band 5 ref*/
    IplImage* img2 = cvCreateImage(cvSize(ncols, nrows), IPL_DEPTH_8U, 1);
    if (!img2)
    {
        sprintf (errstr, "Reading input image data for line %d, "
                 "band %d", row, ib);
        ERROR (errstr, "pcloud");
    }
    //     cvSet2D(img, ncols, nrows, new_swir);
    for (row = 0; row < nrows; row++)
    {
       for (col = 0; col < ncols; col++)
       {
           img2->imageData[row*ncols+col] = new_swir[row][col];
           // cvSet2D(img2, col, row, new_swir[row][col]);
       }
    }

    cvFloodFill(img2, seed_point, color, cvScalarAll(5.0), cvScalarAll(5.0), 
                NULL, 4, NULL );

    //     cvGet2D(img, ncols, nrows, new_swir);
    for (row = 0; row < nrows; row++)
    {
       for (col = 0; col < ncols; col++)
       {
           new_swir[row][col] = img2->imageData[row*ncols+col];
           // new_swir[row][col] = cvGet2D(img2, col, row);          
       }
    }

    /* Release image memory */
    cvReleaseImage(&img2);

    for (row = 0; row < nrows; row++)
    {
      for (col = 0; col < ncols; col++)
      {
        if (new_nir[row][col] < new_swir[row][col])
	  shadow_prob = new_nir[row][col];
        else
	  shadow_prob = new_swir[row][col];
      
        if (shadow_prob > 200)
	  shadow_mask[row][col] = 1;
        else
	  shadow_mask[row][col] = 0;
      }
    }
    status = ias_misc_free_2d_array((void **)new_nir);
    status = ias_misc_free_2d_array((void **)new_swir);
   }

    printf("The sixth pass\n");
    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
       /* Print status on every 100 lines */
        if (!(row%1000)) 
        {
           printf ("Processing line %d\r",row);
           fflush (stdout);
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine(input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                    "band %d", row, ib);
                ERROR (errstr, "pcloud");
            }
        }

	/* For the thermal band */
	/* Read the input thermal band -- data is read into input->therm_buf */
	if (!GetInputThermLine(input, row))
        {
	  sprintf (errstr, "Reading input thermal data for line %d", row);
	  ERROR (errstr, "pcloud");
	}
     for (col = 0; col < ncols; col++)
     {
        /* refine Water mask - Zhe's water mask (no confusion water/cloud) */
        if (water_mask[row][col] == 1 && cloud_mask[row][col] == 0)
          water_mask[row][col] = 1;
        else
	  water_mask[row][col] = 0;

        if (input->therm_buf[col]==-9999)
	{
	  cloud_mask[row][col] = 255;
	  shadow_mask[row][col] = 255;
          //          final_mask[row][col] = 255;
	}
#if 0
      /* Temporary outputs */      
      if (water_mask[row][col] == 1)
	final_mask[row][col] = 1;
      if (snow_mask[row][col] == 1)
        final_mask[row][col] = 3;
      if (shadow_mask[row][col] == 1)
	final_mask[row][col] = 2;
      if (cloud_mask[row][col] == 1)
	final_mask[row][col] = 4;

      if ((water_mask[row][col] != 1) && (snow_mask[row][col] != 1) &&
          (shadow_cal[row][col] != 1) && (cloud_cal[row][col] != 1) &&
          boundary_test[row][col] != 255)
         final_mask[row][col] = 0;
#endif
     }
   }

   printf("t_wtemp,t_templ,t_temph = %f,%f,%f\n",t_wtemp,*t_templ,*t_temph);

    return 0;
}
/*****************************************************************************
MODULE:  potential_cloud_shadow_snow_mask

PURPOSE: Identify the cloud pixels, snow pixels, water pixels, clear land
         pixels, and potential shadow pixels

RETURN: SUCCESS
        FAILURE

HISTORY:
Date        Programmer       Reason
--------    ---------------  -------------------------------------
3/15/2013   Song Guo         Original Development

NOTES:
1. Thermal buffer is expected to be in degrees Celsius with a factor applied
   of 100.  Many values which compare to the thermal buffer in this code are
   hardcoded and assume degrees celsius * 100.
*****************************************************************************/
int potential_cloud_shadow_snow_mask
(
    Input_t * input,            /*I: input structure */
    float cloud_prob_threshold, /*I: cloud probability threshold */
    float *clear_ptm,           /*O: percent of clear-sky pixels */
    float *t_templ,             /*O: percentile of low background temp */
    float *t_temph,             /*O: percentile of high background temp */
    unsigned char *pixel_mask,  /*I/O: pixel mask */
    unsigned char *conf_mask,   /*I/O: confidence mask */
    bool verbose                /*I: value to indicate if intermediate
                                     messages should be printed */
)
{
    char errstr[MAX_STR_LEN];   /* error string */
    int nrows = input->size.l;  /* number of rows */
    int ncols = input->size.s;  /* number of columns */
    int ib = 0;                 /* band index */
    int row = 0;                /* row index */
    int col = 0;                /* column index */
    int image_data_counter = 0;        /* mask counter */
    int clear_pixel_counter = 0;       /* clear sky pixel counter */
    int clear_land_pixel_counter = 0;  /* clear land pixel counter */
    int clear_water_pixel_counter = 0; /* clear water pixel counter */
    float ndvi, ndsi;           /* NDVI and NDSI values */
    int16 *f_temp = NULL;       /* clear land temperature */
    int16 *f_wtemp = NULL;      /* clear water temperature */
    float visi_mean;            /* mean of visible bands */
    float whiteness = 0.0;      /* whiteness value */
    float hot;                  /* hot value for hot test */
    float land_ptm;             /* clear land pixel percentage */
    float water_ptm;            /* clear water pixel percentage */
    unsigned char land_bit;     /* Which clear bit to test all or just land */
    unsigned char water_bit;    /* Which clear bit to test all or just water */
    float l_pt;                 /* low percentile threshold */
    float h_pt;                 /* high percentile threshold */
    float t_wtemp;              /* high percentile water temperature */
    float *wfinal_prob = NULL;  /* final water pixel probabilty value */
    float *final_prob = NULL;   /* final land pixel probability value */
    float wtemp_prob;           /* water temperature probability value */
    int t_bright;               /* brightness test value for water */
    float brightness_prob;      /* brightness probability value */
    int t_buffer;               /* temperature test buffer */
    float temp_l;               /* difference of low/high tempearture
                                   percentiles */
    float temp_prob;            /* temperature probability */
    float vari_prob;            /* probability from NDVI, NDSI, and whiteness */
    float max_value;            /* maximum value */
    float *prob = NULL;         /* probability value */
    float *wprob = NULL;        /* probability value */
    float clr_mask = 0.0;       /* clear sky pixel threshold */
    float wclr_mask = 0.0;      /* water pixel threshold */
    int data_size;              /* Data size for memory allocation */
    int16 *nir = NULL;          /* near infrared band data */
    int16 *swir1 = NULL;        /* short wavelength infrared band data */
    int16 *nir_data = NULL;          /* Data to be filled */
    int16 *swir1_data = NULL;        /* Data to be filled */
    int16 *filled_nir_data = NULL;   /* Filled result */
    int16 *filled_swir1_data = NULL; /* Filled result */
    float nir_boundary;         /* NIR boundary value / background value */
    float swir1_boundary;       /* SWIR1 boundary value / background value */
    int16 shadow_prob;          /* shadow probability */
    int status;                 /* return value */
    int satu_bv;                /* sum of saturated bands 1, 2, 3 value */

    int pixel_index;
    int pixel_count;

    pixel_count = nrows * ncols;

    /* Dynamic memory allocation */
    unsigned char *clear_mask = NULL;

    clear_mask = calloc (pixel_count, sizeof (unsigned char));
    if (clear_mask == NULL)
    {
        RETURN_ERROR ("Allocating mask memory", "pcloud", FAILURE);
    }

    if (verbose)
        printf ("The first pass\n");

    /* Loop through each line in the image */
    for (row = 0; row < nrows; row++)
    {
        if (verbose)
        {
            /* Print status on every 1000 lines */
            if (!(row % 1000))
            {
                printf ("Processing line %d\r", row);
                fflush (stdout);
            }
        }

        /* For each of the image bands */
        for (ib = 0; ib < input->nband; ib++)
        {
            /* Read each input reflective band -- data is read into
               input->buf[ib] */
            if (!GetInputLine (input, ib, row))
            {
                sprintf (errstr, "Reading input image data for line %d, "
                         "band %d", row, ib);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }
        }

        /* For the thermal band */
        /* Read the input thermal band -- data is read into input->therm_buf */
        if (!GetInputThermLine (input, row))
        {
            sprintf (errstr, "Reading input thermal data for line %d", row);
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        for (col = 0; col < ncols; col++)
        {
            pixel_index = row * ncols + col;

            int ib;
            for (ib = 0; ib < BI_REFL_BAND_COUNT; ib++)
            {
                if (input->buf[ib][col] == input->meta.satu_value_ref[ib])
                    input->buf[ib][col] = input->meta.satu_value_max[ib];
            }
            if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                input->therm_buf[col] = input->meta.therm_satu_value_max;

            /* process non-fill pixels only
               Due to a problem with the input LPGS data, the thermal band
               may have values less than FILL_PIXEL after scaling so exclude
               those as well */
            if (input->therm_buf[col] <= FILL_PIXEL
                || input->buf[BI_BLUE][col] == FILL_PIXEL
                || input->buf[BI_GREEN][col] == FILL_PIXEL
                || input->buf[BI_RED][col] == FILL_PIXEL
                || input->buf[BI_NIR][col] == FILL_PIXEL
                || input->buf[BI_SWIR_1][col] == FILL_PIXEL
                || input->buf[BI_SWIR_2][col] == FILL_PIXEL)
            {
                pixel_mask[pixel_index] = CF_FILL_BIT;
                clear_mask[pixel_index] = CF_CLEAR_FILL_BIT;
                continue;
            }
            image_data_counter++;

            if ((input->buf[BI_RED][col] + input->buf[BI_NIR][col]) != 0)
            {
                ndvi = (float) (input->buf[BI_NIR][col]
                                - input->buf[BI_RED][col])
                       / (float) (input->buf[BI_NIR][col]
                                  + input->buf[BI_RED][col]);
            }
            else
                ndvi = 0.01;

            if ((input->buf[BI_GREEN][col] + input->buf[BI_SWIR_1][col]) != 0)
            {
                ndsi = (float) (input->buf[BI_GREEN][col]
                                - input->buf[BI_SWIR_1][col])
                       / (float) (input->buf[BI_GREEN][col]
                                  + input->buf[BI_SWIR_1][col]);
            }
            else
                ndsi = 0.01;

            /* Basic cloud test, equation 1 */
            if (((ndsi - 0.8) < MINSIGMA)
                && ((ndvi - 0.8) < MINSIGMA)
                && (input->buf[BI_SWIR_2][col] > 300)
                && (input->therm_buf[col] < 2700))
            {
                pixel_mask[pixel_index] |= CF_CLOUD_BIT;
            }
            else
                pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;

            /* It takes every snow pixel including snow pixels under thin
               or icy clouds, equation 20 */
            if (((ndsi - 0.15) > MINSIGMA)
                && (input->therm_buf[col] < 1000)
                && (input->buf[BI_NIR][col] > 1100)
                && (input->buf[BI_GREEN][col] > 1000))
            {
                pixel_mask[pixel_index] |= CF_SNOW_BIT;
            }
            else
                pixel_mask[pixel_index] &= ~CF_SNOW_BIT;

            /* Zhe's water test (works over thin cloud), equation 5 */
            if ((((ndvi - 0.01) < MINSIGMA)
                  && (input->buf[BI_NIR][col] < 1100))
                || (((ndvi - 0.1) < MINSIGMA)
                    && (ndvi > MINSIGMA)
                    && (input->buf[BI_NIR][col] < 500)))
            {
                pixel_mask[pixel_index] |= CF_WATER_BIT;
            }
            else
                pixel_mask[pixel_index] &= ~CF_WATER_BIT;

            /* visible bands flatness (sum(abs)/mean < 0.6 => bright and dark
               cloud), equation 2 */
            if (pixel_mask[pixel_index] & CF_CLOUD_BIT)
            {
                visi_mean = (float) (input->buf[BI_BLUE][col]
                                     + input->buf[BI_GREEN][col]
                                     + input->buf[BI_RED][col]) / 3.0;
                if (visi_mean != 0)
                {
                    whiteness =
                        ((fabs ((float) input->buf[BI_BLUE][col] - visi_mean)
                          + fabs ((float) input->buf[BI_GREEN][col] - visi_mean)
                          + fabs ((float) input->buf[BI_RED][col]
                                  - visi_mean))) / visi_mean;
                }
                else
                {
                    /* Just put a large value to remove them from cloud pixel
                       identification */
                    whiteness = 100.0;
                }
            }

            /* Update cloud_mask,  if one visible band is saturated,
               whiteness = 0, due to data type conversion, pixel value
               difference of 1 is possible */
            if ((input->buf[BI_BLUE][col]
                 >= (input->meta.satu_value_max[BI_BLUE] - 1))
                ||
                (input->buf[BI_GREEN][col]
                 >= (input->meta.satu_value_max[BI_GREEN] - 1))
                ||
                (input->buf[BI_RED][col]
                 >= (input->meta.satu_value_max[BI_RED] - 1)))
            {
                whiteness = 0.0;
                satu_bv = 1;
            }
            else
            {
                satu_bv = 0;
            }

            if ((pixel_mask[pixel_index] & CF_CLOUD_BIT) &&
                (whiteness - 0.7) < MINSIGMA)
            {
                pixel_mask[pixel_index] |= CF_CLOUD_BIT;
            }
            else
                pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;

            /* Haze test, equation 3 */
            hot = (float) input->buf[BI_BLUE][col]
                  - 0.5 * (float) input->buf[BI_RED][col]
                  - 800.0;
            if ((pixel_mask[pixel_index] & CF_CLOUD_BIT)
                && (hot > MINSIGMA || satu_bv == 1))
                pixel_mask[pixel_index] |= CF_CLOUD_BIT;
            else
                pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;

            /* Ratio 4/5 > 0.75 test, equation 4 */
            if ((pixel_mask[pixel_index] & CF_CLOUD_BIT) &&
                input->buf[BI_SWIR_1][col] != 0)
            {
                if ((float) input->buf[BI_NIR][col] /
                    (float) input->buf[BI_SWIR_1][col] - 0.75 > MINSIGMA)
                    pixel_mask[pixel_index] |= CF_CLOUD_BIT;
                else
                    pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;
            }
            else
                pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;

            /* Build counters for clear, clear land, and clear water */
            if (pixel_mask[pixel_index] & CF_CLOUD_BIT)
            {
                /* It is cloud so make sure none of the bits are set */
                clear_mask[pixel_index] = CF_CLEAR_NONE;
            }
            else
            {
                clear_mask[pixel_index] = CF_CLEAR_BIT;
                clear_pixel_counter++;

                if (pixel_mask[pixel_index] & CF_WATER_BIT)
                {
                    /* Add the clear water bit */
                    clear_mask[pixel_index] |= CF_CLEAR_WATER_BIT;
                    clear_water_pixel_counter++;
                }
                else
                {
                    /* Add the clear land bit */
                    clear_mask[pixel_index] |= CF_CLEAR_LAND_BIT;
                    clear_land_pixel_counter++;
                }
            }
        }
    }
    printf ("\n");

    *clear_ptm = 100.0 * ((float) clear_pixel_counter
                          / (float) image_data_counter);
    land_ptm = 100.0 * ((float) clear_land_pixel_counter
                        / (float) image_data_counter);
    water_ptm = 100.0 * ((float) clear_water_pixel_counter
                         / (float) image_data_counter);

    if (verbose)
    {
        printf ("(clear_pixels, clear_land_pixels, clear_water_pixels,"
                " image_data_counter) = (%d, %d, %d, %d)\n",
                clear_pixel_counter, clear_land_pixel_counter,
                clear_water_pixel_counter, image_data_counter);
        printf ("(clear_ptm, land_ptm, water_ptm) = (%f, %f, %f)\n",
                *clear_ptm, land_ptm, water_ptm);
    }

    if ((*clear_ptm - 0.1) <= MINSIGMA)
    {
        /* No thermal test is needed, all clouds */
        *t_templ = -1.0;
        *t_temph = -1.0;
        for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
        {
            /* All cloud */
            if (!(pixel_mask[pixel_index] & CF_CLOUD_BIT))
                pixel_mask[pixel_index] |= CF_SHADOW_BIT;
            else
                pixel_mask[pixel_index] &= ~CF_SHADOW_BIT;
        }
    }
    else
    {
        f_temp = calloc (pixel_count, sizeof (int16));
        f_wtemp = calloc (pixel_count, sizeof (int16));
        if (f_temp == NULL || f_wtemp == NULL)
        {
            sprintf (errstr, "Allocating temp memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The second pass\n");

        /* Determine which bit to test for land */
        if ((land_ptm - 0.1) >= MINSIGMA)
        {
            /* use clear land only */
            land_bit = CF_CLEAR_LAND_BIT;
        }
        else
        {
            /* not enough clear land so use all clear pixels */
            land_bit = CF_CLEAR_BIT;
        }

        /* Determine which bit to test for water */
        if ((water_ptm - 0.1) >= MINSIGMA)
        {
            /* use clear water only */
            water_bit = CF_CLEAR_WATER_BIT;
        }
        else
        {
            /* not enough clear water so use all clear pixels */
            water_bit = CF_CLEAR_BIT;
        }

        int16 f_temp_max = SHRT_MIN;
        int16 f_temp_min = SHRT_MAX;
        int16 f_wtemp_max = SHRT_MIN;
        int16 f_wtemp_min = SHRT_MAX;
        int land_count = 0;
        int water_count = 0;
        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For the thermal band, read the input thermal band  */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            for (col = 0; col < ncols; col++)
            {
                pixel_index = row * ncols + col;

                if (clear_mask[pixel_index] & CF_CLEAR_FILL_BIT)
                    continue;

                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                    input->therm_buf[col] = input->meta.therm_satu_value_max;

                /* get clear land temperature */
                if (clear_mask[pixel_index] & land_bit)
                {
                    f_temp[land_count] = input->therm_buf[col];
                    if (f_temp_max < f_temp[land_count])
                        f_temp_max = f_temp[land_count];
                    if (f_temp_min > f_temp[land_count])
                        f_temp_min = f_temp[land_count];
                    land_count++;
                }

                /* get clear water temperature */
                if (clear_mask[pixel_index] & water_bit)
                {
                    f_wtemp[water_count] = input->therm_buf[col];
                    if (f_wtemp_max < f_wtemp[water_count])
                        f_wtemp_max = f_wtemp[water_count];
                    if (f_wtemp_min > f_wtemp[water_count])
                        f_wtemp_min = f_wtemp[water_count];
                    water_count++;
                }
            }
        }
        printf ("\n");

        /* Set maximum and minimum values to zero if no clear land/water
           pixels */
        if (f_temp_min == SHRT_MAX)
            f_temp_min = 0;
        if (f_temp_max == SHRT_MIN)
            f_temp_max = 0;
        if (f_wtemp_min == SHRT_MAX)
            f_wtemp_min = 0;
        if (f_wtemp_max == SHRT_MIN)
            f_wtemp_max = 0;

        /* Tempearture for snow test */
        l_pt = 0.175;
        h_pt = 1.0 - l_pt;

        /* 0.175 percentile background temperature (low) */
        status = prctile (f_temp, land_count, f_temp_min, f_temp_max,
                          100.0 * l_pt, t_templ);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* 0.825 percentile background temperature (high) */
        status = prctile (f_temp, land_count, f_temp_min, f_temp_max,
                          100.0 * h_pt, t_temph);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        status = prctile (f_wtemp, water_count, f_wtemp_min, f_wtemp_max,
                          100.0 * h_pt, &t_wtemp);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Temperature test */
        t_buffer = 4 * 100;
        *t_templ -= (float) t_buffer;
        *t_temph += (float) t_buffer;
        temp_l = *t_temph - *t_templ;

        /* Release f_temp memory */
        free (f_wtemp);
        f_wtemp = NULL;
        free (f_temp);
        f_temp = NULL;

        wfinal_prob = calloc (pixel_count, sizeof (float));
        final_prob = calloc (pixel_count, sizeof (float));
        if (wfinal_prob == NULL || final_prob == NULL)
        {
            sprintf (errstr, "Allocating prob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The third pass\n");

        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            /* Loop through each sample in the image */
            for (col = 0; col < ncols; col++)
            {
                pixel_index = row * ncols + col;

                if (pixel_mask[pixel_index] & CF_FILL_BIT)
                    continue;

                for (ib = 0; ib < BI_REFL_BAND_COUNT - 1; ib++)
                {
                    if (input->buf[ib][col] == input->meta.satu_value_ref[ib])
                        input->buf[ib][col] = input->meta.satu_value_max[ib];
                }
                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                    input->therm_buf[col] = input->meta.therm_satu_value_max;

                if (pixel_mask[pixel_index] & CF_WATER_BIT)
                {
                    /* Get cloud prob over water */
                    /* Temperature test over water */
                    wtemp_prob = (t_wtemp - (float) input->therm_buf[col]) /
                        400.0;
                    if (wtemp_prob < MINSIGMA)
                        wtemp_prob = 0.0;

                    /* Brightness test (over water) */
                    t_bright = 1100;
                    brightness_prob = (float) input->buf[BI_SWIR_1][col] /
                        (float) t_bright;
                    if ((brightness_prob - 1.0) > MINSIGMA)
                        brightness_prob = 1.0;
                    if (brightness_prob < MINSIGMA)
                        brightness_prob = 0.0;

                    /*Final prob mask (water), cloud over water probability */
                    wfinal_prob[pixel_index] =
                        100.0 * wtemp_prob * brightness_prob;
                    final_prob[pixel_index] = 0.0;
                }
                else
                {
                    temp_prob =
                        (*t_temph - (float) input->therm_buf[col]) / temp_l;
                    /* Temperature can have prob > 1 */
                    if (temp_prob < MINSIGMA)
                        temp_prob = 0.0;

                    if ((input->buf[BI_RED][col] + input->buf[BI_NIR][col]) != 0)
                    {
                        ndvi = (float) (input->buf[BI_NIR][col]
                                        - input->buf[BI_RED][col])
                               / (float) (input->buf[BI_NIR][col]
                                          + input->buf[BI_RED][col]);
                    }
                    else
                        ndvi = 0.01;

                    if ((input->buf[BI_GREEN][col]
                         + input->buf[BI_SWIR_1][col]) != 0)
                    {
                        ndsi = (float) (input->buf[BI_GREEN][col]
                                        - input->buf[BI_SWIR_1][col])
                               / (float) (input->buf[BI_GREEN][col]
                                          + input->buf[BI_SWIR_1][col]);
                    }
                    else
                        ndsi = 0.01;

                    /* NDVI and NDSI should not be negative */
                    if (ndsi < MINSIGMA)
                        ndsi = 0.0;
                    if (ndvi < MINSIGMA)
                        ndvi = 0.0;

                    visi_mean = (input->buf[BI_BLUE][col]
                                 + input->buf[BI_GREEN][col]
                                 + input->buf[BI_RED][col]) / 3.0;
                    if (visi_mean != 0)
                    {
                        whiteness =
                            ((fabs ((float) input->buf[BI_BLUE][col]
                                    - visi_mean)
                              + fabs ((float) input->buf[BI_GREEN][col]
                                      - visi_mean)
                              + fabs ((float) input->buf[BI_RED][col]
                                      - visi_mean))) / visi_mean;
                    }
                    else
                        whiteness = 0.0;

                    /* If one visible band is saturated, whiteness = 0 */
                    if ((input->buf[BI_BLUE][col]
                         >= (input->meta.satu_value_max[BI_BLUE] - 1))
                        ||
                        (input->buf[BI_GREEN][col]
                         >= (input->meta.satu_value_max[BI_GREEN] - 1))
                        ||
                        (input->buf[BI_RED][col]
                         >= (input->meta.satu_value_max[BI_RED] - 1)))
                    {
                        whiteness = 0.0;
                    }

                    /* Vari_prob=1-max(max(abs(NDSI),abs(NDVI)),whiteness); */
                    if ((ndsi - ndvi) > MINSIGMA)
                        max_value = ndsi;
                    else
                        max_value = ndvi;
                    if ((whiteness - max_value) > MINSIGMA)
                        max_value = whiteness;
                    vari_prob = 1.0 - max_value;

                    /*Final prob mask (land) */
                    final_prob[pixel_index] = 100.0 * (temp_prob * vari_prob);
                    wfinal_prob[pixel_index] = 0.0;
                }
            }
        }
        printf ("\n");

        /* Allocate memory for prob */
        prob = malloc (pixel_count * sizeof (float));
        if (prob == NULL)
        {
            sprintf (errstr, "Allocating prob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        float prob_max = 0.0;
        float prob_min = 0.0;
        land_count = 0;
        for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
        {
            if (clear_mask[pixel_index] & CF_CLEAR_FILL_BIT)
                continue;

            if (clear_mask[pixel_index] & land_bit)
            {
                prob[land_count] = final_prob[pixel_index];

                if ((prob[land_count] - prob_max) > MINSIGMA)
                    prob_max = prob[land_count];

                if ((prob_min - prob[land_count]) > MINSIGMA)
                    prob_min = prob[land_count];

                land_count++;
            }
        }

        /* Dynamic threshold for land */
        status = prctile2 (prob, land_count, prob_min, prob_max,
                           100.0 * h_pt, &clr_mask);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile2 routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        clr_mask += cloud_prob_threshold;

        /* Release memory for prob */
        free (prob);
        prob = NULL;

        /* Allocate memory for wprob */
        wprob = malloc (pixel_count * sizeof (float));
        if (wprob == NULL)
        {
            sprintf (errstr, "Allocating wprob memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        float wprob_max = 0.0;
        float wprob_min = 0.0;
        water_count = 0;
        for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
        {
            if (clear_mask[pixel_index] & CF_CLEAR_FILL_BIT)
                continue;

            if (clear_mask[pixel_index] & water_bit)
            {
                wprob[water_count] = wfinal_prob[pixel_index];

                if ((wprob[water_count] - wprob_max) > MINSIGMA)
                    wprob_max = wprob[water_count];

                if ((wprob_min - wprob[water_count]) > MINSIGMA)
                    wprob_min = wprob[water_count];

                water_count++;
            }
        }

        /* Dynamic threshold for water */
        status = prctile2 (wprob, water_count, wprob_min, wprob_max,
                           100.0 * h_pt, &wclr_mask);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Error calling prctile2 routine");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        wclr_mask += cloud_prob_threshold;

        /* Release memory for wprob */
        free (wprob);
        wprob = NULL;

        if (verbose)
        {
            printf ("pcloud probability threshold (land) = %.2f\n", clr_mask);
            printf ("pcloud probability threshold (water) = %.2f\n",
                    wclr_mask);

            printf ("The fourth pass\n");
        }

        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            for (col = 0; col < ncols; col++)
            {
                pixel_index = row * ncols + col;

                if (pixel_mask[pixel_index] & CF_FILL_BIT)
                    continue;

                if (input->therm_buf[col] == input->meta.therm_satu_value_ref)
                {
                    input->therm_buf[col] = input->meta.therm_satu_value_max;
                }

                if (((pixel_mask[pixel_index] & CF_CLOUD_BIT)
                     &&
                     (final_prob[pixel_index] > clr_mask)
                     &&
                     (!(pixel_mask[pixel_index] & CF_WATER_BIT)))
                    ||
                    ((pixel_mask[pixel_index] & CF_CLOUD_BIT)
                     &&
                     (wfinal_prob[pixel_index] > wclr_mask)
                     &&
                     (pixel_mask[pixel_index] & CF_WATER_BIT))
                    ||
                    (input->therm_buf[col] < *t_templ + t_buffer - 3500))
                {
                    /* This test indicates a high confidence */
                    conf_mask[pixel_index] = CLOUD_CONFIDENCE_HIGH;

                    /* Original code was only this if test and setting the
                       cloud bit or not */
                    pixel_mask[pixel_index] |= CF_CLOUD_BIT;
                }
                else if (((pixel_mask[pixel_index] & CF_CLOUD_BIT)
                          &&
                          (final_prob[pixel_index] > clr_mask-10.0)
                          &&
                          (!(pixel_mask[pixel_index] & CF_WATER_BIT)))
                         ||
                         ((pixel_mask[pixel_index] & CF_CLOUD_BIT)
                          &&
                          (wfinal_prob[pixel_index] > wclr_mask-10.0)
                          &&
                          (pixel_mask[pixel_index] & CF_WATER_BIT)))
                {
                    /* This test indicates a medium confidence */
                    conf_mask[pixel_index] = CLOUD_CONFIDENCE_MED;

                    /* Don't set the cloud bit per the original code */
                    pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;
                }
                else
                {
                    /* All remaining are a low confidence */
                    conf_mask[pixel_index] = CLOUD_CONFIDENCE_LOW;

                    /* Don't set the cloud bit per the original code */
                    pixel_mask[pixel_index] &= ~CF_CLOUD_BIT;
                }
            }
        }
        printf ("\n");

        /* Free the memory */
        free (wfinal_prob);
        wfinal_prob = NULL;
        free (final_prob);
        final_prob = NULL;

        /* Band NIR & SWIR1 flood fill section */
        data_size = input->size.l * input->size.s;
        nir = calloc (data_size, sizeof (int16));
        swir1 = calloc (data_size, sizeof (int16));
        if (nir == NULL || swir1 == NULL)
        {
            sprintf (errstr, "Allocating nir and swir1 memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The fifth pass\n");

        nir_data = calloc (data_size, sizeof (int16));
        swir1_data = calloc (data_size, sizeof (int16));
        filled_nir_data = calloc (data_size, sizeof (int16));
        filled_swir1_data = calloc (data_size, sizeof (int16));
        if (nir_data == NULL || swir1_data == NULL ||
            filled_nir_data == NULL || filled_swir1_data == NULL)
        {
            sprintf (errstr, "Allocating nir and swir1 memory");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        int16 nir_max = 0;
        int16 nir_min = 0;
        int16 swir1_max = 0;
        int16 swir1_min = 0;
        int nir_count = 0;
        int swir1_count = 0;
        /* Loop through each line in the image */
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            for (col = 0; col < ncols; col++)
            {
                pixel_index = row * ncols + col;

                if (clear_mask[pixel_index] & CF_CLEAR_FILL_BIT)
                    continue;

                if (input->buf[BI_NIR][col]
                    == input->meta.satu_value_ref[BI_NIR])
                {
                    input->buf[BI_NIR][col] =
                        input->meta.satu_value_max[BI_NIR];
                }
                if (input->buf[BI_SWIR_1][col]
                    == input->meta.satu_value_ref[BI_SWIR_1])
                {
                    input->buf[BI_SWIR_1][col] =
                        input->meta.satu_value_max[BI_SWIR_1];
                }

                if (clear_mask[pixel_index] & land_bit)
                {
                    nir[nir_count] = input->buf[BI_NIR][col];
                    if (nir[nir_count] > nir_max)
                        nir_max = nir[nir_count];
                    if (nir[nir_count] < nir_min)
                        nir_min = nir[nir_count];
                    nir_count++;

                    swir1[swir1_count] = input->buf[BI_SWIR_1][col];
                    if (swir1[swir1_count] > swir1_max)
                        swir1_max = swir1[swir1_count];
                    if (swir1[swir1_count] < swir1_min)
                        swir1_min = swir1[swir1_count];
                    swir1_count++;
                }
            }

            /* NIR */
            memcpy(&nir_data[row * input->size.s], &input->buf[BI_NIR][0],
                   input->size.s * sizeof (int16));
            /* SWIR1 */
            memcpy(&swir1_data[row * input->size.s], &input->buf[BI_SWIR_1][0],
                   input->size.s * sizeof (int16));
        }
        printf ("\n");

        /* Estimating background (land) Band NIR Ref */
        status = prctile (nir, nir_count, nir_min, nir_max,
                          100.0 * l_pt, &nir_boundary);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Calling prctile function\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }
        status = prctile (swir1, swir1_count, swir1_min, swir1_max,
                          100.0 * l_pt, &swir1_boundary);
        if (status != SUCCESS)
        {
            sprintf (errstr, "Calling prctile function\n");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        /* Release the memory */
        free (nir);
        free (swir1);
        nir = NULL;
        swir1 = NULL;

        /* Call the fill minima routine to do image fill */
/* Perform them in parallel if threading is enabled */
#ifdef _OPENMP
#pragma omp parallel sections
#endif
{
    {
        if (fill_local_minima_in_image("NIR Band", nir_data,
                                       input->size.l, input->size.s,
                                       nir_boundary, filled_nir_data)
            != SUCCESS)
        {
            printf ("Error Running fill_local_minima_in_image on NIR band");
            status = ERROR;
        }
    }

#ifdef _OPENMP
    #pragma omp section
#endif
    {
        if (fill_local_minima_in_image("SWIR1 Band", swir1_data,
                                       input->size.l, input->size.s,
                                       swir1_boundary, filled_swir1_data)
            != SUCCESS)
        {
            printf ("Error Running fill_local_minima_in_image on SWIR1 band");
            status = ERROR;
        }
    }
}

        /* Release the memory */
        free(nir_data);
        free(swir1_data);
        nir_data = NULL;
        swir1_data = NULL;

        if (status == ERROR)
        {
            free(filled_nir_data);
            free(filled_swir1_data);
            sprintf (errstr, "Running fill_local_minima_in_image");
            RETURN_ERROR (errstr, "pcloud", FAILURE);
        }

        if (verbose)
            printf ("The sixth pass\n");

        int16 new_nir;
        int16 new_swir1;
        for (row = 0; row < nrows; row++)
        {
            if (verbose)
            {
                /* Print status on every 1000 lines */
                if (!(row % 1000))
                {
                    printf ("Processing line %d\r", row);
                    fflush (stdout);
                }
            }

            /* For each of the image bands */
            for (ib = 0; ib < input->nband; ib++)
            {
                /* Read each input reflective band -- data is read into
                   input->buf[ib] */
                if (!GetInputLine (input, ib, row))
                {
                    sprintf (errstr, "Reading input image data for line %d, "
                             "band %d", row, ib);
                    RETURN_ERROR (errstr, "pcloud", FAILURE);
                }
            }

            /* For the thermal band, data is read into input->therm_buf */
            if (!GetInputThermLine (input, row))
            {
                sprintf (errstr, "Reading input thermal data for line %d",
                         row);
                RETURN_ERROR (errstr, "pcloud", FAILURE);
            }

            for (col = 0; col < ncols; col++)
            {
                pixel_index = row * ncols + col;

                if (input->buf[BI_NIR][col]
                    == input->meta.satu_value_ref[BI_NIR])
                {
                    input->buf[BI_NIR][col] =
                        input->meta.satu_value_max[BI_NIR];
                }
                if (input->buf[BI_SWIR_1][col]
                    == input->meta.satu_value_ref[BI_SWIR_1])
                {
                    input->buf[BI_SWIR_1][col] =
                        input->meta.satu_value_max[BI_SWIR_1];
                }

                if (pixel_mask[pixel_index] & CF_FILL_BIT)
                {
                    conf_mask[pixel_index] = CF_FILL_PIXEL;
                    continue;
                }

                new_nir = filled_nir_data[pixel_index] -
                          input->buf[BI_NIR][col];
                new_swir1 = filled_swir1_data[pixel_index] -
                            input->buf[BI_SWIR_1][col];

                if (new_nir < new_swir1)
                    shadow_prob = new_nir;
                else
                    shadow_prob = new_swir1;

                if (shadow_prob > 200)
                    pixel_mask[pixel_index] |= CF_SHADOW_BIT;
                else
                    pixel_mask[pixel_index] &= ~CF_SHADOW_BIT;

                /* refine Water mask (no confusion water/cloud) */
                if ((pixel_mask[pixel_index] & CF_WATER_BIT) &&
                    (pixel_mask[pixel_index] & CF_CLOUD_BIT))
                {
                    pixel_mask[pixel_index] &= ~CF_WATER_BIT;
                }
            }
        }
        printf ("\n");

        /* Release the memory */
        free(nir_data);
        nir_data = NULL;
        free(swir1_data);
        swir1_data = NULL;
        free(filled_nir_data);
        filled_nir_data = NULL;
        free(filled_swir1_data);
        filled_swir1_data = NULL;
    }

    free (clear_mask);
    clear_mask = NULL;

    return SUCCESS;
}