package yi.iu.processor.graphseg;

import yi.iu.processor.graphseg.*;
import yi.iu.ui.SegFrame;
import yi.util.io.OutputResult;
import java.awt.*;
import java.util.Vector;

/**
  * This class extends GraphSeg.  The difference between this class and GraphSeg is
  * that the segmentation in this class includes a postprocessing step.  After the
  * segmenation same as the one from GraphSeg, this method will combine the similar
  * region together, and cluster the small region to the big region
  * @param SMALL_RGN_THRESHOLD is the threshold of the "small region"
  * @author Yi LI
  * @version 1.0.0
  * @see GraphSeg
*/
public class GraphSegAndCluster extends yi.iu.processor.graphseg.GraphSeg
{
    int SMALL_RGN_THRESHOLD=100;
    int COMBINE_THRSH=129;
    int CLUSTER_THRSH=256;

    /**
    * default consturctor
    * make the argument small_region_threshold of Distance measure, DM_HCI, 1
    * which means not adjust the distance while the size of region becomes small
    */
    public GraphSegAndCluster() {
        super(); dm= new DM_HCI(1);
        BSCRGNSIZE=8;
        STOPRGNSIZE=1;
        DISTANCE_THRESHOLD= 72;
    }

    public GraphSegAndCluster(DistanceMeasure _dm, int distance_threshold) {
        super(); dm= _dm;
        BSCRGNSIZE=8;
        STOPRGNSIZE=1;
        DISTANCE_THRESHOLD= distance_threshold;
    }

    /**
      * Same one as the one from GraphSeg, except a postprocessing step
    */
    public int[] segmentation(int[] source, int w, int h)
    {
        int wr=w/BSCRGNSIZE, hr=h/BSCRGNSIZE;       //width and height in term of basci region

        //get the bare information about the source image
        int[] bareData= dm.getBareData(source,new Dimension(w,h));

        //just for demo, use the bareData to get the image for display
        //sourceData=source;

        if (VERBOSE) {
            System.err.println();
            System.err.println("--- block size = "+BSCRGNSIZE+" ---");
        }

        //get the information about every basic regions
        int rgnInfo[][]= dm.getBscRgnInfo(bareData, w, h, BSCRGNSIZE);
        int[] segs_1= new int[wr*hr];
        for (int i=0;i<segs_1.length;i++) segs_1[i]=i;

        //displayRgns_frame(source, w, h, BSCRGNSIZE, segs_1, "candidate block_size="+BSCRGNSIZE);
        //if (rgn1>0) dm.printDiffOfHists(rgnInfo, rgn1, rgn2);
        /*
        if (rgn3>0) {
            //int len= rgnInfo[rgn3].length;
            int len= 26;
            int[][] temp= new int[3][len];
            for (int i=0;i<len-1;i++) {
                temp[0][i]=rgnInfo[rgn3][i]/rgnInfo[rgn3][len-1];
                temp[1][i]=rgnInfo[rgn4][i]/rgnInfo[rgn4][len-1];
                temp[2][i]=Math.abs(temp[0][i]-temp[1][i]);
            }
            temp[0][len-1]=rgnInfo[rgn3][len-1];
            temp[1][len-1]=rgnInfo[rgn4][len-1];
            OutputResult.outputResult("rgn_comp", temp);
            System.err.println(rgn3+ ","+rgn4+": "+dm.distance(rgnInfo[rgn3], rgnInfo[rgn4]));
            return segs_1;
        }
        */
        //if (BSCRGNSIZE==STOPRGNSIZE) { dm.printDiffOfHists(rgnInfo, rgn1, rgn2); traceRgn=rgn1;
        //}
        //else traceRgn=-1;
        //if (rgn1>0) traceRgn=rgn1;
        //else traceRgn=-1;
        traceRgn=-1;

        //region grow on the basic regions
        int[] segs= growOnBscRgn(rgnInfo, wr, hr);
        //displayRgns_frame(source, w, h, BSCRGNSIZE, segs, "grow_step_1");
        //displayRgns_color(w, h, BSCRGNSIZE, segs, "grow_step_1 block_size="+BSCRGNSIZE);


        //check whether the calculation of histogram is correct
        //boolean OK=dm.VerifyRgnInfo(bareData, w, h, BSCRGNSIZE, segs, rgnInfo);
        //if (!OK) System.err.println("wrong histogram calculation after merge on basic blocks!");

        //refine the segmentation
        int[] rfnSegs= refineSeg(bareData, w, h, segs, rgnInfo);
//int[] rfnSegs=segs;
        //OutputResult.outputResult("seg", rfnSegs,w,h);
        //dm.printDiffOfHists(rgnInfo, rgn1, rgn2);


        //rfnSegs= removeSmall(rfnSegs,RGNSIZETHRSH);
        //int[] segimg= yi.util.ImageUtil.makeNewImageFromLabel(rfnSegs);
        //(new SegFrame("refined regions", segimg, w, h, rfnSegs)).show();
        //displayRgns_frame(source, w, h, STOPRGNSIZE, rfnSegs, "result");
        //if (rgn3>0) dm.printDiffOfHists(rgnInfo, rgn3, rgn4);
        /*
        if (rgn1>0) {
            //int len= rgnInfo[rgn3].length;
            int len= 26;
            int[][] temp= new int[3][len];
            for (int i=0;i<len-1;i++) {
                temp[0][i]=rgnInfo[rgn1][i]/rgnInfo[rgn1][len-1];
                temp[1][i]=rgnInfo[rgn2][i]/rgnInfo[rgn2][len-1];
                temp[2][i]=Math.abs(temp[0][i]-temp[1][i]);
            }
            OutputResult.outputResult("rgn_comp", temp);
            temp[0][len-1]=rgnInfo[rgn1][len-1];
            temp[1][len-1]=rgnInfo[rgn2][len-1];
            OutputResult.outputResult("rgn_comp", temp);
            System.err.println(rgn1+","+rgn2+": "+dm.distance(rgnInfo[rgn1], rgnInfo[rgn2]));

            return segs_1;
        }
        */
        /*
        if (traceRgn>0&&traceStep>0) {
            OutputResult.outputResult("trace", traceRec, traceRec[0].length, traceStep);
            OutputResult.outputResult("grow_stat", growStat,1,growStat.length);
            int statB=((DM_LAWS3)dm).getStatBegin();
            int statL=((DM_LAWS3)dm).getStatLen();
            int[] temp= new int[statL];
            for (int k=0;k<statL;k++)
                temp[k]=rgnInfo[traceRgn][k+statB];
            OutputResult.outputResult("grow_stat", temp);

        }
        */
        /*
        int test_rgn=77;
        int[][] hci_rst= new int[rgnInfo[test_rgn][49]][6];
        int ptr=0;
        int[] hci= new int[3];
        for (int i=0;i<rfnSegs.length;i++) {
            if (rfnSegs[i]==test_rgn) {
                    int red=source[i]>>16&255;
                    int green=source[i]>>8&255;
                    int blue=source[i]&255;
                DM_HCI.rgb2hci(red,green,blue,hci);
                if (hci[1]<35) continue;
                hci_rst[ptr][0]=red;
                hci_rst[ptr][1]=green;
                hci_rst[ptr][2]=blue;

                hci_rst[ptr][3]=hci[0];
                hci_rst[ptr][4]=hci[1];
                hci_rst[ptr++][5]=hci[2];
            }
        }
        OutputResult.outputResult("77_hue", hci_rst);
        */

        if (STOPRGNSIZE>1) rfnSegs= getFineRegionMap(w, h, STOPRGNSIZE, rfnSegs);

        //post processing:
        //1. remove small regions tempory
        //2. combine the similar big regions (not necessarily be neighour)
        //3. cluster the small regions to the big regions
        combineSimilar(rfnSegs, rgnInfo, w, h);
        clusterSmall(rfnSegs, rgnInfo, w, h);
        int[] img= yi.util.ImageUtil.makeNewImageFromLabel(rfnSegs);
        (new SegFrame("regions", img, w, h, rfnSegs)).show();

        if (rgn1>0) {
            int len= rgnInfo[rgn1].length;
            System.err.println("length="+len);
            //int len= 26;
            OutputResult.outputResult("rgn_1", rgnInfo[rgn1]);
            OutputResult.outputResult("rgn_2", rgnInfo[rgn2]);
            int[][] temp= new int[3][len];
            for (int i=0;i<len-1;i++) {
                temp[0][i]=rgnInfo[rgn1][i]/rgnInfo[rgn1][len-1];
                temp[1][i]=rgnInfo[rgn2][i]/rgnInfo[rgn2][len-1];
                temp[2][i]=Math.abs(temp[0][i]-temp[1][i]);
            }
            OutputResult.outputResult("rgn_comp", temp);
            temp[0][len-1]=rgnInfo[rgn1][len-1];
            temp[1][len-1]=rgnInfo[rgn2][len-1];
            OutputResult.outputResult("rgn_comp", temp);
            System.err.println(rgn1+","+rgn2+": "+dm.distance(rgnInfo[rgn1], rgnInfo[rgn2]));

        }

        return rfnSegs;

    }

    /**
    * post processing:
    * 1. remove small regions tempory
    * 2. combine the similar big regions (not necessarily be neighour)
    * 3. cluster the small regions to the big regions
    */
    void combineSimilar(int[] segs, int[][] info, int w, int h)
    {
        //find the big region label and represented pixel
        int[] label_stat= new int[info.length];
        int[] represent_pixel= new int[info.length];

        for (int i=0;i<segs.length;i++) {
            if (label_stat[segs[i]]==0) represent_pixel[segs[i]]=i;
            label_stat[segs[i]]++;
        }

        Vector big_rgn= new Vector();
        for (int i=0;i<label_stat.length;i++) {
            if (label_stat[i]>SMALL_RGN_THRESHOLD) {
                big_rgn.addElement(new Integer(i));
                //System.err.println(i+": "+label_stat[i]);
            }
        }

                int threshold=COMBINE_THRSH;
                int loose= threshold*LOOSE_THRESHOLD_RATIO;
                Vector[] dists= new Vector[loose];     //store the distances

        //calculate the distances between any two big regions
        int d;
        int candidate=0;
        //int ld[][]= new int[h][w],ud[][]= new int[h][w];
        for (int i=0;i<big_rgn.size()-1;i++) {
            int rgn_1= ((Integer)big_rgn.elementAt(i)).intValue();
            for (int j=i+1;j<big_rgn.size();j++) {
                int rgn_2= ((Integer)big_rgn.elementAt(j)).intValue();

                d= dm.distance(info[rgn_1], info[rgn_2]);
                            if (d<loose) {
                                if (dists[d]==null) dists[d]=new Vector();
                                dists[d].addElement(new Dimension(represent_pixel[rgn_1],represent_pixel[rgn_2]));
                                candidate++;
                            }
                }
        }

        if (VERBOSE)
            System.err.println("  candidates= "+candidate);
        //display the statistic information about the distances
        int[] ds= new int[loose];
        for (int i=0;i<loose;i++) if (dists[i]!=null) ds[i]= dists[i].size();

        //grow the region with the order of the distance
        growOnGraph( dists, segs, info, threshold);

        label_stat=null;represent_pixel=null;big_rgn=null;dists=null;System.gc();

    }

    void clusterSmall(int[] segs, int[][] info, int w, int h)
    {
        //find the big region label and represented pixel
        int[] label_stat= new int[info.length];

        for (int i=0;i<segs.length;i++) {
            label_stat[segs[i]]++;
        }

        Vector big_rgn= new Vector();
        for (int i=0;i<label_stat.length;i++) {
            if (label_stat[i]>=SMALL_RGN_THRESHOLD) {
                big_rgn.addElement(new Integer(i));
                //System.err.println(i+": "+label_stat[i]);
            }
        }


                int[] parent= new int[info.length];
                for (int i=0;i<info.length;i++) parent[i]=i;

                for (int i=0;i<label_stat.length;i++) {
                    //only consider those small regions
                    if (label_stat[i]==0||label_stat[i]>=SMALL_RGN_THRESHOLD) continue;
                    int minDist=CLUSTER_THRSH, minIndex=-1;
                    for (int j=0;j<big_rgn.size();j++) {
                        int current_big= ((Integer)big_rgn.elementAt(j)).intValue();
                        int current_label= findRgnNum(parent,current_big);

                int d=dm.distance(info[i], info[current_label]);
                if (d<minDist) { minDist=d; minIndex=current_label; };
            }
            if (minIndex>=0) {
                int newRgn=rgnUnion(parent,i, minIndex, info);
            }
            else parent[i]=-1;  //unsegmented region: small and can't belong to every big region
        }

                //recalculate the result according the records in parent
                for (int i = 0; i < w*h; i++ ) {
                    if (parent[segs[i]]==-1) segs[i]=0;     //unsegmented
                    else segs[i]=findRgnNum(parent,segs[i]);
                }
    }

}