java绘制标注框,注册字体

文章目录

    • 场景
    • 思路
    • 步骤
      • 1.注册字体
      • 2.绘制标注框保存文本
      • 3.效果如下:

场景

有个项目需要在java的后台将AI算法的标识框,置信度值,画到上传的报警图片上。以前都在算法部分画,但是效率有点低,所以传过来原始的图片(也会用来训练用)和标识的位置信息移到前端或者java应用端画,但是我这边又有推送给第三方平台的业务,不得不在java端画了。 (“前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。人工智能入门到精通。”)因此,今天介绍下我这边是如何实现的:

思路

测试: 可以使用openCV,但是这个玩意因为引入之后发现jar包太大了,不得不放弃了,使用java自带的画笔Graphics2D去实现,画出来后保存到本地查看效果。
生产: 生产上,不需要保存本地了,直接将原始图片画完标识框等信息,返回base64推送给第三方接口就可了。

步骤

1.注册字体

这个纯属经验,以前遇到windows上可以使用微软雅黑,linux不可以的情况,一种方式是将windows的字体放入linux系统上,但是对于已经部署的项目来说不是很友好,另一种方式是将微软雅黑字体放入java项目的resource文件下,然后注册进去,我是用的这种方式:
java绘制标注框,注册字体_第1张图片
可以在这个网站现在微软雅黑字体,重命名即可。
java绘制标注框,注册字体_第2张图片
代码如下:


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

public class InitFontUtil {


    /**
     * 注册字体
     *
     * @return
     */
    public static Map<String, Object> getGraphicsFontAndEnv() {
        GraphicsEnvironment localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Map<String, Object> map = new HashMap<>();
        Font font = null;
        Font font30 = null;
//        if(isWindows()){
//            String typeface = "Microsoft YaHe";
//            return  new Font(typeface, Font.BOLD, 24);
//        }
        try {
//            if (localGraphicsEnvironment.getAvailableFontFamilyNames() == null || localGraphicsEnvironment.getAvailableFontFamilyNames().length == 0) {
            font = Font.createFont(Font.TRUETYPE_FONT, InitFontUtil.class.getResourceAsStream("/msyhbd.ttf"));
            font30 = Font.createFont(Font.TRUETYPE_FONT, InitFontUtil.class.getResourceAsStream("/msyhbd.ttf"));
            font = font.deriveFont(Font.BOLD, 24f);
            font30 = font30.deriveFont(Font.BOLD, 30f);
            localGraphicsEnvironment.registerFont(font);
            localGraphicsEnvironment.registerFont(font30);
            map.put("font24", font);
            map.put("font30", font30);
            map.put("env", localGraphicsEnvironment);
//            } else {
//                if (!isWindows()) {
//                    Font[] allFonts = localGraphicsEnvironment.getAllFonts();
//                    if (allFonts != null && allFonts.length > 0) {
//                        Font nowFont = allFonts[0];
//                        return nowFont;
//                    }
//                }
//            }
        } catch (FontFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 是否是windows系统
     *
     * @return
     */
    public static boolean isWindows() {
        return System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") >= 0 ? true : false;
    }

}

2.绘制标注框保存文本

PositionDto 标注框位置信息实体

package com.edgemgmt.system.domain;

import java.math.BigDecimal;

public class PositionDto {
    private BigDecimal x;
    private BigDecimal w;
    private BigDecimal h;
    private String txt;
    private BigDecimal y;
    private BigDecimal conf;


    public BigDecimal getX() {
        return x;
    }

    public void setX(BigDecimal x) {
        this.x = x;
    }

    public BigDecimal getW() {
        return w;
    }

    public void setW(BigDecimal w) {
        this.w = w;
    }

    public BigDecimal getH() {
        return h;
    }

    public void setH(BigDecimal h) {
        this.h = h;
    }

    public String getTxt() {
        return txt;
    }

    public void setTxt(String txt) {
        this.txt = txt;
    }

    public BigDecimal getY() {
        return y;
    }

    public void setY(BigDecimal y) {
        this.y = y;
    }

    public BigDecimal getConf() {
        return conf;
    }

    public void setConf(BigDecimal conf) {
        this.conf = conf;
    }

    @Override
    public String toString() {
        return "PositionDto{" +
                "x=" + x +
                ", w=" + w +
                ", h=" + h +
                ", txt='" + txt + '\'' +
                ", y=" + y +
                ", conf=" + conf +
                '}';
    }
}

ImageBase64Utils 画框的工具类

package com.edgemgmt.system.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.edgemgmt.system.domain.PositionDto;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.font.FontDesignMetrics;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/*
 //img = cv2.rectangle(
        //        img,
        //        (int(x * width), int(y * height)),
        //        (int(x * width + w * width), int(y * height + h * height)),
        //        (0, 0, 255),
        //        5
        //      )
*/
public class ImageBase64Utils {
    private static final Logger log = LoggerFactory.getLogger(ImageBase64Utils.class);
    private static String frefix = "png";
    public static String typeface = "Microsoft YaHei";


    //[{"txt":"异常烟火","w":0.18281249701976776,"h":0.3125,"x":0.08124999701976776,"y":0.42934781312942505,"conf":0.8802329301834106}]
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\002.jpg");
        String s = "[{\"txt\":\"人员打电话\",\"w\":0.03072916716337204,\"h\":0.08796296268701553,\"x\":0.5708333253860474,\"y\":0.39629629254341125,\"conf\":0.7689861059188843},{\"txt\":\"人员打电话\",\"w\":0.05104166641831398,\"h\":0.10555555671453476,\"x\":0.5510416626930237,\"y\":0.3444444537162781,\"conf\":0.8071668148040771}]";
       //String s1 = "[{\"txt\":\"未戴口罩\",\"w\":0.07187499850988388,\"h\":0.17499999701976776,\"x\":0.6848958134651184,\"y\":0.19722221791744232,\"conf\":0.8080925941467285}]";
        String s1 = getBase64FromFile(file, s);
        log.info(s1);
    }

    public static String getBase64FromFile(File file, String positionJsonStr) {
        try {
            BufferedImage bimg = ImageIO.read(new FileInputStream(file));
            //大小
            long size = file.length() / 1024;
            // 宽度
            int width = bimg.getWidth();
            // 高度
            int height = bimg.getHeight();
            Graphics2D g2d = getG2d(bimg);
            log.info("图片大小:{} kb;图片宽度:{} 像素;图片高度:{} 像素", size, width, height);

            return convertBase64FromImage(bimg, width, height, g2d, positionJsonStr);
        } catch (Exception e) {
            return null;
        }

    }

    //BufferedImage 转base64
    public static String getBase64FromImage(BufferedImage img) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            // 设置图片的格式
            ImageIO.write(img, "jpg", stream);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] bytes = Base64.encodeBase64(stream.toByteArray());
        String base64 = new String(bytes);
        return "data:image/jpeg;base64," + base64;
    }


    private static String convertBase64FromImage(BufferedImage bimg, int width, int height, Graphics2D g2d, String s) {
        Type type = new TypeReference<List<PositionDto>>() {
        }.getType();
        List<PositionDto> list = JSON.parseObject(s, type);

        for (int i = 0; i < list.size(); i++) {
            PositionDto item = list.get(i);

            //获取 标注框的 起点(x,y)
            BigDecimal boxX = item.getX().multiply(new BigDecimal(width));
            BigDecimal boxY = item.getY().multiply(new BigDecimal(height));
            //获取 标注框的 宽度和高度
            BigDecimal boxWidth = (item.getW()).multiply(new BigDecimal(width));
            BigDecimal boxHeight = (item.getH()).multiply(new BigDecimal(height));
            // 标注框设置宽度
            g2d.setStroke(new BasicStroke(5));
            g2d.setColor(Color.RED);
            //绘制标注框
            g2d.drawRect(boxX.intValue(), boxY.intValue(), boxWidth.intValue(), boxHeight.intValue() - 5);

            //绘制文字背景
            //            //获取文字 起点 (x,y)
            int fontBgX = boxX.intValue();
            int fontBgY = boxY.intValue() - 3;
//            Font font = new Font(typeface, Font.BOLD, 30);
            Map<String, Object> graphicsFontAndEnv = InitFontUtil.getGraphicsFontAndEnv();
            Font font = (Font) graphicsFontAndEnv.get("font30");
            String content = item.getConf().setScale(2, BigDecimal.ROUND_DOWN).toString();
            FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
            int contentWidth = getWordWidth(font, content);//计算文字的宽
            int contentHeight = metrics.getHeight();//计算高

            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            //设置背影为黑色
            g2d.setColor(Color.BLACK);
            g2d.fillRect(fontBgX, fontBgY - 30, contentWidth, 30);
            //绘制文字的底框
            g2d.setFont(font);
            g2d.setColor(Color.WHITE);
            g2d.drawString(content, fontBgX, fontBgY - 3);//图片上写文字
        }
        // 释放对象
        g2d.dispose();
        String base64FromImage = getBase64FromImage(bimg);
        try {
            InputStream inputStream = upload(bimg);
            writeToLocal("D://test" + 4 + ".png", inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return base64FromImage;
    }

    //字体的一段字符串的宽
    public static int getWordWidth(Font font, String content) {
        FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
        int width = 0;
        for (int i = 0; i < content.length(); i++) {
            width += metrics.charWidth(content.charAt(i));
        }
        return width;
    }


    private static void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        downloadFile.close();
        input.close();
    }

    public static InputStream upload(BufferedImage bimg) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        //保存新图片
        ImageIO.write(bimg, frefix, os);
        InputStream is = new ByteArrayInputStream(os.toByteArray());
        return is;
    }

    private static Graphics2D getG2d(BufferedImage bimg) {
        //得到Graphics2D 对象
        Graphics2D g2d = (Graphics2D) bimg.getGraphics();
        // 抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        return g2d;
    }


}


这个类有3个作用

  • 绘制标注框,字体背景图和字体。
  • 写入绘制后的图片到本地
  • 将绘制后的图片转成base64

3.效果如下:

java绘制标注框,注册字体_第3张图片
控制台也打印了base64字符,以后前端可以直接用base64显示图片:
java绘制标注框,注册字体_第4张图片


自此,java就实现了标注框的功能。
在需要调用的地方直接这样调用即可

 //画框的base64赋值
 //原始图片 sysEventRecord.getPicUrl()
 //标注框的位置信息 sysEventRecord.getPosition() 
 sysEventRecordVo.setPicUrlBase64(ImageBase64Utils.getBase64FromFile(new File(sysEventRecord.getPicUrl()), sysEventRecord.getPosition()));

开通了个微信公众号:
搜索: 怒放de每一天
后续可能不定时推送相关文章,期待和大家一起成长!!

在这里插入图片描述


大功告成!!

你可能感兴趣的:(java基础,spring家族,java,开发语言)