 /*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * Copyright 2010 Gdansk University of Technology,
 * Dept. of Biomedical Engineering, Gdansk, Poland
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are not permitted without written permission from the copyright
 * holders.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.cemte.jacek.mpo;

import java.util.Arrays;



public class Morph{


	public int [][] kernel={{1,1,1},
						 	{1,1,1},
				 			{1,1,1}};

	public Morph(int [][] kernel){
		this.kernel=kernel;
	}

	public Morph(){
	}
	
	public void ustawKernel(int [][] kernel){
		this.kernel=kernel;
	}
	
	/** metody splot_wsp i splot_wsp_roll okrelaj¹ zakres wartoci w procesie
	*	splotu wynikaj¹cy z ustalonych warunków brzegowych
	*/

	public int splot_wsp(int x,int zakres){
		if (x > (zakres-1))
			return (zakres-(x-(zakres-1)) );
		if (x < 0){
			return (Math.abs(x)-1 );
		}
		return x;
	}//koniec public int splot_wsp(int x,int zakres)
	
	/**
	*
	*/

	public int splot_wsp_roll(int x,int zakres){
		if (x > (zakres-1))
			return (x-zakres);
		if (x < 0){
			return (x+zakres);
		}
		return x;
	}//koniec public int splot_wsp(int x,int zakres)


	/**
	*
	*/
	public int[][] morph(int typ, int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){

		int ik=(K-1)/2;
		int jl=(L-1)/2;
		double tmp=0.0;
		int[][] oo;
		int macLW=Kx-Sx;
		int macLK=Ky-Sy;
		oo = new int[(macLW)][(macLK)];
		int Mmin=100000, MinTmp=Integer.MAX_VALUE, Mmax=-10000,MaxTmp=Integer.MIN_VALUE,wart=0;
	
		/*
		int Sum = 0;
		for(int k=0;k<K;k++){
			for(int l=0; l<L; l++){
				Sum+=kernel[k][l];
			}
		}
		if(Sum==0) Sum=1;
		komunikat(noweOknoKomunikatow, "Obliczylem sume:"+Sum);
		*/

		for(int n=Sx; n< Kx; n++){
			for(int m=Sy; m<Ky; m++){
				for(int k=0;k<K;k++){
					for(int l=0; l<L; l++){
						if (typ==0){	//0 erozja
							if (typKernel==0){
								if (kernel[k][l]==1)
									MinTmp=io[splot_wsp(n-ik+k,macLW)][splot_wsp(m-jl+l,macLK)];
							}else
								MinTmp=io[splot_wsp(n-ik+k,macLW)][splot_wsp(m-jl+l,macLK)]-kernel[k][l];
							if (MinTmp < Mmin) Mmin=MinTmp;
							wart=Mmin;
						} else
							if(typ==1){  //1 dylacja
								if (typKernel==0){
									if (kernel[k][l]==1)
										MaxTmp=io[splot_wsp(n-ik+k,macLW)][splot_wsp(m-jl+l,macLK)];
								}else
									MaxTmp=io[splot_wsp(n-ik+k,macLW)][splot_wsp(m-jl+l,macLK)]+kernel[k][l];
								if (MaxTmp > Mmax) Mmax=MaxTmp;
								wart=Mmax;
							}
					}
				}
				oo[n][m]=Math.abs(wart);
				Mmin=100000;
				Mmax=-10000;
				wart=0;
			}
		}
		return oo;
	}//koniec public int[][] morph(int typ,int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] erosion(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		return morph(0, Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
	}//koniec public int[][] erosion(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] dilation(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		return morph(1, Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
	}//koniec public int[][] dilation(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] opening(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		return dilation(Sx, Sy, Kx, Ky,(erosion(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel)), kernel, K, L, typKernel);
	}//koniec public int[][] opening(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)
	
	/**
	*
	*/
	public int[][] closing(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		return erosion(Sx, Sy, Kx, Ky,(dilation(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel)), kernel, K, L,typKernel);
	}//koniec public int[][] closing(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] gradient(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		int tmp_e[][]=new int [Kx-Sx][Ky-Sy];
		int tmp_d[][]=new int [Kx-Sx][Ky-Sy];
		tmp_e=erosion(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
		tmp_d=dilation(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
		for (int i=0; i< tmp_e.length; i++){
			for (int j=0; j< tmp_e[0].length; j++){
				io[i][j]=(tmp_d[i][j]-tmp_e[i][j]);
			}
		}
		return io;
	}//koniec public int[][] gradient(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] smothing(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		return (closing(Sx, Sy, Kx, Ky,opening(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel), kernel, K, L,typKernel));
	}//koniec public int[][] smothing(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] laplacian(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		int tmp_e[][]=new int [Kx-Sx][Ky-Sy];
		int tmp_d[][]=new int [Kx-Sx][Ky-Sy];
		tmp_e=erosion(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
		tmp_d=dilation(Sx, Sy, Kx, Ky,io, kernel, K, L,  typKernel);
		for (int i=0; i< tmp_e.length; i++){
			for (int j=0; j< tmp_e[0].length; j++){
				io[i][j]=(tmp_d[i][j]+tmp_e[i][j]-2*io[i][j]);
			}
		}
		return io;
	}//koniec public int[][] laplacian(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] tophatL(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		int tmp[][]=new int [Kx-Sx][Ky-Sy];
		tmp=opening(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
		for (int i=0; i< tmp.length; i++){
			for (int j=0; j< tmp[0].length; j++){
				io[i][j]=io[i][j]-tmp[i][j];
			}
		}
		return io;
	}//koniec public int[][] tophatL(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int[][] tophatD(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel){
		int tmp[][]=new int [Kx-Sx][Ky-Sy];
		tmp=closing(Sx, Sy, Kx, Ky,io, kernel, K, L, typKernel);
		for (int i=0; i< tmp.length; i++){
			for (int j=0; j< tmp[0].length; j++){
				io[i][j]=tmp[i][j]-io[i][j];
			}
		}
		return io;
	}//koniec public int[][] tophatL(int Sx, int Sy,int Kx, int Ky,int[][] io, int[][] kernel, int K, int L, int typKernel)

	/**
	*
	*/
	public int [][] morphObrazu(int typ, int [][] mac){
		int K=kernel.length;
		int L=kernel[0].length;
		int N = mac.length;
		int M = mac[0].length;

		//int m1[][]= new int[N][M];

		// typ operacji morfologicznej domyslnie 0 - erozja
		switch(typ){
			case 1 : return (dilation(0,0,N,M,mac,kernel,K,L,1));
			case 2 : return (opening(0,0,N,M,mac,kernel,K,L,1));
			case 3 : return (closing(0,0,N,M,mac,kernel,K,L,1));
			case 4 : return (gradient(0,0,N,M,mac,kernel,K,L,1));
			case 5 : return (smothing(0,0,N,M,mac,kernel,K,L,1));
			case 6 : return (laplacian(0,0,N,M,mac,kernel,K,L,1));
			case 7 : return (tophatL(0,0,N,M,mac,kernel,K,L,1));
			case 8 : return (tophatD(0,0,N,M,mac,kernel,K,L,1));
			case 9 : return (erosion(0,0,N,M,mac,kernel,K,L,0));
			case 10 : return (dilation(0,0,N,M,mac,kernel,K,L,0));
			case 11 : return (opening(0,0,N,M,mac,kernel,K,L,0));
			case 12 : return (closing(0,0,N,M,mac,kernel,K,L,0));
			case 13 : return (gradient(0,0,N,M,mac,kernel,K,L,0));
			case 14 : return (smothing(0,0,N,M,mac,kernel,K,L,0));
			case 15 : return (laplacian(0,0,N,M,mac,kernel,K,L,0));
			case 16 : return (tophatL(0,0,N,M,mac,kernel,K,L,0));
			case 17 : return (tophatD(0,0,N,M,mac,kernel,K,L,0));

			default: return (erosion(0,0,N,M,mac,kernel,K,L,1));
		}
		//return m1;	
	}//koniec public void morphObrazu(int typ, int rodzajKernela)
	
	
	/**
	*
	*/
	public float [][] morphObrazuFloat(int typ, int [][] mac){
		int K=kernel.length;
		int L=kernel[0].length;
		int N = mac.length;
		int M = mac[0].length;

		int m1[][]= new int[N][M];

		// typ operacji morfologicznej domyslnie 0 - erozja
		switch(typ){
			case 1 : m1=dilation(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 2 : m1=opening(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 3 : m1=closing(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 4 : m1=gradient(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 5 : m1=smothing(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 6 : m1=laplacian(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 7 : m1=tophatL(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 8 : m1=tophatD(0,0,N,M,mac,kernel,K,L,1);
				break;
			case 9 : m1=erosion(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 10 : m1=dilation(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 11 : m1=opening(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 12 : m1=closing(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 13 : m1=gradient(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 14 : m1=smothing(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 15 : m1=laplacian(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 16 : m1=tophatL(0,0,N,M,mac,kernel,K,L,0);
				break;
			case 17 : m1=tophatD(0,0,N,M,mac,kernel,K,L,0);
				break;

			default: m1=erosion(0,0,N,M,mac,kernel,K,L,1);
		}
		float [][] mm=new float[m1.length][m1[0].length];
		for (int i=0; i<m1.length; i++)
			for (int j=0; j<m1[0].length; j++)
				mm[i][j]=(float)m1[i][j];
		return mm;	
	}//koniec public void morphObrazu(int typ, int rodzajKernela)
	
        public static void main(String []a){
            int [][] data= {{0,0,0,0,0},
                            {0,1,1,1,0},
                            {0,1,1,1,0},
                            {0,1,1,1,0},
                            {0,0,0,0,0}
            };
            int [][]out;
            
            System.out.println("DANE:");
            for(int i=0; i<data[0].length; i++)
                System.out.println( ""+Arrays.toString(data[i]));
            
            Morph m = new Morph();
            
            out=m.morphObrazu(10, data);
            
            System.out.println("WYNIK:");
            for(int i=0; i<data[0].length; i++)
                System.out.println( ""+Arrays.toString(out[i]));
            
        }
	
}// koniec class Morpho
