package yi.iu.processor.symmetry;

import java.awt.*;
import yi.iu.processor.*;
import yi.iu.processor.channel.*;

public class IntensitySymFinder extends yi.iu.processor.ImageProcessor
{
    int consideredLines=5;   // how many lines to be considered together
                                    // it better be a odd number.
    int peakSize=7;          // the peak should be the biggest in the area 15*15 if size=7
    double smallNum=3.2;     // what's considered the symmetry point
                                    // the range is 0..2. should be changed with consideredline!!!
                                    // if consideredLines==1->1.6..1.8, 3->4.0..4.2
    int maxWidth=40;         // considered possible max width of the car
    int satsWidth= 120;      // the original image is better to shrink to this width
    int minCarLen=7;         // the possible minimal car width (one side)
        
    public IntensitySymFinder() {super();}
    
    public IntensitySymFinder(int _consideredLines, int _peakSize, double _smallNum,
        int _maxWidth, int _satsWidth, int _minCarLen)
    {
        this();
        consideredLines=_consideredLines;   
        peakSize=_peakSize;  
        smallNum=_smallNum;
        maxWidth=_maxWidth;
        satsWidth= _satsWidth;
        minCarLen=_minCarLen;         
    }
    
    public int[] getProcessedPseudoColor(int source[], Dimension d)
    {
        int w=d.width, h=d.height;
        int[] rst= getProcessed(source,d);
        int nw=d.width, nh=d.height;
        d.width=w; d.height=h;
        
        int[] result= new int[source.length];
        for (int i=0;i<h;i++) {
            for (int j=0;j<w;j++) {
                int r=(source[i*w+j]>>16)&0xff;
                int g=(source[i*w+j]>>8)&0xff;
                int b=source[i*w+j]&0xff;
                r= r/2;
                g= g/2;
                b= b/2;
                result[i*w+j]=255<<24|r<<16|g<<8|b;
                int s= w/nw;
                int ti=i/s, tj=j/s;
                if (ti*nw+tj>=rst.length) break;
                if (rst[ti*nw+tj]==1) result[i*w+j]=0xffffffff;
            }
        }
        /*
        int[] dis= new int[nw*nh];
        for (int i=0;i<nw*nh;i++) 
            if (rst[i]==1) dis[i]=0xffffffff;
            else dis[i]=0xff000000;
        DisplayFrame f= new DisplayFrame(dis, nw, nh);
        f.show();
        */
        return result;
    }

    public int[] getProcessed(int source[], Dimension d)
    {
        int[] src=source;
        int scale= d.width/satsWidth;
        if (scale>1) {
            yi.iu.processor.Resizer rser= new yi.iu.processor.Resizer(scale);
            src= rser.getProcessed(source, d);
        }
        
        IntensityMaker im= new IntensityMaker();
        int[] ins= im.getProcessed(src, d);
        int w=d.width, h=d.height;
        double[][] s= new double[w][maxWidth+1];
        double[][] s_total= new double[w][maxWidth+1];
        double[][][] s_rec= new double[consideredLines][w][maxWidth+1];
        int[] rst= new int[w*h];
        double[][] pkHigh= new double[w][h];
        double[][] pkWidth= new double[w][h];
        
        for (int i=0;i<h;i++) {
            for (int j=0;j<w;j++) {
                // the maxw will be the smaller between j and w-j-1
                int maxw=j;
                if (w-j-1<maxw) maxw=w-j-1;
                if (maxw>maxWidth) maxw=maxWidth;
                // calculate the E(u,xs,w) and O(u,xs,w)
                for (int k=1;k<=maxw;k++) {
                    int[] e= new int[2*k+1];
                    int[] o= new int[2*k+1];
                    int mean=0, num=0;
                    for (int l=-k;l<=k;l++) {
                        e[l+k]=ins[i*w+j+l]+ins[i*w+j-l];
                        o[l+k]=ins[i*w+j+l]-ins[i*w+j-l];
                        mean+=e[l+k]; num++;
                    }
                    mean= (int)((double)mean/num+0.5);
                    for (int l=-k;l<=k;l++) e[l+k]-=mean;
                    long int_e=0, int_o=0;
                    for (int l=-k;l<=k;l++) {
                        int_e+= e[l+k]*e[l+k];
                        int_o+= o[l+k]*o[l+k];
                    }
                    if (int_e==0 && int_o==0) s[j][k]=0;
                    else
                        s[j][k]= (double)(int_e-int_o)/(int_e+int_o);
                    //s[j][k]= (double)k/maxw*(s[j][k]+1);
                    //s[j][k]= (double)k/maxWidth*(s[j][k]+1);
                    if (k<=minCarLen) s[j][k]*= (double)(k/minCarLen)*(k/minCarLen);
                    else s[j][k]*= (double)Math.sqrt(k/minCarLen);
                    s_total[j][k]-= s_rec[i%consideredLines][j][k];
                    s_rec[i%consideredLines][j][k]=s[j][k];
                    s_total[j][k]+= s[j][k];
                }
            }
            if (i%10==0) System.err.println("line "+i);
            if (i<consideredLines-1) continue;
            int[] peaks= getPeaks(s_total,w,maxWidth+1);
            for (int j=0;j<w;j++) {
                if (peaks[j]==0) continue;
                rst[(i-consideredLines/2)*w+j]=1;
                double max=0;
                int imax=0;
                for (int k=peakSize;k<=maxWidth;k++)
                    if (s_total[j][k]>max) {
                        max=s_total[j][k];
                        imax=k;
                    }
                pkHigh[j][i-consideredLines/2]= s_total[j][imax];
                pkWidth[j][i-consideredLines/2]= imax;
            }
            //outputResult(s,w,w);
            /*
            if (i==45) {
            	for (int ii=0;ii<w;ii++) {
            		for (int j=0;j<=maxWidth;j++) {
            			s[ii][j]/=2;
            		}
            	}
                outputResult(s,w,maxWidth+1);
            }
            */
            System.gc();
        }
        
        //outputResult(pkHigh,w,h);
        //outputResult(pkWidth,w,h);

        /*
        int[] rst= new int[w*w];
        for (int i=0;i<w;i++) {
            for (int j=0;j<w;j++) {
                rst[j*w+i]= (int)(s[i][j]*127+0.5);
                if (rst[j*w+i]>255) rst[j*w+i]=255;
            }
        }
        d.width=d.height=w;
        */
        return rst;
    }
    
    // w: the position in the horizontal line;
    // h: the considered width
    // usually, the slope along the h is not too deep
    int[] getPeaks(double[][] data, int w, int h)
    {
        int[] rst= new int[w];
        for (int i=peakSize;i<w-peakSize;i++) {
            double max=0;
            int imax=0;
            for (int j=peakSize;j<h;j++)
                if (data[i][j]>max) {
                    max=data[i][j];
                    imax=j;
                }
            if (max<smallNum) continue;
            boolean isPeak=true;
            for (int m=-peakSize;m<=peakSize;m++) {
                    for (int n=-peakSize;n<=peakSize;n++) {
                        if (imax+n>=h) continue;
                        if (data[i+m][imax+n]> data[i][imax]) {
                            isPeak=false;
                            m=n=peakSize+1;
                        }
                    }
            }
            if (isPeak) rst[i]=1;
                
                
            /*
            for (int j=peakSize;j<h-peakSize;j++) {
                if (data[i][j]<smallNum) continue;
                boolean isPeak=true;
                
                // one method: loose
                for (int m=-peakSize;m<=peakSize;m++) {
                    for (int n=-peakSize;n<=peakSize;n++) {
                        if (data[i+m][j+n]> data[i][j]) {
                            isPeak=false;
                            m=n=peakSize+1;
                        }
                    }
                }
                if (isPeak) rst[i]=1;
                
                // another method: more loose
                for (int m=-peakSize;m<=peakSize;m++) {
                    if (data[i+m][j]> data[i][j]) {
                        isPeak=false;
                        break;
                    }
                }
                if (!isPeak) continue;
                for (int n=-peakSize;n<=peakSize;n++) {
                    if (data[i][j+n]> data[i][j]) {
                        isPeak=false;
                        break;
                    }
                }
                if (isPeak) rst[i]=1;
                
            }
            */
        }
        return rst;
    }
    
}