content: package com.isaac.utils;
  
  import java.util.ArrayList;
  
  import org.opencv.core.Core;
  import org.opencv.core.CvType;
  import org.opencv.core.Mat;
  import org.opencv.core.Scalar;
  import org.opencv.core.Size;
  import org.opencv.imgproc.Imgproc;
  
  public class Filters {
  
  	static {
  		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  	}
  
  	/**
  	 * Guided Image Filter for grayscale image, O(1) time implementation of guided filter
  	 *
  	 * @param I
  	 *            guidance image (should be a gray-scale/single channel image)
  	 * @param p
  	 *            filtering input image (should be a gray-scale/single channel
  	 *            image)
  	 * @param r
  	 *            local window radius
  	 * @param eps
  	 *            regularization parameter
  	 * @return filtered image
  	 */
  	public static Mat GuidedImageFilter(Mat I, Mat p, int r, double eps) {
  		I.convertTo(I, CvType.CV_64FC1);
  		p.convertTo(p, CvType.CV_64FC1);
  		//[hei, wid] = size(I);
  		int rows = I.rows();
  		int cols = I.cols();
  		// N = boxfilter(ones(hei, wid), r); % the size of each local patch; N=(2r+1)^2 except for boundary pixels.
  		Mat N = new Mat();
  		Imgproc.boxFilter(Mat.ones(rows, cols, I.type()), N, -1, new Size(r, r));
  		// mean_I = boxfilter(I, r) ./ N;
  		Mat mean_I = new Mat();
  		Imgproc.boxFilter(I, mean_I, -1, new Size(r, r));
  		// mean_p = boxfilter(p, r) ./ N
  		Mat mean_p = new Mat();
  		Imgproc.boxFilter(p, mean_p, -1, new Size(r, r));
  		// mean_Ip = boxfilter(I.*p, r) ./ N;
  		Mat mean_Ip = new Mat();
  		Imgproc.boxFilter(I.mul(p), mean_Ip, -1, new Size(r, r));
  		// cov_Ip = mean_Ip - mean_I .* mean_p; % this is the covariance of (I, p) in each local patch.
  		Mat cov_Ip = new Mat();
  		Core.subtract(mean_Ip, mean_I.mul(mean_p), cov_Ip);
  		// mean_II = boxfilter(I.*I, r) ./ N;
  		Mat mean_II = new Mat();
  		Imgproc.boxFilter(I.mul(I), mean_II, -1, new Size(r, r));
  		// var_I = mean_II - mean_I .* mean_I;
  		Mat var_I = new Mat();
  		Core.subtract(mean_II, mean_I.mul(mean_I), var_I);
  		// a = cov_Ip ./ (var_I + eps); % Eqn. (5) in the paper;
  		Mat a = new Mat();
  		Core.add(var_I, new Scalar(eps), a);
  		Core.divide(cov_Ip, a, a);
  		//b = mean_p - a .* mean_I; % Eqn. (6) in the paper;
  		Mat b = new Mat();
  		Core.subtract(mean_p, a.mul(mean_I), b);
  		// mean_a = boxfilter(a, r) ./ N;
  		Mat mean_a = new Mat();
  		Imgproc.boxFilter(a, mean_a, -1, new Size(r, r));
  		Core.divide(mean_a, N, mean_a);
  		// mean_b = boxfilter(b, r) ./ N;
  		Mat mean_b = new Mat();
  		Imgproc.boxFilter(b, mean_b, -1, new Size(r, r));
  		Core.divide(mean_b, N, mean_b);
  		// q = mean_a .* I + mean_b; % Eqn. (8) in the paper;
  		Mat q = new Mat();
  		Core.add(mean_a.mul(I), mean_b, q);
  		//for (int i = 0; i < rows; i++) {
  		//	for (int j = 0; j < cols; j++) {
  		//		if (q.get(i, j)[0] <= 0)
  		//			q.put(i, j, 1.0 / 255);
  		//	}
  		//}
  		q.convertTo(q, CvType.CV_32F);
  		return q;
  	}
  	
  	/**
  	 * Guided Image Filter for Color Image
  	 * @param origI guidance image (should be a gray-scale/single channel image)
  	 * @param p filtering input image (should be a gray-scale/single channel image)
  	 * @param r local window radius
  	 * @param eps regularization parameter
  	 * @param s blocks number, s * s
  	 * @param depth image depth
  	 * @return filtered image
  	 */
  	public static Mat GuidedImageFilter_Color(Mat origI, Mat p, int r, double eps, double s, int depth) {
  		/**
  		 * Pre-defined parameters
  		 */
  		ArrayList<Mat> Ichannels = new ArrayList<Mat>();
  		ArrayList<Mat> Isubchannels = new ArrayList<Mat>();
  		int Idepth;
  		double r_sub;
  		Mat mean_I_r = new Mat();
  		Mat mean_I_g = new Mat();
  		Mat mean_I_b = new Mat();
  		Mat invrr = new Mat();
  		Mat invrg = new Mat();
  		Mat invrb = new Mat();
  		Mat invgg = new Mat();
  		Mat invgb = new Mat();
  		Mat invbb = new Mat();
  		// Process
  		Mat I;
  		if (origI.depth() == CvType.CV_32F || origI.depth() == CvType.CV_64F) {
  			I = origI.clone();
  		} else {
  			I = convertTo(origI, CvType.CV_32F);
  		}
  		Idepth = I.depth();
  		Core.split(I, Ichannels);
  		Mat I_sub = new Mat();
  		Imgproc.resize(I, I_sub, new Size(I.cols() / s, I.rows() / s), 0.0, 0.0, Imgproc.INTER_NEAREST);
  		Core.split(I_sub, Isubchannels);
  		r_sub = r / s;
  		mean_I_r = boxfilter(Isubchannels.get(0), (int) r_sub);
  		mean_I_g = boxfilter(Isubchannels.get(1), (int) r_sub);
  		mean_I_b = boxfilter(Isubchannels.get(2), (int) r_sub);
  
  		// variance of I in each local patch: the matrix Sigma in Eqn (14).
  		// Note the variance in each local patch is a 3x3 symmetric matrix:
  		//           rr, rg, rb
  		//   Sigma = rg, gg, gb
  		//           rb, gb, bb    	
  		Mat var_I_rr = new Mat();
  		Mat var_I_rg = new Mat();
  		Mat var_I_rb = new Mat();
  		Mat var_I_gg = new Mat();
  		Mat var_I_gb = new Mat();
  		Mat var_I_bb = new Mat();
  		Mat temp1 = new Mat();
  
  		Core.subtract(boxfilter(Isubchannels.get(0).mul(Isubchannels.get(0)), (int) r_sub), 
  				mean_I_r.mul(mean_I_r), temp1);
  		Core.add(temp1, new Scalar(eps), var_I_rr);
  		Core.subtract(boxfilter(Isubchannels.get(0).mul(Isubchannels.get(1)), (int) r_sub), 
  				mean_I_r.mul(mean_I_g), var_I_rg);
  		Core.subtract(boxfilter(Isubchannels.get(0).mul(Isubchannels.get(2)), (int) r_sub), 
  				mean_I_r.mul(mean_I_b), var_I_rb);
  		Core.subtract(boxfilter(Isubchannels.get(1).mul(Isubchannels.get(1)), (int) r_sub), 
  				mean_I_g.mul(mean_I_g), temp1);
  		Core.add(temp1, new Scalar(eps), var_I_gg);
  		Core.subtract(boxfilter(Isubchannels.get(1).mul(Isubchannels.get(2)), (int) r_sub), 
  				mean_I_g.mul(mean_I_b), var_I_gb);
  		Core.subtract(boxfilter(Isubchannels.get(2).mul(Isubchannels.get(2)), (int) r_sub), 
  				mean_I_b.mul(mean_I_b), temp1);
  		Core.add(temp1, new Scalar(eps), var_I_bb);
  
  		// Inverse of Sigma + eps * I
  		Core.subtract(var_I_gg.mul(var_I_bb), var_I_gb.mul(var_I_gb), invrr);
  		Core.subtract(var_I_gb.mul(var_I_rb), var_I_rg.mul(var_I_bb), invrg);
  		Core.subtract(var_I_rg.mul(var_I_gb), var_I_gg.mul(var_I_rb), invrb);
  		Core.subtract(var_I_rr.mul(var_I_bb), var_I_rb.mul(var_I_rb), invgg);
  		Core.subtract(var_I_rb.mul(var_I_rg), var_I_rr.mul(var_I_gb), invgb);
  		Core.subtract(var_I_rr.mul(var_I_gg), var_I_rg.mul(var_I_rg), invbb);
  
  		Mat covDet = new Mat();
  		Core.add(invrr.mul(var_I_rr), invrg.mul(var_I_rg), temp1);
  		Core.add(temp1, invrb.mul(var_I_rb), covDet);
  
  		Core.divide(invrr, covDet, invrr);
  		Core.divide(invrg, covDet, invrg);
  		Core.divide(invrb, covDet, invrb);
  		Core.divide(invgg, covDet, invgg);
  		Core.divide(invgb, covDet, invgb);
  		Core.divide(invbb, covDet, invbb);
  
  		Mat p2 = convertTo(p, Idepth);
  		Mat result = new Mat();
  		if (p.channels() == 1) {
  			result = filterSingleChannel(p2, s, Isubchannels, Ichannels, mean_I_r, mean_I_g, mean_I_b, invrr, invrg, 
  					invrb, invgg, invgb, invbb, r_sub);
  		} else {
  			ArrayList<Mat> pc = new ArrayList<Mat>();
  			Core.split(p2, pc);
  			for (int i = 0; i < pc.size(); i++) {
  				pc.set(i, filterSingleChannel(pc.get(i), s, Isubchannels, Ichannels, mean_I_r, mean_I_g, mean_I_b, invrr, 
  						invrg, invrb, invgg, invgb, invbb, r_sub));
  			}
  			Core.merge(pc, result);
  		}
  		return convertTo(result, depth == -1 ? p.depth() : depth);
  	}
  	
  	private static Mat boxfilter(Mat I, int r) {
  		Mat result = new Mat();
  		Imgproc.blur(I, result, new Size(r, r));
  		return result;
  	}
  
  	private static Mat convertTo(Mat mat, int depth) {
  		if (mat.depth() == depth) {
  			return mat;
  		}
  		Mat result = new Mat();
  		mat.convertTo(result, depth);
  		return result;
  	}
  
  	private static Mat filterSingleChannel(Mat p, double s, ArrayList<Mat> Isubchannels, ArrayList<Mat> Ichannels, 
  			Mat mean_I_r, Mat mean_I_g, Mat mean_I_b, Mat invrr, Mat invrg, Mat invrb, Mat invgg, Mat invgb, 
  			Mat invbb, double r_sub) {
  		Mat p_sub = new Mat();
  		Imgproc.resize(p, p_sub, new Size(p.cols() / s, p.rows() / s), 0.0, 0.0, Imgproc.INTER_NEAREST);
  
  		Mat mean_p = boxfilter(p_sub, (int) r_sub);
  
  		Mat mean_Ip_r = boxfilter(Isubchannels.get(0).mul(p_sub), (int) r_sub);
  		Mat mean_Ip_g = boxfilter(Isubchannels.get(1).mul(p_sub), (int) r_sub);
  		Mat mean_Ip_b = boxfilter(Isubchannels.get(2).mul(p_sub), (int) r_sub);
  
  		// convariance of (I, p) in each local patch
  		Mat cov_Ip_r = new Mat();
  		Mat cov_Ip_g = new Mat();
  		Mat cov_Ip_b = new Mat();
  		Core.subtract(mean_Ip_r, mean_I_r.mul(mean_p), cov_Ip_r);
  		Core.subtract(mean_Ip_g, mean_I_g.mul(mean_p), cov_Ip_g);
  		Core.subtract(mean_Ip_b, mean_I_b.mul(mean_p), cov_Ip_b);
  
  		Mat temp1 = new Mat();
  		Mat a_r = new Mat();
  		Mat a_g = new Mat();
  		Mat a_b = new Mat();
  		Core.add(invrr.mul(cov_Ip_r), invrg.mul(cov_Ip_g), temp1);
  		Core.add(temp1, invrb.mul(cov_Ip_b), a_r);
  		Core.add(invrg.mul(cov_Ip_r), invgg.mul(cov_Ip_g), temp1);
  		Core.add(temp1, invgb.mul(cov_Ip_b), a_g);
  		Core.add(invrb.mul(cov_Ip_r), invgb.mul(cov_Ip_g), temp1);
  		Core.add(temp1, invbb.mul(cov_Ip_b), a_b);
  
  		Mat b = new Mat();
  		Core.subtract(mean_p, a_r.mul(mean_I_r), b);
  		Core.subtract(b, a_g.mul(mean_I_g), b);
  		Core.subtract(b, a_b.mul(mean_I_b), b);
  
  		Mat mean_a_r = boxfilter(a_r, (int) r_sub);
  		Mat mean_a_g = boxfilter(a_g, (int) r_sub);
  		Mat mean_a_b = boxfilter(a_b, (int) r_sub);
  		Mat mean_b = boxfilter(b, (int) r_sub);
  
  		Imgproc.resize(mean_a_r, mean_a_r, 
  				new Size(Ichannels.get(0).cols(), Ichannels.get(0).rows()), 0.0, 0.0, Imgproc.INTER_LINEAR);
  		Imgproc.resize(mean_a_g, mean_a_g, 
  				new Size(Ichannels.get(0).cols(), Ichannels.get(0).rows()), 0.0, 0.0, Imgproc.INTER_LINEAR);
  		Imgproc.resize(mean_a_b, mean_a_b, 
  				new Size(Ichannels.get(0).cols(), Ichannels.get(0).rows()), 0.0, 0.0, Imgproc.INTER_LINEAR);
  		Imgproc.resize(mean_b, mean_b, 
  				new Size(Ichannels.get(0).cols(), Ichannels.get(0).rows()), 0.0, 0.0, Imgproc.INTER_LINEAR);
  
  		Mat result = new Mat();
  		Core.add(mean_a_r.mul(Ichannels.get(0)), mean_a_g.mul(Ichannels.get(1)), temp1);
  		Core.add(temp1, mean_a_b.mul(Ichannels.get(2)), temp1);
  		Core.add(temp1, mean_b, result);
  		return result;
  	}
  
  }
  