//Source file: D:\\Projects\\src\\yi\\iu\\processor\\texture\\reg\\ColorGuidedTextureSegWPCA.java

package yi.iu.processor.texture.reg;

import yi.ai.cluster.Clusters;
import java.awt.*;
import yi.iu.processor.filter.*;
import yi.util.config.*;
import yi.math.*;
import yi.iu.processor.texture.gabor.*;
import yi.ai.cluster.*;
import yi.util.io.*;
import yi.util.label.Label;
import yi.iu.processor.morph.*;
import yi.iu.processor.resample.*;
import yi.iu.processor.color.reg.merge.*;
import yi.util.data.*;
import yi.stat.kmeans.*;

/**
 * Still use color to guide texture segmentation.  But no post connected
 * component analysis.
 */
public class ColorGuidedTextureSegNCC
    extends ColorGuidedTextureSeg {

  public boolean DEMO_TEXTURE_SEG = true;
  public double MERGE_THRESHOLD = 0.5;

  public ColorGuidedTextureSegNCC() {
    super();
  }


  public int[] getLabels(int[] pixels, int width, int height) {


    // use processor ColorPatternSeg to roughly segment the image to patterned
    // color regions
    ColorPatternSeg color_seg = new LooseColorPatternSeg();
    color_seg.DILATION_DISK_SIZE = DILATION_DISK_SIZE;
    color_seg.COMBINE_THRESHOLD = COMBINE_THRESHOLD;
    DS_ColorPatternRegions color_regions = color_seg.getReusableColorPatternRegions(pixels, width, height);
    int[] color_region_labels = color_regions.getLabels();

    if (DEMO_COLOR_PATTERN_SEG) {
      int[] region_img = yi.util.ImageUtil.makeNewImageFromLabel(color_region_labels);
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      yi.util.io.OutputResult.outputGIFImage(img_name + "_patterned_color_regions.gif", region_img, width, height);
    }

    // use texture segmentation
    int color_region_num = yi.stat.StatUtil.maximum(color_region_labels) + 1;
    Clusters texture_segs = textureSegOnRegions(
      pixels, color_region_labels, color_region_num, width, height);
    int[] texture_region_labels = texture_segs.getClusterMap();
    double[][] texture_means = texture_segs.getClusterMeans();

    // demo the regions after texture segmentation
    if (DEMO_TEXTURE_SEG_BEFORE_FILTER) {
      int[] region_img = yi.util.ImageUtil.makeNewImageFromLabel(texture_region_labels);
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      yi.util.io.OutputResult.outputGIFImage(img_name + "_before_filter.gif", region_img, width, height);
    }

    // now remove those single regions which are super small.
    // find connected components (these are also sequentially labeled)
    // 0 should be treated as background
//    ConnectComponent cc = new ConnectComponent();
//    int[] cc_labels = cc.compactLabeling(texture_region_labels, new Dimension(width, height), true);
//    filterSmallRegions(cc_labels, (int)(MINIMUM_REGION_SIZE * width * height / 10));
//
//    for (int i=0; i<texture_region_labels.length; i++) {
//      if (cc_labels[i] == 0) texture_region_labels[i] = 0;
//    }

    // No filtering here, only filter out small size of regions (the regions can be dis-connected)
//    filterSmallRegions(texture_region_labels, (int)(MINIMUM_REGION_SIZE * width * height));

    // merge close clusters
    Label.sequential(texture_region_labels, null, true);
    double[][] means = Label.getMeans(m_features, texture_region_labels);
    int[] counts = Label.getCounts(texture_region_labels);
    double[] double_counts = Conversion.toDouble(counts);
    int[] label_map = ClosestMerger.merge(means, double_counts, MERGE_THRESHOLD, true);
    for (int i=0; i<texture_region_labels.length; i++) {
      texture_region_labels[i] = label_map[texture_region_labels[i]];
    }

    return texture_region_labels;

  }

  /**
   * Do texture based segmentation on each pre-defined region.  Label 0 will be
   * considered as background and so will be ignored.
   * @param pixels
   * @param region_label
   * @param label_num region number.  The the values of labels are [0..label_num)
   * @param width
   * @param height
   * @return
   */
  Clusters textureSegOnRegions(int[] pixels, int[] labels, int label_num, int width, int height) {

    // calculate Gabor
    DoubleMatrix features = calculateGabor(pixels, width, height);
    double[][] features_data = features.data;

    // remove boundary effect
    int boundary_effect_size = (int)Math.round(Gabor.maskSize*BOUNDARY_EFFECT_SIZE);
    setBoundary(labels, width, height, boundary_effect_size, 0);
    yi.util.label.Label.sequential(labels, null, true);
    label_num = yi.stat.StatUtil.maximum(labels) + 1;


    // clustering on each region
    int seg_num = 1;  // region #0 is igored since it is background
    int pnum = labels.length;
    int[] seg_rst = new int[pnum]; // to record the result

    for (int i = 1; i < label_num; i++) {  // deal with the labels one by one
//      System.out.println("region_number = " + seg_num);
      // construct the binary image corresponding one label
      int[] mask = new int[pnum];
      for (int j=0; j<pnum; j++) mask[j] = (labels[j]==i) ? 1 : MASKED;

      // process on the binary image
      Clusters clusters = getTextureSegWithMask(features_data, features.height, features.width, mask);
//      Clusters clusters = getTextureSegWithMask_eliminateBoundary(int_features_data, features.height, features.width, mask, width, height);

//      System.out.println("region #" + i + " is clustered to " + clusters.getClusterNum() + " clusters");
      int[] segs = clusters.getClusterMap();

      // gether the result on this label into the returning result
      for (int j=0; j<pnum; j++) {
        // in returned binary, 0 is counted as one region
        if (labels[j]==i) seg_rst[j] = seg_num + segs[j];
      }

      // remember the means
      // the means are not that useful since they may have been normalized
      int num_clusters = clusters.getClusterNum();

      // increase the number of current segments
      seg_num += num_clusters;
    }

    // recalculate the means
//    // to save the running time, disable this calculation, actually the results are useless anyway
//    // since the segmented region will be filtered (merge + connected components)
//    double[][] means_array = Region.getMeans(seg_rst, features.data, Gabor.numOrients*Gabor.numScales);
    double[][] means_array = null;
    return new Clusters(seg_num, features.width, means_array, pixels.length, seg_rst);
  }

  /**
   * Texture segmentation on a region within an image.  The region is marked
   * in the parameter mask.  In the returned pixels labels, the masked pixels
   * are remained as 0; the unmasked pixels will be labeled from 0.  It is OK
   * that the masked pixels and the region #0 pixels are all labeled as 0 since
   * the caller know the pixels masked.
   * @param features the feature vectors
   * @param data_len the length of the features
   * @param feature_num the dimension of feature data
   * @param mask the mask
   * @return clusters within the unmasked data
   */
  Clusters getTextureSegWithMask(double[][] features, int data_len, int feature_num, int[] mask) {

    // get the un_masked_num
    int un_masked_num = 0;
    for (int i=0; i<mask.length; i++) {
      if (mask[i] != MASKED) un_masked_num++;
    }

    // construct the data for clustering (eliminate those masked data)
    // also set up a map to the original data
    int[] map = new int[un_masked_num];
    double[][] unmasked_features = new double[un_masked_num][];
    int count = 0;
    for (int i=0; i<data_len; i++) {
      if (mask[i] == MASKED) continue;
      map[count] = i;
      unmasked_features[count] = features[i];
      count ++;
    }

    // k-means clustering
    KMeans kmeans = new KMeans();
    int[] seg_labels = kmeans.clustering(10, feature_num, unmasked_features, null, 10, 0, true);

    // map inversely
    int[] segs = new int[data_len];
    for (int i=0; i<un_masked_num; i++) {
      segs[map[i]] = seg_labels[i];
    }

    // demo cluster results
    if (DEMO_CLUSTERING_WITHIN_COLOR_SEG) {
      Dimension d = yi.util.ImageUtil.getDimension();
      int[] segs_label = new int[mask.length];
      for (int i=0; i<mask.length; i++) {
        segs_label[i] = (mask[i] == MASKED)?0:segs[i]+1;
      }
      int[] segs_demo = yi.util.ImageUtil.makeNewImageFromLabel(segs_label);
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      OutputResult.outputGIFImage(img_name + "color_seg_clst", segs_demo, d.width, d.height, true);
    }

    return new Clusters(kmeans.k, feature_num, kmeans.means, data_len, segs);
  }

  /**
   * Filter out small regions.  0 will be untouched.
   * @param labels
   * @param size_threshold
   * @return
   */
  void filterSmallRegions(int[] labels, int size_threshold) {

    Label.filterSmallRegions(labels, size_threshold);// filtering small component
    Label.sequential(labels,null,true);
  }

  public static class GenTextureLabeledRgnFts {
    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      // get the image name
      String img_name = ReadArgu.getOption('i', args);
      if (img_name == null) {
        System.err.println("Usage: -i image_name");
        System.exit(1);
      }

      // read in image
      // get the scale
      String scale_str = ReadArgu.getOption('s', args);
      int scale = 1;
      if (scale_str != null) {
        scale = Integer.parseInt(scale_str);
      }

      // read in image
      Image image = yi.util.io.ImageReader.JAIReadImage(img_name);
      Dimension d = new Dimension();
      int[] pixels = yi.util.io.ImageReader.getPixels(image, d);
      Dimension old_d = (Dimension)d.clone();

      // rescale the image if necessary
      double the_scale = 1.0;
      if (scale != 1) {
        ColorGuidedTextureSeg dummy = new ColorGuidedTextureSeg();
        ResampledDescriptor resampled = Resampler.scaleDown(pixels, d,
            dummy.RESAMPLER, scale);
        if (resampled != null) {
          pixels = resampled.m_resampled;
          d = resampled.m_newDimension;
          the_scale = resampled.m_scale;
          img_name = img_name + Resampler.RESIZED_FILE_SUFFIX + scale;
        }
      }

      // process
      try {

        Configuration.setParameter("SOURCE_NAME", img_name);
        yi.util.ImageUtil.setPixels(pixels);
        yi.util.ImageUtil.setDimension(d);

        /** @todo use ColorGuidedTextureSegWPCA for now */
        ColorGuidedTextureSegNCC processor = new ColorGuidedTextureSegNCC();
        long startTime = System.currentTimeMillis();
        TextureLabeledRgnFts ds = processor.getReusableTextureLabeledRgnFts(pixels, d.width, d.height, old_d.width, old_d.height);
        long endTime = System.currentTimeMillis();
        System.out.println("running time: " + (endTime-startTime));

        int[] demo = ds.demo();
        yi.util.io.OutputResult.outputGIFImage(img_name + ".text.rgn.gif",
                                               demo, d.width,
                                             d.height, false);


      } catch (Exception e) {
        System.err.println("broken when processing " + img_name);
        e.printStackTrace();
      }

//    System.exit(0);
    }
  }

  public static class GenTextureLabRgnFtsPxlVal {
    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      // get the image name
      String img_name = ReadArgu.getOption('i', args);
      if (img_name == null) {
        System.err.println("Usage: -i image_name");
        System.exit(1);
      }

      // read in image
      // get the scale
      String scale_str = ReadArgu.getOption('s', args);
      int scale = 1;
      if (scale_str != null) {
        scale = Integer.parseInt(scale_str);
      }

      // read in image
      Image image = yi.util.io.ImageReader.JAIReadImage(img_name);
      Dimension d = new Dimension();
      int[] pixels = yi.util.io.ImageReader.getPixels(image, d);
      Dimension old_d = (Dimension)d.clone();

      // rescale the image if necessary
      double the_scale = 1.0;
      if (scale != 1) {
        ColorGuidedTextureSeg dummy = new ColorGuidedTextureSeg();
        ResampledDescriptor resampled = Resampler.scaleDown(pixels, d,
            dummy.RESAMPLER, scale);
        if (resampled != null) {
          pixels = resampled.m_resampled;
          d = resampled.m_newDimension;
          the_scale = resampled.m_scale;
          img_name = img_name + Resampler.RESIZED_FILE_SUFFIX + scale;
        }
      }

      // process
      try {

        Configuration.setParameter("SOURCE_NAME", img_name);
        yi.util.ImageUtil.setPixels(pixels);
        yi.util.ImageUtil.setDimension(d);

        /** @todo use ColorGuidedTextureSegWPCA for now */
        ColorGuidedTextureSegNCC processor = new ColorGuidedTextureSegNCC();
        long startTime = System.currentTimeMillis();
        TextureLabRgnFtsPxlVal ds = processor.getReusableTextureLabRgnFtsPxlVal(pixels, d.width, d.height, old_d.width, old_d.height);
        long endTime = System.currentTimeMillis();
        System.out.println("running time: " + (endTime-startTime));

        int[] demo = ds.demo();
        yi.util.io.OutputResult.outputGIFImage(img_name + ".text.rgn3.gif",
                                               demo, d.width,
                                             d.height, false);


      } catch (Exception e) {
        System.err.println("broken when processing " + img_name);
        e.printStackTrace();
      }

      System.exit(0);
    }
  }

  public static void main(String[] args) {
    // init configuration
    Configuration.init(args);

    // get the image name
    String img_name = ReadArgu.getOption('i', args);
    if (img_name == null) {
      System.err.println("Usage: -i image_name");
      System.exit(1);
    }

    // read in image
    // get the scale
    String scale_str = ReadArgu.getOption('s', args);
    int scale = 1;
    if (scale_str != null) {
      scale = Integer.parseInt(scale_str);
    }

    // read in image
    Image image = yi.util.io.ImageReader.JAIReadImage(img_name);
    Dimension d = new Dimension();
    int[] pixels = yi.util.io.ImageReader.getPixels(image, d);

    // rescale the image if necessary
    double the_scale = 1.0;
    if (scale != 1) {
      ColorGuidedTextureSeg dummy = new ColorGuidedTextureSeg();
      ResampledDescriptor resampled = Resampler.scaleDown(pixels, d,
          dummy.RESAMPLER, scale);
      if (resampled != null) {
        pixels = resampled.m_resampled;
        d = resampled.m_newDimension;
        the_scale = resampled.m_scale;
        img_name = img_name + Resampler.RESIZED_FILE_SUFFIX + scale;
      }
    }

    // process
    try {

      Configuration.setParameter("SOURCE_NAME", img_name);
      yi.util.ImageUtil.setPixels(pixels);
      yi.util.ImageUtil.setDimension(d);

      /** @todo use ColorGuidedTextureSegWPCA for now */
      ColorGuidedTextureSegNCC processor = new ColorGuidedTextureSegNCC();
      long startTime = System.currentTimeMillis();
      //DS_GaborTextureRegions ds = processor.getReusableTextureRegions(pixels, d.width, d.height, 1.0);
      DS_PolygonRegionTextureStat ds = processor.getReusablePolygonRegionTextureStat(pixels, d.width, d.height, the_scale);
      long endTime = System.currentTimeMillis();
      System.out.println("running time: " + (endTime-startTime));

//      int[] demo = ds.getPolygonDemo(null, true);
//      yi.util.io.OutputResult.outputGIFImage(img_name + ".text.poly.gif", demo, d.width,
//                                             d.height, false);

      if (processor.DEMO_ONE_BY_ONE) {
        ds.demoRegions(img_name, pixels, -1, 0xffff0000);
      }

//      // demo the results
//      if (cgts.DEMO_TEXTURE_REGIONS) {
//        int[] labels = rst.getLabels();
//        int[] region_img = yi.util.ImageUtil.makeNewImageFromLabel(labels);
//        yi.util.io.OutputResult.outputGIFImage(img_name + "_gabor_regions.gif", region_img, d.width, d.height);
//      }

    } catch (Exception e) {
      System.err.println("broken when processing " + img_name);
      e.printStackTrace();
    }

//    System.exit(0);
  }


}