package ip.gui.frames;

import collections.sortable.Cshort;
import collections.sortable.Sort;
import ip.gui.dialog.DoubleArrayLog;
import math.Mat;
import ip.gui.Gauss;
import ip.gui.Timer;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Vector;

import utils.MyMath;

public class SpatialFilterFrame extends ConvolutionFrame {
    private SpatialFilterFrame child = null;
    private Menu SpatialFilterMenu = getMenu("SpatialFilter");

    private Menu lowPassMenu = getMenu("LowPass");
    private Menu gaussianMenu = getMenu("gaussian");
    private Menu medianMenu = getMenu("Median");
    private Menu highPassMenu = getMenu("Hi-pass");
    private Menu unsharpMenu = getMenu("unsharp");

    private Menu kernalMenu = getMenu("Convolution Kernal");
    private MenuItem showConvolutionKernal_mi
            = addMenuItem(kernalMenu, "show");
    private MenuItem enterConvolutionKernal33_mi
            = addMenuItem(kernalMenu, "3x3..");
    private MenuItem enterConvolutionKernal55_mi
            = addMenuItem(kernalMenu, "5x5..");
    private MenuItem enterConvolutionKernal77_mi
            = addMenuItem(kernalMenu, "7x7..");

    private MenuItem average_mi = addMenuItem(lowPassMenu, "[a]verage");
    private MenuItem lp1_mi = addMenuItem(lowPassMenu, "[E-1]lp1");
    private MenuItem lp2_mi = addMenuItem(lowPassMenu, "[E-2]lp2");
    private MenuItem lp3_mi = addMenuItem(lowPassMenu, "[E-3]lp3");
    private MenuItem mean3_mi = addMenuItem(lowPassMenu, "[E-4]mean3");
    private MenuItem mean9_mi = addMenuItem(lowPassMenu, "[E-5]mean9");

    private MenuItem gauss3_mi = addMenuItem(gaussianMenu, "[E-6]gauss 3x3");
    private MenuItem gauss7_mi = addMenuItem(gaussianMenu, "[E-7]gauss 7x7");
    private MenuItem gauss15_mi = addMenuItem(gaussianMenu, "[E-9]gauss 15x15");
    private MenuItem gauss31_mi = addMenuItem(gaussianMenu, "[E-T-G]auss 31x31");
    private MenuItem gauss31_fast_mi = addMenuItem(gaussianMenu, "Gauss 31x31 fast");
    private MenuItem gabor_mi = addMenuItem(lowPassMenu, "[T-G]abor");

    private MenuItem medianCross3x3_mi = addMenuItem(medianMenu, "[E-T-+]cross3x3");
    private MenuItem medianSquare3x3_mi = addMenuItem(medianMenu, "[E-T-s]quare 3x3");
    private MenuItem medianOctagon5x5_mi = addMenuItem(medianMenu, "[E-T-o]catgon 5x5");
    private MenuItem medianSquare5x5_mi = addMenuItem(medianMenu, "[E-T-S]quare 5x5");
    private MenuItem medianDiamond7x7_mi = addMenuItem(medianMenu, "[E-T-D]iamond 7x7");
    private MenuItem medianCross7x7_mi = addMenuItem(medianMenu, "[E-T-C]ross 7x7");
    private MenuItem outlierEstimate_mi = addMenuItem(medianMenu, "[E-T-O]utlier estimate");
    private MenuItem saltAndPepper100_mi = addMenuItem(medianMenu, "[E-T-1]saltAndPepper100");
    private MenuItem saltAndPepper1000_mi = addMenuItem(medianMenu, "[E-T-2]saltAndPepper1000");
    private MenuItem saltAndPepper2000_mi = addMenuItem(medianMenu, "[E-T-3]saltAndPepper2000");
    private MenuItem saltAndPepper4000_mi = addMenuItem(medianMenu, "[E-T-4]saltAndPepper4000");

    private MenuItem hp1_mi = addMenuItem(highPassMenu, "[T-1]hp1");
    private MenuItem hp2_mi = addMenuItem(highPassMenu, "[T-2]hp2");
    private MenuItem hp3_mi = addMenuItem(highPassMenu, "[T-3]hp3");
    private MenuItem hp4_mi = addMenuItem(highPassMenu, "[T-4]hp4");
    private MenuItem hp5_mi = addMenuItem(highPassMenu, "[T-5]hp5");

    MenuItem shadowMask_mi = addMenuItem(highPassMenu, "[T-6]shadowMask");

    private MenuItem usp1_mi = addMenuItem(unsharpMenu, "[T-7]usp1");

    private MenuItem subtractChild_mi = addMenuItem(getFileMenu(), "[T-8]subtract child");

    private MenuItem short2Image_mi = addMenuItem(getFileMenu(), "[T-9]short2Image");
    private MenuItem clip_mi = addMenuItem(getFileMenu(), "[T-0]clip");


    private boolean computeOutlier = true;
    private int numberOfOutliers = 0;


    public void makeChild() {
        child = new SpatialFilterFrame("child");
        ImageFrameInterface frame = getChild();
        int width1 = getImageWidth();
        frame.setImageWidth(width1);
        ImageFrameInterface frame1 = getChild();
        int height1 = getImageHeight();
        frame1.setImageHeight(height1);
        getChild().setR(Mat.copyArray(getR()));
        getChild().setG(Mat.copyArray(getG()));
        getChild().setB(Mat.copyArray(getB()));
    }

    public void subtractChild() {
        subtract(this, getChild());
        short2Image();
    }

    public static void subtract(SpatialFilterFrame sff, SpatialFilterFrame f) {
        for (int i = 0; i < sff.getImageWidth(); i++)
            for (int j = 0; j < sff.getImageHeight(); j++) {
                sff.getR()[i][j] = (short) (sff.getR()[i][j] - f.getR()[i][j]);
                sff.getG()[i][j] = (short) (sff.getG()[i][j] - f.getG()[i][j]);
                sff.getB()[i][j] = (short) (sff.getB()[i][j] - f.getB()[i][j]);
            }

    }

    public void outlierEstimate() {
        computeOutlier = !computeOutlier;

        System.out.println(
                "computeOutlier = " + computeOutlier);
        System.out.println(
                "numberOfOutliers = " + numberOfOutliers);
        numberOfOutliers = 0;
    }

    public SpatialFilterFrame(String title) {
        super(title);
        MenuBar mb = getMenuBar();
        getSpatialFilterMenu().add(kernalMenu);
        lowPassMenu.add(gaussianMenu);
        getSpatialFilterMenu().add(lowPassMenu);
        getSpatialFilterMenu().add(medianMenu);
        highPassMenu.add(unsharpMenu);
        getSpatialFilterMenu().add(highPassMenu);
        mb.add(getSpatialFilterMenu());
        setMenuBar(mb);
    }


    public void clip() {
        for (int x = 0; x < getImageWidth(); x++)
            for (int y = 0; y < getImageHeight(); y++) {
                if (getR()[x][y] > 255) getR()[x][y] = 255;
                if (getG()[x][y] > 255) getG()[x][y] = 255;
                if (getB()[x][y] > 255) getB()[x][y] = 255;
                if (getR()[x][y] < 0) getR()[x][y] = 0;
                if (getG()[x][y] < 0) getG()[x][y] = 0;
                if (getB()[x][y] < 0) getB()[x][y] = 0;
            }
    }

    public void enterConvolutionKernal() {
        new DoubleArrayLog(
                this, "Convolution kernal", null, 0, 0, 6);
    }

    public void enterConvolutionKernal(int r, int c) {
        new DoubleArrayLog(
                this, "Convolution kernal", null, r, c, 6);
    }

    public void actionPerformed(ActionEvent e) {

        if (match(e, gabor_mi)) {
            gabor7();
            return;
        }
        if (match(e, enterConvolutionKernal33_mi)) {
            enterConvolutionKernal(3, 3);
            return;
        }

        if (match(e, enterConvolutionKernal55_mi)) {
            enterConvolutionKernal(5, 5);
            return;
        }

        if (match(e, enterConvolutionKernal77_mi)) {
            enterConvolutionKernal(7, 7);
            return;
        }
        if (match(e, showConvolutionKernal_mi)) {
            showConvolutionKernal();
            return;
        }
        if (match(e, clip_mi)) {
            clip();
            return;
        }
        if (match(e, short2Image_mi)) {
            short2Image();
            return;
        }
        if (match(e, subtractChild_mi)) {
            subtractChild();
            return;
        }
        if (match(e, usp1_mi)) {
            usp1();
            return;
        }
        if (match(e, outlierEstimate_mi)) {
            outlierEstimate();
            return;
        }
        if (match(e, medianCross7x7_mi)) {
            medianCross7x7();
            return;
        }
        if (match(e, medianCross3x3_mi)) {
            medianCross3x3();
            return;
        }
        if (match(e, medianSquare3x3_mi)) {
            medianSquare3x3();
            return;
        }
        if (match(e, medianOctagon5x5_mi)) {
            medianOctagon5x5();
            return;
        }
        if (match(e, medianSquare5x5_mi)) {
            medianSquare5x5();
            return;
        }
        if (match(e, medianDiamond7x7_mi)) {
            medianDiamond7x7();
            return;
        }
        if (match(e, mean9_mi)) {
            mean9();
            return;
        }
        if (match(e, mean3_mi)) {
            mean3();
            return;
        }
        if (match(e, saltAndPepper100_mi)) {
            saltAndPepper(100);
            return;
        }
        if (match(e, saltAndPepper1000_mi)) {
            saltAndPepper(1000);
            return;
        }
        if (match(e, saltAndPepper2000_mi)) {
            saltAndPepper(2000);
            return;
        }
        if (match(e, saltAndPepper4000_mi)) {
            saltAndPepper(4000);
            return;
        }
        if (match(e, gauss3_mi)) {
            gauss3();
            return;
        }
        if (match(e, gauss7_mi)) {
            gauss7();
            return;
        }
        if (match(e, gauss15_mi)) {
            gauss15();
            return;
        }
        if (match(e, gauss31_mi)) {
            gauss31();
            return;
        }
        if (match(e, gauss31_fast_mi)) {
            gauss31Fast();
            return;
        }

        if (match(e, lp1_mi)) {
            lp1();
            return;
        }
        if (match(e, lp2_mi)) {
            lp2();
            return;
        }
        if (match(e, lp3_mi)) {
            lp3();
            return;
        }
        if (match(e, hp1_mi)) {
            hp1();
            return;
        }
        if (match(e, hp2_mi)) {
            hp2();
            return;
        }
        if (match(e, hp3_mi)) {
            hp3();
            return;
        }
        if (match(e, hp4_mi)) {
            hp4();
            return;
        }
        if (match(e, hp5_mi)) {
            hp5();
            return;
        }
        if (match(e, average_mi)) {
            average();
            return;
        }

        super.actionPerformed(e);

    }

    public void saltAndPepper(int n) {
        for (int i = 0; i < n; i++) {
            int rx = MyMath.rand(0, getImageWidth() - 1);
            int ry = MyMath.rand(0, getImageHeight() - 1);
            getR()[rx][ry] = 255;
            getG()[rx][ry] = 255;
            getB()[rx][ry] = 255;
            rx = MyMath.rand(0, getImageWidth() - 1);
            ry = MyMath.rand(0, getImageHeight() - 1);
            getR()[rx][ry] = 0;
            getG()[rx][ry] = 0;
            getB()[rx][ry] = 0;
        }
        short2Image();
    }

    public void average() {
        float k[][] = {
            {1, 1, 1},
            {1, 1, 1},
            {1, 1, 1}
        };
        Mat.scale(k, 1 / 9.0);
        convolve(k);
    }


    public void hp1() {
        float k[][] = {
            {0, -1, 0},
            {-1, 10, -1},
            {0, -1, 0}
        };
        Mat.normalize(k);
        convolve(k);
    }

    public void hp2() {
        float k[][] = {
            {0, -1, 0},
            {-1, 8, -1},
            {0, -1, 0}
        };
        Mat.normalize(k);
        convolve(k);
    }

    public void hp3() {
        float k[][] = {
            {0, -1, 0},
            {-1, 5, -1},
            {0, -1, 0}
        };
        Mat.normalize(k);
        convolve(k);
    }

    public void hp4() {
        float k[][] = {
            {1, -2, 1},
            {-2, 5, -2},
            {1, -2, 1}
        };
        convolve(k);
    }

    public void hp5() {
        float k[][] = {
            {-1, -1, -1},
            {-1, 9, -1},
            {-1, -1, -1}
        };
        convolve(k);
    }


    public void usp1() {
        makeChild();
        getChild().gauss3();
        subtract(this, getChild());
        short2Image();
    }

    public void lp1() {
        float k[][] = {
            {1, 1, 1},
            {1, 2, 1},
            {1, 1, 1}
        };
        Mat.scale(k, 1 / 10.0);
        convolve(k);
    }

    public void lp2() {
        float k[][] = {
            {1, 1, 1},
            {1, 4, 1},
            {1, 1, 1}
        };
        Mat.scale(k, 1 / 12.0);
        convolve(k);
    }

    public void lp3() {
        float k[][] = {
            {1, 1, 1},
            {1, 12, 1},
            {1, 1, 1}
        };
        Mat.scale(k, 1 / 20.0);
        convolve(k);
    }

    public void gabor7() {
        float k[][] = {

            {137, 126, 116, 123, 127, 128, 0},
            {148, 168, 133, 104, 117, 127, 0},
            {124, 173, 223, 158, 99, 113, 0},
            {117, 111, 181, 255, 181, 111, 0},
            {126, 113, 99, 158, 223, 173, 0},
            {128, 127, 117, 104, 133, 168, 0},
            {0, 0, 0, 0, 0, 0, 0}
        };
        Mat.normalize(k);
        convolve(k);

    }


    public void mean9() {
        float s = (float) 81.0;
        float k[][] = {
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s},
            {1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s, 1 / s}};

        convolve(k);
    }

    public void mean3() {
        float k[][] = {
            {0.11111111f, 0.11111111f, 0.11111111f},
            {0.11111111f, 0.11111111f, 0.11111111f},
            {0.11111111f, 0.11111111f, 0.11111111f}};
//sum=1.0000000074505806
        convolve(k);
    }

    public void gauss3() {
        float k[][] = {
            {1, 2, 1},
            {2, 4, 2},
            {1, 2, 1}
        };
        Mat.scale(k, 1 / 16.0);
        convolve(k);
    }

// computes an MxN kernel using (9.1)


    public static double oneOnF(
            double x, double y,
            double xc, double yc) {
        double dx = x - xc;
        double dy = y - yc;
        double dx2 = dx * dx;
        double dy2 = dy * dy;
        double eps = 1;
        return
                1 / Math.sqrt(dx2 + dy2 + eps);
    }

    public static float[][] getOneOnFKernel(
            int M, int N) {
        float k[][] = new float[M][N];
        int xc = M / 2;
        int yc = N / 2;
        for (int x = 0; x < k.length; x++)
            for (int y = 0; y < k[0].length; y++)
                k[x][y] = (float) oneOnF(x, y, xc, yc);
        return k;
    }

    public void multOneOnF() {
        int xc = getImageWidth() / 2;
        int yc = getImageHeight() / 2;
        for (int x = 0; x < getImageWidth(); x++)
            for (int y = 0; y < getImageHeight(); y++) {
                double f = oneOnF(x, y, xc, yc);
                getR()[x][y] = (short) (getR()[x][y] * f);
                getG()[x][y] = (short) (getG()[x][y] * f);
                getB()[x][y] = (short) (getB()[x][y] * f);
            }
        short2Image();
    }

    public void printOneOnFKernel() {
        float k[][] = getOneOnFKernel(9, 9);
        Mat.printKernel(k, "OneOnFKernel" + k.length);
    }

    public static void testQuickSort() {
        int a[] = {1, 2, 3, 5, 4, 3, 2, 5, 6, 7};
        Mat.quickSort(a);
        for (int i = 0; i < a.length; i++)
            System.out.println(a[i]);
    }

    public void gauss7() {
        float k [][] = {

            {1, 1, 2, 2, 2, 1, 1},
            {1, 2, 2, 4, 2, 2, 1},
            {2, 2, 4, 8, 4, 2, 2},
            {2, 4, 8, 16, 8, 4, 2},
            {2, 2, 4, 8, 4, 2, 2},
            {1, 2, 2, 4, 2, 2, 1},
            {1, 1, 2, 2, 2, 1, 1}
        };
        Mat.normalize(k);
        convolve(k);
    }

    public void gauss15() {
        float k[][] = {
            {1.9045144E-7f, 9.671922E-7f, 3.8253193E-6f, 1.1782813E-5f, 2.8265502E-5f, 5.2806907E-5f, 7.6833596E-5f, 8.7063876E-5f, 7.6833596E-5f, 5.2806907E-5f, 2.8265502E-5f, 1.1782813E-5f, 3.8253193E-6f, 9.671922E-7f, 1.9045144E-7f},
            {9.671922E-7f, 4.9118075E-6f, 1.9426576E-5f, 5.9838065E-5f, 1.4354405E-4f, 2.681756E-4f, 3.901932E-4f, 4.4214682E-4f, 3.901932E-4f, 2.681756E-4f, 1.4354405E-4f, 5.9838065E-5f, 1.9426576E-5f, 4.9118075E-6f, 9.671922E-7f},
            {3.8253193E-6f, 1.9426576E-5f, 7.6833596E-5f, 2.3666414E-4f, 5.677278E-4f, 0.0010606551f, 0.001543244f, 0.0017487246f, 0.001543244f, 0.0010606551f, 5.677278E-4f, 2.3666414E-4f, 7.6833596E-5f, 1.9426576E-5f, 3.8253193E-6f},
            {1.1782813E-5f, 5.9838065E-5f, 2.3666414E-4f, 7.2897685E-4f, 0.0017487246f, 0.0032670477f, 0.0047535263f, 0.0053864513f, 0.0047535263f, 0.0032670477f, 0.0017487246f, 7.2897685E-4f, 2.3666414E-4f, 5.9838065E-5f, 1.1782813E-5f},
            {2.8265502E-5f, 1.4354405E-4f, 5.677278E-4f, 0.0017487246f, 0.004194972f, 0.00783724f, 0.011403117f, 0.012921424f, 0.011403117f, 0.00783724f, 0.004194972f, 0.0017487246f, 5.677278E-4f, 1.4354405E-4f, 2.8265502E-5f},
            {5.2806907E-5f, 2.681756E-4f, 0.0010606551f, 0.0032670477f, 0.00783724f, 0.014641892f, 0.021303825f, 0.024140399f, 0.021303825f, 0.014641892f, 0.00783724f, 0.0032670477f, 0.0010606551f, 2.681756E-4f, 5.2806907E-5f},
            {7.6833596E-5f, 3.901932E-4f, 0.001543244f, 0.0047535263f, 0.011403117f, 0.021303825f, 0.030996885f, 0.03512407f, 0.030996885f, 0.021303825f, 0.011403117f, 0.0047535263f, 0.001543244f, 3.901932E-4f, 7.6833596E-5f},
            {8.7063876E-5f, 4.4214682E-4f, 0.0017487246f, 0.0053864513f, 0.012921424f, 0.024140399f, 0.03512407f, 0.039800785f, 0.03512407f, 0.024140399f, 0.012921424f, 0.0053864513f, 0.0017487246f, 4.4214682E-4f, 8.7063876E-5f},
            {7.6833596E-5f, 3.901932E-4f, 0.001543244f, 0.0047535263f, 0.011403117f, 0.021303825f, 0.030996885f, 0.03512407f, 0.030996885f, 0.021303825f, 0.011403117f, 0.0047535263f, 0.001543244f, 3.901932E-4f, 7.6833596E-5f},
            {5.2806907E-5f, 2.681756E-4f, 0.0010606551f, 0.0032670477f, 0.00783724f, 0.014641892f, 0.021303825f, 0.024140399f, 0.021303825f, 0.014641892f, 0.00783724f, 0.0032670477f, 0.0010606551f, 2.681756E-4f, 5.2806907E-5f},
            {2.8265502E-5f, 1.4354405E-4f, 5.677278E-4f, 0.0017487246f, 0.004194972f, 0.00783724f, 0.011403117f, 0.012921424f, 0.011403117f, 0.00783724f, 0.004194972f, 0.0017487246f, 5.677278E-4f, 1.4354405E-4f, 2.8265502E-5f},
            {1.1782813E-5f, 5.9838065E-5f, 2.3666414E-4f, 7.2897685E-4f, 0.0017487246f, 0.0032670477f, 0.0047535263f, 0.0053864513f, 0.0047535263f, 0.0032670477f, 0.0017487246f, 7.2897685E-4f, 2.3666414E-4f, 5.9838065E-5f, 1.1782813E-5f},
            {3.8253193E-6f, 1.9426576E-5f, 7.6833596E-5f, 2.3666414E-4f, 5.677278E-4f, 0.0010606551f, 0.001543244f, 0.0017487246f, 0.001543244f, 0.0010606551f, 5.677278E-4f, 2.3666414E-4f, 7.6833596E-5f, 1.9426576E-5f, 3.8253193E-6f},
            {9.671922E-7f, 4.9118075E-6f, 1.9426576E-5f, 5.9838065E-5f, 1.4354405E-4f, 2.681756E-4f, 3.901932E-4f, 4.4214682E-4f, 3.901932E-4f, 2.681756E-4f, 1.4354405E-4f, 5.9838065E-5f, 1.9426576E-5f, 4.9118075E-6f, 9.671922E-7f},
            {1.9045144E-7f, 9.671922E-7f, 3.8253193E-6f, 1.1782813E-5f, 2.8265502E-5f, 5.2806907E-5f, 7.6833596E-5f, 8.7063876E-5f, 7.6833596E-5f, 5.2806907E-5f, 2.8265502E-5f, 1.1782813E-5f, 3.8253193E-6f, 9.671922E-7f, 1.9045144E-7f}};
//sum=0.9999999983459134
        convolve(k);
    }

    public void gauss31Fast() {
        double sigma = 2.0;
        float k[][] = Gauss.getGaussKernel(31, 31, sigma);
        convolveFast(k);
    }

    public void gauss31() {
        double sigma = 2.0;
        float k[][] = Gauss.getGaussKernel(31, 31, sigma);
        convolve(k);
    }

    public int numberOfNonZeros(short k[][]) {
        int umax = k.length;
        int vmax = k[0].length;
        int sum = 0;

        for (int x = 0; x < umax; x++)
            for (int y = 0; y < vmax; y++)
                if (k[x][y] != 0) sum++;
        return sum;
    }

    public short getMax(short a[]) {
        short max = -255;
        for (int i = 0; i < a.length; i++)
            if (a[i] > max)
                max = a[i];
        return max;
    }

    public short getMin(short a[]) {
        short min = 255;
        for (int i = 0; i < a.length; i++)
            if (a[i] < min)
                min = a[i];
        return min;
    }

    public void copyRedToGreenAndBlue() {
        setG(new short[getImageWidth()][getImageHeight()]);
        setB(new short[getImageWidth()][getImageHeight()]);
        for (int x = 0; x < getImageWidth(); x++)
            for (int y = 0; y < getImageHeight(); y++) {
                getG()[x][y] = getR()[x][y];
                getB()[x][y] = getR()[x][y];
            }
    }

    public void medianSquare3x3() {
        short k[][] = {
            {1, 1, 1},
            {1, 1, 1},
            {1, 1, 1}
        };
        median(k);
    }

    public void medianSquare5x5() {
        short k[][] = {
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1}};
        median(k);
    }

    public void medianOctagon5x5() {
        short k[][] = {
            {0, 1, 1, 1, 0},
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1},
            {0, 1, 1, 1, 0}};
        median(k);
    }

    public void medianDiamond7x7() {
        short k[][] = {
            {0, 0, 0, 1, 0, 0, 0},
            {0, 0, 1, 1, 1, 0, 0},
            {0, 1, 1, 1, 1, 1, 0},
            {1, 1, 1, 1, 1, 1, 1},
            {0, 1, 1, 1, 1, 1, 0},
            {0, 0, 1, 1, 1, 0, 0},
            {0, 0, 0, 1, 0, 0, 0}};
        median(k);
    }

    public void medianCross7x7() {
        short k[][] = {
            {0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0},
            {1, 1, 1, 1, 1, 1, 1},
            {0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0}};
        median(k);
    }


    public static void printMedian(short k[][], String name) {
        //printMaple(k);
        System.out.println(
                "\npublic void " + name + "(){\n"
                + "\tfloat k[][] = {");
        int w = k.length;
        int h = k[0].length;

        for (int y = 0; y < h; y++) {
            System.out.print("\t{");
            for (int x = 0; x < w - 1; x++)
                System.out.print(k[x][y] + ", ");
            String s = k[w - 1][y] + "}";
            if (y < h - 1)
                s = s + ",";
            else
                s = s + "};";
            System.out.println(s);
        }

        String s = "\n\tmedian(k);\n}";
        System.out.println(s);

    }

    public void medianSquare7x7() {
        short k[][] = {
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1},
            {1, 1, 1, 1, 1, 1, 1}};
        median(k);
    }

    public void medianCross3x3() {
        short k[][] = {
            {0, 1, 0},
            {1, 1, 1},
            {0, 1, 0}
        };
        median(k);
    }

    public void median(short k[] []) {
        printMedian(k, "color median");
        Timer t = new Timer();
        t.start();
        setR(median(getR(), k));
        setG(median(getG(), k));
        setB(median(getB(), k));
        t.print("Median filter time");
        short2Image();
    }

    public void medianBottom(
            short f[][], short k[][], short h[][]) {
        int windowLength = 0;
        int window[];
        int uc = 0;
        int vc = 0;
        System.out.println("k=" + k.length);
        uc = k.length / 2;
        vc = k[0].length / 2;
        windowLength = numberOfNonZeros(k);
        window = new int[windowLength];

        //median bottom
        for (int x = 0; x < getImageWidth() - 1; x++)
            for (int y = 0; y < vc; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0)
                            window[loc++] = f[cx(x - u)][cy(y - v)];
                h[x][y] = (short) median(window);
            }
    }

    public void medianLeft(
            short f[][], short k[][], short h[][]) {
        int uc = k.length / 2;
        int vc = k[0].length / 2;

        int windowLength = numberOfNonZeros(k);
        int window[] = new int[windowLength];
        //median bottom
        //median left
        for (int x = 0; x < uc; x++)
            for (int y = vc; y < getImageHeight() - vc; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0)
                            window[loc++] = f[cx(x - u)][cy(y - v)];
                h[x][y] = (short) median(window);
            }
    }

    public void medianRightAndTop(
            short f[][], short k[][], short h[][]) {
        int uc = k.length / 2;
        int vc = k[0].length / 2;

        int windowLength = numberOfNonZeros(k);
        int window[] = new int[windowLength];
        //median right
        for (
                int x = getImageWidth() - uc;
                x < getImageWidth() - 1; x++)
            for (int y = vc; y < getImageHeight() - vc; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0)
                            window[loc++] = f[cx(x - u)][cy(y - v)];
                h[x][y] = (short) median(window);
            }

        //median top
        for (int x = 0; x < getImageWidth() - 1; x++) {
            for (
                    int y = getImageHeight() - vc;
                    y < getImageHeight() - 1; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0)
                            window[loc++] = f[cx(x - u)][cy(y - v)];
                h[x][y] = (short) median(window);
            }
        }
    }

    // median, optimze the edges
    public short[][] median(short f[][], short k[][]) {
        short h[][] = medianNoEdge(f, k);
        medianBottom(f, k, h);
        medianLeft(f, k, h);
        medianRightAndTop(f, k, h);
        return h;
    }

    public short[][] medianNoEdge(short f[][], short k[][]) {
        int uc = k.length / 2;
        int vc = k[0].length / 2;
        short h[][] = new short[getImageWidth()][getImageHeight()];

        int windowLength = numberOfNonZeros(k);
        int window[] = new int[windowLength];

        for (int x = uc; x < getImageWidth() - uc; x++) {
            for (int y = vc; y < getImageHeight() - vc; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0)
                            window[loc++] = f[x - u][y - v];
                h[x][y] = (short) median(window);
                f[x][y] = (short) median(window);
            }

        }
        return h;
    }

    public static short median(Vector v) {
        Sort.sort(v, new Vector(), 0, v.size(), true);
        return
                ((Cshort) v.elementAt(v.size() / 2)).getValue();
    }

    public void testMedian() {
        int a[] = {1, 2, 3, 5, 4, 3, 2, 5, 6, 7};
        System.out.println("The median =" + median(a));
    }

    public static void main(String args[]) {
        Mat.testOutlier();
    }

    public int median(int a[]) {
        int mid = a.length / 2 - 1;
        if (computeOutlier) {
            if (!Mat.outlierHere(a)) {
                return a[mid];
            }
        }
        numberOfOutliers++;
        Mat.quickSort(a);

        if ((a.length & 1) == 1)
            return a[mid];
        return (int) ((a[mid] + a[mid + 1] + 0.5) / 2);
    }
// The sloooww way to do a median filter.
// This one uses fancy design patterns,
// including the template method.
// What are these CS people thinking?
// 18 seconds to do a 128x128 grayscale median
// filter with quicksort using MRJ 2.0
// on a powermac 8100/100.
// Gosh!
// Simple quicksort based median filter
// works in 1/2 second for the same image
// yes...36 times faster!
    public short[][] medianSlow(short f[][], short k[][]) {
        int uc = k.length / 2;
        int vc = k[0].length / 2;
        short h[][] = new short[getImageWidth()][getImageHeight()];
        Cshort cs = new Cshort(0);
        Vector window = new Vector();
        int windowLength = numberOfNonZeros(k);
        for (int i = 0; i < windowLength; i++)
            window.addElement(new Cshort(0));

        for (int x = uc; x < getImageWidth() - uc; x++) {
            for (int y = vc; y < getImageHeight() - vc; y++) {
                int loc = 0;
                for (int v = -vc; v <= vc; v++)
                    for (int u = -uc; u <= uc; u++)
                        if (k[u + uc][v + vc] != 0.0f) {
                            cs = (Cshort) window.elementAt(loc++);
                            cs.setValue((f[x - u][y - v]));
                        }
                h[x][y] = median(window);
            }

        }
        return h;
    }

    static void printMaple(short a[][]) {
        printMaple(Mat.shortToFloat(a));
    }

    public static void printMaple(float a[][]) {
        //linalg[matrix](3,3,[1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9]);
        int w = a.length;
        int h = a[0].length;
        System.out.println("evalf(linalg[matrix](" + w
                + "," + h + ",[");
        for (int i = 0; i < w; i++)
            for (int j = 0; j < h; j++) {
                System.out.print(a[i][j]);
                if (i * j == (w - 1) * (h - 1)) break;
                System.out.print(",");
            }
        System.out.println("]));");
    }

    public SpatialFilterFrame getChild() {
        return child;
    }

    public Menu getSpatialFilterMenu() {
        return SpatialFilterMenu;
    }
}