Browse Source

fix:调整手动拍照接口

master
白凤吉 1 week ago
parent
commit
7af8982f27
  1. 105
      src/main/java/com/iflytop/colortitration/app/common/utils/ImageAnalysisUtil.java
  2. 7
      src/main/java/com/iflytop/colortitration/app/controller/PhotoController.java
  3. 22
      src/main/java/com/iflytop/colortitration/app/model/vo/PhotoTakeVO.java
  4. 29
      src/main/java/com/iflytop/colortitration/common/service/PhotosService.java

105
src/main/java/com/iflytop/colortitration/app/common/utils/ImageAnalysisUtil.java

@ -90,47 +90,7 @@ public class ImageAnalysisUtil {
public static boolean analyzeRegionColorMatch(File imageFile, Color targetColor, double tolerancePercent, Rectangle region) throws IOException {
BufferedImage image = loadImage(imageFile);
BufferedImage smoothed = smoothImage(image);
Rectangle validRegion = getValidRegion(smoothed, region);
int width = validRegion.width;
int height = validRegion.height;
int area = width * height;
double[] brightness = new double[area];
int idx = 0;
for (int x = validRegion.x; x < validRegion.x + width; x++) {
for (int y = validRegion.y; y < validRegion.y + height; y++) {
Color c = new Color(smoothed.getRGB(x, y), true);
brightness[idx++] = (c.getRed() + c.getGreen() + c.getBlue()) / 3.0;
}
}
double sum = 0;
for (double v : brightness) sum += v;
double mean = sum / area;
double var = 0;
for (double v : brightness) var += Math.pow(v - mean, 2);
double std = Math.sqrt(var / area);
long sumR = 0, sumG = 0, sumB = 0;
int validCount = 0;
idx = 0;
for (int x = validRegion.x; x < validRegion.x + width; x++) {
for (int y = validRegion.y; y < validRegion.y + height; y++) {
if (Math.abs(brightness[idx++] - mean) <= BUBBLE_STD_MULTIPLIER * std) {
Color c = new Color(smoothed.getRGB(x, y), true);
sumR += c.getRed();
sumG += c.getGreen();
sumB += c.getBlue();
validCount++;
}
}
}
Color avgColor = validCount == 0
? new Color(0, 0, 0)
: new Color(
(int) (sumR / validCount),
(int) (sumG / validCount),
(int) (sumB / validCount)
);
Color avgColor = computeFilteredAverageColor(smoothed, region);
double distance = computeDistance(avgColor, targetColor);
return distance <= convertTolerance(tolerancePercent);
}
@ -191,6 +151,21 @@ public class ImageAnalysisUtil {
return aspect >= minAspectRatio;
}
/**
* 返回指定图像文件区域的平均颜色的16进制字符串
* 使用高斯平滑 + 泡沫过滤处理后计算区域平均颜色格式 "#RRGGBB"
*
* @param imageFile 待分析的图像文件
* @param region 指定区域x, y, width, height
* @return 区域平均颜色的16进制代码
* @throws IOException 如果读取图像文件失败
*/
public static String getRegionAverageHexColor(File imageFile, Rectangle region) throws IOException {
BufferedImage image = loadImage(imageFile);
BufferedImage smoothed = smoothImage(image);
Color avg = computeFilteredAverageColor(smoothed, region);
return String.format("#%02X%02X%02X", avg.getRed(), avg.getGreen(), avg.getBlue());
}
// --------------- 私有辅助方法 ---------------
/**
@ -252,6 +227,54 @@ public class ImageAnalysisUtil {
}
/**
* 先对指定区域像素的亮度进行统计然后使用亮度均值和标准差过滤泡沫噪声再计算剩余像素的平均颜色
*
* @param img 已平滑的图像对象
* @param region 分析区域
* @return 过滤后的区域平均颜色
*/
private static Color computeFilteredAverageColor(BufferedImage img, Rectangle region) {
int w = region.width, h = region.height, area = w * h;
double[] bright = new double[area];
int idx = 0;
for (int x = region.x; x < region.x + w; x++)
for (int y = region.y; y < region.y + h; y++) {
bright[idx++] = getGray(img.getRGB(x, y));
}
double sum = 0;
for (double v : bright) sum += v;
double mean = sum / area;
double var = 0;
for (double v : bright) var += Math.pow(v - mean, 2);
double std = Math.sqrt(var / area);
long sumR = 0, sumG = 0, sumB = 0;
int cnt = 0;
idx = 0;
for (int x = region.x; x < region.x + w; x++)
for (int y = region.y; y < region.y + h; y++) {
if (Math.abs(bright[idx++] - mean) <= BUBBLE_STD_MULTIPLIER * std) {
Color c = new Color(img.getRGB(x, y), true);
sumR += c.getRed();
sumG += c.getGreen();
sumB += c.getBlue();
cnt++;
}
}
return cnt == 0 ? new Color(0, 0, 0) : new Color((int) (sumR / cnt), (int) (sumG / cnt), (int) (sumB / cnt));
}
/**
* 计算单像素的灰度值简单平均法
*
* @param rgb 像素的 RGB 整数值
* @return 灰度值
*/
private static double getGray(int rgb) {
Color c = new Color(rgb, true);
return (c.getRed() + c.getGreen() + c.getBlue()) / 3.0;
}
/**
* 将百分比容差转换为在 RGB 空间中的绝对距离阈值
*
* @param percent 容差百分比0100

7
src/main/java/com/iflytop/colortitration/app/controller/PhotoController.java

@ -2,6 +2,7 @@ package com.iflytop.colortitration.app.controller;
import com.iflytop.colortitration.app.model.dto.PhotoSaveDTO;
import com.iflytop.colortitration.app.model.dto.PhotoTakeDTO;
import com.iflytop.colortitration.app.model.vo.PhotoTakeVO;
import com.iflytop.colortitration.common.base.BasePageQuery;
import com.iflytop.colortitration.common.model.vo.PhotoListVO;
import com.iflytop.colortitration.common.model.vo.PhotoVO;
@ -38,12 +39,10 @@ public class PhotoController {
return Result.success(photosService.get(id));
}
@Operation(summary = "拍摄一张照片")
@PostMapping("/take")
public Result<?> take(@RequestBody PhotoTakeDTO photoTakeDTO) throws Exception {
photosService.take(photoTakeDTO.getModuleCode());
return Result.success();
public Result<PhotoTakeVO> take(@RequestBody PhotoTakeDTO photoTakeDTO) throws Exception {
return Result.success(photosService.take(photoTakeDTO.getModuleCode()));
}
@Operation(summary = "保存照片")

22
src/main/java/com/iflytop/colortitration/app/model/vo/PhotoTakeVO.java

@ -0,0 +1,22 @@
package com.iflytop.colortitration.app.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class PhotoTakeVO {
@Schema(description = "照片名称")
String fileName;
@Schema(description = "16进制颜色")
String color;
@Schema(description = "图像的Url")
String imageUrl;
public PhotoTakeVO(String fileName, String color,String imageUrl) {
this.fileName = fileName;
this.color = color;
this.imageUrl = imageUrl;
}
}

29
src/main/java/com/iflytop/colortitration/common/service/PhotosService.java

@ -5,9 +5,10 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytop.colortitration.app.common.enums.MultipleModuleCode;
import com.iflytop.colortitration.app.common.utils.ImageAnalysisUtil;
import com.iflytop.colortitration.app.core.state.DeviceState;
import com.iflytop.colortitration.app.model.dto.PhotoSaveDTO;
import com.iflytop.colortitration.app.websocket.server.WebSocketMessageType;
import com.iflytop.colortitration.app.model.vo.PhotoTakeVO;
import com.iflytop.colortitration.app.websocket.server.WebSocketSender;
import com.iflytop.colortitration.common.base.BasePageQuery;
import com.iflytop.colortitration.common.enums.PhotoModeType;
@ -25,6 +26,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import java.awt.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
@ -32,7 +34,9 @@ import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
@ -88,21 +92,18 @@ public class PhotosService extends ServiceImpl<PhotosMapper, Photos> {
return null;
}
public void take(MultipleModuleCode moduleCode) throws IOException {
File file = getStaticFile("1.png");
public PhotoTakeVO take(MultipleModuleCode moduleCode) throws IOException {
String fileName = "1.png";
File file = getStaticFile(fileName);
deviceState.getTitrationModuleStateMap().get(moduleCode).setCurrentPhoto(file);
// 1. 读取图片并转 Base64
byte[] bytes = Files.readAllBytes(file.toPath());
String b64 = Base64.getEncoder().encodeToString(bytes);
String dataUrl = "data:image/png;base64," + b64;
// 2. 构造要发送的对象
Map<String, Object> payload = new HashMap<>();
payload.put("moduleCode", moduleCode);
payload.put("image", dataUrl);
webSocketSender.push(WebSocketMessageType.PHOTO, payload);
// Map<String, Object> payload = new HashMap<>();
// payload.put("moduleCode", moduleCode);
// payload.put("image", dataUrl);
// webSocketSender.push(WebSocketMessageType.PHOTO, payload);
String color = ImageAnalysisUtil.getRegionAverageHexColor(file, new Rectangle(1315, 1315, 40, 40));
return new PhotoTakeVO(fileName, color, fileName);
}
public File getStaticFile(String fileName) throws IOException {

Loading…
Cancel
Save