package com.pingpang.image;

import java.awt.AlphaComposite;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.DMatch;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.MatOfRect;
import org.opencv.core.MatOfRect2d;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Rect2d;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.core.TermCriteria;
import org.opencv.dnn.Dnn;
import org.opencv.dnn.Net;
import org.opencv.features2d.FastFeatureDetector;
import org.opencv.features2d.Feature2D;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.FlannBasedMatcher;
import org.opencv.features2d.SIFT;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.utils.Converters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import com.pingpang.util.StringUtil;

import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;


@Component
public class ImageUtil implements CommandLineRunner {
	private static Logger logger = LoggerFactory.getLogger(ImageUtil.class);

	@Value("${opencv.file.path}")
	private String PATH;
			
	@Value("${opencv.save.path}")
	private String SCAN_PATH;
	
	//人脸检测
	private static final String FACEPATH="haarcascade_frontalface_alt2.xml";
	private static final String PROFILEFACEPATH="haarcascade_profileface.xml";
	private static CascadeClassifier FACEBOOK;
	private static CascadeClassifier PROFILEFACEFACEBOOK;
	
	//年龄识别
	private static final String AGE_MODEL = "age_net.caffemodel";
	private static final String AGE_TEXT = "deploy_age2.prototxt";
	private static final String[] AGES = new String[]{"0-2", "4-6", "8-13", "15-20", "25-32", "38-43", "48-53", "60+"};
	private static Net AGENET;
	
	//性别识别
	private static final String GENDER_MODEL = "gender_net.caffemodel";
	private static final String GENDER_TEXT = "deploy_gender2.prototxt";
	private static final String[] GENDERS = new String[]{"男", "女"};;
	private static Net GENDER;
	
	
//	static {
//		try {
//			//PATH=ResourceUtils.getFile("classpath:opencv").getAbsolutePath()+File.separator;
//			//ResourceUtils.
//			InputStream inputStream = ImageUtil.class.getResourceAsStream("/opencv/haarcascade_frontalface_alt2.xml");
//			File docxFile = new File(FACEPATH);
//	        // 使用common-io的工具类即可转换
//	        FileUtils.copyInputStreamToFile(inputStream,docxFile);
//	        inputStream.close();
//	        
//		} catch (FileNotFoundException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
	
	//文件后缀
	private static final List<String> FILETYPELIST = new ArrayList<String>(Arrays.asList( 
            ".jpg", ".bmp", ".jpeg", ".png", ".gif",
            ".JPG", ".BMP", ".JPEG", ".PNG", ".GIF"));
	
	//目标检测
	private static Map<String,List<Mat>> srcMap=new HashMap<String,List<Mat>>();
	
	/**
	 * 读取文件
	 * @param imagePath
	 * @return
	 * @throws IOException
	 */
	public static Mat imagePath2Mat(String imagePath) throws IOException {
		// 注释部分模拟传入base64数据
		BufferedImage image = ImageIO.read(new FileInputStream(imagePath));
		Mat matImage = ImageUtil.BufImg2Mat(image, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);// CvType.CV_8UC3
		return matImage;
	}
	/**
	 * base64转Mat
	 * @param base64
	 * @return
	 * @throws Exception 
	 */
	public static Mat base642Mat(String base64) throws Exception {
		// 对base64进行解码
		//BASE64Decoder decoder = new BASE64Decoder();
		//byte[] origin = decoder.decodeBuffer(base64);
		byte[] origin=decryptBASE64(base64);
		InputStream in = new ByteArrayInputStream(origin); // 将b作为输入流；
		BufferedImage image = ImageIO.read(in);
		Mat matImage = ImageUtil.BufImg2Mat(image, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);// CvType.CV_8UC3
		return matImage;
	}
	/**
	 * base64转Rect
	 * @param base64
	 * @throws Exception 
	 */
	public static Rect base642Rect(String base64) throws Exception {
		//BASE64Decoder decoder = new BASE64Decoder();
		//byte[] origin = decoder.decodeBuffer(base64);
		byte[] origin=decryptBASE64(base64);
		InputStream in = new ByteArrayInputStream(origin); // 将b作为输入流；
		BufferedImage image = ImageIO.read(in);
		return new Rect(0,0,image.getWidth(),image.getHeight());
	}
	
	/**
	 * BufferedImage转rect
	 * @param base64
	 * @throws IOException 
	 */
	public static Rect BufferedImage2Rect(BufferedImage image) throws IOException {
		return new Rect(0,0,image.getWidth(),image.getHeight());
	}
	/**
	 * base64转BufferedImage
	 * @param base64
	 * @throws Exception 
	 */
	public static BufferedImage base642BufferedImage(String base64) throws Exception {
		//BASE64Decoder decoder = new BASE64Decoder();
		//byte[] origin = decoder.decodeBuffer(base64);
		byte[] origin=decryptBASE64(base64);
		InputStream in = new ByteArrayInputStream(origin); // 将b作为输入流；
		return ImageIO.read(in);
	}

	
    /**
     * BASE64Encoder 编码
     * 
     * @param data
     *            要加密的数据
     * @return 加密后的字符串
     */
    public static String encryptBASE64(byte[] data) {
        // BASE64Encoder encoder = new BASE64Encoder();
        // String encode = encoder.encode(data);
        // 从JKD 9开始rt.jar包已废除，从JDK 1.8开始使用java.util.Base64.Encoder
        String encode = Base64.getMimeEncoder().encodeToString(data);
        return encode;
    }
    /**
     * BASE64Decoder 解密
     * 
     * @param data
     *            要解密的字符串
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static byte[] decryptBASE64(String data) throws Exception {
        // BASE64Decoder decoder = new BASE64Decoder();
        // byte[] buffer = decoder.decodeBuffer(data);
        // 从JKD 9开始rt.jar包已废除，从JDK 1.8开始使用java.util.Base64.Decoder
    	if(null!=data && data.indexOf(",")>-1) {
    		data=data.substring(data.indexOf(","));
    	}
        byte[] buffer = Base64.getMimeDecoder().decode(data);
        return buffer;
    }

	
	/**
	 * BufferedImage转换成Mat
	 * 
	 * @param original 要转换的BufferedImage
	 * @param imgType  bufferedImage的类型 如 BufferedImage.TYPE_3BYTE_BGR
	 * @param matType  转换成mat的type 如 CvType.CV_8UC3
	 */
	public static Mat BufImg2Mat(BufferedImage original, int imgType, int matType) {
		if (original == null) {
			throw new IllegalArgumentException("original == null");
		}
		//System.loadLibrary("opencv_java412");
		//System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		//System.load("E:\\opencv\\opencv\\build\\java\\x64\\opencv_java412.dll");
		//logger.info(Core.NATIVE_LIBRARY_NAME);
		// Don't convert if it already has correct type
		if (original.getType() != imgType){
			// Create a buffered image
			BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);

			// Draw the image onto the new buffer
			Graphics2D g = image.createGraphics();
			try {
				g.setComposite(AlphaComposite.Src);
				g.drawImage(original, 0, 0, null);
			} finally {
				g.dispose();
			}
		}

		byte[] pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();
		Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);
		mat.put(0, 0, pixels);
		return mat;
	}

	/**
	 * Mat转换成BufferedImage
	 * 
	 * @param matrix        要转换的Mat
	 * @param fileExtension 格式为 ".jpg", ".png", etc
	 * @return
	 */
	public static BufferedImage Mat2BufImg(Mat matrix, String fileExtension) {
		// convert the matrix into a matrix of bytes appropriate for
		// this file extension
		MatOfByte mob = new MatOfByte();
		Imgcodecs.imencode(fileExtension, matrix, mob);
		// convert the "matrix of bytes" into a byte array
		byte[] byteArray = mob.toArray();
		BufferedImage bufImage = null;
		try {
			InputStream in = new ByteArrayInputStream(byteArray);
			bufImage = ImageIO.read(in);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bufImage;
	}

	/**
	 * Mat转换成BufferedImage
	 * 
	 * @param matrix        要转换的Mat
	 * @param fileExtension 格式为 ".jpg", ".png", etc
	 * @return
	 */
	public static String Mat2Base64(Mat matrix, String fileExtension) {
		// convert the matrix into a matrix of bytes appropriate for
		// this file extension
		MatOfByte mob = new MatOfByte();
		Imgcodecs.imencode(fileExtension, matrix, mob);
		// convert the "matrix of bytes" into a byte array
		byte[] byteArray = mob.toArray();
		BufferedImage bufImage = null;
		String base64="";
		try {
			InputStream in = new ByteArrayInputStream(byteArray);
			bufImage = ImageIO.read(in);
			//输出流
			ByteArrayOutputStream stream = new ByteArrayOutputStream();
			ImageIO.write(bufImage, "png", stream);
			base64 = encryptBASE64(stream.toByteArray());
			//logger.info(base64);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return base64;
	}
	
	/**
	 * Mat转换保存成Image
	 * 
	 * @param matrix        要转换的Mat
	 * @param fileExtension 格式为 ".jpg", ".png", etc
	 * 
	 * @return
	 */
	public static void Mat2Img(Mat matrix, String fileExtension, String pathAndName) {
		// convert the matrix into a matrix of bytes appropriate for
		// this file extension
		MatOfByte mob = new MatOfByte();
		Imgcodecs.imencode(fileExtension, matrix, mob);
		// convert the "matrix of bytes" into a byte array
		byte[] byteArray = mob.toArray();

		BufferedImage bufImage = null;
		try {
			InputStream in = new ByteArrayInputStream(byteArray);
			bufImage = ImageIO.read(in);
			writeImageFile(bufImage, pathAndName);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将bufferdimage转换为图片
	 * 
	 * @param bi
	 * @param pathAndName
	 * @throws IOException
	 */
	public static void writeImageFile(BufferedImage bi, String pathAndName) throws IOException {
		File outputfile = new File(pathAndName);
		ImageIO.write(bi, "jpg", outputfile);
	}

	/**
	 *Scalar(255, 0, 0)  ----表示纯蓝色
     *Scalar(0, 255, 0) ----表示纯绿色
     *Scalar(0, 0, 255) ----表示纯红色
     *Scalar(255, 255, 0) ----表示青色
     *Scalar(0, 255, 255) ----表示黄色
	 * @param image
	 * @return
	 * @throws IOException 
	 */
	public static Mat getFace(Mat image) throws IOException {
		
		//正脸蓝色
		Map<String,Object> resultMap=ImageUtil.getFace(FACEBOOK, image,new Scalar(0,255,255),false);
		if(null==resultMap||resultMap.isEmpty()) {
		  return null;
		}
		if(!(boolean)resultMap.get("check")) {
			//右侧脸青色
			resultMap=ImageUtil.getFace(PROFILEFACEFACEBOOK, image,new Scalar(255, 255, 0),false);
			if(!(boolean)resultMap.get("check")) {
				//左侧脸蓝色
				resultMap=ImageUtil.getFace(PROFILEFACEFACEBOOK, image,new Scalar(255, 0, 0),true);
			}
		}
		
		if((boolean) resultMap.get("check")) {
			//ImageUtil.Mat2Img(image, ".jpeg", "E:\\opencv4.4.0\\age-and-gender-classification\\img\\test_me\\" + UUID.randomUUID()+".jpeg");		
		}
		
		return (Mat) resultMap.get("Mat");
	}

	/**
	 * 
	 * @param cc 识别类
	 * @param image 图片
	 * @param sc 颜色
	 * @param flip 是否反转
	 * @return
	 * @throws IOException 
	 */
	private static Map<String,Object> getFace(CascadeClassifier cc,Mat image,Scalar sc,boolean flip) throws IOException {
		if(image.empty()) {
			return null;
		}
        Map<String,Object> resultMap=new HashMap<String,Object>();
        Mat imageCloneCheck=image.clone();
        Mat imageClone=new Mat();
        Imgproc.cvtColor(image, imageClone, Imgproc.COLOR_BGR2GRAY);
        
		MatOfRect face = new MatOfRect();
		
		if(flip) {
			Core.flip(imageClone, imageClone, 1);
			Core.flip(imageCloneCheck, imageCloneCheck, 1);
		}
		cc.detectMultiScale(imageClone, face);
		
		Rect[] rects = face.toArray();
		logger.info("匹配到 " + rects.length + " 个人脸");
		
		String name="";
		Mat checkMat=null;
		if (rects.length > 0) {
			Map<String,Object> isCheck = ImageUtil.compareHist(image);
			if ("1".equals(isCheck.get("statu"))) {
				name=(String) isCheck.get("name");
				checkMat=(Mat) isCheck.get("image");
				logger.info("-----------------------");
				logger.info("----------"+name+"-------------");
				logger.info("-----------------------");
			}
		}
		
		// 4 为每张识别到的人脸画一个圈
		for (int i = 0; i < rects.length; i++) {
			
			//Mat areaM = new Mat(image, rects[i]);
			//ImageUtil.Mat2Img(areaM, ".jpeg", "E:\\opencv4.4.0\\age-and-gender-classification\\img\\huge\\"+UUID.randomUUID()+".jpeg");
			
			Imgproc.rectangle(imageCloneCheck, new Point(rects[i].x, rects[i].y),
					new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), sc);
			
			String age=analyseAge(imageCloneCheck,rects[i]);
			String gender=analyseGender(imageCloneCheck,rects[i]);
		    
			if(flip) {
				Core.flip(imageCloneCheck, imageCloneCheck, 1);
			}	
			
			//
			Font font = new Font("微软雅黑", Font.PLAIN, 12); 
			BufferedImage bufImg =Mat2BufImg(imageCloneCheck,".png");
			Graphics2D g = bufImg.createGraphics();
            g.drawImage(bufImg, 0, 0, bufImg.getWidth(),bufImg.getHeight(), null);
            g.setFont(font);              //设置字体
            
            //设置水印的坐标
            g.drawString("性别:"+gender+" 年龄:"+age+" 名称:"+(!"".equals(name)?name:" 未识别"), rects[i].x, rects[i].y);
	        g.dispose();
	        
	        imageCloneCheck=ImageUtil.BufImg2Mat(bufImg, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);// CvType.CV_8UC3
            
			/*
			 * Imgproc.putText(image, new String(("性别:" + gender + "年龄:" +
			 * age).getBytes("UTF-8")), new Point(rects[i].x, rects[i].y),
			 * Imgproc.FONT_HERSHEY_PLAIN, 0.8, sc, 1, Imgproc.LINE_AA, false);
			 */
			 
		}
		
		/*
		if(flip) {
			Core.flip(imageCloneCheck, imageCloneCheck, 1);
		}*/
		
		  if(null!=checkMat) {
			    Mat dstImage = Mat.ones(60,60,CvType.CV_8UC3);
		        // 会根据输出图像 dstImage的大小，自动计算出 fx，fy，进行缩放
		        Imgproc.resize(checkMat,dstImage,dstImage.size());
		        Rect rect = new Rect(5,5, dstImage.cols(),dstImage.rows());//x,y坐标，宽，高
		        Mat src_roi = new Mat(imageCloneCheck, rect);
		        //Core.addWeighted(src_roi, 0.5, dstImage, 0.5, 0, src_roi);
		        dstImage.copyTo(src_roi);
		  }
		 
		boolean check=rects.length<1?false:true;
		resultMap.put("check", check);
		resultMap.put("Mat", imageCloneCheck);
		return resultMap;
	}
	
	
	
	/**
	 *Scalar(255, 0, 0)  ----表示纯蓝色
     *Scalar(0, 255, 0) ----表示纯绿色
     *Scalar(0, 0, 255) ----表示纯红色
     *Scalar(255, 255, 0) ----表示青色
     *Scalar(0, 255, 255) ----表示黄色
	 * @param image
	 * @return
	 * @throws IOException 
	 */
	public static Mat getVideoFace(Mat image) throws IOException {
		
		//正脸蓝色
		Map<String,Object> resultMap=ImageUtil.getFace(FACEBOOK, image,new Scalar(0,255,255));
		if(null==resultMap||resultMap.isEmpty()) {
		  return null;
		}
		return (Mat) resultMap.get("Mat");
	}
	/**
	 * 
	 * @param cc 识别类
	 * @param image 图片
	 * @param sc 颜色
	 * @param flip 是否反转
	 * @return
	 * @throws IOException 
	 */
	private static Map<String,Object> getFace(CascadeClassifier cc,Mat image,Scalar sc) throws IOException {
		if(image.empty()) {
			return null;
		}
        Map<String,Object> resultMap=new HashMap<String,Object>();
        //Mat imageCloneCheck=image.clone();
        //Mat imageClone=new Mat();
        //Imgproc.cvtColor(image, imageClone, Imgproc.COLOR_BGR2GRAY);
        
		MatOfRect face = new MatOfRect();
		
		cc.detectMultiScale(image, face);
		
		Rect[] rects = face.toArray();
		logger.info("匹配到 " + rects.length + " 个人脸");
		
		// 4 为每张识别到的人脸画一个圈
		for (int i = 0; i < rects.length; i++) {
			Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y),
					new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), sc);
			
//			String age=analyseAge(imageCloneCheck,rects[i]);
//			String gender=analyseGender(imageCloneCheck,rects[i]);
//		    
//			Font font = new Font("微软雅黑", Font.PLAIN, 12); 
//			BufferedImage bufImg =Mat2BufImg(imageCloneCheck,".png");
//			Graphics2D g = bufImg.createGraphics();
//            g.drawImage(bufImg, 0, 0, bufImg.getWidth(),bufImg.getHeight(), null);
//            g.setFont(font);              //设置字体
//            
//            //设置水印的坐标
//            g.drawString("性别:"+gender+" 年龄:"+age, rects[i].x, rects[i].y);
//	        g.dispose();
	        
	       // imageCloneCheck=ImageUtil.BufImg2Mat(bufImg, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);// CvType.CV_8UC3
			 
		}
		boolean check=rects.length<1?false:true;
		resultMap.put("check", check);
		resultMap.put("Mat", image);
		return resultMap;
	}
	
	private static String analyseAge(Mat mRgba, Rect face) {
        try {
            Mat capturedFace = new Mat(mRgba, face);
            //Resizing pictures to resolution of Caffe model
            Imgproc.resize(capturedFace, capturedFace, new Size(227, 227));
            //Converting RGBA to BGR
            Imgproc.cvtColor(capturedFace, capturedFace, Imgproc.COLOR_RGBA2BGR);

            //Forwarding picture through Dnn
            Mat inputBlob = Dnn.blobFromImage(capturedFace, 1.0f, new Size(227, 227),
                    new Scalar(78.4263377603, 87.7689143744, 114.895847746), false, false);
            AGENET.setInput(inputBlob, "data");
            Mat probs = AGENET.forward("prob").reshape(1, 1);
            Core.MinMaxLocResult mm = Core.minMaxLoc(probs); //Getting largest softmax output

            double result = mm.maxLoc.x; //Result of age recognition prediction
            return AGES[(int) result];
        } catch (Exception e) {
           e.printStackTrace();
        }
        return "";
    }
	
	private static String analyseGender(Mat mRgba, Rect face) {
        try {
            Mat capturedFace = new Mat(mRgba, face);
            //Resizing pictures to resolution of Caffe model
            Imgproc.resize(capturedFace, capturedFace, new Size(227, 227));
            //Converting RGBA to BGR
            Imgproc.cvtColor(capturedFace, capturedFace, Imgproc.COLOR_RGBA2BGR);

            //Forwarding picture through Dnn
            Mat inputBlob = Dnn.blobFromImage(capturedFace, 1.0f, new Size(227, 227),
                    new Scalar(78.4263377603, 87.7689143744, 114.895847746), false, false);
            GENDER.setInput(inputBlob, "data");
            Mat probs = GENDER.forward("prob").reshape(1, 1);
            Core.MinMaxLocResult mm = Core.minMaxLoc(probs); //Getting largest softmax output

            double result = mm.maxLoc.x; //Result of gender recognition prediction. 1 = FEMALE, 0 = MALE
            return GENDERS[(int) result];
        } catch (Exception e) {
        	e.printStackTrace();
        }
        return "";
    }
	
	
	
	public static void initComPare(String path) throws IOException {
		    //srcList.clear();
			File imgFile=new File(path);
			File listFile[]=imgFile.listFiles(new FilenameFilter() {
				//想要保存的文件则，return true;反之return false
				@Override
				public boolean accept(File dir, String name) {
					if(new File(dir,name).isDirectory()) {
						try {
							initComPare(dir.getAbsoluteFile()+File.separator+name);
							return false;
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					String nameType=name.substring(name.lastIndexOf("."));
					if(FILETYPELIST.contains(nameType)){
						return true;
					}
					return false;
				}
			});
			
			
			if(null==listFile) {
				logger.info("获取匹配文件{}:{}个：",imgFile.getName(),0);
				return;
			}
			
			 logger.info("获取匹配文件{}:{}个：",imgFile.getName(),listFile.length);
			 
			 if(listFile.length>0) {
				 //List list=srcMap.get(imgFile.getName());
				 //if(null==list) {
				     srcMap.remove(imgFile.getName());
					 srcMap.put(imgFile.getName(), new ArrayList<Mat>());
				 //}
			 }
			 
			 for(File f:listFile) {
		    	 Mat srcOld=ImageUtil.imagePath2Mat(f.getPath());
		    	 srcMap.get(imgFile.getName()).addAll(getCompareFace(srcOld));
			 }
	}
	
	/**
	 * 通过直方图比较两张图片
	 * @param _src   原始图路径
	 * @param _des   目标图
	 * @return
	 * @throws IOException 
	 */
	@SuppressWarnings("unused")
	private static Map<String,Object> compareHist(Mat _des) throws IOException {
		 Map<String,Object> resultMap=new HashMap<String,Object>();
	     List<Mat> desList=getCompareFace(_des);
	     logger.info("检测人脸："+desList.size());
	     if(desList.size()<1) {
	    	 resultMap.put("statu", "0");
	    	 return resultMap;
	     }
	     Set<String> nameSet=srcMap.keySet();
	    	 for(Mat desM:desList) {
	    	   for(String key:nameSet) {
	    		 for(Mat srcM:srcMap.get(key)) {
	    			 int check=compareHist(srcM,desM);
	    			 if(check>=1) {
	    				 resultMap.put("statu", "1");
	    				 resultMap.put("name", key);
	    				 resultMap.put("image", srcM);
	    				 return resultMap;
	    			 }	    		 
	    		  }
	    		}
	    	 }
	    logger.info("匹配失败");
	    resultMap.put("statu", "0");
   	    return resultMap;
	}
	
	/**
	 * 通过直方图比较两张图片
	 * @param _src  原始图
	 * @param _des   目标图
	 * @return
	 */
	
	private static int i=1;
	private static int compareHist(Mat _src, Mat _des) {
		try {
          /*
			Mat mat_src = _src;
			Mat mat_des = _des;

			if (mat_src.empty() || mat_des.empty()) {
				throw new Exception("no file.");
			}

			Mat hsv_src = new Mat();
			Mat hsv_des = new Mat();

			// 转换成HSV
			Imgproc.cvtColor(mat_src, hsv_src, Imgproc.COLOR_BGR2HSV);
			Imgproc.cvtColor(mat_des, hsv_des, Imgproc.COLOR_BGR2HSV);

			List<Mat> listImg1 = new ArrayList<Mat>();
			List<Mat> listImg2 = new ArrayList<Mat>();
			listImg1.add(hsv_src);
			listImg2.add(hsv_des);

			MatOfFloat ranges = new MatOfFloat(0, 255);
			MatOfInt histSize = new MatOfInt(50);
			MatOfInt channels = new MatOfInt(0);

			Mat histImg1 = new Mat();
			Mat histImg2 = new Mat();

			Imgproc.calcHist(listImg1, channels, new Mat(), histImg1, histSize,
					ranges);
			Imgproc.calcHist(listImg2, channels, new Mat(), histImg2, histSize,
					ranges);

			Core.normalize(histImg1, histImg1, 0, 1, Core.NORM_MINMAX, -1,
					new Mat());
			Core.normalize(histImg2, histImg2, 0, 1, Core.NORM_MINMAX, -1,
					new Mat());
            */
			
			Mat hvs_1 = new Mat();
			Mat hvs_2 = new Mat();
			//图片转HSV COLOR_BGR2HSV COLOR_RGB2GRAY COLOR_BGR2YCrCb
			Imgproc.cvtColor(_src, hvs_1,Imgproc.COLOR_RGB2GRAY);
			Imgproc.cvtColor(_des, hvs_2,Imgproc.COLOR_RGB2GRAY);

			//Imgproc.equalizeHist(hvs_1,hvs_1);
			//Imgproc.equalizeHist(hvs_2,hvs_2);
			
			//ImageUtil.Mat2Img(hvs_1, ".jpeg", "E:\\opencv4.4.0\\age-and-gender-classification\\img\\huge\\compare\\"+i+"_"+ UUID.randomUUID()+".jpeg");
			//ImageUtil.Mat2Img(hvs_2, ".jpeg", "E:\\opencv4.4.0\\age-and-gender-classification\\img\\huge\\compare\\"+i+"_"+ UUID.randomUUID()+".jpeg");
			//i++;
			
			Mat hist_1 = new Mat();
			Mat hist_2 = new Mat();

			//直方图计算
			Imgproc.calcHist(Stream.of(hvs_1).collect(Collectors.toList()),new MatOfInt(0),new Mat(),hist_1,new MatOfInt(100) ,new MatOfFloat(0,256));
			Imgproc.calcHist(Stream.of(hvs_2).collect(Collectors.toList()),new MatOfInt(0),new Mat(),hist_2,new MatOfInt(100) ,new MatOfFloat(0,256));

			//图片归一化
			Core.normalize(hist_1, hist_1, 1, hist_1.rows() , Core.NORM_MINMAX, -1, new Mat() ); 
			Core.normalize(hist_2, hist_2, 1, hist_2.rows() , Core.NORM_MINMAX, -1, new Mat() ); 
			
			double result0, result1, result2, result3;
			result0 = Imgproc.compareHist(hist_1, hist_2, 0);
			result1 = Imgproc.compareHist(hist_1, hist_2, 1);
			result2 = Imgproc.compareHist(hist_1, hist_2, 2);
			result3 = Imgproc.compareHist(hist_1, hist_2, 3);

			// 0 - 相关性：度量越高，匹配越准确 “> 0.9”
			// 1 - 卡方: 度量越低，匹配越准确 "< 0.1"
			// 2 - 交叉核: 度量越高，匹配越准确 "> 1.5"
			// 3 - 巴氏距离: 度量越低，匹配越准确 "< 0.3"
			//logger.info("相关性（度量越高，匹配越准确 [基准：0.9]）,当前值:" + result0);
			//logger.info("卡方（度量越低，匹配越准确 [基准：0.1]）,当前值:" + result1);
			//logger.info("交叉核（度量越高，匹配越准确 [基准：1.5]）,当前值:" + result2);
			//logger.info("巴氏距离（度量越低，匹配越准确 [基准：0.3]）,当前值:" + result3);

			int count = 0;
			if (result0 > 0.9)
				count++;
			if (result1 < 0.1)
				count++;
			if (result2 > 1.5)
				count++;
			if (result3 < 0.3)
				count++;
			int retVal = 0;
			if (count >= 3) {
				//这是相似的图像
				logger.info("测试成功");
				retVal = 1;
			}

			return retVal;
		} catch (Exception e) {
			logger.info("例外:" + e);
		}
		return 0;
	}
	
	
	
	/**
	 * 
	 * @param cc 识别类
	 * @param image 图片
	 * @param sc 颜色
	 * @param flip 是否反转
	 * @return
	 * @throws UnsupportedEncodingException 
	 */
	private static List<Mat> getCompareFace(Mat image) throws UnsupportedEncodingException {
        
		List<Rect> rectList=new ArrayList<Rect>();
		Mat sorce=image.clone();
		
		MatOfRect face = new MatOfRect();
		FACEBOOK.detectMultiScale(sorce, face);
		rectList.addAll(face.toList());
		
		face = new MatOfRect();
		PROFILEFACEFACEBOOK.detectMultiScale(sorce, face);
		rectList.addAll(face.toList());
		
	    Core.flip(sorce, sorce, 1);
	    
	    face = new MatOfRect();
		PROFILEFACEFACEBOOK.detectMultiScale(sorce, face);
		rectList.addAll(face.toList());
		
		//ImageUtil.Mat2Img(image, ".jpeg", "E:\\opencv4.4.0\\age-and-gender-classification\\img\\huge\\test.jpeg");
		List<Mat>matList=new ArrayList<Mat>();
		for(Rect rt : rectList) {
			matList.add(new Mat(image, rt));
		}
		return matList;
	}
	
	public static Mat face2(Mat image) {
		//Dest =(Src * (100 - Opacity) + (Src + 2 * GuassBlur(EPFFilter(Src) - Src + 128) - 256) * Opacity) /100 ;
		Mat dst = new Mat();
		// int value1 = 3, value2 = 1; 磨皮程度与细节程度的确定
		int value1 = 3, value2 = 1;
		int dx = value1 * 5; // 双边滤波参数之一
		double fc = value1 * 12.5; // 双边滤波参数之一
		double p = 0.1f; // 透明度
		Mat temp1 = new Mat(), temp2 = new Mat(), temp3 = new Mat(), temp4 = new Mat();
		// 双边滤波
		Imgproc.bilateralFilter(image, temp1, dx, fc, fc);
		// temp2 = (temp1 - image + 128);
		Mat temp22 = new Mat();
		Core.subtract(temp1, image, temp22);
		// Core.subtract(temp22, new Scalar(128), temp2);
		Core.add(temp22, new Scalar(128, 128, 128, 128), temp2);
		// 高斯模糊
		Imgproc.GaussianBlur(temp2, temp3, new Size(2 * value2 - 1, 2 * value2 - 1), 0, 0);
		// temp4 = image + 2 * temp3 - 255;
		Mat temp44 = new Mat();
		temp3.convertTo(temp44, temp3.type(), 2, -255);
		Core.add(image, temp44, temp4);
		// dst = (image*(100 - p) + temp4*p) / 100;
		Core.addWeighted(image, p, temp4, 1 - p, 0.0, dst);
		Core.add(dst, new Scalar(10, 10, 10), dst);
		return dst;
		}
	
	public static Mat face3(Mat image) {
		int width = image.width();
		int height = image.height();
		int pointCount = width * height;
		int dims = image.channels();
		Mat points=new Mat(pointCount, dims, CvType.CV_32F);
		int index=0;
	    for(int i=0;i<height;i++)
	    	for(int j=0;j<width;j++) {
	    		index = i * width + j;
				points.put(index,0,image.get(i, j)[0]);
				points.put(index,1,image.get(i, j)[1]);
				points.put(index,2,image.get(i, j)[2]);
	    	}
//		Mat points=image.reshape(3, pointCount);
//		points.convertTo(points,  CvType.CV_32F);
		
		// 2.使用K-means聚类；分离出背景色
	    Mat bestLabels=new Mat();
		Mat centers=new Mat(3, 3, CvType.CV_32F);
		TermCriteria criteria=new TermCriteria(TermCriteria.COUNT + TermCriteria.EPS, 10, 0.1);
		Core.kmeans(points, 4, bestLabels, criteria, 3, Core.KMEANS_PP_CENTERS, centers);
		
		/**
		 *Scalar(255, 0, 0)  ----表示纯蓝色
	     *Scalar(0, 255, 0) ----表示纯绿色
	     *Scalar(0, 0, 255) ----表示纯红色
	     *Scalar(255, 255, 0) ----表示青色
	     *Scalar(0, 255, 255) ----表示黄色
	     *Scalar(0, 0, 0) ----表示黑色
	     *Scalar(255, 255, 255) ----表示白色
	     */
		
		double[][] color={{0,0,255},{0,255,0},{255,0,0},{0, 255, 255}};
		
		Mat result = Mat.zeros(image.size(), image.type());
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				index = i * width + j;
				int lable = (int)bestLabels.get(index,0)[0];
				result.put(i, j, color[lable]);
			} 
		}
		HighGui.imshow("zero", result);
		// 3.背景与人物二值化
		Mat mask=new Mat(image.size(),CvType.CV_8UC1);
		index = image.cols() * 2 + 2;
		int bindex = (int)bestLabels.get(index,0)[0];//获得kmeans后背景的标签
		
		double array[]= {0d,0d,0d};
		double array2[]= {255,255,255};
		Mat dst=image.clone();
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				index = i*width + j;
				double label = (int)bestLabels.get(index,0)[0];
				if (Math.abs(label-bindex)==0) {
					dst.put(i, j,array);
					mask.put(i, j,array);
				}
				else {
					mask.put(i, j,array2);
				}
			}
		}
		
		HighGui.imshow("mask", mask);
		HighGui.imshow("kmeans", dst);
		
		
		//对掩码进行腐蚀+高斯模糊
		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(3, 3));
		Imgproc.erode(mask, mask, kernel);
		HighGui.imshow("erode mask", mask);
		Imgproc.GaussianBlur(mask, mask, new Size(3, 3), 0, 0);
		HighGui.imshow("blur mask", mask);
	 
		//通道混合
		Random rand = new Random();
		int array3[]= {rand.nextInt(255 - 0 + 1) + 0,rand.nextInt(255 - 0 + 1) + 0,rand.nextInt(255 - 0 + 1) + 0};
		
		Mat result2=new Mat(image.size(), image.type());
		double w = 0.0;
		int b = 0, g = 0, r = 0;
		int b1 = 0, g1 = 0, r1 = 0;
		int b2 = 0, g2 = 0, r2 = 0;
	 
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				int m = (int)mask.get(i, j)[0];
				if (m == 255) {
				    result2.put(i,j,image.get(i,j));//将原图像中前景赋给结果图像中的前景
				}
				else if (m == 0) {
					result2.put(i,j,new double[]{array3[0],array3[1],array3[2]});//将随机生成的颜色赋给结果图像中的背景
				}
				else {
					w = m / 255.0;//权重
					//边缘前景
					b1 = (int)image.get(i, j)[0];
					g1=  (int)image.get(i, j)[1];
					r1 = (int)image.get(i, j)[2];
					//边缘背景
					b2 = array3[0];
					g2 = array3[1];
					r2 = array3[2];
					//边缘融合
					b = (int) (b1*w + b2 *(1.0 - w));
					g = (int) (g1*w + g2 *(1.0 - w));
					r = (int) (r1*w + r2 *(1.0 - w));
					result2.put(i, j,new double[]{b,g,r});
				}
			}
		}
		
		/*  提取背景特征 */
		// 4.腐蚀 + 高斯模糊：图像与背景交汇处高斯模糊化
		//Mat dstImage = result.clone();
	    //Imgproc.GaussianBlur(result, dstImage, new Size(9,9), 0, 0, Core.BORDER_DEFAULT);
		HighGui.imshow("result2", result2);
		return result2;
	}
	
	public static void getKmeans(Mat image) {
		int width = image.width();
		int height = image.height();
		int pointCount = width * height;
		
		Mat points=image.reshape(image.channels(), pointCount);
		points.convertTo(points,  CvType.CV_32F);
		
		Mat bestLabels=new Mat();
		Mat centers=new Mat(4, 4, CvType.CV_32F);
		TermCriteria criteria=new TermCriteria(TermCriteria.COUNT + TermCriteria.EPS, 10, 0.1);
		Core.kmeans(points, 4, bestLabels, criteria, 5, Core.KMEANS_RANDOM_CENTERS,centers);
        
		double[][] color={{0,0,255},{0,255,0},{255,0,0},{0, 255, 255}};
		
		Mat result = Mat.zeros(image.size(), image.type());
		
		int index=0;
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				index = i * width + j;
				//获取聚类标记的点
				int lable = (int)bestLabels.get(index,0)[0];
				//为聚类相同的像素点填充颜色
				result.put(i, j, color[lable]);
			} 
		}
		
		HighGui.imshow("分类后的", result);
		
		
		Mat zeroImage=new Mat();
		Mat close=new Mat();
		Imgproc.cvtColor(image, zeroImage, Imgproc.COLOR_RGB2HSV);
		HighGui.imshow("cvtColor", zeroImage);
		//高、低阈值范围之内，则在zeroImage图像中令该像素值为255 白色，否则令其为0黑色
		//Scalar是具有三个参数的结构体，三个参数代表 hsv的色相，饱和度，亮度值
//		The red rectangle has value (0,0,240), so you can use:
//			inRange(img, new Scalar(0, 0, 230), new Scalar(0, 0, 255), dst);
//		The green rectangle has value (0,240,0), so you can use:
//			inRange(img, new Scalar(0, 230, 0), new Scalar(0, 255, 0), dst);
//		The blue rectangle has value (240,0,0), so you can use:
//			inRange(img, new Scalar(230, 0, 0), new Scalar(255, 0, 0), dst);
//		The gray rectangle has value (100,100,100), so you can use:
//			inRange(img, new Scalar(90, 90, 90), new Scalar(110, 110, 110), dst);
		Core.inRange(zeroImage, new Scalar( 11, 43, 45), new Scalar( 99, 255, 255), close);
		Mat kernel1 = new Mat(new Size(3, 3), CvType.CV_8UC1, new Scalar(255));
        Imgproc.morphologyEx(close, close, Imgproc.MORPH_CLOSE, kernel1);
        HighGui.imshow("close", close);
        Mat close2=new Mat();
//        Imgproc.medianBlur(close, close2, 5);
        Imgproc.bilateralFilter(close, close2,5, 0, 0);
        HighGui.imshow("close2", close2);
		// 3.背景与人物二值化
		Mat mask=new Mat(image.size(),CvType.CV_8UC1);
		index = image.cols() * 2 + 2;
		int bindex = (int)bestLabels.get(0,0)[0];//获得kmeans后背景的标签
		
		double array2[]= {0,0,0};
		double array[]= {255,255,255};
		Mat dst=image.clone();
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				index = i*width + j;
				double label = (int)bestLabels.get(index,0)[0];
				if (label-bindex==0) {
					dst.put(i, j,array);
					mask.put(i, j,array);
				}
				else {
					mask.put(i, j,array2);
				}
			}
		}
		HighGui.imshow("mask", mask);
		HighGui.imshow("kmeans", dst);
		//对掩码进行腐蚀+高斯模糊
		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE,new Size(5, 5));
		Imgproc.erode(mask, mask, kernel);
		HighGui.imshow("erode mask", mask);
		Imgproc.GaussianBlur(mask, mask, new Size(5, 5), 0, 0);
		HighGui.imshow("blur mask", mask);
		
		Mat out = new Mat();
        Mat tmp = new Mat();
		//Mat kernel2 = new Mat(new Size(6, 6), CvType.CV_8UC1, new Scalar(255));
		Mat kernel2 =Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(6, 6));
		Imgproc.morphologyEx(mask, out, Imgproc.MORPH_OPEN, kernel2);
		Imgproc.morphologyEx(out, tmp, Imgproc.MORPH_CLOSE, kernel2);
		//通道混合
		Random rand = new Random();
		int array3[]= {rand.nextInt(255 - 0 + 1) + 0,rand.nextInt(255 - 0 + 1) + 0,rand.nextInt(255 - 0 + 1) + 0};
		
		Mat result2=new Mat(image.size(), image.type());
		double w = 0.0;
		int b = 0, g = 0, r = 0;
		int b1 = 0, g1 = 0, r1 = 0;
		int b2 = 0, g2 = 0, r2 = 0;
	 
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				int m = (int)close2.get(i, j)[0];
				if (m == 0) {
				    result2.put(i,j,image.get(i,j));//将原图像中前景赋给结果图像中的前景
				}
				else if (m == 255) {
					result2.put(i,j,new double[]{array3[0],array3[1],array3[2]});//将随机生成的颜色赋给结果图像中的背景
				}
				else {
					//result2.put(i,j,new double[]{array3[0],array3[1],array3[2]});
					w = m / 255.0;//权重
					//边缘前景
					b1 = (int)image.get(i, j)[0];
					g1=  (int)image.get(i, j)[1];
					r1 = (int)image.get(i, j)[2];
					//边缘背景
					b2 = array3[0];
					g2 = array3[1];
					r2 = array3[2];
					//边缘融合
					b = (int) (b1*w + b2 *(1.0 - w));
					g = (int) (g1*w + g2 *(1.0 - w));
					r = (int) (r1*w + r2 *(1.0 - w));
					result2.put(i, j,new double[]{b,g,r});
					System.out.println(m);
				}
			}
		}
		HighGui.imshow("result", result2);
	}
	
	
public static void testCanny(Mat image) {
	//高斯滤波
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	//灰度
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
	int lowThresh=45;
	//边缘检测
	Imgproc.Canny(image, image,lowThresh, lowThresh*3,3);
	HighGui.imshow("Canny", image);
}

public static void testSobel(Mat image) {
	//高斯滤波
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	//灰度
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	  //横向
    Mat x = new Mat();
    /*src：源图像
     *dst：检测结果图像
     *ddepth：输出图像的深度
     *dx：x方向上的差分阶数
     *dy：y方向上的差分阶数
     *ksize：sobel核的大小，默认为3
     *scale：缩放因子
     *delta：结果存入输出图像前可选的delta值，默认为0
     *borderType：边界模式，默认BORDER_DEFAULT
     *
     *其中，输出图像的深度，支持如下src.depth()和ddepth的组合：
     *src.depth() = CV_8U         ddepth =-1/CV_16S/CV_32F/CV_64F
     *src.depth() = CV_16U/CV_16S ddepth =-1/CV_32F/CV_64F 
     *src.depth() = CV_32F        ddepth =-1/CV_32F/CV_64F
     *src.depth() = CV_64F        ddepth = -1/CV_64F
     */
    Imgproc.Sobel(image, x, CvType.CV_16S, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
    //需要用convertScaleAbs()函数将其转回原来的uint8形式，否则将无法显示图像
    Core.convertScaleAbs(x, x); 
    HighGui.imshow("x", x);
    //竖向
    Mat y = new Mat();
    Imgproc.Sobel(image, y, CvType.CV_16S, 0, 1, 3, 1, 0, Core.BORDER_DEFAULT);
    Core.convertScaleAbs(y, y); 
    HighGui.imshow("y", y);
    //横竖向图像融合
    Mat xy = new Mat();
    Core.addWeighted(x, 0.5, y, 0.5, 0, xy);
	HighGui.imshow("xy", xy);
}


public static void testScharr(Mat image) {
	//高斯滤波
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	//灰度
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	  //横向
    Mat x = new Mat();
    /*src：源图像
     *dst：检测结果图像
     *ddepth：输出图像的深度
     *dx：x方向上的差分阶数
     *dy：y方向上的差分阶数
     *scale：缩放因子
     *delta：结果存入输出图像前可选的delta值，默认为0
     *borderType：边界模式，默认BORDER_DEFAULT
     *
     *其中，输出图像的深度，支持如下src.depth()和ddepth的组合：
     *src.depth() = CV_8U         ddepth =-1/CV_16S/CV_32F/CV_64F
     *src.depth() = CV_16U/CV_16S ddepth =-1/CV_32F/CV_64F 
     *src.depth() = CV_32F        ddepth =-1/CV_32F/CV_64F
     *src.depth() = CV_64F        ddepth = -1/CV_64F
     */
    Imgproc.Scharr(image, x, CvType.CV_16S, 1, 0,1, 0, Core.BORDER_DEFAULT);
    //需要用convertScaleAbs()函数将其转回原来的uint8形式，否则将无法显示图像
    Core.convertScaleAbs(x, x); 
    HighGui.imshow("x", x);
    //竖向
    Mat y = new Mat();
    Imgproc.Scharr(image, y, CvType.CV_16S, 0, 1, 1, 0, Core.BORDER_DEFAULT);
    Core.convertScaleAbs(y, y); 
    HighGui.imshow("y", y);
    //横竖向图像融合
    Mat xy = new Mat();
    Core.addWeighted(x, 0.5, y, 0.5, 0, xy);
	HighGui.imshow("xy", xy);
}

public static void testLaplacian(Mat image) {
	//高斯滤波
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	//灰度
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	//边缘检测
	Mat dst = new Mat();
	/*src：源图像
     *dst：检测结果图像
     *ddepth：输出图像的深度
     *ksize：计算二阶导数的滤波器的孔径尺寸，必须为正奇数 等于1是四邻域算子，大于1改用八邻域算子
     *scale：缩放因子 计算拉普拉斯值时候可选的比例因子
     *delta：结果存入输出图像前可选的delta值，默认为0
     *borderType：边界模式，默认BORDER_DEFAULT
     */
	Imgproc.Laplacian(image, dst, CvType.CV_16S, 3, 1, 0, Core.BORDER_DEFAULT);
	Core.convertScaleAbs(dst, dst); 
	HighGui.imshow("Laplacian", dst);
}

public static void testThreshold(Mat image) {
	/**
	 *src 是输入的函数图像
     *dst 是输出的函数图像
     *thresh 阈值的具体值 
     *maxval 阈值类型的最大值
     *type 阈值类型
     *THRESH_BINARY = 0,过门限的为maxval其他取零
     *THRESH_BINARY_INV = 1,过门限的为取零，其他maxval
     *THRESH_TRUNC = 2,过门限的取门限，其他不变
     *THRESH_TOZERO = 3,过门限的不变，其他取零
     *THRESH_TOZERO_INV = 4,过门限的值取零其他不变
     *THRESH_MASK = 7,
     *THRESH_OTSU = 8,自动生成阀值，大于阀值的为255 ，小于阀值的为0
     *THRESH_TRIANGLE = 16;和我们设置的阀值大小没有关系，是自动计算出来的
	 */
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	HighGui.imshow("gry", image);
	Mat m=new Mat();
	Imgproc.threshold(image, m, 120, 255, Imgproc.THRESH_BINARY);
	HighGui.imshow("Threshold", m);
}


public static void testAdaptiveThreshold(Mat image) {
  /**
   * src
   * dst
   * maxValue 给像素赋的满足条件的非零值
   * ADAPTIVE_THRESH_MEAN_C 计算均值时每个像素的权值是相等的
   * ADAPTIVE_THRESH_GAUSSIAN_C 计算均值时每个像素的权值根据其到中心点的距离通过高斯方程得到
   * int blockSize,  //计算阈值大小的一个像素的领域尺寸，取值为大于1的奇数
   * double C);  //减去加权平均值后的常数值，通常为正数，少数情况下也可为0或负数
   */
 //(Mat src, Mat dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)	
 //CV_8UC1 in function 'cv::adaptiveThreshold' 先灰度处理
	//高斯滤波
  Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
  Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
  Imgproc.adaptiveThreshold(image, image, 200, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 5, 9);
  HighGui.imshow("adaptiveThreshold", image);   
}

public static void testFindContours(Mat image) {
 //高斯滤波
 Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
 Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
 int lowThresh=45;
 //边缘检测
 Imgproc.Canny(image, image,lowThresh, lowThresh*3,3);
 HighGui.imshow("Canny", image);

 List<MatOfPoint> contours = new ArrayList<>();
 Mat hierarchy = new Mat();
 /**
  * iamge 输入图像，需为8位单通道的二进制图像
  * contours 存储检测到的轮廓，每个轮廓存储为一个点向量
  * hierarchy //包含图像的拓扑信息，作为轮廓数量的表示。
              //每个轮廓对应4个hierarchy元素，分别表示后一个轮廓、
              //前一个轮廓、父轮廓、内嵌轮廓的索引编号。
              //若没有对应项，对应的hierarchy[i]值设置为负数
  *  mode 轮廓检索模式 RETR_EXTERNAL 只检测最外围的轮廓
  *                  RETR_LIST 检测所有轮廓，不建立等级关系，彼此独立
  *                  RETR_CCOMP 检测所有轮廓，但所有轮廓都只建立两个等级关系 
  *                  RETR_TREE 检测所有轮廓，并且所有轮廓建立一个树结构，层次完整。
  *                  RETR_FLOODFILL 洪水填充法
  *  method 轮廓的近似办法 CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点
  *                      CHAIN_APPROX_SIMPLE 压缩水平方向，垂直方向，对角线方向的元素，只保留该方向的终点坐标，例如一个矩形轮廓只需4个点来保存轮廓信息
  *                      CV_CHAIN_APPROX_TC89_L1 使用Teh-Chin 链近似算法
  *                      CV_CHAIN_APPROX_TC89_KCOS 使用Teh-Chin 链近似算法
  *  offset //每个轮廓点的可选偏移量
  */ 
 Imgproc.findContours(image, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
 System.out.println("轮廓数量："+ contours.size());
 System.out.println("hierarchy类型："+ hierarchy);
 // 找出匹配到的最大轮廓
 double area = Imgproc.boundingRect(contours.get(0)).area();
 int index = 0;
 Random rng = new Random();
 Mat drawing = Mat.zeros(image.size(), CvType.CV_8U);
 //Imgproc.drawContours(drawing, contours, -1, new Scalar(255), 3, Imgproc.LINE_8, hierarchy, 0, new Point());
 Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
 for (int i = 0; i < contours.size(); i++) {
				double tempArea = Imgproc.boundingRect(contours.get(i)).area();
				if (tempArea > area) {
					area = tempArea;
					index = i;
				}
 }

	 
     /**
      * image，目标图像，填 Mat 类对象即可。
      * contours，输入的轮廓，每个轮廓都是一组点集，可用 Point 类型的 vector 表示。
      * contourIdx，轮廓的索引编号。若为负值，则绘制所有轮廓。
      * color，轮廓颜色。
      * thickness，轮廓线条的粗细程度，有默认值 1。若其为负值，便会填充轮廓内部空间。
      * lineType，线条的类型，有默认值 8。可去类型如下：
            *                      类型	含义
      *           8	8 连通线型
      *           4	4 连通线型
      *           LINE_AA	抗锯齿线型
      * hierarchy，可选的层次结构信息，有默认值 noArray()。
      * maxLevel，用于绘制轮廓的最大等级，有默认值 INT_MAX。
      * offset，轮廓信息相对于目标图像对应点的偏移量，相当于在每一个轮廓点上加上该偏移量，有默认值 Point() 。在 ROI 区域（感兴趣区域）绘制轮廓时，这个参数便可派上用场。
      */
 Imgproc.drawContours(drawing, contours, index, color, 1, Imgproc.LINE_AA, hierarchy, 0, new Point());
 
 HighGui.imshow("findContours", drawing);  
 
 for(int k=0;k<hierarchy.cols();k++) {
	System.out.println();
	System.out.println("轮廓下标："+k +" { ");
	double[] ds = hierarchy.get(0, k);
	for (int l=0;l<ds.length;l++) {
		switch (l) {
		case 0:
			System.out.print(" 后一个轮廓下标："+ ds[l]);
			break;
		case 1:
			System.out.print("  前一个轮廓下标："+ds[l]);
			break;
		case 2:
			System.out.print("  父轮廓下标："+ds[l]);
			break;
		case 3:
			System.out.print("  内嵌轮廓下标："+ds[l]);
			break;
 
		default:
			break;
		}
	}
 }
 System.out.print(" }\n");
}

public static void testTesseract(Mat image) {
	ITesseract instance = new Tesseract(); 
	Mat src=image.clone();
	//高斯滤波
	Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
	 int lowThresh=45;
	 //边缘检测
	 Mat m=new Mat();
	 Imgproc.threshold(image, image, 165, 255, Imgproc.THRESH_BINARY);
	 //Imgproc.Canny(image, image,lowThresh, lowThresh*3,3);
	 HighGui.imshow("threshold", image);
	 
	 Imgproc.medianBlur(image, image, 3);
	 HighGui.imshow("medianBlur", image);
	 
	 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(5, 5));
	 Imgproc.erode(image, image, kernel);
	 HighGui.imshow("erode", image);
	 
	 List<MatOfPoint> contours = new ArrayList<>();
	 Mat hierarchy = new Mat();
	 /**
	  * iamge 输入图像，需为8位单通道的二进制图像
	  * contours 存储检测到的轮廓，每个轮廓存储为一个点向量
	  * hierarchy //包含图像的拓扑信息，作为轮廓数量的表示。
	              //每个轮廓对应4个hierarchy元素，分别表示后一个轮廓、
	              //前一个轮廓、父轮廓、内嵌轮廓的索引编号。
	              //若没有对应项，对应的hierarchy[i]值设置为负数
	  *  mode 轮廓检索模式 RETR_EXTERNAL 只检测最外围的轮廓
	  *                  RETR_LIST 检测所有轮廓，不建立等级关系，彼此独立
	  *                  RETR_CCOMP 检测所有轮廓，但所有轮廓都只建立两个等级关系 
	  *                  RETR_TREE 检测所有轮廓，并且所有轮廓建立一个树结构，层次完整。
	  *                  RETR_FLOODFILL 洪水填充法
	  *  method 轮廓的近似办法 CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点
	  *                      CHAIN_APPROX_SIMPLE 压缩水平方向，垂直方向，对角线方向的元素，只保留该方向的终点坐标，例如一个矩形轮廓只需4个点来保存轮廓信息
	  *                      CV_CHAIN_APPROX_TC89_L1 使用Teh-Chin 链近似算法
	  *                      CV_CHAIN_APPROX_TC89_KCOS 使用Teh-Chin 链近似算法
	  *  offset //每个轮廓点的可选偏移量
	  */ 
	 Imgproc.findContours(image, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
	 System.out.println("轮廓数量："+ contours.size());
	 System.out.println("hierarchy类型："+ hierarchy);
	 
	 Random rng = new Random();
	 Mat drawing = Mat.zeros(image.size(), CvType.CV_8U);
	 //Imgproc.drawContours(drawing, contours, -1, new Scalar(255), 3, Imgproc.LINE_8, hierarchy, 0, new Point());
	 Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
	 for (int i = 0; i < contours.size(); i++) {
		 Rect rect =Imgproc.boundingRect(contours.get(i));
		 Imgproc.rectangle(src, rect, new Scalar(0, 0, 255));
	        // 定义身份证号位置大于图片的一半，并且宽度是高度的6倍以上
	        if (rect.y > image.rows() / 2 && rect.width / rect.height > 6)
	        {
	        	//HighGui.imshow("number", new Mat(src,rect));  
	        }
	     /**
	      * image，目标图像，填 Mat 类对象即可。
	      * contours，输入的轮廓，每个轮廓都是一组点集，可用 Point 类型的 vector 表示。
	      * contourIdx，轮廓的索引编号。若为负值，则绘制所有轮廓。
	      * color，轮廓颜色。
	      * thickness，轮廓线条的粗细程度，有默认值 1。若其为负值，便会填充轮廓内部空间。
	      * lineType，线条的类型，有默认值 8。可去类型如下：
	            *                      类型	含义
	      *           8	8 连通线型
	      *           4	4 连通线型
	      *           LINE_AA	抗锯齿线型
	      * hierarchy，可选的层次结构信息，有默认值 noArray()。
	      * maxLevel，用于绘制轮廓的最大等级，有默认值 INT_MAX。
	      * offset，轮廓信息相对于目标图像对应点的偏移量，相当于在每一个轮廓点上加上该偏移量，有默认值 Point() 。在 ROI 区域（感兴趣区域）绘制轮廓时，这个参数便可派上用场。
	      */
	 Imgproc.drawContours(drawing, contours, i, color, 1, Imgproc.LINE_AA, hierarchy, 0, new Point());
	 
}
	 HighGui.imshow("findContours", src);  
	 
	 for(int k=0;k<hierarchy.cols();k++) {
		System.out.println();
		System.out.println("轮廓下标："+k +" { ");
		double[] ds = hierarchy.get(0, k);
		for (int l=0;l<ds.length;l++) {
			switch (l) {
			case 0:
				System.out.print(" 后一个轮廓下标："+ ds[l]);
				break;
			case 1:
				System.out.print("  前一个轮廓下标："+ds[l]);
				break;
			case 2:
				System.out.print("  父轮廓下标："+ds[l]);
				break;
			case 3:
				System.out.print("  内嵌轮廓下标："+ds[l]);
				break;
	 
			default:
				break;
			}
		}
	 }
	 System.out.print(" }\n");
	 
} 

/**
 * 识别图片信息
 * @param img
 * @return
 */
public static String getImageMessage(BufferedImage img,String language){
    String result="";
    try{
        ITesseract instance = new Tesseract();
        instance.setTessVariable("user_defined_dpi", "300");
        File tessDataFolder = new File("E:\\tessdata-master");
        instance.setLanguage(language);
        instance.setDatapath(tessDataFolder.getAbsolutePath());
        result = instance.doOCR(img);
        System.out.println(result);
    }catch(Exception e){
    	e.printStackTrace();
    }
    return result;
}

public static String card(Mat mat){
    Point point1=new Point(mat.cols()*0.34,mat.rows()*0.80);
    Point point2=new Point(mat.cols()*0.34,mat.rows()*0.80);
    Point point3=new Point(mat.cols()*0.89,mat.rows()*0.91);
    Point point4=new Point(mat.cols()*0.89,mat.rows()*0.91);
    List<Point> list=new ArrayList<>();
    list.add(point1);
    list.add(point2);
    list.add(point3);
    list.add(point4);
    Mat card= shear(mat,list);
    card=ImageUtil.drawContours(card,50);
    HighGui.imshow("card", card); 
  //高斯滤波
  	Imgproc.cvtColor(card, card,Imgproc.COLOR_BGR2GRAY);
  	Imgproc.GaussianBlur(card, card, new Size(3, 3), 0, 0);
  	Imgproc.threshold(card, card, 165, 255, Imgproc.THRESH_BINARY);
	 //Imgproc.Canny(image, image,lowThresh, lowThresh*3,3);
    System.out.println(ImageUtil.getImageMessage(Mat2BufImg(card,".jpg"),"eng"));
    return null;
}

/**
 * 轮廓检测
 * @param mat
 * @return
 */
public static List<MatOfPoint> findContours(Mat mat){
    List<MatOfPoint> contours=new ArrayList<>();
    Mat hierarchy = new Mat();
    Mat clone=mat.clone();
    Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
    Imgproc.findContours(clone, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
    return contours;
}

/**
 * 获取轮廓的面积
 * @param contour
 * @return
 */
public static double area (MatOfPoint contour){
    MatOfPoint2f mat2f=new MatOfPoint2f();
    contour.convertTo(mat2f,CvType.CV_32FC1);
    RotatedRect rect=Imgproc.minAreaRect(mat2f);
    return rect.boundingRect().area();
}

/**
 * 清除小面积轮廓
 * @param mat
 * @param size
 * @return
 */
public static Mat drawContours(Mat mat,int size){
    List<MatOfPoint> cardContours=ImageUtil.findContours(mat);
    for (int i = 0; i < cardContours.size(); i++)
    {
        double area=ImageUtil.area(cardContours.get(i));
        if(area<size){
            Imgproc.drawContours(mat, cardContours, i, new Scalar( 0, 0, 0),-1 );
        }
    }
    return mat;
}
/**
 * 根据四点坐标截取模板图片
 * @param mat
 * @param pointList
 * @return
 */
public static Mat shear (Mat mat,List<Point> pointList){
    int x=minX(pointList);
    int y=minY(pointList);
    int xl=xLength(pointList)>mat.cols()-x?mat.cols()-x:xLength(pointList);
    int yl=yLength(pointList)>mat.rows()-y?mat.rows()-y:yLength(pointList);
    Rect re=new Rect(x,y,xl,yl);
    return new Mat(mat,re);
}

/**
 * 获取最小的X坐标
 * @param points
 * @return
 */
public  static int minX(List<Point> points){
    Collections.sort(points, new XComparator(false));
    return (int)(points.get(0).x>0?points.get(0).x:-points.get(0).x);
}

/**
 * 获取最小的Y坐标
 * @param points
 * @return
 */
public  static int minY(List<Point> points){
    Collections.sort(points, new YComparator(false));
    return (int)(points.get(0).y>0?points.get(0).y:-points.get(0).y);
}

/**
 * 获取最长的X坐标距离
 * @param points
 * @return
 */
public static int xLength(List<Point> points){
    Collections.sort(points, new XComparator(false));
    return (int)(points.get(3).x-points.get(0).x);
}

/**
 * 获取最长的Y坐标距离
 * @param points
 * @return
 */
public  static int yLength(List<Point> points){
    Collections.sort(points, new YComparator(false));
    return (int)(points.get(3).y-points.get(0).y);
}

//集合排序规则（根据X坐标排序）
public static class XComparator implements Comparator<Point> {
    private boolean reverseOrder; // 是否倒序
    public XComparator(boolean reverseOrder) {
        this.reverseOrder = reverseOrder;
    }

    public int compare(Point arg0, Point arg1) {
        if(reverseOrder)
            return (int)arg1.x - (int)arg0.x;
        else
            return (int)arg0.x - (int)arg1.x;
    }
}

//集合排序规则（根据Y坐标排序）
public static class YComparator implements Comparator<Point> {
    private boolean reverseOrder; // 是否倒序
    public YComparator(boolean reverseOrder) {
        this.reverseOrder = reverseOrder;
    }

    public int compare(Point arg0, Point arg1) {
        if(reverseOrder)
            return (int)arg1.y - (int)arg0.y;
        else
            return (int)arg0.y - (int)arg1.y;
    }
}

public static void correct(Mat image) {
	//高斯滤波
	 Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	 Imgproc.cvtColor(image, image,Imgproc.COLOR_BGR2GRAY);
	//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
	 int lowThresh=45;
	 //边缘检测
	 Imgproc.Canny(image, image,lowThresh, lowThresh*3,3);
	 HighGui.imshow("Canny", image);

	 List<MatOfPoint> contours = new ArrayList<>();
	 Mat hierarchy = new Mat();
	 /**
	  * iamge 输入图像，需为8位单通道的二进制图像
	  * contours 存储检测到的轮廓，每个轮廓存储为一个点向量
	  * hierarchy //包含图像的拓扑信息，作为轮廓数量的表示。
	              //每个轮廓对应4个hierarchy元素，分别表示后一个轮廓、
	              //前一个轮廓、父轮廓、内嵌轮廓的索引编号。
	              //若没有对应项，对应的hierarchy[i]值设置为负数
	  *  mode 轮廓检索模式 RETR_EXTERNAL 只检测最外围的轮廓
	  *                  RETR_LIST 检测所有轮廓，不建立等级关系，彼此独立
	  *                  RETR_CCOMP 检测所有轮廓，但所有轮廓都只建立两个等级关系 
	  *                  RETR_TREE 检测所有轮廓，并且所有轮廓建立一个树结构，层次完整。
	  *                  RETR_FLOODFILL 洪水填充法
	  *  method 轮廓的近似办法 CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点
	  *                      CHAIN_APPROX_SIMPLE 压缩水平方向，垂直方向，对角线方向的元素，只保留该方向的终点坐标，例如一个矩形轮廓只需4个点来保存轮廓信息
	  *                      CV_CHAIN_APPROX_TC89_L1 使用Teh-Chin 链近似算法
	  *                      CV_CHAIN_APPROX_TC89_KCOS 使用Teh-Chin 链近似算法
	  *  offset //每个轮廓点的可选偏移量
	  */ 
	 Imgproc.findContours(image, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
	 System.out.println("轮廓数量："+ contours.size());
	 System.out.println("hierarchy类型："+ hierarchy);
	 // 找出匹配到的最大轮廓
	 double area = Imgproc.boundingRect(contours.get(0)).area();
	 int index = 0;
	 Random rng = new Random();
	 Mat drawing = Mat.zeros(image.size(), CvType.CV_8U);
	 //Imgproc.drawContours(drawing, contours, -1, new Scalar(255), 3, Imgproc.LINE_8, hierarchy, 0, new Point());
	 Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
	 for (int i = 0; i < contours.size(); i++) {
					double tempArea = Imgproc.boundingRect(contours.get(i)).area();
					if (tempArea > area) {
						area = tempArea;
						index = i;
					}
	 }

		 
	     /**
	      * image，目标图像，填 Mat 类对象即可。
	      * contours，输入的轮廓，每个轮廓都是一组点集，可用 Point 类型的 vector 表示。
	      * contourIdx，轮廓的索引编号。若为负值，则绘制所有轮廓。
	      * color，轮廓颜色。
	      * thickness，轮廓线条的粗细程度，有默认值 1。若其为负值，便会填充轮廓内部空间。
	      * lineType，线条的类型，有默认值 8。可去类型如下：
	            *                      类型	含义
	      *           8	8 连通线型
	      *           4	4 连通线型
	      *           LINE_AA	抗锯齿线型
	      * hierarchy，可选的层次结构信息，有默认值 noArray()。
	      * maxLevel，用于绘制轮廓的最大等级，有默认值 INT_MAX。
	      * offset，轮廓信息相对于目标图像对应点的偏移量，相当于在每一个轮廓点上加上该偏移量，有默认值 Point() 。在 ROI 区域（感兴趣区域）绘制轮廓时，这个参数便可派上用场。
	      */
	 Imgproc.drawContours(drawing, contours, index, color, 1, Imgproc.LINE_AA, hierarchy, 0, new Point());
	 
	 HighGui.imshow("findContours", drawing);  
	 
}

public static void houghLines(Mat image){

Mat clone=image.clone();
Imgproc.GaussianBlur(clone, clone, new Size(3, 3), 0, 0);
//HighGui.imshow("GaussianBlur", clone);

Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
//HighGui.imshow("GRY", clone);
//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
int lowThresh=20;
////边缘检测
Imgproc.Canny(clone, clone,lowThresh, lowThresh*3,3);
//HighGui.imshow("Canny", clone);

Mat storage = new Mat();
/**
 * HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn, double min_theta, double max_theta)
 * image 原图
 * lines 霍夫线变换检测到线条的输出矢量,由(ρ,θ)表示
 * rho   以像素为单位的距离精度（直线搜索时的进步尺寸的单位半径）
 * theta 以弧度为单位的角度精度（直线搜索时的进步尺寸的角度单位）
 * threshold 累加平面的阈值参数（直线被识别时它在累加平面中必须达到的值）
 * srn    对于多尺度霍夫变换，这是第三个参数进步尺寸的除数距离。
  *        粗略累加器进步尺寸直接是rho，精确的累加器进步尺寸为rho/srn
 * min_theta 检测到的直线的最小角度
 * max_theta 测到的直线的最大角度
*/
Imgproc.HoughLines(clone, storage, 1, Math.PI/ 180.0, 200, 0, 0);
for (int x = 0; x < storage.rows(); x++) {
	double[] vec = storage.get(x, 0);

	double rho = vec[0];
	double theta = vec[1];

	Point pt1 = new Point();
	Point pt2 = new Point();

	double a = Math.cos(theta);
	double b = Math.sin(theta);

	double x0 = a * rho;
	double y0 = b * rho;

	pt1.x = Math.round(x0 + 1000 * (-b));
	pt1.y = Math.round(y0 + 1000 * (a));
	pt2.x = Math.round(x0 - 1000 * (-b));
	pt2.y = Math.round(y0 - 1000 * (a));
	Imgproc.line(image, pt1, pt2, new Scalar(0, 0, 255), 1, Imgproc.LINE_4, 0);
}

HighGui.imshow("houghLines", image);
}

public static void houghLinesP(Mat image){
Mat clone=image.clone();
Imgproc.GaussianBlur(clone, clone, new Size(3, 3), 0, 0);
//HighGui.imshow("GaussianBlur", clone);

Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
//HighGui.imshow("GRY", clone);
//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
int lowThresh=20;
////边缘检测
Imgproc.Canny(clone, clone,lowThresh, lowThresh*3,3);
//HighGui.imshow("Canny", clone);
Mat storage = new Mat();
/**
 * HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
 * image 原图
 * lines 霍夫线变换检测到线条的输出矢量,由(ρ,θ)表示
 * rho   以像素为单位的距离精度（直线搜索时的进步尺寸的单位半径）
 * theta 以弧度为单位的角度精度（直线搜索时的进步尺寸的角度单位）
 * threshold 累加平面的阈值参数（直线被识别时它在累加平面中必须达到的值）
 * minLineLength 最低线段的长度，比它短的线段不能被显示出来。
 * maxLineGap 允许将同一行点与点之间连接起来的最大距离
 */
Imgproc.HoughLinesP(clone, storage,  1, Math.PI / 180, 50, 0, 0);
for (int x = 0; x < storage.rows(); x++)
{
    double[] vec = storage.get(x, 0);
    double x1 = vec[0], y1 = vec[1], x2 = vec[2], y2 = vec[3];
    Point start = new Point(x1, y1);
    Point end = new Point(x2, y2);
    Imgproc.line(image, start, end, new Scalar(0, 0, 255, 255), 1, Imgproc.LINE_4, 0);
}
HighGui.imshow("HoughLinesP", image); 
}


public static void houghCircles(Mat image){
 Mat dst = image.clone();
 Imgproc.cvtColor(image, dst, Imgproc.COLOR_BGR2GRAY);

 Mat circles = new Mat();
 /**
  * HoughCircles(Mat image, Mat circles, int method, double dp, double minDist, double param1, double param2, int minRadius, int maxRadius)
  * image 原图
  * circles 检测到的圆的输出矢量，每个矢量由(x,y,radius)表示
  * method 检测方法，填HOUGH_GRADIENT即可
  * dp 用来检测圆心的累加器图像的分辨率于输入图像之比的倒数，
              允许创建一个比输入图像分辨率低的累加器。
               若dp=2，则累加器由输入图像一半大的宽度和高度）
  * minDist 霍夫变换检测到的圆的圆心之间的最小距离
  * param1  传给canny算子的高阈值，而低阈值为高阈值的一半
  * param2 在检测阶段圆心的累加器阈值
                       越小越可以检测不存在的圆，越大检测的圆更接近完美的圆形
  * minRadius 圆半径的最小值
  * maxRadius 圆半径的最大值
  */
 Imgproc.HoughCircles(dst, circles, Imgproc.HOUGH_GRADIENT, 1, 100, 440, 50, 0, 345);
 // Imgproc.HoughCircles(dst, circles, Imgproc.HOUGH_GRADIENT, 1, 100,
 // 440, 50, 0, 0);
 for (int i = 0; i < circles.cols(); i++)
 {
     double[] vCircle = circles.get(0, i);
     Point center = new Point(vCircle[0], vCircle[1]);
     int radius = (int) Math.round(vCircle[2]);
     // circle center
     Imgproc.circle(image, center, 3, new Scalar(0, 255, 0), -1, 8, 0);
     // circle outline
     Imgproc.circle(image, center, radius, new Scalar(0, 0, 255), 3, 8, 0);
 }
HighGui.imshow("HoughCircles", image); 
}


/**
 * 返回边缘检测之后的最大矩形,并返回
 * 
 * @param cannyMat
 *            Canny之后的mat矩阵
 * @return
 */
public static RotatedRect findMaxRect(Mat cannyMat) {
    Mat image=cannyMat.clone();
    List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat();
    int lowThresh=45;
    Imgproc.cvtColor(cannyMat, cannyMat,Imgproc.COLOR_BGR2GRAY);
    //边缘检测
    Imgproc.Canny(cannyMat, cannyMat,lowThresh, lowThresh*3,3);
    // 寻找轮廓
    Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);

    // 找出匹配到的最大轮廓
    double area = Imgproc.boundingRect(contours.get(0)).area();
    int index = 0;

    // 找出匹配到的最大轮廓
    for (int i = 0; i < contours.size(); i++) {
        double tempArea = Imgproc.boundingRect(contours.get(i)).area();
        if (tempArea > area) {
            area = tempArea;
            index = i;
        }
    }
    Random rng = new Random();
    Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
    Imgproc.drawContours(cannyMat, contours, index, color, 1, Imgproc.LINE_AA, hierarchy, 0, new Point());
    
    MatOfPoint2f matOfPoint2f = new MatOfPoint2f(contours.get(index).toArray());

    RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);
    HighGui.imshow("boundingRect", cannyMat);
   // rotation(image,rect);
    return rect;
}

/**
 * 旋转矩形
 * 
 * @param src
 *            mat矩阵
 * @param rect
 *            矩形
 * @return
 */
public static Mat rotation(Mat cannyMat, RotatedRect rect) {
    // 获取矩形的四个顶点
    Point[] rectPoint = new Point[4];
    rect.points(rectPoint);

    double angle = rect.angle + 10;

    Point center = rect.center;

    Mat CorrectImg = new Mat(cannyMat.size(), cannyMat.type());

    cannyMat.copyTo(CorrectImg);

    // 得到旋转矩阵算子
    Mat matrix = Imgproc.getRotationMatrix2D(center, angle, 0.8);

    Imgproc.warpAffine(CorrectImg, CorrectImg, matrix, CorrectImg.size(), 1, 0, new Scalar(0, 0, 0));

    HighGui.imshow("rotation", CorrectImg);
    
    return CorrectImg;
}

public static Mat getMaxRect2(Mat image) {
Mat clone=image.clone();
Mat src=image.clone();

int width = image.width();
int height = image.height();
int pointCount = width * height;
Mat points=image.reshape(3, pointCount);
points.convertTo(points,  CvType.CV_32F);

Imgproc.GaussianBlur(clone, clone, new Size(3, 3), 0, 0);
HighGui.imshow("GaussianBlur", clone);

Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
HighGui.imshow("GRY", clone);

int lowThresh=20;
//边缘检测
Imgproc.Canny(clone, clone,lowThresh, lowThresh*3,3);
HighGui.imshow("Canny1", clone);

List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();

// 寻找轮廓
Imgproc.findContours(clone, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE,
        new Point(0, 0));

// 找出匹配到的最大轮廓
double area = Imgproc.boundingRect(contours.get(0)).area();
int index = 0;

// 找出匹配到的最大轮廓
for (int i = 0; i < contours.size(); i++) {
    double tempArea = Imgproc.boundingRect(contours.get(i)).area();
    if (tempArea > area) {
        area = tempArea;
        index = i;
    }
}

MatOfPoint2f matOfPoint2f = new MatOfPoint2f(contours.get(index).toArray());

RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);
    
 // 获取矩形的四个顶点
Point[] rectpoint = new Point[4];
rect.points(rectpoint);

double line1 = Math.sqrt((rectpoint[1].y - rectpoint[0].y)*(rectpoint[1].y - rectpoint[0].y) + (rectpoint[1].x - rectpoint[0].x)*(rectpoint[1].x - rectpoint[0].x));
double line2 = Math.sqrt((rectpoint[3].y - rectpoint[0].y)*(rectpoint[3].y - rectpoint[0].y) + (rectpoint[3].x - rectpoint[0].x)*(rectpoint[3].x - rectpoint[0].x));

double angle = rect.angle;

if (line1 > line2) 
{
	angle = 90 + angle;
}

Point center = rect.center;
Mat CorrectImg = new Mat(clone.size(), clone.type());
clone.copyTo(CorrectImg);
// 得到旋转矩阵算子
Mat matrix = Imgproc.getRotationMatrix2D(center, angle, 1);
Imgproc.warpAffine(src, src, matrix, CorrectImg.size(), 1, 0, new Scalar(0, 0, 0));

HighGui.imshow("rotation", src);
return src;
}


public static void getMaxRect(Mat image) {
Mat clone=image.clone();
Mat src=image.clone();

int width = image.width();
int height = image.height();
int pointCount = width * height;
Mat points=image.reshape(3, pointCount);
points.convertTo(points,  CvType.CV_32F);

Imgproc.GaussianBlur(clone, clone, new Size(3, 3), 0, 0);
HighGui.imshow("GaussianBlur", clone);

Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
HighGui.imshow("GRY", clone);
//一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
int lowThresh=20;
//边缘检测
Imgproc.Canny(clone, clone,lowThresh, lowThresh*3,3);
HighGui.imshow("Canny", clone);

Mat storage = new Mat();
/**
	 * HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn, double min_theta, double max_theta)
	 * image 原图
	 * lines 霍夫线变换检测到线条的输出矢量,由(ρ,θ)表示
	 * rho   以像素为单位的距离精度（直线搜索时的进步尺寸的单位半径）
	 * theta 以弧度为单位的角度精度（直线搜索时的进步尺寸的角度单位）
	 * threshold 累加平面的阈值参数（直线被识别时它在累加平面中必须达到的值）
	 * srn    对于多尺度霍夫变换，这是第三个参数进步尺寸的除数距离。
        *        粗略累加器进步尺寸直接是rho，精确的累加器进步尺寸为rho/srn
	 * min_theta 检测到的直线的最小角度
	 * max_theta 测到的直线的最大角度
	 */
double sum = 0;
double angle=0;
Imgproc.HoughLines(clone, storage, 1, Math.PI/ 180.0, 200, 0, 0);
for (int x = 0; x < storage.rows(); x++) {
	double[] vec = storage.get(x, 0);

	double rho = vec[0];
	double theta = vec[1];

	Point pt1 = new Point();
	Point pt2 = new Point();

	double a = Math.cos(theta);
	double b = Math.sin(theta);

	double x0 = a * rho;
	double y0 = b * rho;

	pt1.x = Math.round(x0 + 1000 * (-b));
	pt1.y = Math.round(y0 + 1000 * (a));
	pt2.x = Math.round(x0 - 1000 * (-b));
	pt2.y = Math.round(y0 - 1000 * (a));
	sum += theta;
	Imgproc.line(clone, pt1, pt2, new Scalar(255, 255, 255, 255), 1, Imgproc.LINE_4, 0);
}

HighGui.imshow("houghLines", clone);

double average = sum / storage.rows(); //对所有角度求平均，这样做旋转效果会更好
angle = average/ Math.PI * 180 - 90;
System.out.println("average:"+angle);
Point center=new Point();
center.x=image.cols()/2;
center.y=image.rows()/2;

// 得到旋转矩阵算子
Mat matrix = Imgproc.getRotationMatrix2D(center, angle, 1);
Imgproc.warpAffine(src, src, matrix,src.size(), 1, 0, new Scalar(0, 0, 0));

HighGui.imshow("rotation", src);
}
//度数转换
public static double DegreeTrans(double theta)
{
double res = theta / Math.PI * 180;
return res;
}

public static void spin(Mat image) {
	Mat clone=image.clone();
	int width = image.width();
	int height = image.height();
	int pointCount = width * height;
	int dims = image.channels();
	Mat points=new Mat(pointCount, dims, CvType.CV_32F);
	int index=0;
    for(int i=0;i<height;i++)
    	for(int j=0;j<width;j++) {
    		index = i * width + j;
			points.put(index,0,image.get(i, j)[0]);
			points.put(index,1,image.get(i, j)[1]);
			points.put(index,2,image.get(i, j)[2]);
    	}
//	Mat points=image.reshape(3, pointCount);
//	points.convertTo(points,  CvType.CV_32F);
	
	// 2.使用K-means聚类；分离出背景色
    Mat bestLabels=new Mat();
	Mat centers=new Mat(3, 3, CvType.CV_32F);
	TermCriteria criteria=new TermCriteria(TermCriteria.COUNT + TermCriteria.EPS, 10, 0.1);
	Core.kmeans(points, 4, bestLabels, criteria, 3, Core.KMEANS_PP_CENTERS, centers);
	
	/**
	 *Scalar(255, 0, 0)  ----表示纯蓝色
     *Scalar(0, 255, 0) ----表示纯绿色
     *Scalar(0, 0, 255) ----表示纯红色
     *Scalar(255, 255, 0) ----表示青色
     *Scalar(0, 255, 255) ----表示黄色
     *Scalar(0, 0, 0) ----表示黑色
     *Scalar(255, 255, 255) ----表示白色
     */
	
	double[][] color={{0,0,255},{0,255,0},{255,0,0},{0, 255, 255}};
	
	Mat result = Mat.zeros(image.size(), image.type());
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			index = i * width + j;
			int lable = (int)bestLabels.get(index,0)[0];
			result.put(i, j, color[lable]);
		} 
	}
	HighGui.imshow("zero", result);
	
	
	Imgproc.GaussianBlur(image, image, new Size(3, 3), 0, 0);
	HighGui.imshow("GaussianBlur", image);
	Imgproc.cvtColor(result, result,Imgproc.COLOR_BGR2GRAY);
	HighGui.imshow("GRY", result);
    //一般来说，高阈值maxVal推荐是低阈值minVal的2~3倍
	int lowThresh=50;
//	//边缘检测
	Imgproc.Canny(result, result,lowThresh, lowThresh*3,3);
	HighGui.imshow("Canny", result);
	
//	image=result.clone();
	
	Mat storage = new Mat();
	/**
	 * HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn, double min_theta, double max_theta)
	 * image 原图
	 * lines 霍夫线变换检测到线条的输出矢量,由(ρ,θ)表示
	 * rho   以像素为单位的距离精度（直线搜索时的进步尺寸的单位半径）
	 * theta 以弧度为单位的角度精度（直线搜索时的进步尺寸的角度单位）
	 * threshold 累加平面的阈值参数（直线被识别时它在累加平面中必须达到的值）
	 * srn    对于多尺度霍夫变换，这是第三个参数进步尺寸的除数距离。
        *        粗略累加器进步尺寸直接是rho，精确的累加器进步尺寸为rho/srn
	 * min_theta 检测到的直线的最小角度
	 * max_theta 测到的直线的最大角度
	 */
	double sum = 0;
	Imgproc.HoughLines(result, storage, 1, Imgproc.HOUGH_GRADIENT/ 180.0, 200, 0, 0, 0, 80);
	for (int x = 0; x < storage.rows(); x++) {
		double[] vec = storage.get(x, 0);

		double rho = vec[0];
		double theta = vec[1];

		Point pt1 = new Point();
		Point pt2 = new Point();

		double a = Math.cos(theta);
		double b = Math.sin(theta);

		double x0 = a * rho;
		double y0 = b * rho;

		pt1.x = Math.round(x0 + 1000 * (-b));
		pt1.y = Math.round(y0 + 1000 * (a));
		pt2.x = Math.round(x0 - 1000 * (-b));
		pt2.y = Math.round(y0 - 1000 * (a));
		sum += theta;
		System.out.println(theta);
//		if (theta >= 0) {
			Imgproc.line(clone, pt1, pt2, new Scalar(255, 255, 255, 255), 1, Imgproc.LINE_4, 0);
//		}
	}
	
	HighGui.imshow("houghLines", clone);
	
	double average = sum / storage.rows(); //对所有角度求平均，这样做旋转效果会更好
	average= average/Imgproc.HOUGH_GRADIENT;
	
	Point center=new Point();
	center.x=image.cols()/2;
	center.y=image.rows()/2;
	//int length = 0;
	//length = (int) Math.sqrt(image.cols()*image.cols() + image.rows()*image.rows());
	
	// 得到旋转矩阵算子
    Mat matrix = Imgproc.getRotationMatrix2D(center, average, 1);

    Imgproc.warpAffine(clone, clone, matrix,clone.size(), 1, 0, new Scalar(0, 0, 0));

    HighGui.imshow("rotation", clone);
    
    //ImageUtil.card(image.clone());
    //Mat cut=cutRect(clone,clone);
    
    
}

/**
 * 把矫正后的图像切割出来
 * 
 * @param correctMat
 *            图像矫正后的Mat矩阵
 */
public static Mat cutRect(Mat image) {
Mat clone=image.clone();
Mat src=image.clone();

Imgproc.GaussianBlur(clone, clone, new Size(3, 3), 0, 0);
HighGui.imshow("GaussianBlur1", clone);

Imgproc.cvtColor(clone, clone,Imgproc.COLOR_BGR2GRAY);
HighGui.imshow("GRY1", clone);

int lowThresh=20;
//边缘检测
Imgproc.Canny(clone, clone,lowThresh, lowThresh*3,3);
HighGui.imshow("Canny1", clone);

List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();

// 寻找轮廓
Imgproc.findContours(clone, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
System.out.println("轮廓:"+contours.size());
// 找出匹配到的最大轮廓
double area = Imgproc.boundingRect(contours.get(0)).area();
int index = 0;

// 找出匹配到的最大轮廓
for (int i = 0; i < contours.size(); i++) {
    double tempArea = Imgproc.boundingRect(contours.get(i)).area();
    if (tempArea > area) {
        area = tempArea;
        index = i;
    }
}

MatOfPoint2f matOfPoint2f = new MatOfPoint2f(contours.get(index).toArray());

RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);

Mat temp = new Mat(src , rect.boundingRect());
Mat t = new Mat();
temp.copyTo(t);

HighGui.imshow("cut", temp);
return t;
}



public static void TestSift(Mat image1,Mat image2) {
//Imgproc.resize(image1, image1, new Size(image1.width()*0.2,image1.height()*0.2));
Mat clone1=image1.clone();
Mat src1=image1.clone();

Mat clone2=image2.clone();
Mat src2=image2.clone();

Imgproc.GaussianBlur(clone1, clone1, new Size(3, 3), 0, 0);
Imgproc.cvtColor(clone1, clone1,Imgproc.COLOR_BGR2GRAY);

Imgproc.GaussianBlur(clone2, clone2, new Size(3, 3), 0, 0);
Imgproc.cvtColor(clone2, clone2,Imgproc.COLOR_BGR2GRAY);

MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
Mat des1=new Mat();
Mat des2=new Mat();
//	匹配的点数
SIFT sift=SIFT.create(200);
	//提取对象关键点
//	sift.detect(clone1,keypoints1);
//	sift.detect(clone2,keypoints2);
	//提取描述子
//	sift.compute(clone1,keypoints1, des1);
//	sift.compute(clone2,keypoints2, des2);
	
sift.detectAndCompute(clone1, new Mat(), keypoints1, des1);
sift.detectAndCompute(clone2, new Mat(), keypoints2, des2);

MatOfDMatch md=new  MatOfDMatch();
FlannBasedMatcher matcher=FlannBasedMatcher.create();

matcher.match(des1, des2, md);

double maxDist = 0;
double minDist = 50;

DMatch[] mats = md.toArray();
List<DMatch> bestMatches= new ArrayList<DMatch>();

for (int i = 0; i < mats.length; i++) {
	double dist = mats[i].distance;
	if (dist < minDist) {
		minDist = dist;
	}
	if (dist > maxDist) {
		maxDist = dist;
	}
}
System.out.println("max_dist : "+maxDist);
System.out.println("min_dist : "+minDist);

double threshold = 3 * minDist;
double threshold2 = 2 * minDist;

if (threshold2 >= maxDist){
    threshold = minDist * 1.1;
}
else if (threshold >= maxDist){
    threshold = threshold2 * 1.4;
}

if(0d==threshold) {
	threshold=0.3*maxDist;
}

System.out.println("Threshold : "+threshold);

for (int i = 0; i < mats.length; i++)
{
	//distance越小,代表DMatch的匹配率越高,
    Double dist = (double) mats[i].distance;
    System.out.println(String.format(i + " match distance best : %s", dist));
    if (dist <= threshold)
    {
        bestMatches.add(mats[i]);
        System.out.println(String.format(i + " best match added : %s", dist));
    }
}
md.fromList(bestMatches);
Mat result=new Mat();
Features2d.drawMatches(src1, keypoints1, src2, keypoints2, md, result);
HighGui.imshow("SIFT", result);
}

public static void TestSiftKnn(Mat image1,Mat image2) {
	//FastFeatureDetector detector = FastFeatureDetector.create(FastFeatureDetector.FAST_N);
	Mat clone1=image1.clone();
	Mat src1=image1.clone();
	
	Mat clone2=image2.clone();
	Mat src2=image2.clone();

	Imgproc.GaussianBlur(clone1, clone1, new Size(3, 3), 0, 0);
	Imgproc.cvtColor(clone1, clone1,Imgproc.COLOR_BGR2GRAY);
	
	Imgproc.GaussianBlur(clone2, clone2, new Size(3, 3), 0, 0);
	Imgproc.cvtColor(clone2, clone2,Imgproc.COLOR_BGR2GRAY);
	
	MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
	MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
	Mat des1=new Mat();
	Mat des2=new Mat();
	SIFT sift=SIFT.create(200);
	//提取对象关键点
//	sift.detect(clone1,keypoints1);
//	sift.detect(clone2,keypoints2);
	//提取描述子
//	sift.compute(clone1,keypoints1, des1);
//	sift.compute(clone2,keypoints2, des2);
	
	sift.detectAndCompute(clone1, new Mat(), keypoints1, des1);
	sift.detectAndCompute(clone2, new Mat(), keypoints2, des2);
	
	MatOfDMatch md=new  MatOfDMatch();
	FlannBasedMatcher matcher=FlannBasedMatcher.create();
	
	//matcher.match(des1, des2, md);
	
	List<Mat> matList=new ArrayList<Mat>();
	List<MatOfDMatch> mdList=new ArrayList<MatOfDMatch>();
	matList.add(des1);
	
    matcher.add(matList);
    matcher.train();
    
    matcher.knnMatch(des2, mdList, 2);
    
    List<DMatch> dMatchList = new ArrayList<DMatch>();
    Iterator<MatOfDMatch> ite=mdList.iterator();
    while(ite.hasNext()) {
    	MatOfDMatch next=ite.next();
    	if (next.toArray()[0].distance < 0.2 * next.toArray()[1].distance) {
    		System.out.println(next.toArray()[0].distance+"->"+next.toArray()[1].distance);
    		dMatchList.add(next.toArray()[0]);
    	}else {
    		ite.remove();
    	}
    }
   md.fromList(dMatchList);
    
	Mat result=new Mat();
//	Features2d.drawMatches(src1, keypoints1, src2, keypoints2, md, result);
	Features2d.drawMatchesKnn(src1, keypoints1, src2, keypoints2, mdList, result);
	HighGui.imshow("SIFT", result);
}


public static void testYOLO(Mat image) throws IOException {
String config = "E:/opencv4.4.0/age-and-gender-classification/yolo/yolov4.cfg";
String weights = "E:/opencv4.4.0/age-and-gender-classification/yolo/yolov4.weights";
String classesFile = "E:/opencv4.4.0/age-and-gender-classification/yolo/coco.names";

List<String> classes = new ArrayList<String>(); // 存放类别的列表
InputStream in = new FileInputStream(classesFile);
int iAvail = in.available(); // 适用于本地一次读取多个字节时，返回得到的字节数。
byte[] bytes = new byte[iAvail];
in.read(bytes);
String allContent = new String(bytes); // 文件中的所有内容
String[] tempContent = allContent.trim().split("\n"); // allContent去除首尾空格，再按换行符分割。

// 遍历tempContent，添加到保存类别名的列表classes里。
for (int i = 0; i < tempContent.length; i++) {
	classes.add(tempContent[i]);
}
System.out.println(classes.size());

Net net = Dnn.readNetFromDarknet(config, weights);
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
//	Don't understand command line argument "-cl-no-subgroup-ifp"
//  net.setPreferableTarget(Dnn.DNN_TARGET_OPENCL);
// 改为cpu
net.setPreferableTarget(Dnn.DNN_TARGET_CPU);

Mat im = image.clone();
if (im.empty()) {
	System.out.println("图片加载失败");
}

Size sz = new Size(416, 416);

List<Mat> outs = new ArrayList<>();
//    Mat inputBlob = Dnn.blobFromImage(im, scale, sz, new Scalar(0), false, false);
//    net.setInput(inputBlob, "data"); 
Mat blob = Dnn.blobFromImage(image, 0.00392, sz, new Scalar(0), true, false); // We feed one frame of video into
																				// the network at a time, we
																				// have to convert the image to
																				// a blob. A blob is a
																				// pre-processed image that
																				// serves as the input.//
net.setInput(blob);
net.forward(outs, getOutputNames(net));

float confThreshold = 0.8f;
List<Rect2d> boxes = new ArrayList<Rect2d>(); // 矩形框列表
List<Integer> classIds = new ArrayList<Integer>(); // 类的序号列表
List<Float> confidences = new ArrayList<Float>(); // 置信度列表
for (int i = 0; i < outs.size(); ++i) {
	Mat level = outs.get(i);
	for (int j = 0; j < level.rows(); ++j) {
		Mat row = level.row(j);
		// [x,y,h,w,c,class1,class2] 所以是标号5
		Mat scores = row.colRange(5, level.cols());
		Core.MinMaxLocResult mm = Core.minMaxLoc(scores);
		float confidence = (float) mm.maxVal;
		Point classIdPoint = mm.maxLoc;

		int size = (int) (level.cols() * level.channels());
		float[] data = new float[size];
		level.get(j, 0, data);

		if (confidence > confThreshold) {
			float x = data[0]; // centerX 矩形中心点的X坐标
			float y = data[1]; // centerY 矩形中心点的Y坐标
			float width = data[2]; // 矩形框的宽
			float height = data[3]; // 矩形框的高
			float xLeftBottom = (x - width / 2) * im.cols(); // 矩形左下角点的X坐标
			float yLeftBottom = (y - height / 2) * im.rows(); // 矩形左下角点的Y坐标
			float xRightTop = (x + width / 2) * im.cols(); // 矩形右上角点的X坐标
			float yRightTop = (y + height / 2) * im.rows(); // 矩形右上角点的Y坐标

			// boxes列表填值 Rect对象，参数是两个点
			boxes.add(new Rect2d(new Point(xLeftBottom, yLeftBottom), new Point(xRightTop, yRightTop)));
			confidences.add(confidence);
			classIds.add((int) classIdPoint.x);

//              Imgproc.rectangle(image, new Point(xLeftBottom, yLeftBottom),
//  					new Point(xRightTop, yRightTop), new Scalar(255, 0, 0));
//              Imgproc.putText(image, classes.get((int)classIdPoint.x)+" "+confidence, new Point(xLeftBottom, yLeftBottom),Imgproc.FONT_HERSHEY_PLAIN,0.8, new Scalar(255, 0, 0));
		}
	}
}
System.out.println(classIds);
System.out.println(confidences);
System.out.println(boxes.size());
System.out.println(boxes);

float nmsThresh = 0.5f;
MatOfFloat confidences2 = new MatOfFloat(Converters.vector_float_to_Mat(confidences));
//    Rect2d[] boxesArray = boxes.toArray(new Rect2d[0]);
//    MatOfRect boxes2 = new MatOfRect(boxesArray);
MatOfRect2d m = new MatOfRect2d();
m.fromList(boxes);
MatOfInt indices = new MatOfInt();
/**
 * NMS(Non Maximum Suppression)，又名非极大值抑制 删除高度冗余的Rect2d
 */
Dnn.NMSBoxes(m, confidences2, confThreshold, nmsThresh, indices); // We draw the bounding boxes for objects
																	// here//

int[] ind = indices.toArray();
for (int i = 0; i < ind.length; ++i) {
	int index = ind[i];
	Rect2d box = boxes.get(index);
	int idGuy = classIds.get(index);
	float conf = confidences.get(index);
	Imgproc.rectangle(image, box.tl(), box.br(), new Scalar(255, 0, 0));
	Imgproc.putText(image, classes.get(idGuy) + " " + conf, box.tl(), Imgproc.FONT_HERSHEY_PLAIN, 0.8,
			new Scalar(255, 0, 0));
}
HighGui.imshow("yolo", image);

}

public static void TestOnnx(Mat blob) {
	String onnx_path="E:\\opencv4.4.0\\age-and-gender-classification\\onnx\\rvm_resnet50_fp32.onnx";
	Net onnNet=Dnn.readNetFromONNX(onnx_path);
	
	onnNet.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
//	Don't understand command line argument "-cl-no-subgroup-ifp"
//  net.setPreferableTarget(Dnn.DNN_TARGET_OPENCL);
// 改为cpu
	onnNet.setPreferableTarget(Dnn.DNN_TARGET_CPU);
	
	onnNet.setInput(blob);
	List<Mat> outs = new ArrayList<>();
	onnNet.forward(outs, getOutputNames(onnNet));
	if(outs.size()>0) {
		HighGui.imshow("onnx", outs.get(0));
	}
	
}

private static List<String> getOutputNames(Net net) {
	List<String> names = new ArrayList<>();

    List<Integer> outLayers = net.getUnconnectedOutLayers().toList();
    List<String> layersNames = net.getLayerNames();

    outLayers.forEach((item) -> names.add(layersNames.get(item - 1)));//unfold and create R-CNN layers from the loaded YOLO model//
    return names;
}
	public static void main(String[] args) throws Exception {
		new ImageUtil().run("");
		
		/*
		 * String msg=
		 * "\"data:image/jpeg;base64, \"";
		 * Mat mat=ImageUtil.base642Mat(msg); ImageUtil.Mat2Img(mat, ".jpeg",
		 * "E:\\1.jpeg");
		 * 
		 * Mat image =ImageUtil.base642Mat(msg); Mat check=ImageUtil.getFace(image);
		 * 
		 * ImageUtil.Mat2Img(check, ".jpeg", "E:\\2.jpeg");
		 */
	    
	    //String checkSucess=ImageUtil.Mat2BufImg(check, ".png").toString();
		String path="E:\\opencv4.4.0\\age-and-gender-classification\\img\\huge\\";
		/*
		File imgFile = new File(path);
		File listFile[] = imgFile.listFiles();
		for (File f : listFile) {
			if (f.getName().endsWith(".jpeg")) {
				Mat test = ImageUtil.imagePath2Mat(f.getPath());
				Mat check = ImageUtil.getFace(test);
				ImageUtil.Mat2Img(check, ".jpeg", path + UUID.randomUUID()+".jpeg");
			}
		}
		 */
		 //Mat _des=ImageUtil.imagePath2Mat("E:\\opencv4.4.0\\age-and-gender-classification\\img\\012.jpg");
		 //Mat check=ImageUtil.getFace(_des);
		 //ImageUtil.compareHist(_des);
		 //展示
		 //HighGui.imshow("本地视频识别人脸", ImageUtil.face3(_des));
//		 Mat mat =Imgcodecs.imread("E:\\opencv4.4.0\\age-and-gender-classification\\img\\sfz02.jpg");//fp.jfif sfz02.jpg 110.jpg 110.png
//		 HighGui.imshow("原始", mat);
//		 ImageUtil.getKmeans(mat);
//		 ImageUtil.face3(mat.clone());
//		 ImageUtil.testCanny(mat.clone());
//		 ImageUtil.testSobel(mat.clone());
//		 ImageUtil.testScharr(mat.clone());
//		 ImageUtil.testLaplacian(mat.clone());
//		 ImageUtil.testThreshold(mat.clone());
//		 ImageUtil.testAdaptiveThreshold(mat.clone());
//		 ImageUtil.testFindContours(mat.clone());
//		 ImageUtil.testTesseract(mat.clone());
//		 ImageUtil.card(mat.clone());
//		 ImageUtil.correct(mat.clone());
//		 ImageUtil.houghLines(Imgcodecs.imread("E:\\opencv4.4.0\\age-and-gender-classification\\img\\110.png"));
//		 ImageUtil.houghLinesP(Imgcodecs.imread("E:\\opencv4.4.0\\age-and-gender-classification\\img\\110.png"));
//		 ImageUtil.houghCircles(Imgcodecs.imread("E:\\opencv4.4.0\\age-and-gender-classification\\img\\part5_3.png"));
//		 ImageUtil.findMaxRect(mat.clone());
//		 ImageUtil.spin(mat.clone());
//		 ImageUtil.getMaxRect(mat.clone());
//		 ImageUtil.getMaxRect2(mat.clone());
//		 ImageUtil.card(ImageUtil.cutRect(ImageUtil.getMaxRect2(mat.clone())));
		
		 String imagePath="E:\\opencv4.4.0\\age-and-gender-classification\\img\\";
//		 Mat mat1 =Imgcodecs.imread(imagePath+"wz01.jpg");
//		 Mat mat2 =Imgcodecs.imread(imagePath+"wz02.jpeg");
//		 ImageUtil.TestSift(mat1,mat2);
//		 ImageUtil.TestSiftKnn(mat1,mat2);
		 //ImageUtil.testYOLO(Imgcodecs.imread(imagePath+"dog.jpg"));
		 //HighGui.waitKey();
		 //读
		 //Mat mat = Imgcodecs.imread("D:/opencv/1.jpg");
		 // 4. 写图片
	     //Imgcodecs.imwrite("D:/opencv/1-copy.jpg", mat);
		 ImageUtil.TestOnnx(Imgcodecs.imread(imagePath+"001.jpg"));
		 HighGui.waitKey();
		 
	}
	
	/*
	static{
			// 在使用OpenCV前必须加载Core.NATIVE_LIBRARY_NAME类,否则会报错
			logger.info("load OPENCV...");
	        logger.info("library:"+System.getProperty("java.library.path"));
			//System.loadLibrary("opencv_java412");
			//System.load("E:/opencv/opencv//build/java/x64/opencv_java412.dll");
			logger.info(Core.NATIVE_LIBRARY_NAME);
			System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
			
			FACEBOOK = new CascadeClassifier(FACEPATH);
			PROFILEFACEFACEBOOK = new CascadeClassifier(PROFILEFACEPATH);
			 
			AGENET = Dnn.readNetFromCaffe(AGE_TEXT, AGE_MODEL);
			
			GENDER=Dnn.readNetFromCaffe(GENDER_TEXT, GENDER_MODEL);
			
			try {
				initComPare("E:\\opencv4.4.0\\age-and-gender-classification\\img\\test_me");
			} catch (IOException e) {
				e.printStackTrace();
			}
	}*/

	/**
	 * 将jar包的文件复制到能读取的地方
	 * @param src
	 * @param target
	 * @throws IOException
	 */
	public void copyFile(String src,String target) throws IOException {
		ClassPathResource classPathResource = new ClassPathResource(src);
		InputStream inputStream =classPathResource.getInputStream();
		File docxFile = new File(target);
        // 使用common-io的工具类即可转换
        FileUtils.copyInputStreamToFile(inputStream,docxFile);
        inputStream.close();
	}
	
	@Override
	public void run(String... args) throws Exception {
//		if(StringUtil.isNUll(PATH)) {
//			PATH="E:\\opencv4.4.0\\";
//		}
		
//		// 在使用OpenCV前必须加载Core.NATIVE_LIBRARY_NAME类,否则会报错
//					logger.info("load OPENCV...");
//			        logger.info("library:"+System.getProperty("java.library.path"));
//					//System.loadLibrary("opencv_java412");
//					//System.load("E:/opencv/opencv//build/java/x64/opencv_java412.dll");
//					logger.info(Core.NATIVE_LIBRARY_NAME);
//					System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//					logger.info("加载opencv文件路径:"+PATH);
//				
//			        copyFile("/opencv/"+FACEPATH,PATH+FACEPATH);
//			        copyFile("/opencv/"+PROFILEFACEPATH,PATH+PROFILEFACEPATH);
//			        copyFile("/opencv/"+AGE_TEXT,PATH+AGE_TEXT);
//			        copyFile("/opencv/"+AGE_MODEL,PATH+AGE_MODEL);
//			        copyFile("/opencv/"+GENDER_TEXT,PATH+GENDER_TEXT);
//			        copyFile("/opencv/"+GENDER_MODEL,PATH+GENDER_MODEL);
//			        
//			        
//					FACEBOOK = new CascadeClassifier(PATH+FACEPATH);
//					PROFILEFACEFACEBOOK = new CascadeClassifier(PATH+PROFILEFACEPATH);
//					 
//					AGENET = Dnn.readNetFromCaffe(PATH+AGE_TEXT, PATH+AGE_MODEL);
//					
//					GENDER=Dnn.readNetFromCaffe(PATH+GENDER_TEXT, PATH+GENDER_MODEL);
//					
////					try {
////						srcMap.clear();
////						initComPare(SCAN_PATH);
////					} catch (IOException e) {
////						e.printStackTrace();
////					}
	}
}