//Source file: D:\\Projects\\src\\yi\\iu\\processor\\color\\reg\\merge\\KMeansHSISeg.java

package yi.iu.processor.color.reg.merge;

import java.awt.*;

import yi.ai.cluster.KMCluster;
import yi.iu.imgdb.learn.*;
import yi.iu.processor.cluster.*;
import yi.iu.processor.color.*;
import yi.iu.processor.color.reg.*;
import yi.iu.processor.morph.*;
import yi.iu.processor.resample.*;
import yi.stat.hist.*;
import yi.util.config.*;
import yi.util.io.input.*;
import yi.util.io.output.*;

/**
KMeansHSISeg does color segmentation on HSI color space using K-Means
 */
public class KMeansHSISeg implements Configurable, RepresentableFeatureGenerator
{
   public int RESAMPLER = Resampler.NEAREST_RESAMPLER;
   public boolean DEMO_STEPS = false;
   public boolean DEMO_STEP2 = false;
   public boolean LEARN_NEIGHBOR = false;

   public double MERGE_THRESHOLD = 0.1;

   /**
   output the initial segmentation result
    */
   public boolean DEMO_INITIAL_SEG = false;

   /**
   Demo the resultant regions on pixel level
    */
   public boolean DEMO_IN_PIXEL = false;
   public boolean DEMO_ONE_BY_ONE = false;
   public static final int INIT_BY_GONG_PROMINENT = 0;
   public static final int INIT_BY_CYCOLOR_KMEANS = 1;
   public static final int INIT_BY_DOMINANT_COLOR = 2;

   /**
   define the method used to do the initial segmentation
    */
   public int INITIAL_SEG_METHOD = INIT_BY_GONG_PROMINENT;
   public int REUSE = DIU.INPUT_NONE;
   public int OUTPUT = DOU.OUTPUT_NONE;
   public int LOG_LEVEL = 3;

   public KMeansHSISeg()
   {
    Configuration.configParameters(this);
   }

   public String[] getParameterNames()
   {
    return new String[] {
        "RESAMPLER",
        "INITIAL_SEG_METHOD",
        "DEMO_INITIAL_SEG",
        "DEMO_MERGE",
        "DEMO_STEPS",
        "DEMO_IN_PIXEL",
        "DEMO_ONE_BY_ONE",
        "REUSE",
        "OUTPUT",
        "LOG_LEVEL",
    };
   }

   private int[][] getInitMeans()
   {
    int[][] means = new int[8 * 2 * 2 + 4][];
    int count = 0;
    for (int h = 15; h < 255; h += 32) {
      for (int s = 100; s < 255; s += 100) {
        for (int b = 63; b < 255; b += 128) {
          means[count++] = new int[] {h, s, b};
        }
      }
    }
    means[count++] = new int[] {0, 0, 31};
    means[count++] = new int[] {0, 0, 31 + 64};
    means[count++] = new int[] {0, 0, 31 + 64 * 2};
    means[count++] = new int[] {0, 0, 31 + 64 * 3};
    return means;
   }


   private int[] getBigRegionMask(int[] cc_labels, Dimension d, int threshold)
   {
    // get the region size
    SimpleHistogram region_size = new SimpleHistogram(cc_labels);
    int[] region_sizes = region_size.getHistogram();

    //
    int[] mask = new int[d.width * d.height];
    for (int i=0; i<mask.length; i++) {
      if (region_sizes[cc_labels[i]] >= threshold) {
        mask[i] = 1;
      }
    }
    return mask;
   }

   private Dimension findClosestRelation(int[][] neighour_matrix)
   {
    int size = neighour_matrix.length;
    double highest_ratio = 0;
    int highest_ratio_y = 0, highest_ratio_x = 0;
    for (int y=0; y<size; y++) {
      for (int x=0; x<size; x++) {
        if (x==y) continue;
        int close_to_itself = neighour_matrix[y][y];
        int close_to_others = neighour_matrix[y][x];
        if (close_to_itself == 0) close_to_itself = 1;
        double  ratio = (double)close_to_others/Math.sqrt(close_to_itself);
        if ( ratio> highest_ratio) {
          highest_ratio = ratio;
          highest_ratio_x = x;
          highest_ratio_y = y;
        }
      }
    }
    if (highest_ratio <= 0) return null;
    return new Dimension(highest_ratio_x, highest_ratio_y);
   }

   private Dimension findClosestRelation(double[][] dist_matrix)
   {
    int size = dist_matrix.length;
    double impossible_big = 1000;
    double closest_ratio = impossible_big;
    int closest_ratio_y = 0, closest_ratio_x = 0;
    for (int y=0; y<size; y++) {
      for (int x=0; x<size; x++) {
        if (x==y) continue;
        double close_to_itself = dist_matrix[y][y];
        double close_to_others = dist_matrix[y][x];
        if (close_to_itself == 0) close_to_itself = 1;
//        double  ratio = close_to_others/close_to_itself;
        double  ratio = close_to_others;
        if ( ratio< closest_ratio) {
          closest_ratio = ratio;
          closest_ratio_x = x;
          closest_ratio_y = y;
        }
      }
    }
    if (closest_ratio >= impossible_big) return null;
    return new Dimension(closest_ratio_x, closest_ratio_y);
   }

   private int[] generateTwoLabels(int[] labels, int l1, int l2, int[] mask)
   {
    int[] demo = new int[labels.length];
    for (int i=0; i<demo.length; i++) {
      if (mask[i] == 1) continue;
      if (labels[i] == l1) demo[i] = 1;
      else if (labels[i] == l2) demo[i] = 2;
      else demo[i] = 0;
    }
    return demo;
   }

   private int[] generateTwoLabels(int[] labels, int l1, int l2)
   {
    int[] demo = new int[labels.length];
    for (int i=0; i<demo.length; i++) {
      if (labels[i] == l1) demo[i] = 1;
      else if (labels[i] == l2) demo[i] = 2;
      else demo[i] = 0;
    }
    return demo;
   }

   /**
    * Segmentation.  MAIN method of the algorithm
    * @param pixels
    * @param d
    * @return
    */
   public int[] seg(int[] pixels, Dimension d)
   {


    int[] labels = null;

    switch (INITIAL_SEG_METHOD) {

      case INIT_BY_GONG_PROMINENT:
        DPU_DominantColor dc = new DPU_GongProminentColor();
        DS_DominantColor dc_rst = dc.getDominantColors(pixels, d.width, d.height);
        labels = dc_rst.getColorMap();
        break;

      case INIT_BY_CYCOLOR_KMEANS:
        yi.iu.processor.cluster.KMCluster c = CYColorKMCluster.getCluster(16, KMCluster.LINEAR, pixels);
        labels = c.getLabels();
        break;

      case INIT_BY_DOMINANT_COLOR:
      default:
        dc = new DPU_DominantColor();
        dc_rst = dc.getDominantColors(pixels, d.width, d.height);
        labels = dc_rst.getColorMap();

    }

    if (DEMO_INITIAL_SEG) {
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      int[] label_demo = yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
      yi.util.io.OutputResult.outputGIFImage(img_name + ".initalseg.gif",
                                             label_demo, d.width, d.height);
    }

    OneWayNeighborMerger merger = new OneWayNeighborMerger();

    int max = yi.stat.StatUtil.maximum(labels);
    while (true) {
      if (false) {
        int[] rst = yi.util.ImageUtil.makeNewImageFromLabel(labels, max, false);
        yi.util.io.OutputResult.outputGIFImage("merge", rst, d.width,
                                               d.height, true);
      }
      if (!merger.oneStepMerge(pixels, labels, d)) break;
    }
    if (DEMO_STEPS) {
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
      yi.util.io.OutputResult.outputGIFImage(img_name + ".merge.gif", rst, d.width,
                                       d.height, false);
    }

    SimpleHistogram hist = new SimpleHistogram(labels);
    System.out.println(hist);

    // connected component
    ConnectComponent cc = new ConnectComponent();
    labels = cc.compactLabeling(labels, d, false);

    if (DEMO_STEPS) {
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
      yi.util.io.OutputResult.outputGIFImage(img_name + ".merge.cc.gif", rst, d.width,
                                       d.height, false);
    }

    hist = new SimpleHistogram(labels);
    System.out.println(hist);

//    ClosestCCMerge cccmerger = new ClosestCCMerge();
//    CIELabDifferentialImage diffs = new CIELabDifferentialImage(pixels, d);
//    cccmerger.merge(labels, diffs, d, 1000, (int)(d.width * d.height * 0.005));

/*
        ClosestCCMerge merger = new ClosestCCMerge();
    CIELabDifferentialImage diffs = new CIELabDifferentialImage(pixels, d);
//    merger.mergeOnce(labels, diffs, d, 20, (int)(d.width * d.height));
    merger.merge(labels, diffs, d, 20, (int)(d.width * d.height));
        */

//   ClosestCCMerge postmerger = new ClosestCCMerge();
//   postmerger.merge(labels, diffs, d, 20, (int)(d.width * d.height));
//
    ClosestCCMerge postmerger = new ClosestCCMerge();
    labels = postmerger.seg(pixels, labels, d, -1);

    yi.util.label.Label.sequential(labels, null, false);

    if (DEMO_STEPS) {
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
      yi.util.io.OutputResult.outputGIFImage(img_name + ".merge.pm.gif", rst, d.width,
                                       d.height, false);
    }

    hist = new SimpleHistogram(labels);
    System.out.println(hist);


    return labels;
   }

//   /**
//   @param pixels
//   @param width
//   @param height
//   @param scale
//   @return yi.iu.processor.color.reg.DS_PolygonRegionColorStat
//    */
//   public DS_PolygonRegionColorStat getPolygonRegionColorStat(int[] pixels, int width, int height, double scale)
//   {
//    int[] labels = seg(pixels, new Dimension(width, height));
//
//    if (DEMO_IN_PIXEL) {
//      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
//      int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
//      yi.util.io.OutputResult.outputGIFImage(img_name + ".pixelreg.gif", rst, width,
//                                       height, false);
//    }
//
//    DS_PolygonRegionColorStat ds = new DS_PolygonRegionColorStat(pixels, labels, width, height);
//    ds.setScale(scale);
//    return ds;
//   }
//
//   public DS_PolygonRegionColorStat getReusablePolygonRegionColorStat(int[] pixels, int width, int height, double scale)
//   {
//
//      DS_PolygonRegionColorStat rst = (DS_PolygonRegionColorStat)reuse(REUSE);
//
//      if (rst == null) {
//        rst = getPolygonRegionColorStat(pixels, width, height, scale);
//      }
//
//      output(OUTPUT, rst);
//
//      return rst;
//   }

   public ColorLabeledRgnFts getColorLabeledRgnFts(int[] pixels, int width, int height,
                                                   int old_w, int old_h) {
     int[] labels = seg(pixels, new Dimension(width, height));

     if (DEMO_IN_PIXEL) {
       String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
       int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
       yi.util.io.OutputResult.outputGIFImage(img_name + ".pixelreg.gif", rst, width,
                                        height, false);
     }

     ColorLabeledRgnFts ds = new ColorLabeledRgnFts(pixels, labels, width, height, old_w, old_h, false);
     return ds;
   }


   public CIELabLabRgnFtsPxlVal getColorLabRgnFtsPxlVal(int[] pixels, int width, int height,
       int old_w, int old_h) {
     int[] labels = seg(pixels, new Dimension(width, height));

//     int[] labels = new int[pixels.length];
//     int grid_width = 8;
//     int grid_height = 8;
//     int grids_on_hor = width/grid_width;
//     int xy = 0;
//     for (int y=0; y<height; y++) {
//       for (int x=0; x<width; x++, xy++) {
//         int v = x/grid_width, h=y/grid_height;
//         labels[xy]= h*grids_on_hor+v;
//       }
//     }

     if (DEMO_IN_PIXEL) {
       String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
       int[] rst= yi.util.ImageUtil.makeNewImageFromLabel(labels, false);
       yi.util.io.OutputResult.outputGIFImage(img_name + ".pixelreg.gif", rst, width,
                                        height, false);
     }

     CIELabLabRgnFtsPxlVal ds = new CIELabLabRgnFtsPxlVal(pixels, labels, width, height, old_w, old_h, false);
     return ds;
   }

//   public ColorLabeledRgnFts getReusableColorLabeledRgnFts(int[] pixels, int width, int height,
//       int old_w, int old_h)
//   {
//
//      ColorLabeledRgnFts rst = (ColorLabeledRgnFts)reuseColorLabeledRgnFts(REUSE);
//
//      if (rst == null) {
//        rst = getColorLabeledRgnFts(pixels, width, height, old_w, old_h);
//      }
//
//      outputColorLabeledRgnFts(OUTPUT, rst);
//
//      return rst;
//   }
//
//   public ColorLabRgnFtsPxlVal getReusableColorLabRgnFtsPxlVal(int[] pixels, int width, int height,
//       int old_w, int old_h)
//   {
//
//      ColorLabRgnFtsPxlVal rst = (ColorLabRgnFtsPxlVal)reuseColorLabRgnFtsPxlVal(REUSE);
//
//      if (rst == null) {
//        rst = getColorLabRgnFtsPxlVal(pixels, width, height, old_w, old_h);
//      }
//
//      outputColorLabRgnFtsPxlVal(OUTPUT, rst);
//
//      return rst;
//   }
//
//   public Object reuse(int reuse_level)
//   {
//    if (reuse_level != DIU.INPUT_NONE) {
//      try {
//        DS_PolygonRegionColorStat rst = (DS_PolygonRegionColorStat)DIU.in(
//            reuse_level,
//            DOU.shortName(this.getClass().getName()),
//            "DS_PolygonRegionColorStat",
//            Class.forName("yi.iu.processor.color.reg.DS_PolygonRegionColorStat"));
//        return rst;
//      }
//      catch (Exception e) {
//        LogHandler.log(this, LOG_LEVEL, 1, "Can't Reuse DS_PolygonRegionColorStat:\n" + e);
//      }
//    }
//    return null;
//   }
//
//   void output(int output_level, DS_PolygonRegionColorStat ds)
//   {
//    if (output_level != DOU.OUTPUT_NONE) {
//      try {
//        DOU.out(
//            output_level,
//            DOU.shortName(this.getClass().getName()),
//            "DS_PolygonRegionColorStat",
//            ds);
//      } catch (DataOutputException e) {
//        LogHandler.log(this, LOG_LEVEL, 1, e.toString());
//      }
//    }
//   }
//
//   public Object reuseColorLabeledRgnFts(int reuse_level)
//   {
//    if (reuse_level != DIU.INPUT_NONE) {
//      try {
//        ColorLabeledRgnFts rst = (ColorLabeledRgnFts)DIU.in(
//            reuse_level,
//            DOU.shortName(this.getClass().getName()),
//            "ColorLabeledRgnFts",
//            Class.forName("yi.iu.processor.color.reg.ColorLabeledRgnFts"));
//        return rst;
//      }
//      catch (Exception e) {
//        LogHandler.log(this, LOG_LEVEL, 1, "Can't Reuse ColorLabeledRgnFts:\n" + e);
//      }
//    }
//    return null;
//   }
//
//   public Object reuseColorLabRgnFtsPxlVal(int reuse_level)
//   {
//    if (reuse_level != DIU.INPUT_NONE) {
//      try {
//        ColorLabRgnFtsPxlVal rst = (ColorLabRgnFtsPxlVal)DIU.in(
//            reuse_level,
//            DOU.shortName(this.getClass().getName()),
//            "ColorLabRgnFtsPxlVal",
//            Class.forName("yi.iu.processor.color.reg.ColorLabRgnFtsPxlVal"));
//        return rst;
//      }
//      catch (Exception e) {
//        LogHandler.log(this, LOG_LEVEL, 1, "Can't Reuse ColorLabRgnFtsPxlVal:\n" + e);
//        e.printStackTrace();
//      }
//    }
//    return null;
//   }
//
//
//   void outputColorLabeledRgnFts(int output_level, ColorLabeledRgnFts ds)
//   {
//    if (output_level != DOU.OUTPUT_NONE) {
//      try {
//        DOU.out(
//            output_level,
//            DOU.shortName(this.getClass().getName()),
//            "ColorLabeledRgnFts",
//            ds);
//      } catch (DataOutputException e) {
//        LogHandler.log(this, LOG_LEVEL, 1, e.toString());
//      }
//    }
//   }
//
//   void outputColorLabRgnFtsPxlVal(int output_level, ColorLabRgnFtsPxlVal ds)
//   {
//    if (output_level != DOU.OUTPUT_NONE) {
//      try {
//        DOU.out(
//            output_level,
//            DOU.shortName(this.getClass().getName()),
//            "ColorLabRgnFtsPxlVal",
//            ds);
//      } catch (DataOutputException e) {
//        LogHandler.log(this, LOG_LEVEL, 1, e.toString());
//      }
//    }
//   }

   private double[][] getDistanceMatrix(int[] pixels, int[] labels, Dimension d)
   {
    int width = d.width, height = d.height;


    // init the neighour matrix
    int max_label = yi.stat.StatUtil.maximum(labels);
    double[][] distance_matrix = new double[max_label + 1][max_label + 1];
    int[][] neighbor_matrix = new int[max_label + 1][max_label + 1];

    // only consider 4-connection neighours
    for (int y = 0; y < height - 1; y++) {
      for (int x = 0; x < width - 1; x++) {
        int label = labels[y * width + x];
        int right_label = labels[y * width + x + 1];
        // put into the neighour matrix
        putIntoNeighourMatrix(neighbor_matrix, label, right_label);
        distanceAccumulate(distance_matrix, label, right_label,
                           CIELab.distance(pixels[y * width + x], pixels[y * width + x + 1]));
        // to the bottom
        int down_label = labels[ (y + 1) * width + x];
        // put into the neighour matrix
        putIntoNeighourMatrix(neighbor_matrix, label, down_label);
        distanceAccumulate(distance_matrix, label, down_label,
                           CIELab.distance(pixels[y * width + x], pixels[(y + 1) * width + x]));
      }
    }

    // average the distance
    average_distance_matrix(distance_matrix, neighbor_matrix);
    yi.util.io.OutputResult.print("dist_matrix", distance_matrix, distance_matrix.length, distance_matrix.length);


    return distance_matrix;
   }

   private void putIntoNeighourMatrix(int[][] matrix, int label1, int label2)
   {
    matrix[label1][label2] ++;
    matrix[label2][label1] ++;
   }

   private void distanceAccumulate(double[][] dist_matrix, int label1, int label2, double dist)
   {
    dist_matrix[label1][label2] += dist;
    dist_matrix[label2][label1] += dist;
   }

   private void average_distance_matrix(double[][] distance_matrix, int[][] neighbor_matrix)
   {
    for (int y=0; y<distance_matrix.length; y++) {
      for (int x=0; x<distance_matrix.length; x++) {
        distance_matrix[y][x] /= neighbor_matrix[y][x];
      }

    }
   }

   private void mergeLabels(int[] labels, Dimension d, int from, int to)
   {
    for (int i=0; i<labels.length; i++) {
      if (labels[i] == from) labels[i]= to;
    }
   }

   public int[] merge(int[] pixels, int[] labels, Dimension d)
   {

    // find the connected components
    ConnectComponent cc = new ConnectComponent();
    int[] cc_labels = cc.compactLabeling(labels, d, false);

    while (true) {
      if (DEMO_STEP2) {
        int[] rst = yi.util.ImageUtil.makeNewImageFromLabel(cc_labels, false);
        yi.util.io.OutputResult.outputGIFImage("merge", rst, d.width,
                                               d.height, true);
      }

      double[][] distance_matrix = getDistanceMatrix(pixels, cc_labels, d);
      Dimension closest = findClosestRelation(distance_matrix);
      if (closest == null) {
        yi.util.io.OutputResult.print("distance_matrix", distance_matrix,
                                      distance_matrix.length,
                                      distance_matrix.length);
        break;
      }
      if (closest != null) {
        System.out.println("closest: " + closest.width + "," + closest.height +
                           " = " +
                           distance_matrix[closest.height][closest.width] + "/" +
                           distance_matrix[closest.height][closest.height]);
//        if ( (double) distance_matrix[closest.height][closest.width] /
//            distance_matrix[closest.height][closest.height] >= 2) {
        if ( (double) distance_matrix[closest.height][closest.width] >= 20) {
          yi.util.io.OutputResult.print("distance_matrix", distance_matrix,
                                        distance_matrix.length,
                                        distance_matrix.length);
          break;
        }
      }
      if (DEMO_STEP2) {

        int[] two_labels = generateTwoLabels(cc_labels, closest.width,
                                             closest.height);
        int[] two_labels_demo = yi.util.ImageUtil.makeNewImageFromLabel(two_labels);
        yi.util.io.OutputResult.outputGIFImage("merge", two_labels_demo,
                                               d.width,
                                               d.height, true);
      }

      mergeLabels(cc_labels, d, closest.height, closest.width);

      if (DEMO_STEP2) {

        int[] two_labels = generateTwoLabels(cc_labels, closest.width,
                                             closest.height);
        int[] two_labels_demo = yi.util.ImageUtil.makeNewImageFromLabel(two_labels);
        yi.util.io.OutputResult.outputGIFImage("merge", two_labels_demo,
                                               d.width,
                                               d.height, true);
      }

      cc_labels = cc.compactLabeling(cc_labels, d, false);
    }

    //int[] merged_label = mergeColorCloseRegion(labels, d, distance_matrix, threshold);
    return cc_labels;
   }


//   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);
//    }
//
//    // 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) {
//      KMeansHSISeg dummy = new KMeansHSISeg();
//      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;
//      }
//    }
//
//    try {
//      // process
//      Configuration.setParameter("SOURCE_NAME", img_name);
//      yi.util.ImageUtil.setPixels(pixels);
//      yi.util.ImageUtil.setDimension(d);
//
//      long startTime = System.currentTimeMillis();
//      KMeansHSISeg processor = new KMeansHSISeg();
//      DS_PolygonRegionColorStat ds = processor.getReusablePolygonRegionColorStat(
//          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 + ".color.reg.gif", demo, d.width,
//                                             d.height, false);
//
//      demo = ds.getEllipseDemo(null);
//      yi.util.io.OutputResult.outputGIFImage(img_name + ".color.ellipse.gif", demo, d.width,
//                                             d.height, false);
//
//      if (processor.DEMO_ONE_BY_ONE) {
//        ds.demoRegions(img_name, pixels, -1, 0xffff0000);
//      }
//
//    } catch (Exception e) {
//      System.err.println("KMeansHSISeg Terminated.");
//      e.printStackTrace();
//    }
//
//   }

  public FeatureRepresentable generateFeatureRepresentable(String feature_type, int[] pixels, int width, int height) {

    FeatureRepresentable rst = null;

    int[] labels = seg(pixels, new Dimension(width, height));

    if (feature_type.indexOf("CIELabLabRgnFtsPxlVal") >= 0) {
      rst = new CIELabLabRgnFtsPxlVal(pixels, labels, width, height, width, height, false);

    }
    else if (feature_type.indexOf("CIELabLabeledRgnFts") >= 0) {
      rst = new CIELabLabeledRgnFts(pixels, labels, width, height, width, height, false);
    }

    return rst;
  }
}
