当前位置: 首页 > news >正文

java对图片进行表单,生成本地图片或者流式输出

一、最终效果(红框为标定,支持标定多个复选框)

在这里插入图片描述

二、代码(通过4个点位进行绘制)

[{"x":0.296094,"y":0.509722},{"x":0.508594,"y":0.509722},{"x":0.508594,"y":0.816667},{"x":0.296094,"y":0.816667}]
package com.jeesite.modules.inspectionDayReport.service;import cn.hutool.json.ObjectMapper;
import com.alibaba.fastjson.TypeReference;
import com.jeesite.modules.config.DeviceTypeConstants;
import com.jeesite.modules.hk.entity.inspection.HkInspectionDialIndicatorParam;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;/*** 图片坐标绘制服务类* 用于在图片上绘制标定线条并返回处理后的图片路径*/
@Service
public class drawCoordinatesOnImageService {private static final Logger log = LoggerFactory.getLogger(drawCoordinatesOnImageService.class);/*** 在图片上绘制标定线条*/public String drawCoordinatesFile(String originalImgPath,List<List<Map<String, Double>>> coordinatesList,  // 多区域坐标(每个子列表为一个区域)List<String> labelsList                          // 区域标签(与坐标区域数量匹配)) throws IOException {// 1. 验证输入参数if (originalImgPath == null || originalImgPath.isEmpty()) {throw new IllegalArgumentException("原始图片路径不能为空");}if (coordinatesList == null || coordinatesList.isEmpty()) {throw new IllegalArgumentException("坐标区域列表不能为空");}// 修改2:新增标签列表校验(非空且与坐标区域数量一致)if (labelsList == null || labelsList.size() != coordinatesList.size()) {throw new IllegalArgumentException("标签列表为空或与坐标区域数量不匹配");}// 2. 读取原始图片File originalFile = new File(originalImgPath);if (!originalFile.exists()) {throw new IOException("原始图片不存在: " + originalImgPath);}BufferedImage image = ImageIO.read(originalFile);// 3. 获取图片尺寸并创建绘图上下文int imageWidth = image.getWidth();int imageHeight = image.getHeight();Graphics2D g2d = image.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿// 4. 设置绘制参数(线条颜色、宽度、文字样式)g2d.setColor(Color.RED);g2d.setStroke(new BasicStroke(3.0f));// 修改3:设置文字样式(支持中文、大小20px)g2d.setFont(new Font("SimHei", Font.PLAIN, 20));  // 中文显示字体g2d.setColor(Color.RED);  // 文字颜色与线条一致// 5. 循环绘制每个区域及标签(修改为多区域循环)for (int i = 0; i < coordinatesList.size(); i++) {List<Map<String, Double>> coordinates = coordinatesList.get(i);  // 当前区域坐标String label = labelsList.get(i);  // 当前区域标签if (coordinates.size() == 1) {// ====== 补充:单个点绘制圆形的核心变量定义 ======Map<String, Double> centerPoint = coordinates.get(0);  // 获取圆心相对坐标// 计算圆心像素坐标(相对坐标转像素坐标)int centerX = (int) (centerPoint.get("x") * imageWidth);int centerY = (int) (centerPoint.get("y") * imageHeight);// 计算半径(相对半径取图片宽度的5%)double relativeRadius = 0.05;  // 可根据需求调整int pixelRadius = (int) (relativeRadius * imageWidth);  // 像素半径// 绘制圆形g2d.drawOval(centerX - pixelRadius, centerY - pixelRadius, 1 * pixelRadius, 1 * pixelRadius);// ====== 文字坐标计算(原逻辑保留)======int textX = centerX - pixelRadius;  // 文字X坐标(圆形左对齐)int textY = centerY - pixelRadius - 10;  // 文字Y坐标(圆形上方10px)g2d.drawString(label, textX, textY);}  else {// ... 现有多边形绘制逻辑 ...for (int j = 0; j < coordinates.size(); j++) {Map<String, Double> currentPoint = coordinates.get(j);Map<String, Double> nextPoint = coordinates.get((j + 1) % coordinates.size());int x1 = (int) (currentPoint.get("x") * imageWidth);int y1 = (int) (currentPoint.get("y") * imageHeight);int x2 = (int) (nextPoint.get("x") * imageWidth);int y2 = (int) (nextPoint.get("y") * imageHeight);g2d.drawLine(x1, y1, x2, y2);}// ====== 新增:在多边形左上角绘制文字 ======Map<String, Double> firstPoint = coordinates.get(0);  // 取第一个点作为文字基准int textX = (int) (firstPoint.get("x") * imageWidth);int textY = (int) (firstPoint.get("y") * imageHeight) - 10;  // 多边形上方10像素g2d.drawString(label, textX, textY);}}// 6. 释放绘图资源g2d.dispose();// 7. 生成指定目录保存处理后的图片String outputDir = "D:\\111\\photo\\ss"; // 生产环境图片保存目录File dir = new File(outputDir);if (!dir.exists()) {dir.mkdirs(); // 自动创建多级目录(若不存在)}// 获取原始图片文件名(保留扩展名)String originalFileName = new File(originalImgPath).getName();File outputFile = new File(dir,  "marked_" + originalFileName );ImageIO.write(image, "jpg", outputFile);// 8. 返回处理后的图片路径return outputFile.getAbsolutePath();}/*** 在图片上绘制标定线条*/public  InputStream drawCoordinatesInputStream(String originalImgPath, List<HkInspectionDialIndicatorParam> hkInspectionDialIndicatorParamList) {try {// 1. 验证输入参数if (originalImgPath == null || originalImgPath.isEmpty()) {throw new IllegalArgumentException("原始图片路径不能为空");}// 2. 读取原始图片File originalFile = new File(originalImgPath);if (!originalFile.exists()) {throw new IOException("原始图片不存在: " + originalImgPath);}BufferedImage image = ImageIO.read(originalFile);// 3. 获取图片尺寸并创建绘图上下文int imageWidth = image.getWidth();int imageHeight = image.getHeight();Graphics2D g2d = image.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿// 4. 设置绘制参数(红色线条,3px宽度)g2d.setColor(Color.RED);g2d.setStroke(new BasicStroke(3.0f));List<List<Map<String, Double>>> coordinatesList = new ArrayList<>();List<String> labelsList = new ArrayList<>();  // 存储每个区域的文字说明for (int i = 0; i < hkInspectionDialIndicatorParamList.size(); i++) {HkInspectionDialIndicatorParam param = hkInspectionDialIndicatorParamList.get(i);List<Map<String, Double>> dwMap = new ArrayList<>();String targetRegion =  param.getTargetRegion();JSONArray jsonArray = JSONUtil.parseArray(targetRegion);for (Object obj : jsonArray) {JSONObject jsonObj = (JSONObject) obj;Map<String, Double> coord = new HashMap<>();// 提取 x/y 坐标并转换为 Doublecoord.put("x", jsonObj.getDouble("x"));coord.put("y", jsonObj.getDouble("y"));dwMap.add(coord); // 添加到当前边框坐标列表}coordinatesList.add(dwMap);// 生成标签:使用检测类型名称(如"指针类"、"数字类")String readingType = param.getReadingType();String label = DeviceTypeConstants.getTypeName(readingType);labelsList.add(StringUtils.isNotBlank(label) ? label + (i+1) : "未知区域");  // 默认为"未知区域"}// 5. 循环绘制每个边框(新增外层循环处理多个边框)for (int i = 0; i < coordinatesList.size(); i++) {List<Map<String, Double>> coordinates = coordinatesList.get(i);String label = labelsList.get(i);  // 获取当前区域的文字说明// ====== 新增:设置文字样式(字体、颜色、大小)======g2d.setFont(new Font("SimHei", Font.PLAIN, 20));  // 支持中文的字体g2d.setColor(Color.RED);  // 文字颜色(黑色醒目)if (coordinates.size() == 1) {// 单个点:绘制圆形Map<String, Double> centerPoint = coordinates.get(0);int centerX = (int) (centerPoint.get("x") * imageWidth);int centerY = (int) (centerPoint.get("y") * imageHeight);double relativeRadius = 0.05;int pixelRadius = (int) (relativeRadius * imageWidth);g2d.drawOval(centerX - pixelRadius, centerY - pixelRadius, 1 * pixelRadius, 1 * pixelRadius);// ====== 新增:在圆形上方绘制文字 ======int textX = centerX - pixelRadius;  // 文字X坐标(与圆形左对齐)int textY = centerY - pixelRadius - 10;  // 文字Y坐标(圆形上方10像素)g2d.drawString(label, textX, textY);  // 绘制文字} else if (coordinates.size() == 2) {// 两个点:绘制圆形Map<String, Double> point1 = coordinates.get(0);Map<String, Double> point2 = coordinates.get(1);int x1 = (int) (point1.get("x") * imageWidth);int y1 = (int) (point1.get("y") * imageHeight);int x2 = (int) (point2.get("x") * imageWidth);int y2 = (int) (point2.get("y") * imageHeight);int centerX = (x1 + x2) / 2;int centerY = (y1 + y2) / 2;double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));int radius = (int) (distance / 2);g2d.drawOval(centerX - radius, centerY - radius, 2 * radius, 2 * radius);// ====== 新增:在圆形上方绘制文字 ======int textX = centerX - radius;int textY = centerY - radius - 10;g2d.drawString(label, textX, textY);} else {// 多点:绘制多边形for (int j = 0; j < coordinates.size(); j++) {Map<String, Double> currentPoint = coordinates.get(j);Map<String, Double> nextPoint = coordinates.get((j + 1) % coordinates.size());int x1 = (int) (currentPoint.get("x") * imageWidth);int y1 = (int) (currentPoint.get("y") * imageHeight);int x2 = (int) (nextPoint.get("x") * imageWidth);int y2 = (int) (nextPoint.get("y") * imageHeight);g2d.drawLine(x1, y1, x2, y2);}// ====== 新增:在多边形左上角绘制文字 ======Map<String, Double> firstPoint = coordinates.get(0);  // 取第一个点作为文字基准int textX = (int) (firstPoint.get("x") * imageWidth);int textY = (int) (firstPoint.get("y") * imageHeight) - 10;  // 多边形上方10像素g2d.drawString(label, textX, textY);}}// 6. 释放绘图资源g2d.dispose();// 7. 将处理后的图片写入字节数组输出流(替换文件保存逻辑)ByteArrayOutputStream baos = new ByteArrayOutputStream();ImageIO.write(image, "jpg", baos);// 8. 转换为字节数组输入流并返回return new ByteArrayInputStream(baos.toByteArray());}catch (Exception e){log.error("绘图失败:" + e);}return null;}@Testpublic void test() throws IOException {List<List<Map<String, Double>>> coordinatesList  = new ArrayList<>();// 示例坐标:矩形区域(Java 8兼容版本)List<Map<String, Double>> coordinates = new ArrayList<>();// 添加第一个坐标点Map<String, Double> point1 = new HashMap<>();point1.put("x", 0.020312);point1.put("y", 0.141667);coordinates.add(point1);// 添加第二个坐标点Map<String, Double> point2 = new HashMap<>();point2.put("x", 0.167187);point2.put("y", 0.141667);coordinates.add(point2);// 添加第三个坐标点Map<String, Double> point3 = new HashMap<>();point3.put("x", 0.167187);point3.put("y", 0.390278);coordinates.add(point3);// 添加第四个坐标点Map<String, Double> point4 = new HashMap<>();point4.put("x", 0.020312);point4.put("y", 0.390278);coordinates.add(point4);coordinatesList.add( coordinates);List<String> labelsList = new ArrayList<>();labelsList.add("标定1");// 调用绘制服务获取处理后的图片路径drawCoordinatesFile("D:\\111\\photo\\snapshot_20251020143453692.jpg", coordinatesList,labelsList);System.out.println("图片处理完成!");}
}
http://www.dtcms.com/a/549436.html

相关文章:

  • Python 虚拟环境:告别依赖冲突的实用指南
  • LP8773S第二代准谐振PWM控制器12V芯片内部框架图及应用信息介绍
  • Ruby Mysql:深度解析Ruby与MySQL的交互
  • 清空回收站后的文件还能恢复吗?分析原理,讲清方法
  • 怎样做外部网站推广郑州网站建设知识分享
  • 赋能采购革新:星合智联如何为企业开启TI芯片直采数字化新体验?
  • 构建可持续私域运营体系:从黑五营销到长期用户沉淀指南
  • 科技部网站php做网站不兼容ie8
  • 玉田网站制作wordpress百度mlp
  • Vite 大型项目优化方案
  • git处理分支
  • ELK日志系统部署与使用(Elasticsearch、Logstash、Kibana)
  • Gitee:代码管理
  • 购物网站建设论文织梦cms网站迁移
  • CP网站建设搭建需要多少钱大冶市城乡建设局网站
  • FramelessBaseWindow - 通用Qt无边框窗口基类
  • seo查询 站长工具利用织梦搭网站
  • 第238题 除自身以外数组的乘积
  • Vue 状态管理库相关收录
  • CG-5重力仪外壳漏电怎么办?
  • 商务网站规划与建设课设的项目需求seo网站有优化培训吗
  • 从 VLDB‘25 看向量数据库发展方向:行业观察与技术前瞻
  • 生鲜电商企业微信私域代运营:从去中心化运营看微盛AI·企微管家SCRM适配案例
  • 企业微信如何正确营销获客?精准定位与场景触达的实践框架
  • 企业微信私有化服务商怎么选?从数据安全与定制化需求看适配方向
  • 【百度AI】Postman调用OCR服务-解决官方教程请求失败问题
  • 【RabbitMQ】消息队列·详解+实操演示+功能实现(微服务架构)
  • 视频网站制作wordpress博客费用
  • 第三方编辑网站怎么做怎么查自己专业是否符合一建
  • spring boot入门篇之开发环境搭建