feat(image): 添加图像超分处理功能

- 新增 ImageSRStage 类实现图像超分辨率处理
- 在 AioDeviceController 中启用图像超分和增强的 Stage
- 修改 ImageEnhanceStage 配置检查逻辑,增加空值和占位符检测
- 为图像处理 Pipeline 添加超分 Stage
- 增加 ImageSRStage 的单元测试覆盖各种配置和执行情况
- 实现百度云图像超分 API 的调用和结果处理逻辑
This commit is contained in:
2025-11-27 18:45:10 +08:00
parent d60d7d9ad8
commit 610a183be1
4 changed files with 660 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ import com.ycwl.basic.image.pipeline.core.PipelineBuilder;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.stages.DownloadStage;
import com.ycwl.basic.image.pipeline.stages.ImageEnhanceStage;
import com.ycwl.basic.image.pipeline.stages.ImageSRStage;
import com.ycwl.basic.image.pipeline.stages.SourcePhotoUpdateStage;
import com.ycwl.basic.image.pipeline.stages.CleanupStage;
import com.ycwl.basic.mapper.AioDeviceMapper;
@@ -155,10 +156,9 @@ public class AioDeviceController {
photo.getGoodsId(), photo.getUrl(), photo.getScenicId()
);
// 启用图像增强Stage
Map<String, Boolean> stageConfig = new HashMap<>();
stageConfig.put("image_enhance", true);
context.loadStageConfig(null, stageConfig);
// 启用图像增强和超分的Stage
context.enableStage("image_enhance");
context.enableStage("image_sr");
// 执行Pipeline
boolean success = superResolutionPipeline.execute(context);
@@ -243,7 +243,7 @@ public class AioDeviceController {
return new PipelineBuilder<PhotoProcessContext>("SourcePhotoSuperResolutionPipeline")
.addStage(new DownloadStage()) // 1. 下载图片
.addStage(new ImageEnhanceStage(config)) // 2. 图像增强(超分)
.addStage(new ImageEnhanceStage(config)).addStage(new ImageSRStage(config)) // 2. 图像增强(超分)
.addStage(new SourcePhotoUpdateStage(sourceService, sourceId)) // 3. 上传并更新数据库
.addStage(new CleanupStage()) // 4. 清理临时文件
.build();

View File

@@ -137,9 +137,15 @@ public class ImageEnhanceStage extends AbstractPipelineStage<PhotoProcessContext
String apiKey = enhancerConfig.getApiKey();
String secretKey = enhancerConfig.getSecretKey();
if (appId == null ||
apiKey == null ||
secretKey == null) {
// 检查字段是否为 null 或空
if (appId == null || appId.isBlank() ||
apiKey == null || apiKey.isBlank() ||
secretKey == null || secretKey.isBlank()) {
return false;
}
// 检查是否包含 TODO 占位符
if (appId.contains("TODO") || apiKey.contains("TODO") || secretKey.contains("TODO")) {
return false;
}

View File

@@ -0,0 +1,190 @@
package com.ycwl.basic.image.pipeline.stages;
import com.ycwl.basic.image.enhancer.adapter.BceImageSR;
import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig;
import com.ycwl.basic.image.pipeline.annotation.StageConfig;
import com.ycwl.basic.image.pipeline.core.AbstractPipelineStage;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.core.StageResult;
import com.ycwl.basic.image.pipeline.enums.ImageSource;
import com.ycwl.basic.image.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 图像超分辨率Stage
* 使用百度云图像超分辨率API提升图片分辨率和清晰度
*
* 与ImageEnhanceStage的区别:
* - ImageEnhanceStage: 使用BceImageEnhancer进行清晰度增强
* - ImageSRStage: 使用BceImageSR进行超分辨率处理
*/
@Slf4j
@StageConfig(
stageId = "image_sr",
optionalMode = StageOptionalMode.SUPPORT,
description = "图像超分辨率处理",
defaultEnabled = false // 默认不启用,需要外部配置开启
)
public class ImageSRStage extends AbstractPipelineStage<PhotoProcessContext> {
private BceEnhancerConfig enhancerConfig;
/**
* 构造函数 - 使用默认配置
*/
public ImageSRStage() {
this(buildConfigFromEnvironment());
}
/**
* 构造函数 - 使用自定义配置
*
* @param enhancerConfig 图像增强配置
*/
public ImageSRStage(BceEnhancerConfig enhancerConfig) {
this.enhancerConfig = enhancerConfig;
}
@Override
public String getName() {
return "ImageSRStage";
}
@Override
protected boolean shouldExecuteByBusinessLogic(PhotoProcessContext context) {
// 仅对照片源为IPC的图片进行超分辨率处理
return context.getSource() == ImageSource.IPC;
}
@Override
protected StageResult<PhotoProcessContext> doExecute(PhotoProcessContext context) {
// 检查配置是否完整
if (!isConfigValid()) {
log.warn("图像超分辨率配置不完整,跳过处理。请配置百度云API凭证");
return StageResult.skipped("配置不完整,跳过超分辨率");
}
File currentFile = context.getCurrentFile();
if (currentFile == null || !currentFile.exists()) {
return StageResult.skipped("当前文件不存在");
}
try {
log.debug("开始图像超分辨率处理: {}", currentFile.getName());
// 创建百度云图像超分辨率客户端
BceImageSR srEnhancer = new BceImageSR();
srEnhancer.setConfig(enhancerConfig);
// 调用图像超分辨率API
// 注意:百度云API需要传入图片URL,这里使用本地文件的绝对路径
String imageUrl = currentFile.getAbsolutePath();
MultipartFile enhancedImage = srEnhancer.enhance(imageUrl);
if (enhancedImage == null || enhancedImage.isEmpty()) {
log.warn("图像超分辨率返回空结果,可能是API调用失败");
return StageResult.degraded("超分辨率失败,使用原图");
}
// 保存超分辨率后的图片到临时文件
File enhancedFile = context.getTempFileManager()
.createTempFile("sr_enhanced", ".jpg");
saveMultipartFileToFile(enhancedImage, enhancedFile);
if (!enhancedFile.exists() || enhancedFile.length() == 0) {
return StageResult.degraded("超分辨率结果保存失败,使用原图");
}
// 更新处理后的文件
context.updateProcessedFile(enhancedFile);
long originalSize = currentFile.length();
long enhancedSize = enhancedFile.length();
double sizeRatio = (double) enhancedSize / originalSize;
log.info("图像超分辨率完成: 原始{}KB -> 超分后{}KB (比例: {})",
originalSize / 1024,
enhancedSize / 1024,
String.format("%.2f", sizeRatio));
return StageResult.success(String.format("超分辨率完成 (%dKB -> %dKB)",
originalSize / 1024,
enhancedSize / 1024));
} catch (Exception e) {
log.error("图像超分辨率失败: {}", e.getMessage(), e);
// 超分辨率失败时返回降级状态,继续使用原图
return StageResult.degraded("超分辨率失败: " + e.getMessage());
}
}
/**
* 检查配置是否有效
*/
private boolean isConfigValid() {
if (enhancerConfig == null) {
return false;
}
String appId = enhancerConfig.getAppId();
String apiKey = enhancerConfig.getApiKey();
String secretKey = enhancerConfig.getSecretKey();
// 检查字段是否为 null 或空
if (appId == null || appId.isBlank() ||
apiKey == null || apiKey.isBlank() ||
secretKey == null || secretKey.isBlank()) {
return false;
}
// 检查是否包含 TODO 占位符
if (appId.contains("TODO") || apiKey.contains("TODO") || secretKey.contains("TODO")) {
return false;
}
return true;
}
/**
* 从环境变量构建配置
*/
private static BceEnhancerConfig buildConfigFromEnvironment() {
BceEnhancerConfig config = new BceEnhancerConfig();
config.setAppId(System.getenv("BCE_IMAGE_APP_ID"));
config.setApiKey(System.getenv("BCE_IMAGE_API_KEY"));
config.setSecretKey(System.getenv("BCE_IMAGE_SECRET_KEY"));
config.setQps(1.0f);
return config;
}
/**
* 保存MultipartFile到本地文件
*/
private void saveMultipartFileToFile(MultipartFile multipartFile, File targetFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
fos.write(multipartFile.getBytes());
fos.flush();
}
}
/**
* 获取当前配置(用于调试)
*/
public BceEnhancerConfig getEnhancerConfig() {
return enhancerConfig;
}
/**
* 设置配置(用于动态配置)
*/
public void setEnhancerConfig(BceEnhancerConfig enhancerConfig) {
this.enhancerConfig = enhancerConfig;
}
}