Java程序为身份证照片添加马赛克

一、创建枚举类型

package com.chuangtu;

/**
 * 比例枚举:此枚举是计算出的各个位置在身份证上的比例信息
 */
public enum ProportioEnum {

	//身份证号
	idNumber(0.5416666666666667, 0.0805467372134039, 0.3157894736842105, 0.8055784832451499),
	//姓名
	name(0.2460850111856823, 0.0986524822695035, 0.1733780760626398, 0.104113475177305),
	//性别
	gender(0.0503355704697987, 0.0797872340425532, 0.1733780760626398, 0.2446808510638298),
	//民族
	national(0.0503355704697987, 0.0797872340425532,0.3713646532438479, 0.2446808510638298),
	//生日
	birthday(0.3557046979865772, 0.0797872340425532,0.1733780760626398,0.3687943262411348),
	//地址
	address(0.421834451901566, 0.2482269503546099, 0.1733780760626398, 0.4893617021276596),
	//面部
	face(0,0,0,0)
	;


	ProportioEnum(double targetWith , double targetHeight, double iocationWith, double iocationHeight) {
		this.targetWith = targetWith;
		this.targetHeight = targetHeight;
		this.iocationWith = iocationWith;
		this.iocationHeight = iocationHeight;
	}

	private double targetWith;
	private double targetHeight;
	private double iocationWith;
	private double iocationHeight;

	/**
	 * 获取目标对象的宽度比
	 * @return
	 */
	public double getTargetWith(){
		return targetWith;
	}

	/**
	 * 获取目标对象的高度比
	 * @return
	 */
	public double getTargetHeight(){
		return targetHeight;
	}
	/**
	 * 获取位置坐标的宽度比
	 * @return
	 */
	public double getIocationWith(){
		return iocationWith;
	}
	/**
	 * 获取位置坐标的高度比
	 * @return
	 */
	public double getIocationHeight(){
		return iocationHeight;
	}
}

二、添加打码工具类

package com.creatoo.hn.util;

import com.creatoo.hn.util.enums.ProportioEnum;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Map;

/**
 * 图片处理类
 * @author hty
 *
 */
public class IdCardUtil {


	/**
	 * 身份证脱敏方法
	 * @param file 要脱敏的图片文件
	 * @param p 身份证的四个顶点坐标
	 * @param center 人脸中心点坐标
	 * @param proportion 要遮罩的部分(姓名、性别、民族、生日、地址、身份证号、人脸)
	 * @param map 人脸尺寸
	 * @return
	 * @throws Exception
	 */
	public static File desensitization(File file, Point[] p, Point center, ProportioEnum proportion, Map<String,Double> map) throws Exception{

        if ("face".equals(proportion.name())){
            //获取马赛克区域的长和宽
            double width = map.get("width");
            double height = map.get("height");
            int radius = 0;
            if (width>height){
                radius = (int) (width/2);
            }else {
                radius = (int) (height/2);
            }
            //获取人脸顶点坐标
            Point[] points = getFacePoint(radius,center);
            //给人脸加马赛克
            File faceFile = mosaicFace(file,points,radius,20);
            if (faceFile != null) {
                file = faceFile;
                return file;
            }
        }

            int firstIdex = getInterval(p, center);

            Point first = p[firstIdex];
            //获取身份证长度和宽度
            double with = getLength(first, p[getIndex(firstIdex, p.length, 1)]);
            double height = getLength(first, p[getIndex(firstIdex, p.length, p.length - 1)]);
            double xAngle = getAngle(first, p[getIndex(firstIdex, p.length, 1)]);
            double yAngle = getAngle(first, p[getIndex(firstIdex, p.length, p.length - 1)]);

            //身份证号长宽
            double targetWith = with * proportion.getTargetWith();
            double targetHeight = height * proportion.getTargetHeight();

            //位置坐标长宽
            double locationWith = with * proportion.getIocationWith();
            double locationHeight = height * proportion.getIocationHeight();

            double y = first.y + locationWith * Math.sin(xAngle) + locationHeight * Math.sin(yAngle);
            double x = (first.x + locationHeight * Math.cos(yAngle)) + locationWith * Math.cos(xAngle);

            Point a = new Point(x, y);
            Point b = getPoint(a, targetWith, xAngle);
            Point c = getPoint(b, targetHeight, yAngle);
            Point d = getPoint(a, targetHeight, yAngle);

            return mosaic(file, a, b, c, d, 15);

    }

	/**
	 * 给图片指定位置打马赛克

	 * @param mosaicSize 马赛克尺寸,即每个矩形的长宽
	 * @return
	 * @throws IOException
	 */
	@SuppressWarnings("static-access")
	public static File mosaic(File file, Point p1, Point p2, Point p3, Point p4, int mosaicSize) throws IOException {
		if (!file.isFile()) {
			throw new RuntimeException("请上传文件");
		}

		int index = file.getName().lastIndexOf(".");
		if (index == -1){
			throw new RuntimeException("文件格式不正确");
		}
		String suffix = file.getName().substring(index + 1);
		if (!suffix.equalsIgnoreCase("png") && !suffix.equalsIgnoreCase("jpg") &&
				!suffix.equalsIgnoreCase("gif") && !suffix.equalsIgnoreCase("bmp")) {
			throw new RuntimeException("文件格式不正确");
		}


		BufferedImage bi = ImageIO.read(file); // 读取该图片
		BufferedImage spinImage = new BufferedImage(bi.getWidth(),
				bi.getHeight(), bi.TYPE_INT_RGB);

		double width = getLength(p1, p2);
		double height = getLength(p1, p4);

		//2. 设置各方向绘制的马赛克块个数
		int xcount = (int)Math.floor( width/ mosaicSize) +1; // 方向绘制个数
		int ycount = (int)Math.floor( height/ mosaicSize) +1; // y方向绘制个数


		//3. 绘制马赛克(绘制矩形并填充颜色)
		Graphics2D gs = spinImage.createGraphics();
		gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		gs.drawImage(bi, 0, 0, null);
		double xAngle = getAngle(p1 ,p2);
		double yAngle = getAngle(p1 ,p4);
		Point tmp = new Point(p1.x, p1.y);


		for (int i = 0; i < ycount; i++) {

			Point newline = tmp;
			for (int j = 0; j < xcount; j++) {
				//马赛克矩形格大小
				double mwidth = mosaicSize;
				double mheight = mosaicSize;
				//矩形颜色取中心像素点RGB值
				double centerX = tmp.x;
				double centerY = tmp.y;
				if (mwidth % 2 == 0) {
					centerX += mwidth / 2;
				} else {
					centerX += (mwidth - 1) / 2;
				}
				if (mheight % 2 == 0) {
					centerY += mheight / 2;
				} else {
					centerY += (mheight - 1) / 2;
				}
				Color color = new Color(bi.getRGB((int) centerX, (int) centerY));
				gs.setColor(color);
				Point tmp2 = getPoint(tmp, mwidth,xAngle);
				Point tmp3 = getPoint(tmp2, mheight,yAngle);
				Point tmp4 = getPoint(tmp, mheight,yAngle);

				if(j == 0){
					newline = tmp4;
				}

				gs.fill(getCutPath(tmp, tmp2, tmp3, tmp4));
				tmp = tmp2;
			}
			tmp = newline;
		}

		gs.dispose();

		File outputfile = new File(IDUtils.getID()+"."+suffix);
		ImageIO.write(spinImage, suffix, outputfile);
		return outputfile;
	}

    /**
     * 给人脸区域添加马赛克
     * @param file 要脱敏的图片文件
     * @param points 人脸四个顶点的坐标
     * @param radius 打码区域边长的一半
     * @param mosaicSize 马赛克粒度
     * @return 处理后的图片文件
     * @throws IOException
     */
    private static File mosaicFace(File file,Point[] points,Integer radius,int mosaicSize) throws IOException {

        if (!file.isFile()) {
            throw new RuntimeException("传入的不是文件");
        }
        int index = file.getName().lastIndexOf(".");
        String suffix = file.getName().substring(index + 1);
        // 读取图片文件
        BufferedImage bi = ImageIO.read(file);
        BufferedImage spinImage = new BufferedImage(bi.getWidth(),
                bi.getHeight(), BufferedImage.TYPE_INT_RGB);

        // 设置绘制的马赛克块个数
        int count = (int)Math.floor( radius*2/ mosaicSize) +1; // 方向绘制个数

        //创建一个 Graphics2D ,可以用来绘制这个 BufferedImage 。
        Graphics2D gs = spinImage.createGraphics();
        gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        gs.drawImage(bi, 0, 0, null);

        //起点坐标
        Point tmp = points[0];

        for (int i = 0; i < count; i++) {//行
            Point newline = tmp;
            for (int j = 0; j < count; j++) {//列
                //马赛克矩形格大小
                double mwidth = mosaicSize;
                //取矩形中心点坐标
                double centerX = tmp.x;
                double centerY = tmp.y;
                if (mwidth % 2 == 0) {
                    centerX += mwidth / 2;
                    centerY += mwidth / 2;
                } else {
                    centerX += (mwidth - 1) / 2;
                    centerY += (mwidth - 1) / 2;
                }
                //取矩形中心点坐标颜色
                Color color = new Color(bi.getRGB((int) centerX, (int) centerY));
                gs.setColor(color);
                //获取每个小方格的顶点坐标
                Point tmp2 = new Point(tmp.x+mwidth,tmp.y);
                Point tmp3 = new Point(tmp.x+mwidth,tmp.y+mwidth);
                Point tmp4 = new Point(tmp.x,tmp.y+mwidth);

                //控制第二行的起点
                if(j == 0){
                    newline = tmp4;
                }
                //填充图形
                gs.fill(getCutPath(tmp, tmp2, tmp3, tmp4));
                tmp = tmp2;
            }
            tmp = newline;
        }
        //关闭资源
        gs.dispose();
        //将文件持久化到本地
        File outputfile = new File(IDUtils.getID()+"."+suffix);
        ImageIO.write(spinImage, suffix, outputfile);
        return outputfile;
    }

    /**
     * 获取人脸顶点坐标
     * @param radius 尺寸信息
     * @param center 人脸中心点坐标
     * @return
     */
    private static Point[] getFacePoint(int radius, Point center) {
        Point[] points = new Point[4];
        Point p1 = new Point(center.x - radius, center.y - radius);
        Point p2 = new Point(center.x + radius, center.y - radius);
        Point p3 = new Point(center.x + radius, center.y + radius);
        Point p4 = new Point(center.x - radius, center.y + radius);

        points[0] = p1;
        points[1] = p2;
        points[2] = p3;
        points[3] = p4;
        return points;
    }


	/**
	 * 获取区域路径
	 *
	 * @return
	 */
	public static GeneralPath getCutPath(Point p1, Point p2, Point p3, Point p4) {
		GeneralPath path = new GeneralPath();
		path.moveTo(p1.x, p1.y);
		path.lineTo(p2.x, p2.y);
		path.lineTo(p3.x, p3.y);
		path.lineTo(p4.x, p4.y);
		path.lineTo(p1.x, p1.y);

		return path;
	}


	/**
	 * 与x轴角度
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static double getAngle(Point p1, Point p2) {
		return  Math.atan2((p2.y - p1.y), (p2.x - p1.x));
	}


	/**
	 * 获取指定区域的点坐标
	 * @param p
	 * @param length
	 * @param angle
	 * @return
	 */
	public static Point getPoint(Point p, double length, double angle) {
		double x = length * Math.cos(angle) + p.x;
		double y = (length * Math.sin(angle)) + p.y;

		return new Point(x,y);
	}


	/**
	 * 获取两点间长度
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static double getLength(Point p1, Point p2) {
		return Math.sqrt(Math.abs((p1.getX() - p2.getX())* (p1.getX() - p2.getX())+(p1.getY() - p2.getY()) * (p1.getY() - p2.getY())));

	}
	
	
	/**
	 * 返回身份证左上角坐标
	 * @return
			 */
	public static  int getIndex(int current,int length, int size){
		int next = current;

		for(int i =0; i<size; i++){
			if(next == length -1){
				next = 0;
			}else {
				next ++;
			}

		}

		return next;

	}

	/**
	 * 返回身份证左上角坐标
	 * @param center
	 * @return
	 */
	public static  int getInterval(Point[] p, Point center){

		double with = getLength(p[0], p[1]);
		double height = getLength(p[0], p[3]);

		// p[0] - p[1], p[2]-p[3]
		if(with > height){
			Point minpoint = minpoint(p[0], p[1]);
			Point minpoint2 = minpoint(p[2], p[3]);
			if(check(p[0],minpoint,minpoint2,p[3],center)){
				return 2;
			}else {
				return 0;
			}

			//p1-p4 , p2-p3
		}else {
			Point minpoint = minpoint(p[0], p[3]);
			Point minpoint2 = minpoint(p[1], p[2]);
			if(check(p[0],minpoint,minpoint2,p[1],center)){
				return 3;
			}else {
				return 1;
			}
		}

	}


	//两点坐标的中点
	public static Point minpoint(Point a,Point b){
		double x=(a.getX()+b.getX())/2;
		double y=(a.getY()+b.getY())/2;
		return new Point(x, y);
	}


	/**
	 * 一个点是否在多边形内
	 * @return
	 */
	private static boolean check(Point p1, Point p2, Point p3, Point p4, Point center) {
		Point2D.Double center2 = new Point2D.Double(center.x, center.y);
		Point2D.Double d1 = new Point2D.Double(p1.x, p1.y);
		Point2D.Double d2 = new Point2D.Double(p2.x, p2.y);
		Point2D.Double d3 = new Point2D.Double(p3.x, p3.y);
		Point2D.Double d4 = new Point2D.Double(p4.x, p4.y);
		
		GeneralPath peneralPath = new GeneralPath();

		peneralPath.moveTo(d1.x, d1.y);
		peneralPath.lineTo(d2.x, d2.y);
		peneralPath.lineTo(d3.x, d3.y);
		peneralPath.lineTo(d4.x, d4.y);
		peneralPath.lineTo(d1.x, d1.y);
		peneralPath.closePath();
		// 测试指定的 Point2D 是否在 Shape 的边界内。
		return peneralPath.contains(center2);
	}

}

测试

package com.chuangtu;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class Test {


	public static void main(String[] args) {
		try {
			
			//============= 通过阿里orc服务获取参数========
			//身份证四个顶点坐标
			Point[] p =  new Point[4];
			p[0] = new Point(19,463);
			p[1] = new Point(931,460);
			p[2] = new Point(931,1044);
			p[3] = new Point(25,1046);
			//头像中心点坐标
			Point center = new Point(721,728);

			//人脸尺寸
            Map<String,Double> map = new HashMap<String, Double>();
            map.put("width",(double)324);
            map.put("height",(double)268);
            
           
            File file = new File("C:\\Users\\PC0825\\Desktop\\zhaopian(1)\\2.jpg");
            //脸部打码
            File desensitizationFace = IdCardUtil.desensitization(file, p, center, ProportioEnum.face,map);
            //身份证号打码
            File desensitizationId = IdCardUtil.desensitization(desensitizationFace, p, center, ProportioEnum.idNumber,map);
            //姓名打码
            File desensitizationName = IdCardUtil.desensitization(desensitizationId, p, center, ProportioEnum.name,map);
            //住址打码
            File desensitization = IdCardUtil.desensitization(desensitizationName, p, center, ProportioEnum.address,map);

			//文件持久化
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(desensitization));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\fillOval2.jpg"));
            byte[]bys = new byte[1024];
            int len;
            while ((len=bis.read(bys))!=-1){
                bos.write(bys,0,len);
            }
            //关闭流资源
            bos.close();
            bis.close();
            //删除项目中产生的图片文件
            desensitizationFace.deleteOnExit();
            desensitizationId.deleteOnExit();
            desensitizationName.deleteOnExit();
            desensitization.deleteOnExit();
        } catch (Exception e) {
			System.err.println("出错了!!!!");
			e.printStackTrace();
		}

	}

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值