使用java进行图片合成或重绘

背景:之前使用了java+opencv进行人脸识别,精度上基本达不到要求,估计还是得使用第三方的api接口,比如阿里的人脸识别、百度的人脸识别等,当然,都是要钱的,哈哈哈。下面给大家介绍以下图片合成的两种方式。

方法一,基于opencv进行图片合成,这边主要使用到的是Core、Mat、Rect、Imgcodecs这几个基类。(ps:看过我之前写的那篇关于人脸识别的博客的小伙伴,如果已经配置的opencv,那推荐使用这个方法哦)

package org.Litluecat.utils;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 基于opencv4.1.0进行图片合成
 * @author Litluecat
 */
public class ImageOverlapUtils {

    private static final Logger log = LoggerFactory.getLogger(ImageOverlapUtils.class);

    /**
     * 第一张图权重
     */
    private static final double bottomImgAlpha = 1.0;
    /**
     * 第二张图权重
     */
    private static final double topBetaImgAlpha = 1.0;
    /**
     * 额外添加权重
     */
    private static final double gamma = 0;
    /**
     * 默认合成起始x坐标为0
     */
    private static final int x = 0;
    /**
     * 默认合成起始y坐标为0
     */
    private static final int y = 0;

    /**
     * 图片覆盖重叠合成
     * @param bottomImgUrl 底部图片,必须大等于顶部图片
     * @param topImgUrl 顶部图片
     * @param saveImgUrl 最终合成图片保存位置
     * @param x 顶部图片合成的点x坐标
     * @param y 顶部图片合成的点y坐标
     * @return 合成成功,返回true
     */
    public static boolean imgOverlap(String bottomImgUrl, String topImgUrl, String saveImgUrl, int x, int y){
        log.info("开始图片合成。。。");
        long begin = System.currentTimeMillis();
        boolean isTrue = false;
        Mat bottomImage = Imgcodecs.imread(bottomImgUrl);
        Mat topImage = Imgcodecs.imread(topImgUrl);
        if(bottomImage.empty() || topImage.empty()){
            log.info("请确定待合成图片文件存在,bottomImage={},topImage={}", bottomImage, topImage);
        }else{
            if(bottomImage.cols() >= topImage.cols() && bottomImage.rows() >= topImage.rows()){
                Mat imageROI = new Mat(bottomImage,(new Rect(x,y,topImage.cols(),topImage.rows())));
                Core.addWeighted(imageROI, bottomImgAlpha, topImage, topBetaImgAlpha, gamma, imageROI);
                Imgcodecs.imwrite(saveImgUrl , bottomImage);
                isTrue = true;
            }else{
                log.info("请确定底部图片大于顶部图片,bottomImage={},topImage={}", bottomImage, topImage);
            }
        }
        log.info("图片合成结束,耗时:{}ms,saveImgUrl={}", (System.currentTimeMillis() - begin),saveImgUrl);
        return isTrue;
    }
    /**
     * 图片覆盖重叠合成,默认合成起始坐标为(0,0)
     * @param bottomImgUrl 底部图片,必须大等于顶部图片
     * @param topImgUrl 顶部图片
     * @param saveImgUrl 最终合成图片保存位置
     * @return 合成成功,返回true
     */
    public static boolean imgOverlap(String bottomImgUrl, String topImgUrl, String saveImgUrl){
        return imgOverlap(bottomImgUrl,topImgUrl,saveImgUrl,x,y);
    }
    
    //测试类
	public static void main(String[] args) {
		String endImgUrl = "图片地址";
	    //图片合成
       ImageOverlapUtils.imgOverlap(endImgUrl+"imageROI.jpg", endImgUrl+"img1.png", endImgUrl+"img2.png");
	}
}

方法二,是直接使用java的Graphics2D基类,好处就是不需要额外搭配环境,只需要简单的引入几个包就行了。(ps:这边还提供了一个base64编码转图片的功能,不需要的小伙伴可以不调用它,直接调用图片合成的方法就行了。)

package org.Litluecat.utils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.imageio.ImageIO;

/**
 * 基于java的图片合成重绘
 * @author Litluecat
 */
public class ImageUtils {
    private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);

    /**
     * 合成图片的后缀名
     */
    private static String formatName = "png";

    /**
     * 将图片写入缓存
     * @param imgName 图片地址
     * @return 返回图片缓存
     */
    private static BufferedImage loadImageLocal(String imgName) {
        try {
            return ImageIO.read(new File(imgName));
        } catch (IOException e) {
            log.info("图片写入缓存失败,img={},失败原因:{}", imgName, e.getMessage());
        }
        return null;
    }

    /**
     * 将合成后图片保存到本地
     * @param newImage 保存地址
     * @param img 合成后的图片缓存
     */
    private static void writeImageLocal(String newImage, BufferedImage img) {
        if (null != newImage && null != img) {
            try {
                File outPutFile = new File(newImage);
                ImageIO.write(img, formatName, outPutFile);
                log.info("图片合成结束,新图片地址:{}", newImage);
            } catch (IOException e) {
                log.info("图片保存失败,失败原因:{}", e.getMessage());
            }
        }
    }

    /**
     * 照片合成重绘
     * @param t 顶部照片
     * @param b 底部照片
     * @return 返回重绘后的照片
     */
    private static BufferedImage modifyImagetogeter(BufferedImage t, BufferedImage b) {
        try {
            int topW = t.getWidth();
            int topH = t.getHeight();
            int backgroundW = b.getWidth();
            int backgroundH = b.getHeight();
            if(backgroundW >= topW && backgroundH >= topH){
                int x = (backgroundW-topW) / 2;
                int y = (backgroundH-topH) / 2;
                Graphics2D g = b.createGraphics();
                g.drawImage(t, (x-1), (y-1), topW, topH, null);
                g.dispose();
            }else{
                log.info("底部照片大小比顶部照片小,请确定入参是否正确");
            }
        } catch (Exception e) {
            log.info("照片合成重绘失败,失败原因:{}",e.getMessage());
        }
        return b;
    }

    /**
     * 照片合成工具类
     * @param topImg 顶部照片
     * @param backgroundImg 底部照片
     * @param endImg 合成后照片存储地址
     */
    public static void imgOverlap(String topImg, String backgroundImg, String endImg){
        BufferedImage t = loadImageLocal(topImg);
        BufferedImage b = loadImageLocal(backgroundImg);
        if(null != t && null != b){
            writeImageLocal(endImg, modifyImagetogeter(t, b));
        }else{
            log.info("请确定图片缓存区不为空,topImgBuf={}, backgroundBuf={}", t, b);
        }

    }

    /**
     * 对字节数组字符串进行Base64解码并生成图片
     * @param imgStr 图片的base64编码
     * @param imgFilePath base64转图片的保存地址
     * @return
     */
    public static boolean GenerateImage(String imgStr, String imgFilePath) {
        // 图像数据为空
        if (StringUtils.isBlank(imgStr)) {
            log.info("图片的base64编码为空,imgStr={}", imgStr);
            return false;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] bytes = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {
                    // 调整异常数据
                    bytes[i] += 256;
                }
            }
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(bytes);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            log.info("图片的base64编码解析异常,异常原因:{}", e.getMessage());
            return false;
        }
    }

    /**
     * 测试类
     * @param args
     */
    public static void main(String[] args) {
        log.info("开始图片合成");
        long begin = System.currentTimeMillis();
        //签名图片地址
        String signatureImgUtl = "C:\\Users\\lenovo\\Desktop\\qm.jpg";
        //签名图片的base64编码
        String imgBase64 = "base64编码";
        //banse64编码转图片
        if(ImageUtils.GenerateImage(imgBase64, signatureImgUtl)){
            //指纹图片地址
            String fingerprintImgUrl = "C:\\Users\\lenovo\\Desktop\\img1.png";
            //合成后的图片保存地址
            String newImgUrl = "C:\\Users\\lenovo\\Desktop\\img5.png";
            //开始图片合成
            ImageUtils.imgOverlap(fingerprintImgUrl, signatureImgUtl, newImgUrl);
        }else{
            log.info("图片的base64编码解析异常");
        }
        log.info("图片合成结束,耗时:{}ms", (System.currentTimeMillis() - begin));
    }
}

总结:如果已经配置了opencv,就尽量去使用它,功能还是蛮多了,可以去多熟悉它。如果只是为了实现图片合成这个功能,那就没必要特意去配置opencv,性价比很低,直接使用java进行图片合成就行了。

你可能感兴趣的:(java,java,opencv)