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 d2b4221..d7f02ed 100644 --- a/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java +++ b/src/main/java/com/inspect/simulator/domain/Infrared/Coordinate.java @@ -29,6 +29,8 @@ public class Coordinate { private String maxValueX; private String maxValueY; + private int[] points; + public Coordinate(int firstX, int firstY, int secondX, int secondY, int width, int height) { this.firstX = firstX; this.firstY = firstY; @@ -37,4 +39,10 @@ public class Coordinate { this.width = width; this.height = height; } + + public Coordinate(int[] points, int width, int height) { + this.points = points; + 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 index 1ff9037..a2a2de1 100644 --- a/src/main/java/com/inspect/simulator/service/ImageAnnotationService.java +++ b/src/main/java/com/inspect/simulator/service/ImageAnnotationService.java @@ -8,6 +8,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import java.awt.*; +import java.awt.geom.PathIterator; import java.awt.image.BufferedImage; import java.io.InputStream; import java.util.ArrayList; @@ -25,6 +26,10 @@ public class ImageAnnotationService { */ private static final int CROSSHAIR_R = 6, CROSSHAIR_ARM = 6; private Font chineseFont; + // 最高温度标注颜色 + private static final Color MAX_TEMP_COLOR = new Color(255, 23, 68); + // 最低温度标注颜色 + private static final Color MIN_TEMP_COLOR = new Color(41, 121, 255); private static int clamp(int v, int lo, int hi) { return Math.max(lo, Math.min(hi, v)); @@ -46,6 +51,20 @@ public class ImageAnnotationService { return false; } + private static double[][] shapeVertices(Shape shape) { + PathIterator it = shape.getPathIterator(null); + List pts = new ArrayList<>(); + double[] c = new double[6]; + while (!it.isDone()) { + int type = it.currentSegment(c); + if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) { + pts.add(new double[]{c[0], c[1]}); + } + it.next(); + } + return pts.toArray(new double[0][]); + } + /** * @param originalImage 原图 * @param infraredInfo 热成像信息 @@ -59,102 +78,98 @@ public class ImageAnnotationService { infraredInfo.setMaxTemp(tempData.getMaxTemp()); infraredInfo.setMinTemp(tempData.getMinTemp()); - // 插值比例(红外测温分辨率和可见光图片分辨率可能存在不一致情况) - float scaleX = (float) originalImage.getWidth() / tempData.getCols(); - float scaleY = (float) originalImage.getHeight() / tempData.getRows(); + int originalWidth = originalImage.getWidth(); + int originalHeight = originalImage.getHeight(); - int outW = originalImage.getWidth(); - int outH = originalImage.getHeight(); + // 图片宽高 和 温度矩阵宽高 比例 + float scaleX = (float) originalWidth / tempData.getCols(); + float scaleY = (float) originalHeight / tempData.getRows(); - BufferedImage canvas = new BufferedImage(outW, outH, BufferedImage.TYPE_INT_RGB); + BufferedImage canvas = new BufferedImage(originalWidth, originalHeight, 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); + canvasImage = ThermalGenerator.generate(tempData, originalWidth, originalHeight); } else { canvasImage = originalImage; } g.drawImage(canvasImage, 0, 0, null); - // 加载字体 ensureFont(); - int fontSize = Math.max(16, originalImage.getWidth() / 70); + int fontSize = Math.min(16, Math.max(12, originalImage.getWidth() / 40)); 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)); + if (true) { + // 1.转换全图最高温和最低温的点坐标 + int maxX = Math.round(tempData.getMaxCol() * scaleX); + int maxY = Math.round(tempData.getMaxRow() * scaleY); + int minX = Math.round(tempData.getMinCol() * scaleX); + int minY = Math.round(tempData.getMinRow() * scaleY); + + // 2.根据坐标绘制十字图形 + drawCrosshair(g, maxX, maxY, MAX_TEMP_COLOR); + drawCrosshair(g, minX, minY, MIN_TEMP_COLOR); + + // 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); + } - // 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[] points = coordinate.getPoints(); + int coordinateWidth = coordinate.getWidth(); + int coordinateHeight = coordinate.getHeight(); + // 图片宽高 和 画框时图片宽高 比例 + float rateX = (float) originalWidth / coordinateWidth; + float rateY = (float) originalHeight / coordinateHeight; + Polygon region = new Polygon(); + for (int i = 0; i + 1 < points.length; i += 2) { + region.addPoint(Math.round(points[i] * rateX), Math.round(points[i + 1] * rateY)); + } + double[][] vertices = shapeVertices(region); + double[] xs = new double[vertices.length]; + double[] ys = new double[vertices.length]; - 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); + for (int i = 0; i < vertices.length; i++) { + xs[i] = vertices[i][0] / scaleX; + ys[i] = vertices[i][1] / scaleY; + } g.setColor(Color.white); g.setStroke(new BasicStroke(3)); - g.drawRect(rx1, ry1, rx2 - rx1, ry2 - ry1); + g.draw(region); - String boxText = String.format("最高温度: %.1f 最低温度: %.1f 平均温度: %.1f", stats.getMax(), stats.getMin(), stats.getAvg()); - - // 用于文本避障 - Rectangle occupied = new Rectangle(rx1, ry1, rx2 - rx1, ry2 - ry1); + TemperatureDataService.RegionStats stats = tempData.calcRegion(xs, ys); - labels.add(createLabel(boxText, rx1, ry1 - 5, fm, true, occupied)); + java.awt.Rectangle bounds = region.getBounds(); + String boxText = String.format("最高温度: %.1f 最低温度: %.1f 平均温度: %.1f", stats.getMax(), stats.getMin(), stats.getAvg()); + labels.add(createLabel(boxText, bounds.x, bounds.y - 5, fm, true, bounds)); + + if (stats.getMaxRow() >= 0) { + int rMaxX = Math.round(stats.getMaxCol() * scaleX); + int rMaxY = Math.round(stats.getMaxRow() * scaleY); + int rMinX = Math.round(stats.getMinCol() * scaleX); + int rMinY = Math.round(stats.getMinRow() * scaleY); + drawDot(g, rMaxX, rMaxY, MAX_TEMP_COLOR); + drawDot(g, rMinX, rMinY, MIN_TEMP_COLOR); + } infraredInfo.setFrameMax(stats.getMax()); infraredInfo.setFrameMin(stats.getMin()); @@ -221,7 +236,7 @@ public class ImageAnnotationService { int r = CROSSHAIR_R; int arm = CROSSHAIR_ARM; g.setColor(color); - g.setStroke(new BasicStroke(3)); + g.setStroke(new BasicStroke(2)); 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); @@ -229,6 +244,19 @@ public class ImageAnnotationService { g.drawOval(x - r, y - r, r * 2, r * 2); } + /** + * 实心点 + 白色描边圆环,用于标注最高/最低温度位置 + */ + private void drawDot(Graphics2D g, int x, int y, Color color) { + int dotR = 3; + int ringR = 5; + g.setColor(Color.WHITE); + g.setStroke(new BasicStroke(2)); + g.drawOval(x - ringR, y - ringR, ringR * 2, ringR * 2); + g.setColor(color); + g.fillOval(x - dotR, y - dotR, dotR * 2, dotR * 2); + } + /** * 加载字体文件 */ diff --git a/src/main/java/com/inspect/simulator/service/TemperatureDataService.java b/src/main/java/com/inspect/simulator/service/TemperatureDataService.java index 1d0a732..a3390d6 100644 --- a/src/main/java/com/inspect/simulator/service/TemperatureDataService.java +++ b/src/main/java/com/inspect/simulator/service/TemperatureDataService.java @@ -43,32 +43,55 @@ public class TemperatureDataService { } } - public RegionStats calcRegion(int rowStart, int rowEnd, int colStart, int colEnd) { + public RegionStats calcRegion(double[] xs, double[] ys) { + if (xs == null || ys == null || xs.length != ys.length || xs.length < 3) { + return new RegionStats(0, 0, 0, -1, -1,-1,-1); + } + + // Bounding box of the polygon, clamped to the CSV grid + double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE; + double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE; + for (int i = 0; i < xs.length; i++) { + minX = Math.min(minX, xs[i]); maxX = Math.max(maxX, xs[i]); + minY = Math.min(minY, ys[i]); maxY = Math.max(maxY, ys[i]); + } + int r0 = Math.max(0, (int) Math.floor(minY)); + int r1 = Math.min(rows, (int) Math.ceil(maxY)); + int c0 = Math.max(0, (int) Math.floor(minX)); + int c1 = Math.min(cols, (int) Math.ceil(maxX)); + 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); - + int minR = -1, minC = -1, maxR = -1, maxC = -1; for (int r = r0; r < r1; r++) { for (int c = c0; c < c1; c++) { + if (!containsPoint(xs, ys, c + 0.5, r + 0.5)) continue; float v = temperatures[r][c]; if (!Float.isNaN(v)) { - if (v < min) { - min = v; - } - if (v > max) { - max = v; - } + if (v < min) { min = v; minR = r; minC = c; } + if (v > max) { max = v; maxR = r; maxC = c; } sum += v; count++; } } } - if (count == 0) { - return new RegionStats(0, 0, 0); + if (count == 0) return new RegionStats(0, 0, 0, -1, -1,-1,-1); + return new RegionStats(min, max, sum / count, minR, minC, maxR, maxC); + } + + private static boolean containsPoint(double[] xs, double[] ys, double px, double py) { + boolean inside = false; + for (int i = 0, j = xs.length - 1; i < xs.length; j = i++) { + double yi = ys[i], yj = ys[j]; + if ((yi > py) != (yj > py)) { + double xi = xs[i], xj = xs[j]; + if (px < (xj - xi) * (py - yi) / (yj - yi) + xi) { + inside = !inside; + } + } } - return new RegionStats(min, max, sum / count); + return inside; } @Getter @@ -76,11 +99,19 @@ public class TemperatureDataService { private final float min; private final float max; private final double avg; + private final int minRow; + private final int minCol; + private final int maxRow; + private final int maxCol; - public RegionStats(float min, float max, double avg) { + public RegionStats(float min, float max, double avg, int minRow, int minCol, int maxRow, int maxCol) { this.min = min; this.max = max; this.avg = avg; + this.minRow = minRow; + this.minCol = minCol; + this.maxRow = maxRow; + this.maxCol = maxCol; } } 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 aac3540..cc026e8 100644 --- a/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java +++ b/src/main/java/com/inspect/simulator/service/impl/HikVisionServiceImpl.java @@ -1,7 +1,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.databind.JsonNode; @@ -26,21 +25,6 @@ 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; @@ -56,6 +40,24 @@ 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.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.List; +import java.util.concurrent.TimeUnit; + /** * 海康威视测试服务类 */ @@ -99,6 +101,116 @@ public class HikVisionServiceImpl implements HikVisionService { @Resource private ImageAnnotationService annotationService; + //校验 + public static byte[] locateAndReadInfraredData(byte[] fileData) { + // 文件末尾标识 + final byte[] FOOTER_SIGN = { + (byte) 0x37, (byte) 0x66, (byte) 0x07, (byte) 0x1a, + (byte) 0x12, (byte) 0x3a, (byte) 0x4c, (byte) 0x9f, + (byte) 0xa9, (byte) 0x5d, (byte) 0x21, (byte) 0xd2, + (byte) 0xda, (byte) 0x7d, (byte) 0x26, (byte) 0xbc + }; + + try { + // 1. 检查数据大小 + if (fileData.length < FOOTER_SIGN.length + 4) { + System.err.println("错误:数据太小"); + return null; + } + + // 2. 读取并验证文件尾标识 + byte[] footer = new byte[FOOTER_SIGN.length]; + // 从数组末尾读取FOOTER_SIGN + System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length, footer, 0, FOOTER_SIGN.length); + + if (!Arrays.equals(footer, FOOTER_SIGN)) { + System.err.println("错误:文件标识不匹配"); + return null; + } + + // 3. 读取偏移量(小端序) + byte[] offsetBytes = new byte[4]; + // 读取FOOTER_SIGN前的4个字节作为偏移量 + System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length - 4, offsetBytes, 0, 4); + int dataOffset = ByteBuffer.wrap(offsetBytes) + .order(ByteOrder.LITTLE_ENDIAN) + .getInt(); + + // 4. 验证偏移量有效性 + if (dataOffset < 0 || dataOffset >= fileData.length - FOOTER_SIGN.length - 4) { + System.err.println("错误:无效的偏移量"); + return null; + } + + // 5. 定位到偏移量处读取数据(直到文件尾标识前) + int dataLength = fileData.length - FOOTER_SIGN.length - 4 - dataOffset; + if (dataLength <= 0) { + System.err.println("错误:数据长度为0"); + return null; + } + + byte[] infraredData = new byte[dataLength]; + System.arraycopy(fileData, dataOffset, infraredData, 0, dataLength); + + return infraredData; + + } catch (Exception e) { + System.err.println("处理错误: " + e.getMessage()); + return null; + } + } + + //二维数组转 CSV + private static String convertMatrixToCsv(float[][] matrix) { + StringBuilder csvBuilder = new StringBuilder(); + for (float[] row : matrix) { + for (int i = 0; i < row.length; i++) { + csvBuilder.append(row[i]); + if (i < row.length - 1) { + csvBuilder.append(","); + } + } + csvBuilder.append("\n"); + } + return csvBuilder.toString(); + } + + public static String truncateFileNameToCsv(String originalPath) { + // 获取文件名部分(最后一个 '/' 之后的内容) + int lastSlashIndex = originalPath.lastIndexOf('/'); + if (lastSlashIndex == -1) { + // 如果没有路径分隔符,直接处理整个字符串 + return processFileNameToCsv(originalPath); + } + + // 分离路径和文件名 + String path = originalPath.substring(0, lastSlashIndex + 1); + String fileName = originalPath.substring(lastSlashIndex + 1); + + // 处理文件名部分并强制改为.csv + String processedFileName = processFileNameToCsv(fileName); + + + return path + processedFileName; + } + + private static String processFileNameToCsv(String fileName) { + // 去掉文件扩展名(如果有) + int lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex != -1) { + fileName = fileName.substring(0, lastDotIndex); + } + + // 去掉最后一个 '_' 及其后面的部分 + int lastUnderscoreIndex = fileName.lastIndexOf('_'); + if (lastUnderscoreIndex != -1) { + fileName = fileName.substring(0, lastUnderscoreIndex); + } + + // 强制添加.csv后缀 + return fileName + ".csv"; + } + @Override public AjaxResult capturePicture(InfraPictureInfo infraPictureInfo) { log.info("capturePicture, coordinate:{}", infraPictureInfo); @@ -281,78 +393,6 @@ public class HikVisionServiceImpl implements HikVisionService { return infraredInfo; } - //校验 - public static byte[] locateAndReadInfraredData(byte[] fileData) { - // 文件末尾标识 - final byte[] FOOTER_SIGN = { - (byte) 0x37, (byte) 0x66, (byte) 0x07, (byte) 0x1a, - (byte) 0x12, (byte) 0x3a, (byte) 0x4c, (byte) 0x9f, - (byte) 0xa9, (byte) 0x5d, (byte) 0x21, (byte) 0xd2, - (byte) 0xda, (byte) 0x7d, (byte) 0x26, (byte) 0xbc - }; - - try { - // 1. 检查数据大小 - if (fileData.length < FOOTER_SIGN.length + 4) { - System.err.println("错误:数据太小"); - return null; - } - - // 2. 读取并验证文件尾标识 - byte[] footer = new byte[FOOTER_SIGN.length]; - // 从数组末尾读取FOOTER_SIGN - System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length, footer, 0, FOOTER_SIGN.length); - - if (!Arrays.equals(footer, FOOTER_SIGN)) { - System.err.println("错误:文件标识不匹配"); - return null; - } - - // 3. 读取偏移量(小端序) - byte[] offsetBytes = new byte[4]; - // 读取FOOTER_SIGN前的4个字节作为偏移量 - System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length - 4, offsetBytes, 0, 4); - int dataOffset = ByteBuffer.wrap(offsetBytes) - .order(ByteOrder.LITTLE_ENDIAN) - .getInt(); - - // 4. 验证偏移量有效性 - if (dataOffset < 0 || dataOffset >= fileData.length - FOOTER_SIGN.length - 4) { - System.err.println("错误:无效的偏移量"); - return null; - } - - // 5. 定位到偏移量处读取数据(直到文件尾标识前) - int dataLength = fileData.length - FOOTER_SIGN.length - 4 - dataOffset; - if (dataLength <= 0) { - System.err.println("错误:数据长度为0"); - return null; - } - - byte[] infraredData = new byte[dataLength]; - System.arraycopy(fileData, dataOffset, infraredData, 0, dataLength); - - return infraredData; - - } catch (Exception e) { - System.err.println("处理错误: " + e.getMessage()); - return null; - } - } - //二维数组转 CSV - private static String convertMatrixToCsv(float[][] matrix) { - StringBuilder csvBuilder = new StringBuilder(); - for (float[] row : matrix) { - for (int i = 0; i < row.length; i++) { - csvBuilder.append(row[i]); - if (i < row.length - 1) { - csvBuilder.append(","); - } - } - csvBuilder.append("\n"); - } - return csvBuilder.toString(); - } //调用华软接口获取csv文件 public float[][] UploadFtpImage(String url, String type, String image) { log.info("imageName" + image); @@ -691,7 +731,8 @@ public class HikVisionServiceImpl implements HikVisionService { if (inputStream != null) { try { inputStream.close(); - } catch (IOException ignored) {} + } catch (IOException ignored) { + } } // 关闭FTP连接 @@ -835,12 +876,26 @@ public class HikVisionServiceImpl implements HikVisionService { 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])); + + if (parts.length > 2) { + int last1 = parts[parts.length - 1]; + int last2 = parts[parts.length - 2]; + int width = Math.max(last1, last2); + int height = Math.min(last1, last2); + int[] points; + // 移动设备的矩形框,只有6个值:分为左上角(x,y) 右下角(x,y) 图片高宽(h,w) + if (parts.length == 6 && last1 > last2) { + points = new int[]{ + parts[0], parts[1], + parts[2], parts[1], + parts[2], parts[3], + parts[0], parts[3] + }; + } else { + points = new int[parts.length - 2]; + System.arraycopy(parts, 0, points, 0, parts.length - 2); + } + coordinates.add(new Coordinate(points, width, height)); } else { log.error("{} 点位画框数据异常,请检查!", basedataPatrolPoint.getPatrolPointId()); } @@ -860,32 +915,42 @@ public class HikVisionServiceImpl implements HikVisionService { infraredInfo = readDataHw(imageBytes); //绘制框选标识 imageAnnotation(infraPictureInfo, infraredInfo); - } catch (IOException e) { log.error("error" + e); } } else if (AlgConstants.INFRA_CAMERA_REVERSE.equals(imageType)) { - //相机反算红外 + // 相机反算红外:优先DL/T664,其次反算 InputStream inputStreamPath = downloadFtp(imagePath); if (StringUtils.isNull(inputStreamPath)) { log.error("inputStreamPath图片为空"); } - - //调取实时测温温度 - //传值,相机 ip - TemperatureData temperatureData = TemperatureMeasurement(basedataPatrolPoint); - - 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); + try { + byte[] imageBytes = IOUtils.toByteArray(inputStreamPath); + inputStream.close(); + // 1.判断是否为DL/T664 + byte[] infraredData = locateAndReadInfraredData(imageBytes); + if (infraredData == null) { + InputStream reusableStream = new ByteArrayInputStream(imageBytes); + //调取实时测温温度 + //传值,相机 ip + TemperatureData temperatureData = TemperatureMeasurement(basedataPatrolPoint); + + if (temperatureData != null) { + String minTemperature = temperatureData.getMinTemperature(); + String maxTemperature = temperatureData.getMaxTemperature(); + log.info("[INFRARED] 最高温度值:{}", maxTemperature); + log.info("[INFRARED] 最低温度值:{}", minTemperature); + float[][] matrix = tempCount.countTemp(reusableStream, Double.valueOf(maxTemperature), Double.valueOf(minTemperature));//图,最高值,最低值 + infraredInfo.setTemperatureMatrix(matrix); + } + } else { + infraredInfo = readDataHw(imageBytes); + } + imageAnnotation(infraPictureInfo, infraredInfo); + } catch (Exception e) { + log.error("error" + e); } - - imageAnnotation(infraPictureInfo, infraredInfo); } else if (AlgConstants.INFRA_1800.equals(imageType)) { //ivs1800红外图 imageAnnotation(infraPictureInfo, infraredInfo); @@ -1034,43 +1099,6 @@ public class HikVisionServiceImpl implements HikVisionService { return inputStream; } - - public static String truncateFileNameToCsv(String originalPath) { - // 获取文件名部分(最后一个 '/' 之后的内容) - int lastSlashIndex = originalPath.lastIndexOf('/'); - if (lastSlashIndex == -1) { - // 如果没有路径分隔符,直接处理整个字符串 - return processFileNameToCsv(originalPath); - } - - // 分离路径和文件名 - String path = originalPath.substring(0, lastSlashIndex + 1); - String fileName = originalPath.substring(lastSlashIndex + 1); - - // 处理文件名部分并强制改为.csv - String processedFileName = processFileNameToCsv(fileName); - - - return path + processedFileName; - } - - private static String processFileNameToCsv(String fileName) { - // 去掉文件扩展名(如果有) - int lastDotIndex = fileName.lastIndexOf('.'); - if (lastDotIndex != -1) { - fileName = fileName.substring(0, lastDotIndex); - } - - // 去掉最后一个 '_' 及其后面的部分 - int lastUnderscoreIndex = fileName.lastIndexOf('_'); - if (lastUnderscoreIndex != -1) { - fileName = fileName.substring(0, lastUnderscoreIndex); - } - - // 强制添加.csv后缀 - return fileName + ".csv"; - } - //解析获取csv文件温度--返回流文件 private InputStream getFtpFileAsStream(String csvUrl) throws IOException {