//Source file: D:\\Projects\\src\\yi\\iu\\imgdb\\learn\\LabeledRgnFtRepList.java

package yi.iu.imgdb.learn;

import yi.ds.*;
import yi.util.label.*;
import java.util.*;
import yi.util.ImageUtil;
import yi.util.config.Configuration;
import yi.util.io.ReadArgu;
import yi.util.io.OutputResult;
import java.io.PrintStream;
import java.io.*;
import java.awt.Dimension;


/**
 * Labeled region feature representation list.  The image regions are stored
 * in a label array.  Label 0 is background so there is no corresponding feature
 * data for it.  The first element in the feature list is corresponding to label
 * 1.
 */
public class LabeledRgnFtRepList extends RgnFeatureRepresentationList
{
  static final long serialVersionUID = -2333720763535525117L;
  /**
   * label 0 is background.
   */
  private int[] m_labels;

  public LabeledRgnFtRepList(double[][] data, int ft_dim, int[] labels, int image_width, int image_height)
  {
   super(data, ft_dim, image_width, image_height);
   m_labels = labels;
   imgWidth = image_width;
   imgHeight = image_height;
  }

  public LabeledRgnFtRepList(FeatureRepresentationList list, int[] labels, int image_width, int image_height) {
   super(list.featureDimension, image_width, image_height);
   super.addAll(list);
   m_labels = labels;
   imgWidth = image_width;
   imgHeight = image_height;
  }

  public int[] getLabels() {
    return m_labels;
  }

  /**
   * Combine two LabeledRgnFtRepList.
   * @param ft_list1
   * @param ft_list2
   * @param size_threshold
   * @return
   */
  public static LabeledRgnFtRepList intersectTwoRegionFeatures(
      LabeledRgnFtRepList ft_list1, LabeledRgnFtRepList ft_list2, int size_threshold) {
    int[] label1 = ft_list1.m_labels;
    int[] label2 = ft_list2.m_labels;

    HashMapHashMap overlap_table = new HashMapHashMap();
    int index = 1; // avoid 0
    int[] new_label = new int[label1.length];
    for (int i=0; i<label1.length; i++) {
      if (label1[i] == 0 || label2[i] == 0) continue;
      // first check whether there is a new label for such a combination
      Object obj = overlap_table.get(new Integer(label1[i]), new Integer(label2[i]));
      if (obj == null) {  // if not give it a new one
        obj = new Integer(index);
        overlap_table.put(new Integer(label1[i]), new Integer(label2[i]), obj);
        index++;
      }
      new_label[i] = ((Integer)obj).intValue();
    }

    // sort the labels
    Label.filterSmallRegions(new_label, size_threshold);// filtering small component
    int n_labels = yi.stat.StatUtil.maximum(new_label) + 1;
    int[] map = new int[n_labels];
    int new_n_labels = Label.sequential(new_label, map, true) + 1;

    // find all the useful labels after filtering
    HashSet label_set = new HashSet();
    for (int i=0; i<map.length; i++) {
      if (map[i] > 0) label_set.add(new Integer(i));
    }

    // combine the feature vectors
    // -1 because no value for label 0 (background)
    double[][] data = new double[new_n_labels - 1][ft_list1.featureDimension + ft_list2.featureDimension];

    int i_index = 1; // 0 is background
    for (Iterator i=ft_list1.iterator(); i.hasNext(); i_index++) {
      FeatureRepresentation rep1 = (FeatureRepresentation) i.next();
      int j_index = 1;
      for (Iterator j=ft_list2.iterator(); j.hasNext(); j_index++) {
        FeatureRepresentation rep2 = (FeatureRepresentation) j.next();
        Integer the_index = (Integer)overlap_table.get(new Integer(i_index), new Integer(j_index));
        if (label_set.contains(the_index)) {  // if it was not filtered out
          double[] f1 = rep1.data;
          double[] f2 = rep2.data;
          double[] new_f = new double[f1.length + f2.length];
          for (int k=0; k<f1.length; k++) new_f[k] = f1[k];
          for (int k=0; k<f2.length; k++) new_f[f1.length+k] = f2[k];
          int new_index = map[(the_index.intValue())]; // new index after sequencing
          data[new_index - 1] = new_f; // -1 because background
        }
      }
    }

    // construct the new LabeledRgnFtRepList
    return new LabeledRgnFtRepList(data, ft_list1.featureDimension + ft_list2.featureDimension,
                                   new_label, ft_list1.imgWidth, ft_list2.imgHeight);

  }

  /**
   * Combine the features in the image data center
   */
  public static class IntersectRegionRepresentations {

    public final static String USAGE = "Usage: -i image_name -r representation_descriptors(3) [-h output_html_file] -t size_threshold";

    public static void main(String[] args) {
      try {

        // init configuration
        Configuration.init(args);

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

        String image_path = Configuration.getParameter("IMAGE_PATH", "");
        String rep_path = Configuration.getParameter("REPRESENTATION_PATH", "");

        // representation descriptor
        String rep_descriptors_str = ReadArgu.getOption('r', args);
        if (rep_descriptors_str == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        // size size_threshold
        int size_threshold = ReadArgu.getIntegerOption('t', args, 0);

        // representations
        RepresentationDescriptor[] rep_descriptors = RepresentationDescriptor.parse(rep_descriptors_str);

        LabeledRgnFtRepList rep1 = (LabeledRgnFtRepList)FeatureUtil.readRepresentation(
            img_name, rep_descriptors[0]);

        LabeledRgnFtRepList rep2 = (LabeledRgnFtRepList)FeatureUtil.readRepresentation(
            img_name, rep_descriptors[1]);

        LabeledRgnFtRepList frl = intersectTwoRegionFeatures(
            rep1, rep2, size_threshold);

        System.out.println("feature dim = " + frl.featureDimension + " x " + frl.size());
        OutputResult.print(System.out, frl.as2DDoubleArray());

        String output_html_str = ReadArgu.getOption('h', args);
        FeatureUtil.writeRepresentation(img_name, rep_descriptors[2], frl, output_html_str, true);

      }catch (Exception e) {
        e.printStackTrace();
      }
    }
  }


  /**
   * Draw the regions with the color representing the score of corresponding
   * feature.  After having the score from FeatureScoringCapable, the score
   * will be scaled by "scale", and then shifted by "shift".  The resultant
   * number will be put into the given pixel array
   * @param scoring
   * @param shift
   * @param scale
   * @return
   */
  public int[] drawScoreDemo(Object obj, FeatureScoringCapable scoring, double shift,
                            double scale) {
    // the map from label -> integer score
    int[] color_map = new int[size() + 1];
    color_map[0] = 0; // background always has score 0

    // loop through each feature
    int i_index = 1; // 0 is background
    for (Iterator i=iterator(); i.hasNext(); i_index++) {
      FeatureRepresentation ft = (FeatureRepresentation)i.next();
      double[] data = ft.data;
      // get the score for the feature
      double score = scoring.getFeatureScore(data, obj);
      // shift and scale the score
      score = (score * scale) + shift;
      // get the color from the score
      int color = (int)Math.round(score);
      color_map[i_index] = color;
    }
    return ImageUtil.makeNewImageFromLabel(m_labels, color_map);
  }

  /**
   * Draw the demo with specified region->class (and the mapping score) map.
   * Each class will be represented by a color
   * @param class_map
   * @param score_map
   * @param class2color
   * @return
   */
  public int[] drawClassificationDemo(int[] class_map, double[] score_map, int[] class2color) {
    int[] color_demo = new int[m_labels.length];
    for (int i=0; i<color_demo.length; i++) {
      if (m_labels[i] == 0) { // bg, not represented by features
        color_demo[i] = class2color[0];
      } else {
        color_demo[i] = class2color[class_map[m_labels[i]-1]]; // -1 for bg
      }
    }
    return color_demo;
  }

  public int[] drawProbabilityDemo(int[] class_map, double[] score_map, int[] class2color) {
    int[] gray_demo = new int[m_labels.length];
    for (int i=0; i<gray_demo.length; i++) {
      if (m_labels[i] == 0) { // bg, not represented by features
        gray_demo[i] = 0;
      } else {
//        assert(class_map[m_labels[i]-1] < score_map.length);

        gray_demo[i] = (int)Math.floor(score_map[m_labels[i]-1] * 255); // -1 for bg
      }
    }
    yi.util.ImageUtil.makeImageFromGray(gray_demo);
    return gray_demo;
  }

//  /**
//   * Paint the picked region with the pixels from the "from" image.  Those
//   * regions not picked will be painted as "background".  The region will be
//   * picked (or not) based on its feature vector
//   * @param ojb
//   * @param picker
//   * @param from
//   * @param background
//   * @return
//   */
//  public int[] drawPickedFeature(Object obj, FeaturePickable picker, int[] from, int background) {
//    // the map from label -> integer score
//    boolean[] pick_map = new boolean[size() + 1];
//    pick_map[0] = false; // background always has score 0
//
//    // loop through each feature
//    int i_index = 1; // 0 is background
//    for (Iterator i=iterator(); i.hasNext(); i_index++) {
//      FeatureRepresentation ft = (FeatureRepresentation)i.next();
//      double[] data = ft.data;
//      // whether it is picked or not
//      pick_map[i_index] = picker.isFeaturePick(data, obj);
//    }
//    int[] painted = new int[m_labels.length];
//    for (int i=0; i<m_labels.length; i++) {
//      painted[i] = pick_map[m_labels[i]]? from[i]: background;
//    }
//    return painted;
//  }
//
}
