文章目录
- 场景
- 思路
- 步骤
- 1.注册字体
- 2.绘制标注框保存文本
- 3.效果如下:
场景
有个项目需要在java的后台将AI算法的标识框,置信度值,画到上传的报警图片上。以前都在算法部分画,但是效率有点低,所以传过来原始的图片(也会用来训练用)和标识的位置信息移到前端或者java应用端画,但是我这边又有推送给第三方平台的业务,不得不在java端画了。 (“前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。人工智能入门到精通。”)因此,今天介绍下我这边是如何实现的:
思路
测试: 可以使用openCV,但是这个玩意因为引入之后发现jar包太大了,不得不放弃了,使用java自带的画笔Graphics2D
去实现,画出来后保存到本地查看效果。
生产: 生产上,不需要保存本地了,直接将原始图片画完标识框等信息,返回base64推送给第三方接口就可了。
步骤
1.注册字体
这个纯属经验,以前遇到windows上可以使用微软雅黑,linux不可以的情况,一种方式是将windows的字体放入linux系统上,但是对于已经部署的项目来说不是很友好,另一种方式是将微软雅黑字体放入java项目的resource文件下,然后注册进去,我是用的这种方式:
可以在这个网站现在微软雅黑字体,重命名即可。
代码如下:
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.效果如下:
控制台也打印了base64字符,以后前端可以直接用base64显示图片:
自此,java就实现了标注框的功能。
在需要调用的地方直接这样调用即可
//画框的base64赋值
//原始图片 sysEventRecord.getPicUrl()
//标注框的位置信息 sysEventRecord.getPosition()
sysEventRecordVo.setPicUrlBase64(ImageBase64Utils.getBase64FromFile(new File(sysEventRecord.getPicUrl()), sysEventRecord.getPosition()));
开通了个微信公众号:
搜索: 怒放de每一天
后续可能不定时推送相关文章,期待和大家一起成长!!
大功告成!!