diff --git a/src/main/java/com/inspect/simulator/constant/AlgConstants.java b/src/main/java/com/inspect/simulator/constant/AlgConstants.java index 38440be..552082c 100644 --- a/src/main/java/com/inspect/simulator/constant/AlgConstants.java +++ b/src/main/java/com/inspect/simulator/constant/AlgConstants.java @@ -3,8 +3,11 @@ package com.inspect.simulator.constant; public class AlgConstants { public static final String METER = "meter"; public static final String INFRA_1800 = "infra_1800"; + /** 华软无人机的红外 **/ public static final String INFRA_YU3 = "infra_yu3"; + /** DLT664协议的红外 **/ public static final String INFRA_CAMERA = "infra_camera"; + /** 海康/大华摄像机的红外析 **/ public static final String INFRA_CAMERA_REVERSE = "infra_camera_reverse"; public static final String XB = "xb"; diff --git a/src/main/java/com/inspect/simulator/controller/ModelController.java b/src/main/java/com/inspect/simulator/controller/ModelController.java index 8f94f23..9197a31 100644 --- a/src/main/java/com/inspect/simulator/controller/ModelController.java +++ b/src/main/java/com/inspect/simulator/controller/ModelController.java @@ -1,14 +1,10 @@ package com.inspect.simulator.controller; import com.inspect.simulator.domain.AjaxResult; -import com.inspect.simulator.domain.visual.ApiVisualRequest; import com.inspect.simulator.domain.visual.ImageData; import com.inspect.simulator.service.ModelService; -import lombok.extern.log4j.Log4j; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import sun.rmi.runtime.Log; import javax.annotation.Resource; diff --git a/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java b/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java index 34bc039..d2b4221 100644 --- a/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java +++ b/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java @@ -14,25 +14,27 @@ public class Coordinate { private Integer secondX; private Integer secondY; - private String maxValue; + private String maxValue; - private String minValue; + private String minValue; - private String avgValue; + private String avgValue; - private String boxName; + private String boxName; - private String width; + private Integer width; - private String height; + private Integer height; - private String maxValueX; - private String maxValueY; + private String maxValueX; + private String maxValueY; - public Coordinate(int firstX, int firstY, int secondX, int secondY) { - this.firstX=firstX; - this.firstY=firstY; - this.secondX=secondX; - this.secondY=secondY; + public Coordinate(int firstX, int firstY, int secondX, int secondY, int width, int height) { + this.firstX = firstX; + this.firstY = firstY; + this.secondX = secondX; + this.secondY = secondY; + this.width = width; + this.height = height; } } diff --git a/src/main/java/com/inspect/simulator/service/ImageAnnotationService.java b/src/main/java/com/inspect/simulator/service/ImageAnnotationService.java new file mode 100644 index 0000000..4b0de04 --- /dev/null +++ b/src/main/java/com/inspect/simulator/service/ImageAnnotationService.java @@ -0,0 +1,281 @@ +package com.inspect.simulator.service; + +import com.inspect.simulator.domain.Infrared.Coordinate; +import com.inspect.simulator.domain.Infrared.InfraredInfo; +import com.inspect.simulator.utils.ThermalGenerator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +public class ImageAnnotationService { + /** + * 字体文件路径 + */ + private static final String FONT_PATH = "/fonts/NotoSansSC-Regular.ttf"; + /** + * 绘制十字的空心圆半径和外长 + */ + private static final int CROSSHAIR_R = 6, CROSSHAIR_ARM = 6; + private Font chineseFont; + + private static int clamp(int v, int lo, int hi) { + return Math.max(lo, Math.min(hi, v)); + } + + /** + * 是否生成伪彩色热图 + */ + public static boolean isGenThermal(String imagePath) { + // 继保机器狗 + String[] keywords = {"R100-001"}; + for (String key : keywords) { + String flag = "_" + key + "_"; + + if (imagePath.contains(flag)) { + return true; + } + } + return false; + } + + /** + * @param originalImage 原图 + * @param infraredInfo 热成像信息 + * @param coordinates 画框坐标 + */ + public BufferedImage annotate(BufferedImage originalImage, InfraredInfo infraredInfo, List coordinates, boolean genThermal) { + // 转为标注数据 + TemperatureDataService tempData = new TemperatureDataService(); + tempData.load(infraredInfo.getTemperatureMatrix()); + + infraredInfo.setMaxTemp(tempData.getMaxTemp()); + infraredInfo.setMinTemp(tempData.getMinTemp()); + + // 插值比例(红外测温分辨率和可见光图片分辨率可能存在不一致情况) + float scaleX = (float) originalImage.getWidth() / tempData.getCols(); + float scaleY = (float) originalImage.getHeight() / tempData.getRows(); + + int outW = originalImage.getWidth(); + int outH = originalImage.getHeight(); + + BufferedImage canvas = new BufferedImage(outW, outH, BufferedImage.TYPE_INT_RGB); + Graphics2D g = canvas.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + BufferedImage canvasImage; + if (genThermal) { + canvasImage = ThermalGenerator.generate(tempData, outW, outH); + } else { + canvasImage = originalImage; + } + g.drawImage(canvasImage, 0, 0, null); + + + // 加载字体 + ensureFont(); + int fontSize = Math.max(16, originalImage.getWidth() / 70); + g.setFont(chineseFont.deriveFont(Font.PLAIN, fontSize)); + FontMetrics fm = g.getFontMetrics(); + + List labels = new ArrayList<>(); + + /** + * 标注全图最高温和最低温 + */ + // 1.转换全图最高温和最低温的点坐标 + int maxX = Math.round(tempData.getMaxCol() * scaleX) + 1; + int maxY = Math.round(tempData.getMaxRow() * scaleY) + 1; + int minX = Math.round(tempData.getMinCol() * scaleX) + 1; + int minY = Math.round(tempData.getMinRow() * scaleY) + 1; + + // 2.根据坐标绘制十字图形 + drawCrosshair(g, maxX, maxY, new Color(255, 23, 68)); + drawCrosshair(g, minX, minY, new Color(41, 121, 255)); + + // 3.根据坐标绘制文本,并对十字进行避障展示 + int r = CROSSHAIR_R; + int arm = CROSSHAIR_ARM; + Rectangle maxCrosshairRect = new Rectangle(maxX - r - arm, maxY - r - arm, (r + arm) * 2, (r + arm) * 2); + Rectangle minCrosshairRect = new Rectangle(minX - r - arm, minY - r - arm, (r + arm) * 2, (r + arm) * 2); + String maxText = String.format("最高温度: %.1f", tempData.getMaxTemp()); + String minText = String.format("最低温度: %.1f", tempData.getMinTemp()); + labels.add(createLabel(maxText, maxX, maxY, fm, true, maxCrosshairRect)); + labels.add(createLabel(minText, minX, minY, fm, true, minCrosshairRect)); + + log.info("最高温度: {}, 表格坐标: ({},{}), 图片坐标: ({},{})", tempData.getMaxTemp(), tempData.getMaxCol(), tempData.getMaxRow(), maxX, maxY); + log.info("最低温度: {}, 表格坐标: ({},{}), 图片坐标: ({},{})", tempData.getMinTemp(), tempData.getMinCol(), tempData.getMinRow(), minX, minY); + + /** + * 画框并标注内部最高温、最低温、平均温 + * TODO: 后续可拓展为多组画框,需前端支持 + */ + if (coordinates.size() > 0) { + // 暂时只标注第一组坐标 + Coordinate coordinate = coordinates.get(0); + + int fx = coordinate.getFirstX(); + int fy = coordinate.getFirstY(); + int sx = coordinate.getSecondX(); + int sy = coordinate.getSecondY(); + int w = coordinate.getWidth(); + int h = coordinate.getHeight(); + + float rateX = (float) outW / w; + float rateY = (float) outH / h; + int x1 = Math.round(fx * rateX); + int y1 = Math.round(fy * rateY); + int x2 = Math.round(sx * rateX); + int y2 = Math.round(sy * rateY); + + int rx1 = Math.min(x1, x2), ry1 = Math.min(y1, y2); + int rx2 = Math.max(x1, x2), ry2 = Math.max(y1, y2); + + int csvCol1 = Math.round(rx1 / scaleX), csvCol2 = Math.round((rx2 + scaleX - 1) / scaleX); + int csvRow1 = Math.round(ry1 / scaleY), csvRow2 = Math.round((ry2 + scaleY - 1) / scaleY); + + // 统计框内温度 + TemperatureDataService.RegionStats stats = tempData.calcRegion(csvRow1, csvRow2, csvCol1, csvCol2); + + g.setColor(Color.white); + g.setStroke(new BasicStroke(3)); + g.drawRect(rx1, ry1, rx2 - rx1, ry2 - ry1); + + String boxText = String.format("最高温度: %.1f 最低温度: %.1f 平均温度: %.1f", stats.getMax(), stats.getMin(), stats.getAvg()); + + // 用于文本避障 + Rectangle occupied = new Rectangle(rx1, ry1, rx2 - rx1, ry2 - ry1); + + labels.add(createLabel(boxText, rx1, ry1 - 5, fm, true, occupied)); + + infraredInfo.setFrameMax(stats.getMax()); + infraredInfo.setFrameMin(stats.getMin()); + } + + drawLabels(g, labels, canvas.getWidth(), canvas.getHeight()); + + g.dispose(); + + return canvas; + } + + private TextLabel createLabel(String text, int anchorX, int anchorY, FontMetrics fm, boolean alignLeft, Rectangle occupied) { + int textWidth = fm.stringWidth(text); + int textHeight = fm.getHeight(); + float ascent = fm.getAscent(); + float descent = fm.getDescent(); + return new TextLabel(text, anchorX, anchorY, textWidth, textHeight, ascent, descent, alignLeft, occupied); + } + + /** + * 绘制文本和背景蒙层,避免与图形重合 + */ + private void drawLabels(Graphics2D g, List labels, int imgW, int imgH) { + for (TextLabel label : labels) { + int textW = label.width; + int textH = label.height; + int padX = 2, padY = 1; + int marginY = 4; + + int bgW = textW + padX * 2; + int bgH = textH + padY * 2; + int bgX = label.alignLeft ? clamp(label.anchorX - padX, 0, imgW - bgW) : clamp(label.anchorX - textW / 2 - padX, 0, imgW - bgW); + int bgY = label.anchorY - textH / 2 - padY; + + // 文本与图形避免重合 + // 文本优先展示位置:图形上方 > 图形下方 > 图形中 + java.awt.Rectangle occupied = label.occupied; + if (false && occupied != null) { + if (occupied.y - bgH - marginY >= 0) { + bgY = occupied.y - bgH - marginY; + } else if (occupied.y + occupied.height + bgH + marginY <= imgH) { + bgY = occupied.y + occupied.height + marginY; + } else { + bgY = occupied.y + marginY; + } + } + + g.setColor(new Color(0, 0, 0, 128)); + g.fillRoundRect(bgX, bgY, bgW, bgH, 0, 0); + + // 文本在背景蒙层中居中 + int baseline = bgY + (bgH - textH) / 2 + Math.round(label.ascent); + + g.setColor(Color.WHITE); + g.drawString(label.text, bgX + padX, baseline); + } + } + + /** + * 绘制内部为空心圆的十字 + */ + private void drawCrosshair(Graphics2D g, int x, int y, Color color) { + int r = CROSSHAIR_R; + int arm = CROSSHAIR_ARM; + g.setColor(color); + g.setStroke(new BasicStroke(3)); + g.drawLine(x - r - arm, y, x - r, y); + g.drawLine(x + r, y, x + r + arm, y); + g.drawLine(x, y - r - arm, x, y - r); + g.drawLine(x, y + r, x, y + r + arm); + g.drawOval(x - r, y - r, r * 2, r * 2); + } + + /** + * 加载字体文件 + */ + private void ensureFont() { + if (chineseFont != null) { + return; + } + // 首先尝试从类路径加载 + try { + InputStream is = new ClassPathResource(FONT_PATH).getInputStream(); + chineseFont = Font.createFont(Font.TRUETYPE_FONT, is); + is.close(); + return; + } catch (Exception ignored) { + } + + // 备用方案:尝试使用系统常用的中文字体 + String[] candidates = {"Microsoft YaHei", "SimHei", "SimSun", "WenQuanYi Micro Hei", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC"}; + for (String name : candidates) { + Font f = new Font(name, Font.PLAIN, 14); + if (f.canDisplayUpTo("温度") == -1) { + chineseFont = f; + return; + } + } + // 兜底方案:使用默认字体(中文字符将显示为方框) + chineseFont = new Font(Font.SANS_SERIF, Font.PLAIN, 14); + } + + static class TextLabel { + String text; + int anchorX, anchorY; + int width, height; + float ascent, descent; + boolean alignLeft; // true = 文本左对齐, false = 文本居中对齐 + Rectangle occupied; // 文字需要避开该区域展示 + + TextLabel(String text, int ax, int ay, int w, int h, float asc, float desc, boolean al, Rectangle oc) { + this.text = text; + anchorX = ax; + anchorY = ay; + width = w; + height = h; + ascent = asc; + descent = desc; + alignLeft = al; + occupied = oc; + } + } +} diff --git a/src/main/java/com/inspect/simulator/service/TemperatureDataService.java b/src/main/java/com/inspect/simulator/service/TemperatureDataService.java new file mode 100644 index 0000000..1d0a732 --- /dev/null +++ b/src/main/java/com/inspect/simulator/service/TemperatureDataService.java @@ -0,0 +1,87 @@ +package com.inspect.simulator.service; + +import lombok.Data; +import lombok.Getter; + +@Data +public class TemperatureDataService { + private float[][] temperatures; // [row][col] = [y][x] + private int rows; + private int cols; + + private int minRow, minCol; + private int maxRow, maxCol; + private float minTemp = Float.MAX_VALUE; + private float maxTemp = Float.MIN_VALUE; + + @Override + public String toString() { + return "TemperatureDataService{" + "rows=" + rows + ", cols=" + cols + ", minRow=" + minRow + ", minCol=" + minCol + ", maxRow=" + maxRow + ", maxCol=" + maxCol + ", minTemp=" + minTemp + ", maxTemp=" + maxTemp + '}'; + } + + public void load(float[][] temperatureMatrix) { + temperatures = temperatureMatrix; + rows = temperatures.length; + cols = temperatures[0].length; + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + float v = temperatures[r][c]; + if (!Float.isNaN(v)) { + if (v < minTemp) { + minTemp = v; + minRow = r; + minCol = c; + } + if (v > maxTemp) { + maxTemp = v; + maxRow = r; + maxCol = c; + } + } + } + } + } + + public RegionStats calcRegion(int rowStart, int rowEnd, int colStart, int colEnd) { + float min = Float.MAX_VALUE, max = Float.MIN_VALUE; + double sum = 0; + int count = 0; + int r0 = Math.max(0, rowStart), r1 = Math.min(rows, rowEnd); + int c0 = Math.max(0, colStart), c1 = Math.min(cols, colEnd); + + for (int r = r0; r < r1; r++) { + for (int c = c0; c < c1; c++) { + float v = temperatures[r][c]; + if (!Float.isNaN(v)) { + if (v < min) { + min = v; + } + if (v > max) { + max = v; + } + sum += v; + count++; + } + } + } + if (count == 0) { + return new RegionStats(0, 0, 0); + } + return new RegionStats(min, max, sum / count); + } + + @Getter + public static class RegionStats { + private final float min; + private final float max; + private final double avg; + + public RegionStats(float min, float max, double avg) { + this.min = min; + this.max = max; + this.avg = avg; + } + + } +} diff --git a/src/main/java/com/inspect/simulator/service/impl/AlgorithmServiceImpl.java b/src/main/java/com/inspect/simulator/service/impl/AlgorithmServiceImpl.java index 759171a..18a4e15 100644 --- a/src/main/java/com/inspect/simulator/service/impl/AlgorithmServiceImpl.java +++ b/src/main/java/com/inspect/simulator/service/impl/AlgorithmServiceImpl.java @@ -279,15 +279,19 @@ public class AlgorithmServiceImpl implements AlgorithmService { // analyseResPoint.setConf(String.format("%.2f", (double) infraredInfo.getFrameMax())); analyseResPoint.setResImageUrl(infraredInfo.getOutPath()); analyseResPoint.setType(typeListStr); - if (infraredInfo.getFrameMax() == -1 && infraredInfo.getFrameMin() == -1) { + + float value = infraredInfo.getFrameMax() == 0.0f ? infraredInfo.getMaxTemp() : infraredInfo.getFrameMax(); + + if (value == 0.0f) { analyseResPoint.setCode("2002"); analyseResPoint.setValue("--"); analyseResPoint.setDesc("分析失败"); } else { analyseResPoint.setCode("2000"); analyseResPoint.setDesc("正常"); - analyseResPoint.setValue(String.format("%.2f", (double) infraredInfo.getFrameMax())); + analyseResPoint.setValue(String.format("%.1f", value)); } + if (StringUtils.isNotBlank(analyseResPoint.getImageNormalUrlPath())) { String imageNormalUrlPath = resultAnalysisMapper.selectImg(patrolPointId); if (StringUtils.isBlank(imageNormalUrlPath)) { diff --git a/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java b/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java index a5aeec0..aac3540 100644 --- a/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java +++ b/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java @@ -4,7 +4,6 @@ package com.inspect.simulator.service.impl; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; @@ -22,10 +21,26 @@ import com.inspect.simulator.mapper.BasedataEqpBookChannelMapper; import com.inspect.simulator.mapper.BasedataPatrolPointMapper; import com.inspect.simulator.mapper.InfraredBoxMapper; import com.inspect.simulator.service.HikVisionService; +import com.inspect.simulator.service.ImageAnnotationService; import com.inspect.simulator.tempCount.TempCount; import com.inspect.simulator.utils.StringUtils; import com.inspect.simulator.utils.redis.RedisService; import com.inspect.simulator.utils.sftp.SftpClient; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; import okhttp3.*; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.csv.CSVFormat; @@ -41,22 +56,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.List; -import java.util.concurrent.TimeUnit; - /** * 海康威视测试服务类 */ @@ -65,272 +64,41 @@ public class HikVisionServiceImpl implements HikVisionService { private final Logger log = LoggerFactory.getLogger(this.getClass()); -// @Autowired -// private HCNetSDK hcNetSDK; -// -// @Resource -// private DjService djService; - + @Resource + public InfraredBoxMapper infraredBoxMapper; private String picPath; - @Value("${file.hrUavUrl:null}") -// private String hrUavUrl = "http://192.168.4.167:2000/"; -// private String hrUavUrl = "http://192.168.4.160:2000/"; private String hrUavUrl; - @Value("${file.hrFtpUrl:null}") -// private String hrFtpUrl = "ftp://ftpuser:atia2018@192.168.4.129:10012/"; private String hrFtpUrl; - @Value("${file.ftpUrlAddress:null}") private String ftpUrlAddress; -// private String ftpUrlAddress = "192.168.4.129"; -// private String ftpUrlAddress = "192.168.1.116"; - @Value("${file.ftpUrlAccount:null}") private String ftpUrlAccount; -// private String ftpUrlAccount = "ftpuser"; - @Value("${file.ftpUrlPwd:null}") private String ftpUrlPwd; -// private String ftpUrlPwd = "atia2018"; - @Value("${file.ftpUrlPort:null}") private Integer ftpUrlPort; - @Value("${file.infraredUrl:null}") private String infraredUrl; -// private Integer ftpUrlPort = 10012; -// private Integer ftpUrlPort = 10990; @Value("${file.standardImgWidth:1280}") private Integer standardImgWidth; @Value("${file.standardImgHeight:1024}") private Integer standardImgHeight; - // @Value("${file.produceEnvironment:true}") private Boolean produceEnvironment = true; - @Resource private SftpClient sftpClient; - @Resource private TempCount tempCount; - @Resource private BasedataEqpBookChannelMapper basedataEqpBookChannelMapper; - - @Resource - public InfraredBoxMapper infraredBoxMapper; @Autowired private BasedataPatrolPointMapper basedataPatrolPointMapper; @Resource private RedisService redisService; + @Resource + private ImageAnnotationService annotationService; -// @Override -// public AjaxResult login(NvrInfo nvrInfo) { -// return login_V40(nvrInfo); -// } - - -// /** -// * 设备登录V40 与V30功能一致 -// */ -// public AjaxResult login_V40(NvrInfo nvrInfo) { -// -// HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = HikVisionUtils.login_V40(nvrInfo.getNvrIp(), nvrInfo.getServerPort().shortValue(), nvrInfo.getAccount(), nvrInfo.getPassword());//设备登录信息 -// HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息 -// -// int lUserID = hcNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo); -// if (lUserID == -1) { -// System.out.println("登录失败,错误码为" + hcNetSDK.NET_DVR_GetLastError()); -// return AjaxResult.error(hcNetSDK.NET_DVR_GetErrorMsg(new IntByReference(hcNetSDK.NET_DVR_GetLastError()))); -// } else { -// System.out.println(":设备登录成功!" + lUserID); -//// birdNvrMapper.updateNvrInfo(nvrInfo); -//// redisTemplate.opsForValue().set(nvrInfo.getNvrIp() + "_userId", lUserID); -// //若果设备序列号为空时,即为第一次登陆,写入基础数据 -// if (nvrInfo.getNvrSerial() == null || nvrInfo.getNvrSerial().equals("")) { -// m_strDeviceInfo.read(); -// HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceinfo_v30 = m_strDeviceInfo.struDeviceV30; -// nvrInfo.setNvrSerial(StringUtils.bytetoString(deviceinfo_v30.sSerialNumber, "GB2312")); -// //模拟通道不为0,并且数字通道为0,即为模拟通道 -// if (deviceinfo_v30.byChanNum != 0 && deviceinfo_v30.byIPChanNum == 0) { -// nvrInfo.setChannelType(0); -// nvrInfo.setChannelNumber(deviceinfo_v30.byChanNum + deviceinfo_v30.byHighDChanNum * 256); -// nvrInfo.setStartChannel((int) deviceinfo_v30.byStartChan); -// } -// //模拟通道为0,并且数字通道不为0,即为数字通道 -// if (deviceinfo_v30.byChanNum == 0 && deviceinfo_v30.byIPChanNum != 0) { -// nvrInfo.setChannelType(1); -// nvrInfo.setChannelNumber(deviceinfo_v30.byIPChanNum + deviceinfo_v30.byHighDChanNum * 256); -// nvrInfo.setStartChannel((int) deviceinfo_v30.byStartDChan); -// } -// //模拟通道不为0,并且数字通道不为0,即为双通道。 -// if (deviceinfo_v30.byChanNum != 0 && deviceinfo_v30.byIPChanNum != 0) { -// nvrInfo.setChannelType(2); -// nvrInfo.setChannelNumber(deviceinfo_v30.byChanNum + deviceinfo_v30.byHighDChanNum * 256); -// nvrInfo.setStartChannel((int) deviceinfo_v30.byStartChan); -// } -// nvrInfo.setCalicheNumber((int) deviceinfo_v30.byDiskNum); -// nvrInfo.setCharEncodeType((int) m_strDeviceInfo.byCharEncodeType); -// nvrInfo.setSyncDate(new Date()); -//// nvrInfoMapper.updateNvrInfo(nvrInfo); -// } -// return AjaxResult.success(hcNetSDK.NET_DVR_GetErrorMsg(new IntByReference(hcNetSDK.NET_DVR_GetLastError()))); -// } -// } - - - // @Override -// public AjaxResult cameraHongWaiHk(Camera camera) { -// //红外图谱信息 -// InfraredInfo infraredInfo = new InfraredInfo(); -// -// HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = HikVisionUtils.login_V40(camera.getIp(), (short) camera.getPort(), camera.getUserName(), camera.getPassword());//设备登录信息 -// HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息 -// -// int lUserID = hcNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo); -//// int lUserID=(int)redisTemplate.opsForValue().get(nvrInfo.getNvrIp()+"_userId"); -// if (lUserID != 0) { -// System.out.println("测温设备登录失败,错误码为" + hcNetSDK.NET_DVR_GetLastError()); -// return AjaxResult.error(hcNetSDK.NET_DVR_GetErrorMsg(new IntByReference(hcNetSDK.NET_DVR_GetLastError()))); -// } else { -// -// String s = ""; -// Date date = new Date(); -// SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss"); -// String newName = sf.format(date); -// SimpleDateFormat sfz = new SimpleDateFormat("yyyy"); -// String year = sfz.format(date); -// String strURL = "POST /ISAPI/Thermal/channels/2/thermometry/jpegPicWithAppendData?format=json&subtype=0"; -// HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(1024); -// System.arraycopy(strURL.getBytes(), 0, ptrUrl.byValue, 0, strURL.length()); -// ptrUrl.write(); -// HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(1024 * 1024 * 10); -// ptrInBuffer.read(); -// String strInbuffer = "{\n" + -// "\t\"JpegPicWithAppendDataParam\":\n" + -// "\t{\n" + -// "\t\t\"jpegPicEnabled\":\"true\",\n" + -// "\t\t\"captureMode\":\"standard\",\n" + -// "\t\t\"rulesOverlayEnabled\":\"true\"\n" + -// "\t}\n" + -// "}\n"; -// ptrInBuffer.byValue = strInbuffer.getBytes(); -// ptrInBuffer.write(); -// HCNetSDK.NET_DVR_XML_CONFIG_INPUT net_dvr_xml_config_input = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT(); -// net_dvr_xml_config_input.read(); -// net_dvr_xml_config_input.dwSize = net_dvr_xml_config_input.size(); -// net_dvr_xml_config_input.lpRequestUrl = ptrUrl.getPointer(); -// net_dvr_xml_config_input.dwRequestUrlLen = ptrUrl.byValue.length; -// net_dvr_xml_config_input.lpInBuffer = ptrInBuffer.getPointer(); -// net_dvr_xml_config_input.dwInBufferSize = ptrInBuffer.byValue.length; -// net_dvr_xml_config_input.write(); -// //输出参数设置 -// HCNetSDK.BYTE_ARRAY ptrOutByte = new HCNetSDK.BYTE_ARRAY(1024 * 1024 * 10); -// ptrOutByte.read(); -// HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(1024 * 1024 * 10); -// ptrStatusByte.read(); -// HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT net_dvr_xml_config_output = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT(); -// net_dvr_xml_config_output.read(); -// net_dvr_xml_config_output.dwSize = net_dvr_xml_config_output.size(); -// net_dvr_xml_config_output.lpOutBuffer = ptrOutByte.getPointer(); -// net_dvr_xml_config_output.dwOutBufferSize = ptrOutByte.size(); -// net_dvr_xml_config_output.write(); -// if (!hcNetSDK.NET_DVR_STDXMLConfig(lUserID, net_dvr_xml_config_input, net_dvr_xml_config_output)) { -// System.out.println("信息获取失败,错误号:" + hcNetSDK.NET_DVR_GetLastError()); -// } else { -// net_dvr_xml_config_output.read(); -// System.out.println("获取数据"); -// FileOutputStream pic; -// try { -// String filename = picPath + "/pic/hw/"; -// //判断路径是否存在 -// File filePath = new File(filename); -// if (!filePath.exists()) { -// //不存在,创建目录 -// filePath.mkdirs(); -// } -// String picname = filename + newName + "PLAY_CELLPHONE_" + ".jpg"; -// -// pic = new FileOutputStream(picname); -// //将字节写入文件 -// long offset = 0; -// ByteBuffer buffers = net_dvr_xml_config_output.lpOutBuffer.getByteBuffer(offset, net_dvr_xml_config_output.dwReturnedXMLSize); -// byte[] bytes = new byte[net_dvr_xml_config_output.dwReturnedXMLSize]; -// buffers.rewind(); -// buffers.get(bytes); -// -// //字符串截取 -// String indexStr = "Content-Length:"; -// String str = new String(bytes, StandardCharsets.UTF_8);//定义一个字符串 -// int index = str.indexOf(indexStr); -// /** -// * 去掉以下部分 -// * HTTP/1.1 200 OK -// * MIME-Version: 1.0 -// * Content-Type: multipart/form-data; boundary=boundary -// * -// * --boundary -// * Content-Type: application/json; charset="UTF-8" -// * Content-Length: -// */ -// System.arraycopy(bytes, index + indexStr.length(), bytes, 0, net_dvr_xml_config_output.dwReturnedXMLSize - (index + indexStr.length())); -// String str1 = new String(bytes, StandardCharsets.UTF_8);//定义一个字符串 -// int index2 = str1.indexOf(indexStr); -// /** -// * 去掉以下部分 -// * 131 -// * -// * { -// * "JpegPictureWithAppendDataResult": { -// * "channel": 2, -// * "appendDataVersion": "DL/T 664-2016", -// * "appendDataDataLen": 1459158 -// * } -// * } -// * --boundary -// * Content-Disposition: form-data; -// * Content-Type: application/octet-stream -// * Content-Length: -// */ -// System.arraycopy(bytes, index2 + indexStr.length(), bytes, 0, net_dvr_xml_config_output.dwReturnedXMLSize - (index2 + indexStr.length())); -// String str2 = new String(bytes, StandardCharsets.UTF_8); -// String s1 = "\r\n\r\n"; -// int index3 = str2.indexOf("\r\n\r\n"); -// /** -// * 去掉 -// * 1459158 -// * -// */ -// byte[] file_length_bytes = new byte[index3]; -// System.arraycopy(bytes, 0, file_length_bytes, 0, index3); -// -// System.arraycopy(bytes, index3 + s1.length(), bytes, 0, net_dvr_xml_config_output.dwReturnedXMLSize - (index3 + s1.length())); -// System.out.println(file_length_bytes.toString()); -// int file_length = Integer.parseInt(new String(file_length_bytes, StandardCharsets.UTF_8)); -// -// //解析红外图片数据 -// byte[] subBytes = Arrays.copyOfRange(bytes, 0, file_length); -// infraredInfo = readDataHw(subBytes, null); -// -// if (bytes != null) { -// pic.write(bytes, 0, file_length); -// } -// //关流 -// pic.close(); -// -// } catch (FileNotFoundException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } catch (IOException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// -// } -// } -// return AjaxResult.success(infraredInfo); -// } @Override public AjaxResult capturePicture(InfraPictureInfo infraPictureInfo) { log.info("capturePicture, coordinate:{}", infraPictureInfo); @@ -401,7 +169,7 @@ public class HikVisionServiceImpl implements HikVisionService { } //相机红外图谱获取 - public InfraredInfo readDataHw(byte[] bytes, Coordinate coordinate) throws IOException { + public InfraredInfo readDataHw(byte[] bytes) throws IOException { InfraredInfo infraredInfo = new InfraredInfo(); byte[] infraredData = locateAndReadInfraredData(bytes); @@ -504,11 +272,6 @@ public class HikVisionServiceImpl implements HikVisionService { // } // System.out.println(); // } - //绘制框选标识 - if (coordinate != null) { - //String s = ImageOverlaysNvr(coordinate, infraredInfo); - //infraredInfo.setOutPath(s); - } } else { System.err.println("错误:剩余数据不足以解析环境参数"); } @@ -518,93 +281,6 @@ public class HikVisionServiceImpl implements HikVisionService { return infraredInfo; } - //点测温 -// public InfraredInfo PointTemperatureShow(Coordinate coordinate, short width, short height, float[][] temperatureMatrix) { -// InfraredInfo infraredInfo = new InfraredInfo(); -// // 获取指定坐标的温度 -// if (ObjectUtil.isNotEmpty(coordinate.getFirstX())) { -// if (coordinate.getImgType() != 3) {//无人机不需要对比底图 -// //比例缩放像素 获取底图温度值 -// double xMultiple = (coordinate.getImgWidth().doubleValue() / width) * 100 / 100.0; -// double yMultiple = (coordinate.getImgHeight().doubleValue() / height) * 100 / 100.0; -// -// int x = (int) (coordinate.getFirstX() / xMultiple); -// int y = (int) (coordinate.getFirstY() / yMultiple); -// coordinate.setFirstX(x); -// coordinate.setFirstY(y); -// } -// -// if (coordinate.getFirstX() >= 0 && coordinate.getFirstX() <= width && coordinate.getFirstY() >= 0 && coordinate.getFirstY() <= height) { -// float pointTemperature = temperatureMatrix[coordinate.getFirstY()][coordinate.getFirstX()]; // 注意:y是行,x是列 -// infraredInfo.setPointTemperature(pointTemperature); -// } else { -// System.err.println("错误:坐标超出范围"); -// } -// } -// return infraredInfo; -// } - - //框选温度值计算 - public InfraredInfo matrixTemperatureShow(Coordinate coordinate, short width, short height, float[][] temperatureMatrix) { - InfraredInfo infraredInfo = new InfraredInfo(); - //存储框选矩阵温度值 - List values = new ArrayList<>(); - // 用于记录最高温度的坐标 - int maxTempX = -1; - int maxTempY = -1; - float currentMax = Float.MIN_VALUE; - if (ObjectUtil.isNotEmpty(coordinate.getFirstX()) && ObjectUtil.isNotEmpty(coordinate.getSecondX())) { - //if (coordinate.getImgType() != 3) -// {//无人机不需要对比底图 -// //倍数计算 -// double xMultiple = (coordinate.getImgWidth().doubleValue() / width) * 100 / 100.0; -// double yMultiple = (coordinate.getImgHeight().doubleValue() / height) * 100 / 100.0; -// -// //比例缩放像素 获取底图温度值 -// int x1 = (int) (coordinate.getFirstX() / xMultiple); -// int y1 = (int) (coordinate.getFirstY() / yMultiple); -// int x2 = (int) (coordinate.getSecondX() / xMultiple); -// int y2 = (int) (coordinate.getSecondY() / yMultiple); -// coordinate.setFirstX(x1); -// coordinate.setFirstY(y1); -// coordinate.setSecondX(x2); -// coordinate.setSecondY(y2); -// } - if (coordinate.getSecondX() != 0 && coordinate.getSecondY() != 0) { - for (int j = coordinate.getFirstY(); j <= coordinate.getSecondY(); j++) { // 列 - if (j < 0 || j >= temperatureMatrix.length) continue; - for (int i = coordinate.getFirstX(); i <= coordinate.getSecondX(); i++) { // 行(固定列,遍历行) - if (i < 0 || i >= temperatureMatrix[j].length) continue; - float temp = temperatureMatrix[j][i]; - values.add(temp); - - if (temp > currentMax) { - currentMax = temp; - maxTempX = i; - maxTempY = j; - } - } - } - } - // 计算框选矩阵温度数值 - float frameAverage = (float) values.stream().mapToDouble(f -> f).average().orElse(0); - float frameMax = (float) values.stream().mapToDouble(f -> f).max().orElse(0); - float frameMin = (float) values.stream().mapToDouble(f -> f).min().orElse(0); - - // 设置最高温度坐标(如果有找到的话) - if (maxTempX != -1 && maxTempY != -1) { - infraredInfo.setMaxTempX(maxTempX); - infraredInfo.setMaxTempY(maxTempY); - log.info("最大温度坐标:{}, {}", maxTempX, maxTempY); - } - - infraredInfo.setFrameAverage(frameAverage); - infraredInfo.setFrameMax(frameMax); - infraredInfo.setFrameMin(frameMin); - } - return infraredInfo; - } - //校验 public static byte[] locateAndReadInfraredData(byte[] fileData) { // 文件末尾标识 @@ -663,312 +339,6 @@ public class HikVisionServiceImpl implements HikVisionService { return null; } } - - - //图片标注__无人机/ivs1800/nvr - public String ImageOverlays(InfraPictureInfo infraPictureInfo, InfraredInfo infraredInfo) { - - List coordinates = infraPictureInfo.getCoordinates(); - - Date date = new Date(); - SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss"); - String markPicName = sf.format(date); - // 图片路径(请替换为实际路径) - String imagePath = infraPictureInfo.getFilePath(); - // 获取文件名(不含扩展名) - File file = new File(imagePath); - String fileName = file.getName(); - String pureName = fileName.substring(0, fileName.lastIndexOf('.')); - String filename = picPath; - log.info("filename图片路径:{}", filename); - String outputPath = picPath + pureName + "_" + markPicName + ".jpg"; - //判断路径是否存在 - File filePath = new File(filename); - if (!filePath.exists()) { - //不存在,创建目录 - filePath.mkdirs(); - } - try { - InputStream inputStreamPath = downloadFtp(imagePath); - if (inputStreamPath == null) { - System.out.println("无法加载图片: " + imagePath); - return null; - } - // 加载原始图片 - BufferedImage originalImage = ImageIO.read(inputStreamPath); - // 创建可编辑的图片副本 - BufferedImage annotatedImage = new BufferedImage( - originalImage.getWidth(), - originalImage.getHeight(), - BufferedImage.TYPE_INT_RGB - ); - // 绘制原始图片 - Graphics2D g2d = annotatedImage.createGraphics(); - g2d.drawImage(originalImage, 0, 0, null); - // 设置标注样式 - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); -// Font font = new Font("微软雅黑", Font.BOLD, 17); - Font font = new Font("WenQuanYi Zen Hei", Font.BOLD, 23); - final String imageType = infraPictureInfo.getImgType(); - if (AlgConstants.INFRA_YU3.equals(imageType)) { - for (Coordinate c : coordinates) { - //点标注 - if (c.getFirstX() != null && c.getFirstY() != null && c.getSecondX() == null && c.getSecondY() == null) { - g2d.setFont(font); - // 标注点坐标 - g2d.setColor(Color.WHITE); - int x = c.getFirstX(); - int y = c.getFirstY(); - - // 确保坐标在图片范围内 - if (x >= 0 && x < originalImage.getWidth() && - y >= 0 && y < originalImage.getHeight()) { - // 绘制点(用实心圆表示) - g2d.fillOval(x - 3, y - 3, 6, 6); - infraPictureInfo.setFirstX(x); - infraPictureInfo.setFirstY(y); -// InfraredInfo drawStringPoint = PointTemperatureShow(c, infraredInfo.getMatrixWidth(), infraredInfo.getMatrixHeight(), infraredInfo.getTemperatureMatrix()); -// // 添加坐标标签 -// g2d.drawString("(" + String.format("%.2f", drawStringPoint.getPointTemperature()) + ")", x + 8, y - 8); - } - } - //矩阵标注 - if (c.getSecondX() != null && c.getSecondY() != null) { - // 标注矩形(每两个点确定一个矩形) - g2d.setColor(Color.WHITE); - int x1 = c.getFirstX(); - int y1 = c.getFirstY(); - int x2 = c.getSecondX(); - int y2 = c.getSecondY(); - - // 确保坐标在图片范围内 - if (x1 >= 0 && x1 < originalImage.getWidth() && y1 >= 0 && y1 < originalImage.getHeight() && - x2 >= 0 && x2 < originalImage.getWidth() && y2 >= 0 && y2 < originalImage.getHeight()) { - - // 确保x1,y1是左上角,x2,y2是右下角 - int rectX = Math.min(x1, x2); - int rectY = Math.min(y1, y2); - int width = Math.abs(x2 - x1); - int height = Math.abs(y2 - y1); - - // 设置更粗的画笔(加粗矩形边框) - g2d.setStroke(new BasicStroke(3)); // 3像素宽 - // 绘制矩形 - g2d.drawRect(rectX, rectY, width, height); - infraPictureInfo.setFirstX(x1); - infraPictureInfo.setFirstY(y1); - infraPictureInfo.setSecondX(x2); - infraPictureInfo.setSecondY(y2); - - InfraredInfo drawStringMatrix = matrixTemperatureShow(c, infraredInfo.getMatrixWidth(), infraredInfo.getMatrixHeight(), infraredInfo.getTemperatureMatrix()); - infraredInfo.setFrameMax(Math.round(drawStringMatrix.getFrameMax() * 100) / 100f); - Integer maxTempX = drawStringMatrix.getMaxTempX(); - Integer maxTempY = drawStringMatrix.getMaxTempY(); - log.info("最高温度值坐标点:" + "maxTempX:" + maxTempX + " maxTempY:" + maxTempY); - - - // 添加矩形标签 - String line1 = "平均温度:" + String.format("%.2f", drawStringMatrix.getFrameAverage()); - String line2 = "最高温度:" + String.format("%.2f", drawStringMatrix.getFrameMax()); - String line3 = "最低温度:" + String.format("%.2f", drawStringMatrix.getFrameMin()); - g2d.drawString(line1, rectX + 5, rectY + 15); - g2d.drawString(line2, rectX + 5, rectY + 30); - g2d.drawString(line3, rectX + 5, rectY + 45); - - // 在最高温度点画一个空心圆(红色边框) - if (maxTempX != null && maxTempY != null) { - log.info("画最高温度的点,坐标:x={}, y={}", maxTempX, maxTempY); - - // 边界检查 - if (maxTempX >= 0 && maxTempX < originalImage.getWidth() && - maxTempY >= 0 && maxTempY < originalImage.getHeight()) { - - // 设置醒目的颜色 - g2d.setColor(Color.RED); - g2d.setStroke(new BasicStroke(5)); - - // 画空心圆 - int circleDiameter = 8; - g2d.drawOval(maxTempX - circleDiameter / 2, maxTempY - circleDiameter / 2, - circleDiameter, circleDiameter); - - // 画十字标记 - g2d.drawLine(maxTempX - 3, maxTempY, maxTempX + 3, maxTempY); - g2d.drawLine(maxTempX, maxTempY - 3, maxTempX, maxTempY + 3); - - log.info("成功绘制最高温度点标记"); - } else { - log.warn("最高温度点坐标超出图像范围:x={}, y={}", maxTempX, maxTempY); - } - } - } else { - log.error("画框坐标超出图片范围"); - // 确保x1,y1是左上角,x2,y2是右下角 - int rectX = Math.min(1, 639); - int rectY = Math.min(1, 511); - int width = Math.abs(639 - 1); - int height = Math.abs(511 - 1); - - // 设置更粗的画笔(加粗矩形边框) - g2d.setStroke(new BasicStroke(3)); // 3像素宽 - // 绘制矩形 - g2d.drawRect(rectX, rectY, width, height); - infraPictureInfo.setFirstX(x1); - infraPictureInfo.setFirstY(y1); - infraPictureInfo.setSecondX(x2); - infraPictureInfo.setSecondY(y2); - - InfraredInfo drawStringMatrix = matrixTemperatureShow(c, infraredInfo.getMatrixWidth(), infraredInfo.getMatrixHeight(), infraredInfo.getTemperatureMatrix()); - infraredInfo.setFrameMax(Math.round(drawStringMatrix.getFrameMax() * 100) / 100f); - // 添加矩形标签 - String line1 = "平均温度:" + String.format("%.2f", drawStringMatrix.getFrameAverage()); - String line2 = "最高温度:" + String.format("%.2f", drawStringMatrix.getFrameMax()); - String line3 = "最低温度:" + String.format("%.2f", drawStringMatrix.getFrameMin()); - g2d.drawString(line1, rectX + 5, rectY + 15); - g2d.drawString(line2, rectX + 5, rectY + 30); - g2d.drawString(line3, rectX + 5, rectY + 45); - } - } - } - } else if (AlgConstants.INFRA_1800.equals(imageType)) { - for (Coordinate c : coordinates) { - g2d.setFont(font); - //矩阵标注 - if (c.getSecondX() != null && c.getSecondY() != null) { - // 标注矩形(每两个点确定一个矩形) - g2d.setColor(Color.GREEN); - int x1 = c.getFirstX(); - int y1 = c.getFirstY(); - int x2 = c.getSecondX(); - int y2 = c.getSecondY(); - - // 确保坐标在图片范围内 - if (x1 >= 0 && x1 < originalImage.getWidth() && y1 >= 0 && y1 < originalImage.getHeight() && - x2 >= 0 && x2 < originalImage.getWidth() && y2 >= 0 && y2 < originalImage.getHeight()) { - - // 确保x1,y1是左上角,x2,y2是右下角 - int rectX = Math.min(x1, x2); - int rectY = Math.min(y1, y2); - int width = Math.abs(x2 - x1); - int height = Math.abs(y2 - y1); - // 设置更粗的画笔(加粗矩形边框) - g2d.setStroke(new BasicStroke(3)); // 3像素宽 - // 绘制矩形 -// g2d.drawRect(rectX, rectY, width, height); - - infraPictureInfo.setFirstX(x1); - infraPictureInfo.setFirstY(y1); - infraPictureInfo.setSecondX(x2); - infraPictureInfo.setSecondY(y2); - // 添加矩形标签 - //图片路径取值 - String[] dividePath = Paths.get(imagePath).getFileName().toString().replaceAll("(?i)\\.jpg$", "").split("_"); - String max = dividePath[dividePath.length - 1]; // 最大值 - infraredInfo.setFrameMax(Float.parseFloat(max)); -// String line1 = "最高温度:" + max; -// g2d.drawString(line1, rectX + 5, rectY + 15); - - } - } - } - } else if (AlgConstants.INFRA_CAMERA_REVERSE.equals(imageType)) { - for (Coordinate c : coordinates) { - g2d.setFont(font); - //矩阵标注 - if (c.getSecondX() != null && c.getSecondY() != null) { - // 标注矩形(每两个点确定一个矩形) - g2d.setColor(Color.WHITE); - int x1 = c.getFirstX(); - int y1 = c.getFirstY(); - int x2 = c.getSecondX(); - int y2 = c.getSecondY(); - - // 确保坐标在图片范围内 - if (x1 >= 0 && x1 < originalImage.getWidth() && y1 >= 0 && y1 < originalImage.getHeight() && - x2 >= 0 && x2 < originalImage.getWidth() && y2 >= 0 && y2 < originalImage.getHeight()) { - - // 确保x1,y1是左上角,x2,y2是右下角 - int rectX = Math.min(x1, x2); - int rectY = Math.min(y1, y2); - int width = Math.abs(x2 - x1); - int height = Math.abs(y2 - y1); - // 设置更粗的画笔(加粗矩形边框) - g2d.setStroke(new BasicStroke(3)); // 3像素宽 - // 绘制矩形 - g2d.drawRect(rectX, rectY, width, height); - infraPictureInfo.setFirstX(x1); - infraPictureInfo.setFirstY(y1); - infraPictureInfo.setSecondX(x2); - infraPictureInfo.setSecondY(y2); - - InfraredInfo drawStringMatrix = matrixTemperatureShow(c, infraredInfo.getMatrixWidth(), infraredInfo.getMatrixHeight(), infraredInfo.getTemperatureMatrix()); - infraredInfo.setFrameMax(Math.round(drawStringMatrix.getFrameMax() * 100) / 100f); - infraredInfo.setFrameMin(Math.round(drawStringMatrix.getFrameMin() * 100) / 100f); - Integer maxTempX = drawStringMatrix.getMaxTempX(); - Integer maxTempY = drawStringMatrix.getMaxTempY(); - log.info("最高温度值坐标点:" + "maxTempX:" + maxTempX + " maxTempY:" + maxTempY); - // 添加矩形标签 - String line1 = "平均温度:" + String.format("%.2f", drawStringMatrix.getFrameAverage()); - String line2 = "最高温度:" + String.format("%.2f", drawStringMatrix.getFrameMax()); - String line3 = "最低温度:" + String.format("%.2f", drawStringMatrix.getFrameMin()); - g2d.drawString(line1, rectX + 15, rectY + 30); - g2d.drawString(line2, rectX + 15, rectY + 60); - g2d.drawString(line3, rectX + 15, rectY + 90); - - - // 在最高温度点画一个空心圆(红色边框) - if (maxTempX != null && maxTempY != null) { - log.info("画最高温度的点,坐标:x={}, y={}", maxTempX, maxTempY); - - // 边界检查 - if (maxTempX >= 0 && maxTempX < originalImage.getWidth() && - maxTempY >= 0 && maxTempY < originalImage.getHeight()) { - - // 设置醒目的颜色 - g2d.setColor(Color.RED); - g2d.setStroke(new BasicStroke(5)); - - // 画空心圆 - int circleDiameter = 8; - g2d.drawOval(maxTempX - circleDiameter / 2, maxTempY - circleDiameter / 2, - circleDiameter, circleDiameter); - - // 画十字标记 - g2d.drawLine(maxTempX - 3, maxTempY, maxTempX + 3, maxTempY); - g2d.drawLine(maxTempX, maxTempY - 3, maxTempX, maxTempY + 3); - - log.info("成功绘制最高温度点标记"); - } else { - log.warn("最高温度点坐标超出图像范围:x={}, y={}", maxTempX, maxTempY); - } - } - - - } - } - } - float[][] temperatureMatrix = infraredInfo.getTemperatureMatrix(); - String csvData = convertMatrixToCsv(temperatureMatrix); - InputStream csvStream = new ByteArrayInputStream(csvData.getBytes(StandardCharsets.UTF_8)); - - String csvPath = picPath + pureName + ".csv"; - log.info("保存csv文件:{}", csvPath); - picFtp(csvPath, csvStream, ftpUrlAddress, ftpUrlPort, ftpUrlAccount, ftpUrlPwd); - } - g2d.dispose(); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ImageIO.write(annotatedImage, "jpg", os); - InputStream inputStream = new ByteArrayInputStream(os.toByteArray()); - - - picFtp(outputPath, inputStream, ftpUrlAddress, ftpUrlPort, ftpUrlAccount, ftpUrlPwd); - - } catch (IOException e) { - log.info("处理图片时出错: " + e.getMessage()); - e.printStackTrace(); - } - return outputPath; - } //二维数组转 CSV private static String convertMatrixToCsv(float[][] matrix) { StringBuilder csvBuilder = new StringBuilder(); @@ -1125,7 +495,6 @@ public class HikVisionServiceImpl implements HikVisionService { } //从ftp获取csv流转换成二维数组 - private float[][] parseCsvFromFtp2(String csvUrl) throws IOException, URISyntaxException { log.info("连接ftp,获取csv文件"); InputStream inputStream = null; @@ -1277,10 +646,10 @@ public class HikVisionServiceImpl implements HikVisionService { return null; } - ftps.setFileType(2); // 二进制模式 + ftps.setFileType(FTP.BINARY_FILE_TYPE); ftps.enterLocalPassiveMode(); - ftps.setControlEncoding("UTF-8"); - ftps.setFileTransferMode(10); + ftps.setControlEncoding(StandardCharsets.UTF_8.name()); + ftps.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); ftps.execPROT("P"); // 获取文件流 @@ -1338,7 +707,6 @@ public class HikVisionServiceImpl implements HikVisionService { } } - //异常处理返回本地csv private float[][] exceptionHandling() throws IOException { //i.csv文件存放到resources下 @@ -1411,8 +779,7 @@ public class HikVisionServiceImpl implements HikVisionService { // infraPictureInfo.setChannelId(); infraPictureInfo.setImgType(typeListImg[0]); infraPictureInfo.setFilePath(imageUrlList[0]); - InfraredInfo infraredInfo = calculatePicture(infraPictureInfo, "1,1,639,511,640,512",new BasedataPatrolPoint()); - + InfraredInfo infraredInfo = calculatePicture(infraPictureInfo, "1,1,639,511,640,512", new BasedataPatrolPoint()); AnalyseResult analyseResult = new AnalyseResult(); analyseResult.setRequestId(analyseRequest.getRequestId()); @@ -1451,7 +818,8 @@ public class HikVisionServiceImpl implements HikVisionService { return ResponseEntity.ok().body("{\"code\":\"200\"}"); } - public InfraredInfo calculatePicture(InfraPictureInfo infraPictureInfo, String picData,BasedataPatrolPoint basedataPatrolPoint) { + @Override + public InfraredInfo calculatePicture(InfraPictureInfo infraPictureInfo, String picData, BasedataPatrolPoint basedataPatrolPoint) { log.info("进入红外图片接口!"); @@ -1461,61 +829,37 @@ public class HikVisionServiceImpl implements HikVisionService { //输出路径 int lastSlashIndex = imagePath.lastIndexOf('/'); picPath = (lastSlashIndex >= 0) ? imagePath.substring(0, lastSlashIndex + 1) : "/"; - log.info("输出图片路径:{}", picPath); InputStream inputStream = downloadFtp(imagePath); - int firstX = 1; - int firstY = 1; - int secondX = 639; - int secondY = 511; - int imgWidth = 640; - int imgHeight = 512; - if (picData != null) { + List coordinates = new ArrayList<>(); - String[] parts = picData.split(","); - if(parts.length==6){ - // 解析坐标 - firstX = Integer.parseInt(parts[0]); - firstY = Integer.parseInt(parts[1]); - secondX = Integer.parseInt(parts[2]); - secondY = Integer.parseInt(parts[3]); - imgHeight = Integer.parseInt(parts[4]); - imgWidth = Integer.parseInt(parts[5]); + if (picData != null) { + int[] parts = Arrays.stream(picData.split(",")).mapToInt(Integer::parseInt).toArray(); + // 6个值:分为左上角(x,y) 右下角(x,y) 图片高宽(h,w) + if (parts.length == 6) { + coordinates.add(new Coordinate(parts[0], parts[1], parts[2], parts[3], parts[5], parts[4])); + } else if (parts.length == 10) { + // 10个值:分为左上角(x,y) 右上角(x,y) 右下角(x,y) 左下角(x,y) 图片宽高(w,h) + coordinates.add(new Coordinate(parts[0], parts[1], parts[4], parts[5], parts[8], parts[9])); } else { - // 解析坐标 - // 解析坐标 - 修复整数除法问题 - firstX = (int) (Double.parseDouble(parts[0]) / Integer.parseInt(parts[8]) * standardImgWidth); - firstY = (int) (Double.parseDouble(parts[1]) / Integer.parseInt(parts[9]) * standardImgHeight); - secondX = (int) (Double.parseDouble(parts[4]) / Integer.parseInt(parts[8]) * standardImgWidth); - secondY = (int) (Double.parseDouble(parts[5]) / Integer.parseInt(parts[9]) * standardImgHeight); - imgHeight = standardImgHeight; - imgWidth = standardImgWidth; - log.error("点位信息!firstX:{},firstY:{},secondX:{},secondY:{},imgHeight:{},imgWidth:{}", firstX, firstY, secondX, secondY, imgHeight, imgWidth); + log.error("{} 点位画框数据异常,请检查!", basedataPatrolPoint.getPatrolPointId()); } - } else { - log.error("无点位信息!"); + log.error("{} 无点位信息!", basedataPatrolPoint.getPatrolPointId()); } - List coordinates = new ArrayList<>(); - coordinates.add(new Coordinate(firstX, firstY, secondX, secondY)); - infraPictureInfo.setImgWidth(imgWidth); - infraPictureInfo.setImgHeight(imgHeight); infraPictureInfo.setCoordinates(coordinates); final String imageType = infraPictureInfo.getImgType(); if (AlgConstants.INFRA_CAMERA.equals(imageType)) { //相机红外 - byte[] imageBytes = new byte[0]; try { //ftp下载图片流转byte[] InputStream imgInputStream = downloadFtp(imagePath); - imageBytes = IOUtils.toByteArray(imgInputStream); -// imageBytes = Files.readAllBytes(Paths.get(imagePath)); + byte[] imageBytes = IOUtils.toByteArray(imgInputStream); //读取红外信息 - infraredInfo = readDataHw(imageBytes, infraPictureInfo.getCoordinates().get(0)); + infraredInfo = readDataHw(imageBytes); //绘制框选标识 - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); + imageAnnotation(infraPictureInfo, infraredInfo); } catch (IOException e) { log.error("error" + e); @@ -1524,7 +868,7 @@ public class HikVisionServiceImpl implements HikVisionService { } else if (AlgConstants.INFRA_CAMERA_REVERSE.equals(imageType)) { //相机反算红外 InputStream inputStreamPath = downloadFtp(imagePath); - if(StringUtils.isNull(inputStreamPath)){ + if (StringUtils.isNull(inputStreamPath)) { log.error("inputStreamPath图片为空"); } @@ -1532,32 +876,19 @@ public class HikVisionServiceImpl implements HikVisionService { //传值,相机 ip TemperatureData temperatureData = TemperatureMeasurement(basedataPatrolPoint); - String minTemperature = temperatureData.getMinTemperature(); - String maxTemperature = temperatureData.getMaxTemperature(); - log.info("[INFRARED] 最高温度值:{}", maxTemperature); - log.info("[INFRARED] 最低温度值:{}", minTemperature); - - float[][] matrix = tempCount.countTemp(inputStreamPath, Double.valueOf(maxTemperature), Double.valueOf(minTemperature));//图,最高值,最低值 -// float[][] matrix = tempCount.countTemp(inputStreamPath, 20.2,60.5);//图,最高值,最低值 - infraredInfo.setTemperatureMatrix(matrix); - //画框标注 - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); - log.info("计算过后最高温:{}, 最低温:{}",infraredInfo.getFrameMax(), infraredInfo.getFrameMin()); - log.info("计算过后最高温 ->是否等于0.0f {}",infraredInfo.getFrameMax() == 0.0f); - //温度未取到 - if(StringUtils.isNull(maxTemperature)){ - maxTemperature = "-1"; - infraredInfo.setFrameMax(-1); - infraredInfo.setFrameMin(-1); - } else if(infraredInfo.getFrameMax() == 0.0f){ - infraredInfo.setFrameMax(Float.parseFloat((maxTemperature))); - infraredInfo.setFrameMin(Float.parseFloat((minTemperature))); + if (temperatureData != null) { + String minTemperature = temperatureData.getMinTemperature(); + String maxTemperature = temperatureData.getMaxTemperature(); + log.info("[INFRARED] 最高温度值:{}", maxTemperature); + log.info("[INFRARED] 最低温度值:{}", minTemperature); + float[][] matrix = tempCount.countTemp(inputStreamPath, Double.valueOf(maxTemperature), Double.valueOf(minTemperature));//图,最高值,最低值 + infraredInfo.setTemperatureMatrix(matrix); } + + imageAnnotation(infraPictureInfo, infraredInfo); } else if (AlgConstants.INFRA_1800.equals(imageType)) { //ivs1800红外图 - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); + imageAnnotation(infraPictureInfo, infraredInfo); } else if (AlgConstants.INFRA_YU3.equals(imageType)) { //无人机红外图 if (!produceEnvironment) { @@ -1594,8 +925,7 @@ public class HikVisionServiceImpl implements HikVisionService { log.error("温度解析错误,接口无法调用!"); } } - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); + imageAnnotation(infraPictureInfo, infraredInfo); } else { //华软ftp不能登录,使用本地csv try { @@ -1605,30 +935,15 @@ public class HikVisionServiceImpl implements HikVisionService { log.error("error" + e); log.error("温度解析错误,ftp无法登陆!" + e); } - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); + imageAnnotation(infraPictureInfo, infraredInfo); } } else { - String imageName = Paths.get(imagePath).getFileName().toString(); String ftpUrlName = "[\"ftp://" + ftpUrlAccount + ":" + ftpUrlPwd + "@" + ftpUrlAddress + ":" + ftpUrlPort + imagePath + "\"]"; - float[][] floats = new float[0][]; - floats = UploadFtpImage(hrUavUrl, null, ftpUrlName); + float[][] floats = UploadFtpImage(hrUavUrl, null, ftpUrlName); if (floats != null && floats.length > 0 && floats[0].length > 0) { infraredInfo.setTemperatureMatrix(floats); log.info("配置的点位" + infraPictureInfo.getFirstX(), infraPictureInfo.getFirstY()); - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); - } else { - //不能调用华软接口,使用本地csv - try { - floats = exceptionHandling(); - infraredInfo.setTemperatureMatrix(floats); - } catch (IOException e) { - e.printStackTrace(); - log.error("温度解析错误,接口无法调用!"); - } - String s = ImageOverlays(infraPictureInfo, infraredInfo); - infraredInfo.setOutPath(s); + imageAnnotation(infraPictureInfo, infraredInfo); } } @@ -1665,8 +980,8 @@ public class HikVisionServiceImpl implements HikVisionService { // 构造 JSON 请求体 MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - log.info("eqpchannel{}",eqpchannel); - log.info("eqpchannel-address{}",eqpchannel.getAddress()); + log.info("eqpchannel{}", eqpchannel); + log.info("eqpchannel-address{}", eqpchannel.getAddress()); String[] eqpAddress = eqpchannel.getAddress().split(":"); ObjectMapper req_mapper = new ObjectMapper(); Map requestBody = new HashMap<>(); @@ -1678,74 +993,13 @@ public class HikVisionServiceImpl implements HikVisionService { requestBody.put("cameraType", eqpAddress[4]); requestBody.put("userName", eqpAddress[10]); requestBody.put("password", eqpAddress[11]); - log.info("requestBody 请求体: " +requestBody.toString()); - if(StringUtils.isNotNull(redisService.redisTemplate.opsForValue().get(eqpAddress[7] +'_'+ eqpchannel.getPresetPosCode()))){ - temperatureData = convertJsonToTemperatureData(redisService.redisTemplate.opsForValue().get(eqpAddress[7] +'_'+ eqpchannel.getPresetPosCode()).toString()); + log.info("requestBody 请求体: " + requestBody.toString()); + if (StringUtils.isNotNull(redisService.redisTemplate.opsForValue().get(eqpAddress[7] + '_' + eqpchannel.getPresetPosCode()))) { + temperatureData = convertJsonToTemperatureData(redisService.redisTemplate.opsForValue().get(eqpAddress[7] + '_' + eqpchannel.getPresetPosCode()).toString()); } else { - temperatureData.setMaxTemperature("-1"); - temperatureData.setMinTemperature("-1"); + temperatureData = null; } - - String jsonBody = null; - try { - jsonBody = req_mapper.writeValueAsString(requestBody); - log.info("JSON 请求体: " +jsonBody); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - RequestBody body = RequestBody.create(JSON, jsonBody); - -// String jsonBody = "{\"ip\":\"192.168.12.11\",\"port\":\"8000\",\"userName\":\"admin\",\"password\":\"sshw1234\",\"cameraType\":\"0\"}"; -// log.info("JSON 请求体: " +jsonBody); -// RequestBody body = RequestBody.create(JSON, requestBody.toString()); - - try { - // 构建 multipart 请求体 -// RequestBody requestBody = new MultipartBody.Builder() -// .setType(MultipartBody.FORM) -// .addFormDataPart("ip", "192.168.1.195") -// .addFormDataPart("port", "8000") -// .addFormDataPart("username", "admin") -// .addFormDataPart("password", "htjc2018") -// .build(); - - // 构建请求"http://172.21.101.79:8080/hw/cameraHong" -// Request request = new Request.Builder() -// .url(infraredUrl) -// .post(body) -// .addHeader("Content-Type", "application/json") -// .build(); -// -// -// Response response = client.newCall(request).execute(); -// if (!response.isSuccessful()) { -// log.error("请求测温接口失败,状态码: " + response.code()); -// } -// String responseBody = response.body() != null ? response.body().string() : null; -// -// // 解析 JSON 并提取 image_raw_flow -// ObjectMapper mapper = new ObjectMapper(); -// JsonNode jsonArray = mapper.readTree(responseBody); -// if (jsonArray.isEmpty()) { -// throw new RuntimeException("JSON数组为空"); -// } -// JsonNode firstItem = jsonArray.get("data"); -// JsonNode maxTemperature = firstItem.get("maxTemperature"); -// JsonNode minTemperature = firstItem.get("minTemperature"); -// JsonNode avgTemperature = firstItem.get("avgTemperature"); -// JsonNode temperatureDiff = firstItem.get("temperatureDiff"); -// JsonNode channel = firstItem.get("channel"); -// JsonNode ruleId = firstItem.get("ruleId"); -// -// -// temperatureData.setMaxTemperature(maxTemperature.toString()); -// temperatureData.setMinTemperature(minTemperature.toString()); -// System.out.println("firstItem: " + firstItem); - - } catch (Exception e) { - e.printStackTrace(); - } return temperatureData; } @@ -1777,46 +1031,6 @@ public class HikVisionServiceImpl implements HikVisionService { } } -// if (!produceEnvironment) { -// //调用华软接口 -// String protocol = hrFtpUrl.substring(0, 6); // "ftp://" -// String withoutProtocol = hrFtpUrl.substring(6); // "zthr02:zthr02@123.184.14.138:50021/" -// String[] userAndHost = withoutProtocol.split("@"); -// if (userAndHost.length != 2) { -// throw new IllegalArgumentException("URL 格式错误,缺少 @ 分隔符"); -// } -// String[] userPass = userAndHost[0].split(":"); -// String username = userPass[0]; -// String password = userPass[1]; -// String hostPort = userAndHost[1].replace("/", ""); // 移除末尾的 "/" -// String[] hostAndPort = hostPort.split(":"); -// String host = hostAndPort[0]; -// int port = Integer.parseInt(hostAndPort[1]); -// String imageName = Paths.get(filePath).getFileName().toString(); -// //将图片上传到华软 -// boolean isLoginHr = pic2Ftp(imageName, inputStreamPath, host, port, username, password); -// String ftpUrlName = "[\"" + hrFtpUrl + imageName + "\"]"; -// if (isLoginHr) { -// //可以登陆华软ftp -// inputStream = UploadFtpImageStream(hrUavUrl, ftpUrlName); -// if (inputStream != null) { -// return inputStream; -// } -// } else { -// log.error("调用华软接口失败,使用本地1.csv文件!"); -// inputStream = getClass().getClassLoader().getResourceAsStream("1.csv"); -// return inputStream; -// } -// } else { -// //共用同一ftp -// String imageName = Paths.get(filePath).getFileName().toString(); -// String ftpUrlName = "[\"ftp://" + ftpUrlAccount + ":" + ftpUrlPwd + "@" + ftpUrlAddress + ":" + ftpUrlPort + "/" + imageName + "\"]"; -// inputStream = UploadFtpImageStream(hrUavUrl, ftpUrlName); -// if (inputStream != null) { -// -// return inputStream; -// } -// } return inputStream; } @@ -1947,7 +1161,6 @@ public class HikVisionServiceImpl implements HikVisionService { return inputStream; } - @Override public int insertInfraredBoxList(InfraPictureInfo infraPictureInfo) { List infraredBoxes = new ArrayList<>(); @@ -1965,8 +1178,8 @@ public class HikVisionServiceImpl implements HikVisionService { box.setMinValue(infraPictureInfo.getCoordinates().get(i).getMinValue()); box.setAvgValue(infraPictureInfo.getCoordinates().get(i).getAvgValue()); box.setBoxName(infraPictureInfo.getCoordinates().get(i).getBoxName()); - box.setWidth(infraPictureInfo.getCoordinates().get(i).getWidth()); - box.setHeight(infraPictureInfo.getCoordinates().get(i).getHeight()); + box.setWidth(infraPictureInfo.getCoordinates().get(i).getWidth().toString()); + box.setHeight(infraPictureInfo.getCoordinates().get(i).getHeight().toString()); box.setMaxValueX(infraPictureInfo.getCoordinates().get(i).getMaxValueX()); box.setMaxValueY(infraPictureInfo.getCoordinates().get(i).getMaxValueY()); infraredBoxes.add(box); @@ -1985,4 +1198,66 @@ public class HikVisionServiceImpl implements HikVisionService { return infraredBoxMapper.selectInfraredBoxListByImgName(imgName); } + /** + * 图片标注(画框、温度) + */ + private void imageAnnotation(InfraPictureInfo infraPictureInfo, InfraredInfo infraredInfo) { + List coordinates = infraPictureInfo.getCoordinates(); + String imagePath = infraPictureInfo.getFilePath(); + + try { + if (infraredInfo.getTemperatureMatrix() == null) { + throw new Exception("无温度数据"); + } + + InputStream inputStreamPath = downloadFtp(imagePath); + BufferedImage originalImage = ImageIO.read(inputStreamPath); + + boolean genThermal = ImageAnnotationService.isGenThermal(imagePath); + + BufferedImage result = annotationService.annotate(originalImage, infraredInfo, coordinates, genThermal); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(result, "jpg", baos); + InputStream inputStream = new ByteArrayInputStream(baos.toByteArray()); + + // 新图片名称 + String outputPath = getOutputPath(infraPictureInfo.getFilePath()); + picFtp(outputPath, inputStream, ftpUrlAddress, ftpUrlPort, ftpUrlAccount, ftpUrlPwd); + + final String algType = infraPictureInfo.getImgType(); + + // 生成CSV文件 + if (AlgConstants.INFRA_CAMERA_REVERSE.equals(algType) + || AlgConstants.INFRA_CAMERA.equals(algType)) { + String csvData = convertMatrixToCsv(infraredInfo.getTemperatureMatrix()); + InputStream csvStream = new ByteArrayInputStream(csvData.getBytes(StandardCharsets.UTF_8)); + + String pureName = imagePath.substring(0, imagePath.lastIndexOf('.')); + String csvPath = pureName + ".csv"; + log.info("保存csv文件:{}", csvPath); + picFtp(csvPath, csvStream, ftpUrlAddress, ftpUrlPort, ftpUrlAccount, ftpUrlPwd); + } + + infraredInfo.setOutPath(outputPath); + } catch (Exception e) { + log.error("Image annotation error occurred: {}", e.getMessage()); + infraredInfo.setOutPath(imagePath); + } + } + + private String getOutputPath(String imagePath) { + Date date = new Date(); + SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss"); + String markPicName = sf.format(date); + // 获取文件名(不含扩展名) + File file = new File(imagePath); + String fileName = file.getName(); + String pureName = fileName.substring(0, fileName.lastIndexOf('.')); + String filename = picPath; + log.info("filename图片路径:{}", filename); + String outputPath = picPath + pureName + "_" + markPicName + ".jpg"; + + return outputPath; + } } diff --git a/src/main/java/com/inspect/simulator/utils/ThermalGenerator.java b/src/main/java/com/inspect/simulator/utils/ThermalGenerator.java new file mode 100644 index 0000000..c1df753 --- /dev/null +++ b/src/main/java/com/inspect/simulator/utils/ThermalGenerator.java @@ -0,0 +1,84 @@ +package com.inspect.simulator.utils; + +import com.inspect.simulator.service.TemperatureDataService; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +public class ThermalGenerator { + private ThermalGenerator() {} + + /** + * 生成伪彩色热图。 + * + * @param temps 温度矩阵 [row][col] + * @param tMin 全图最低温 + * @param tMax 全图最高温 + * @param outW 输出宽度 + * @param outH 输出高度 + * @param savePath 保存路径 (null 则不保存) + * @return BufferedImage + */ + public static BufferedImage generate( + float[][] temps, float tMin, float tMax, int outW, int outH, String savePath) + throws IOException { + int irH = temps.length, irW = temps[0].length; + double sx = (double) irW / outW; + double sy = (double) irH / outH; + + BufferedImage img = new BufferedImage(outW, outH, BufferedImage.TYPE_INT_RGB); + + for (int y = 0; y < outH; y++) { + for (int x = 0; x < outW; x++) { + float temp = bilinear(temps, irW, irH, x * sx, y * sy); + img.setRGB(x, y, ThermalPalette.map(temp, tMin, tMax)); + } + } + + if (savePath != null) { + File f = new File(savePath); + File p = f.getParentFile(); + if (p != null && !p.exists()) { + p.mkdirs(); + } + ImageIO.write(img, "JPEG", f); + } + return img; + } + + public static BufferedImage generate(TemperatureDataService tempData, int outW, int outH) { + float[][] temps = tempData.getTemperatures(); + float tempMax = tempData.getMaxTemp(); + float tempMin = tempData.getMinTemp(); + + int irH = temps.length, irW = temps[0].length; + double sx = (double) irW / outW; + double sy = (double) irH / outH; + + BufferedImage img = new BufferedImage(outW, outH, BufferedImage.TYPE_INT_RGB); + + for (int y = 0; y < outH; y++) { + for (int x = 0; x < outW; x++) { + float temp = bilinear(temps, irW, irH, x * sx, y * sy); + img.setRGB(x, y, ThermalPalette.map(temp, tempMin, tempMax)); + } + } + + return img; + } + + /** 双线性插值 */ + private static float bilinear(float[][] temps, int w, int h, double x, double y) { + int x0 = (int) Math.floor(x), y0 = (int) Math.floor(y); + int x1 = Math.min(x0 + 1, w - 1), y1 = Math.min(y0 + 1, h - 1); + x0 = Math.max(0, x0); + y0 = Math.max(0, y0); + double fx = x - x0, fy = y - y0; + return (float) + (temps[y0][x0] * (1 - fx) * (1 - fy) + + temps[y0][x1] * fx * (1 - fy) + + temps[y1][x0] * (1 - fx) * fy + + temps[y1][x1] * fx * fy); + } +} diff --git a/src/main/java/com/inspect/simulator/utils/ThermalPalette.java b/src/main/java/com/inspect/simulator/utils/ThermalPalette.java new file mode 100644 index 0000000..068c31c --- /dev/null +++ b/src/main/java/com/inspect/simulator/utils/ThermalPalette.java @@ -0,0 +1,64 @@ +package com.inspect.simulator.utils; + +/** + * 根据红外图片生成伪热力图 + */ +public final class ThermalPalette { + + // 控制点 (归一化位置 0~1, R, G, B) + private static final float[][] PTS = { + { 0.000f, 0, 0, 0 }, // 纯黑 + { 0.082f, 50, 16, 51 }, // 深紫黑 + { 0.123f, 21, 16, 55 }, // 深靛蓝 + { 0.178f, 20, 15, 93 }, // 蓝 + { 0.219f, 37, 16, 116 }, // 亮蓝 + { 0.260f, 62, 18, 123 }, // 蓝紫 + { 0.315f, 102, 28, 133 }, // 紫色 + { 0.356f, 125, 22, 135 }, // 紫品红 + { 0.397f, 150, 19, 138 }, // 品红 + { 0.438f, 174, 30, 135 }, // 品红偏红 + { 0.466f, 184, 37, 125 }, // 红品红 + { 0.507f, 192, 59, 112 }, // 红色 + { 0.548f, 208, 65, 68 }, // 红橙 + { 0.575f, 212, 73, 42 }, // 橙红 + { 0.616f, 213, 93, 28 }, // 橙色 + { 0.657f, 219, 119, 33 }, // 橙黄 + { 0.712f, 215, 131, 30 }, // 黄色 + { 0.753f, 202, 139, 37 }, // 黄 + { 0.795f, 215, 167, 36 }, // 亮黄 + { 0.836f, 211, 178, 40 }, // 黄白 + { 0.877f, 196, 173, 80 }, // 浅黄 + { 0.918f, 227, 216, 147 }, // 极浅黄 + { 1.000f, 255, 255, 255 }, // 纯白 + }; + + private ThermalPalette() {} + + /** + * 将温度值映射为 RGB 颜色。 + * @param temp 温度值 (单位: °C) + * @param tMin 全图最低温 + * @param tMax 全图最高温 + * @return 0xRRGGBB + */ + public static int map(float temp, float tMin, float tMax) { + float t = (temp - tMin) / (tMax - tMin); + t = Math.max(0, Math.min(1, t)); + + for (int i = 0; i < PTS.length - 1; i++) { + float p0 = PTS[i][0], p1 = PTS[i + 1][0]; + if (t >= p0 && t <= p1) { + float lt = (t - p0) / (p1 - p0); + int r = lerp(PTS[i][1], PTS[i + 1][1], lt); + int g = lerp(PTS[i][2], PTS[i + 1][2], lt); + int b = lerp(PTS[i][3], PTS[i + 1][3], lt); + return (r << 16) | (g << 8) | b; + } + } + return 0xFFFFFF; + } + + private static int lerp(float a, float b, float t) { + return Math.round(a + (b - a) * t); + } +} diff --git a/src/main/resources/fonts/NotoSansSC-Regular.ttf b/src/main/resources/fonts/NotoSansSC-Regular.ttf new file mode 100644 index 0000000..d350ffa Binary files /dev/null and b/src/main/resources/fonts/NotoSansSC-Regular.ttf differ