YOLO12目标检测模型在SpringBoot微服务中的集成与应用
YOLO12目标检测模型在SpringBoot微服务中的集成与应用
最近在做一个智慧园区的项目,需要实时检测监控视频里的人流和车辆。一开始我们用的是传统的目标检测方案,但效果总是不太理想——要么检测速度跟不上,要么准确率不够高。后来团队决定试试最新的YOLO12模型,结果发现它的性能确实很出色,特别是那个注意力机制,让检测精度提升了不少。
但问题来了:怎么把这个强大的模型集成到我们现有的SpringBoot微服务架构里呢?总不能每个服务都单独部署一套模型吧?那样资源浪费不说,维护起来也是个噩梦。经过一番摸索,我们找到了一套比较成熟的集成方案,今天就来跟大家分享一下我们的实践经验。
1. 为什么要在微服务中集成YOLO12?
你可能会有疑问:目标检测模型不是直接跑在服务器上就行了吗,为什么非要集成到微服务里?其实这里面有几个很实际的原因。
首先,微服务架构现在已经是企业级应用的主流选择了。我们的智慧园区系统有十几个不同的服务模块——用户管理、设备管理、告警处理、数据分析等等。如果每个需要目标检测的服务都自己部署一套YOLO12,那资源消耗就太大了。想象一下,十几台服务器每台都跑着一个几GB的模型,这成本谁都受不了。
其次,集中管理真的很重要。通过微服务的方式,我们可以把YOLO12封装成一个独立的检测服务,其他服务通过API来调用。这样有几个好处:模型升级只需要更新这一个服务,不用到处修改;可以统一做性能监控和日志收集;还能根据负载情况动态扩缩容。
还有一个很重要的点是实时性要求。我们的监控系统需要7x24小时不间断运行,对延迟非常敏感。YOLO12本身已经很快了,但如果调用方式设计得不好,网络延迟可能会成为瓶颈。所以我们需要一个既高效又稳定的集成方案。
最后是扩展性考虑。今天我们用YOLO12做目标检测,明天可能就需要换其他模型,或者同时用多个模型。微服务架构让这种切换和扩展变得很简单,只需要调整服务间的调用关系就行,不用动整个系统架构。
2. 整体架构设计思路
我们的设计方案其实挺简单的,核心思想就是“专事专办”。YOLO12专门负责目标检测,其他业务逻辑交给对应的微服务处理。
整个架构分为三层。最上层是业务服务层,包括视频流服务、告警服务、数据分析服务等等。这些服务负责各自的业务逻辑,当需要做目标检测时,就调用中间层的检测服务。
中间层就是我们的主角——目标检测服务。这个服务专门封装了YOLO12模型,提供统一的检测接口。它接收图片或视频流,调用模型进行检测,然后把结果返回给调用方。这个服务是独立部署的,可以根据负载情况动态调整实例数量。
最下层是基础设施层,包括消息队列、数据库、缓存等等。我们特别用到了Redis来做结果缓存,因为很多场景下同一帧图片可能会被多个服务请求检测,缓存能大大减少重复计算。
数据流向也很清晰。视频流服务从摄像头获取实时视频,按帧发送给检测服务。检测服务处理完后,把结果同时推送给多个地方:原始结果存到数据库供后续分析,实时告警信息发送给告警服务,统计信息更新到缓存供前端展示。
这种设计有几个明显的优点。首先是解耦,检测逻辑和业务逻辑完全分离,各自可以独立开发和部署。其次是可扩展,检测服务可以水平扩展,应对高并发场景。还有就是易维护,模型升级、参数调整都只需要改动检测服务,不影响其他业务。
3. SpringBoot服务端实现细节
现在来看看检测服务具体是怎么实现的。我们用一个简单的SpringBoot应用来包装YOLO12模型。
首先得准备好模型文件。YOLO12支持多种格式,我们选择了ONNX格式,因为它在不同框架间的兼容性最好。模型文件大概300MB左右,我们把它放在项目的resources目录下,启动时加载到内存。
@Service
public class YOLO12DetectionService {
private OrtSession session;
private OrtEnvironment env;
@PostConstruct
public void init() throws OrtException {
// 加载ONNX模型
env = OrtEnvironment.getEnvironment();
OrtSession.SessionOptions sessionOptions = new OrtSession.SessionOptions();
// 可以根据需要设置优化选项
sessionOptions.setOptimizationLevel(OrtSession.SessionOptions.OptimizationLevel.ALL_OPT);
sessionOptions.setInterOpNumThreads(4);
sessionOptions.setIntraOpNumThreads(4);
// 加载模型文件
String modelPath = "classpath:models/yolo12n.onnx";
try (InputStream modelStream = getClass().getResourceAsStream("/models/yolo12n.onnx")) {
byte[] modelBytes = modelStream.readAllBytes();
session = env.createSession(modelBytes, sessionOptions);
}
log.info("YOLO12模型加载完成");
}
}
接下来是核心的检测方法。我们设计了一个通用的检测接口,支持图片和视频帧的检测。
public DetectionResult detect(byte[] imageData, DetectionConfig config) {
try {
// 1. 图片预处理
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
float[][][] processedImage = preprocessImage(image);
// 2. 创建输入Tensor
long[] shape = {1, 3, config.getInputHeight(), config.getInputWidth()};
OnnxTensor inputTensor = OnnxTensor.createTensor(env,
FloatBuffer.wrap(flattenArray(processedImage)), shape);
// 3. 执行推理
Map<String, OnnxTensor> inputs = new HashMap<>();
inputs.put("images", inputTensor);
OrtSession.Result results = session.run(inputs);
// 4. 后处理:解析检测结果
float[][] output = (float[][]) results.get(0).getValue();
List<DetectionBox> boxes = postprocessOutput(output, image.getWidth(), image.getHeight());
// 5. 过滤和排序
List<DetectionBox> filteredBoxes = filterBoxes(boxes, config.getConfidenceThreshold());
return new DetectionResult(filteredBoxes, System.currentTimeMillis());
} catch (Exception e) {
log.error("目标检测失败", e);
throw new DetectionException("检测处理失败", e);
}
}
图片预处理是关键的一步。YOLO12需要固定尺寸的输入,通常是640x640,所以我们需要把原始图片缩放到这个尺寸,同时还要做归一化处理。
private float[][][] preprocessImage(BufferedImage image) {
// 调整尺寸到640x640
BufferedImage resizedImage = resizeImage(image, 640, 640);
// 转换为RGB数组并归一化
int width = resizedImage.getWidth();
int height = resizedImage.getHeight();
float[][][] processed = new float[3][height][width];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = resizedImage.getRGB(x, y);
// 提取RGB通道并归一化到0-1
processed[0][y][x] = ((rgb >> 16) & 0xFF) / 255.0f; // R
processed[1][y][x] = ((rgb >> 8) & 0xFF) / 255.0f; // G
processed[2][y][x] = (rgb & 0xFF) / 255.0f; // B
}
}
return processed;
}
后处理部分主要是解析模型输出。YOLO12的输出包含了边界框坐标、置信度和类别信息,我们需要把这些信息转换成更易用的格式。
private List<DetectionBox> postprocessOutput(float[][] output, int originalWidth, int originalHeight) {
List<DetectionBox> boxes = new ArrayList<>();
// output[0]是检测结果,每行代表一个检测框
// 格式:[x_center, y_center, width, height, confidence, class_scores...]
for (float[] detection : output[0]) {
if (detection[4] < 0.01) continue; // 置信度过低直接跳过
float x = detection[0];
float y = detection[1];
float w = detection[2];
float h = detection[3];
float confidence = detection[4];
// 找到类别
int classId = 0;
float maxScore = 0;
for (int i = 5; i < detection.length; i++) {
if (detection[i] > maxScore) {
maxScore = detection[i];
classId = i - 5;
}
}
// 转换回原始图片坐标
float x1 = (x - w / 2) * originalWidth / 640;
float y1 = (y - h / 2) * originalHeight / 640;
float x2 = (x + w / 2) * originalWidth / 640;
float y2 = (y + h / 2) * originalHeight / 640;
boxes.add(new DetectionBox(x1, y1, x2, y2, confidence, classId));
}
return boxes;
}
4. RESTful API设计与实现
有了检测服务,接下来要设计对外提供的API。我们采用了RESTful风格,这样其他服务调用起来比较方便。
首先定义请求和响应的数据结构:
@Data
public class DetectionRequest {
@NotNull
private String imageBase64; // Base64编码的图片数据
private Float confidenceThreshold = 0.5f; // 置信度阈值
private Integer maxDetections = 100; // 最大检测数量
private List<String> targetClasses; // 指定检测的类别
}
@Data
public class DetectionResponse {
private String requestId;
private Long processingTime; // 处理耗时(ms)
private List<DetectionItem> detections;
private String errorMessage;
@Data
public static class DetectionItem {
private String className;
private Float confidence;
private BoundingBox box;
}
@Data
public static class BoundingBox {
private Float x1;
private Float y1;
private Float x2;
private Float y2;
}
}
控制器层的实现相对简单,主要是参数校验和结果封装:
@RestController
@RequestMapping("/api/v1/detection")
@Slf4j
public class DetectionController {
@Autowired
private DetectionService detectionService;
@PostMapping("/detect")
public ResponseEntity<DetectionResponse> detect(@RequestBody DetectionRequest request) {
String requestId = UUID.randomUUID().toString();
long startTime = System.currentTimeMillis();
try {
// 参数校验
if (StringUtils.isEmpty(request.getImageBase64())) {
return ResponseEntity.badRequest()
.body(DetectionResponse.error(requestId, "图片数据不能为空"));
}
// Base64解码
byte[] imageData = Base64.getDecoder().decode(request.getImageBase64());
// 构建检测配置
DetectionConfig config = DetectionConfig.builder()
.confidenceThreshold(request.getConfidenceThreshold())
.maxDetections(request.getMaxDetections())
.targetClasses(request.getTargetClasses())
.build();
// 执行检测
DetectionResult result = detectionService.detect(imageData, config);
// 构建响应
DetectionResponse response = new DetectionResponse();
response.setRequestId(requestId);
response.setProcessingTime(System.currentTimeMillis() - startTime);
response.setDetections(convertToResponseItems(result));
log.info("检测完成,requestId: {}, 耗时: {}ms",
requestId, response.getProcessingTime());
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("检测处理异常,requestId: {}", requestId, e);
return ResponseEntity.internalServerError()
.body(DetectionResponse.error(requestId, "检测服务异常"));
}
}
@PostMapping("/batch-detect")
public ResponseEntity<BatchDetectionResponse> batchDetect(
@RequestBody BatchDetectionRequest request) {
// 批量检测实现,支持异步处理
// ...
}
}
考虑到性能,我们还实现了批量检测接口。这个接口支持异步处理,适合视频流场景:
@PostMapping("/batch-detect")
public ResponseEntity<BatchDetectionResponse> batchDetect(
@RequestBody BatchDetectionRequest request) {
String batchId = UUID.randomUUID().toString();
List<CompletableFuture<DetectionResult>> futures = new ArrayList<>();
for (String imageBase64 : request.getImages()) {
CompletableFuture<DetectionResult> future = CompletableFuture.supplyAsync(() -> {
try {
byte[] imageData = Base64.getDecoder().decode(imageBase64);
return detectionService.detect(imageData, request.getConfig());
} catch (Exception e) {
log.error("批量检测单张图片失败", e);
return null;
}
}, detectionExecutor);
futures.add(future);
}
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 收集结果
List<DetectionResponse> responses = new ArrayList<>();
for (int i = 0; i < futures.size(); i++) {
DetectionResult result = futures.get(i).get();
if (result != null) {
responses.add(convertToResponse(result, i));
}
}
return ResponseEntity.ok(new BatchDetectionResponse(batchId, responses));
}
5. 客户端调用与服务治理
服务端准备好了,客户端怎么调用呢?我们提供了两种方式:同步调用和异步调用。
同步调用适合实时性要求高的场景,比如实时监控:
@Service
public class DetectionClient {
@Autowired
private RestTemplate restTemplate;
private String detectionServiceUrl = "http://detection-service/api/v1/detection";
public DetectionResponse detectImage(BufferedImage image) {
// 图片转Base64
String imageBase64 = convertToBase64(image);
DetectionRequest request = new DetectionRequest();
request.setImageBase64(imageBase64);
request.setConfidenceThreshold(0.5f);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DetectionRequest> entity = new HttpEntity<>(request, headers);
try {
ResponseEntity<DetectionResponse> response = restTemplate.postForEntity(
detectionServiceUrl + "/detect",
entity,
DetectionResponse.class
);
return response.getBody();
} catch (RestClientException e) {
log.error("调用检测服务失败", e);
throw new ServiceException("检测服务调用失败", e);
}
}
}
异步调用适合批量处理,比如历史视频分析:
public CompletableFuture<DetectionResponse> detectImageAsync(BufferedImage image) {
return CompletableFuture.supplyAsync(() -> detectImage(image), executor);
}
// 批量处理示例
public List<DetectionResponse> batchDetect(List<BufferedImage> images) {
List<CompletableFuture<DetectionResponse>> futures = images.stream()
.map(this::detectImageAsync)
.collect(Collectors.toList());
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
服务治理方面,我们用了Spring Cloud Gateway做网关,统一处理路由、限流、认证等。检测服务注册到Nacos,客户端通过服务名来调用,不用关心具体的实例地址。
熔断和降级也很重要。我们用Resilience4j实现了熔断器,当检测服务出现问题时,可以快速失败,避免雪崩效应:
@Bean
public CircuitBreaker detectionCircuitBreaker() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值
.waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断后等待时间
.slidingWindowSize(10) // 滑动窗口大小
.build();
return CircuitBreaker.of("detectionService", config);
}
@CircuitBreaker(name = "detectionService", fallbackMethod = "fallbackDetect")
public DetectionResponse detectWithCircuitBreaker(BufferedImage image) {
return detectImage(image);
}
private DetectionResponse fallbackDetect(BufferedImage image, Throwable t) {
log.warn("检测服务熔断,使用降级策略", t);
// 返回空结果或缓存结果
return new DetectionResponse();
}
负载均衡方面,我们配置了多个检测服务实例,客户端请求会自动分配到不同的实例上。监控方面,集成了Prometheus和Grafana,可以实时查看服务状态、请求量、响应时间等指标。
6. 性能优化实践
在实际使用中,我们发现了一些性能瓶颈,也做了一些优化。
首先是模型推理的优化。YOLO12支持TensorRT加速,我们把这个特性用了起来:
public class TensorRTDetectionService {
private TRTModel trtModel;
public void init() {
// 加载TensorRT引擎
String enginePath = "models/yolo12n.trt";
trtModel = new TRTModel(enginePath);
// 设置优化参数
trtModel.setOptimizationProfile(0)
.setMaxBatchSize(8)
.setMaxWorkspaceSize(1 << 30); // 1GB
}
public DetectionResult detectWithTRT(byte[] imageData) {
// TensorRT推理,速度比ONNX快2-3倍
float[][] output = trtModel.inference(preprocessImage(imageData));
return postprocessOutput(output);
}
}
缓存是另一个重要的优化点。很多场景下,同一张图片可能会被多次请求检测,比如多个服务都需要分析同一帧监控画面。我们加了Redis缓存:
@Service
public class CachedDetectionService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private DetectionService detectionService;
public DetectionResult detectWithCache(byte[] imageData, DetectionConfig config) {
// 生成缓存key
String cacheKey = generateCacheKey(imageData, config);
// 先查缓存
DetectionResult cachedResult = (DetectionResult) redisTemplate.opsForValue().get(cacheKey);
if (cachedResult != null) {
log.debug("缓存命中,key: {}", cacheKey);
return cachedResult;
}
// 缓存未命中,执行检测
DetectionResult result = detectionService.detect(imageData, config);
// 写入缓存,设置过期时间
redisTemplate.opsForValue().set(cacheKey, result, 5, TimeUnit.MINUTES);
return result;
}
private String generateCacheKey(byte[] imageData, DetectionConfig config) {
// 使用MD5生成图片指纹
String imageHash = DigestUtils.md5DigestAsHex(imageData);
String configHash = Objects.hash(config.getConfidenceThreshold(),
config.getMaxDetections(), config.getTargetClasses());
return String.format("detection:%s:%d", imageHash, configHash);
}
}
批量处理也能显著提升性能。我们实现了图片批处理,一次推理处理多张图片:
public List<DetectionResult> batchDetect(List<byte[]> imageDataList, DetectionConfig config) {
// 将多张图片打包成一个batch
float[][][][] batchInput = new float[imageDataList.size()][3][640][640];
for (int i = 0; i < imageDataList.size(); i++) {
batchInput[i] = preprocessImage(imageDataList.get(i));
}
// 批量推理
float[][][] batchOutput = model.batchInference(batchInput);
// 批量后处理
List<DetectionResult> results = new ArrayList<>();
for (int i = 0; i < batchOutput.length; i++) {
results.add(postprocessOutput(batchOutput[i], config));
}
return results;
}
连接池配置也很重要。检测服务通常会有大量并发请求,合理的连接池配置能避免连接瓶颈:
# application.yml
http:
pool:
max-total: 200 # 最大连接数
default-max-per-route: 50 # 每个路由最大连接数
validate-after-inactivity: 5000 # 空闲连接验证间隔
connection-timeout: 5000 # 连接超时
socket-timeout: 30000 # 读取超时
最后是监控和调优。我们给服务加了详细的监控指标:
@RestController
@Slf4j
public class MetricsController {
private final MeterRegistry meterRegistry;
// 记录检测耗时
private final Timer detectionTimer;
// 记录请求量
private final Counter requestCounter;
public MetricsController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.detectionTimer = Timer.builder("detection.latency")
.description("检测服务延迟")
.register(meterRegistry);
this.requestCounter = Counter.builder("detection.requests")
.description("检测请求数量")
.register(meterRegistry);
}
@PostMapping("/detect")
public DetectionResponse detect(@RequestBody DetectionRequest request) {
requestCounter.increment();
return detectionTimer.record(() -> {
// 执行检测逻辑
return detectionService.detect(request);
});
}
}
7. 实际应用案例
说了这么多理论,来看看实际应用效果。我们在智慧园区项目里用这套方案解决了几个具体问题。
第一个是出入口人车识别。园区有十几个出入口,每个出入口都有摄像头。原来保安需要盯着监控屏幕,现在系统自动识别进出的人和车,还能统计数量。我们给每个出入口部署了一个检测服务实例,实时处理视频流。识别到车辆时,会自动记录车牌号;识别到人员时,会跟门禁系统联动,有权限的自动放行。
实现代码大概长这样:
@Service
public class EntranceMonitoringService {
@Autowired
private DetectionClient detectionClient;
@Autowired
private AlertService alertService;
@Autowired
private AccessControlService accessControlService;
public void processVideoFrame(String cameraId, byte[] frameData) {
// 调用检测服务
DetectionResponse response = detectionClient.detectImage(frameData);
// 处理检测结果
for (DetectionItem item : response.getDetections()) {
if ("person".equals(item.getClassName())) {
handlePersonDetection(cameraId, item);
} else if ("car".equals(item.getClassName())) {
handleCarDetection(cameraId, item);
}
}
// 更新统计信息
updateStatistics(cameraId, response);
}
private void handlePersonDetection(String cameraId, DetectionItem person) {
// 人脸识别(如果有)
String personId = faceRecognitionService.recognize(person.getBox());
if (personId != null) {
// 检查门禁权限
boolean hasAccess = accessControlService.checkAccess(personId, cameraId);
if (!hasAccess) {
alertService.sendAlert("未授权人员进入", cameraId, person.getBox());
}
} else {
// 陌生人,发送告警
alertService.sendAlert("陌生人进入", cameraId, person.getBox());
}
}
}
第二个应用是消防通道占用检测。消防通道不能堆放杂物,也不能停车,但总有人不自觉。我们在关键位置装了摄像头,用YOLO12检测是否有物体堵塞通道。检测到异常时,系统会自动告警,并通知最近的保安去处理。
这个场景对实时性要求很高,因为安全无小事。我们做了专门的优化:
@Service
public class FirePassageMonitoringService {
// 使用专门的检测配置
private static final DetectionConfig FIRE_PASSAGE_CONFIG = DetectionConfig.builder()
.confidenceThreshold(0.3f) // 降低阈值,提高敏感度
.targetClasses(Arrays.asList("person", "car", "truck", "motorcycle"))
.build();
public void monitorFirePassage(String cameraId, byte[] frameData) {
// 快速检测
DetectionResponse response = detectionClient.detectWithConfig(frameData, FIRE_PASSAGE_CONFIG);
// 判断是否占用
boolean isBlocked = isPassageBlocked(response);
if (isBlocked) {
// 立即告警
alertService.urgentAlert("消防通道被占用", cameraId);
// 记录证据
evidenceService.saveEvidence(cameraId, frameData, response);
// 通知相关人员
notificationService.notifySecurity(cameraId);
}
}
}
第三个应用是周界入侵检测。园区围墙周边装了热成像摄像头,晚上也能清晰成像。系统检测到有人靠近围墙时,会自动跟踪,并启动声光报警。这个场景需要7x24小时运行,对稳定性要求很高。
我们为这个场景做了容错设计:
@Service
public class PerimeterMonitoringService {
@Autowired
@Qualifier("primaryDetectionClient")
private DetectionClient primaryClient;
@Autowired
@Qualifier("backupDetectionClient")
private DetectionClient backupClient;
@CircuitBreaker(name = "perimeterDetection", fallbackMethod = "fallbackDetect")
public DetectionResponse monitorPerimeter(String cameraId, byte[] frameData) {
// 优先使用主服务
return primaryClient.detectImage(frameData);
}
private DetectionResponse fallbackDetect(String cameraId, byte[] frameData, Throwable t) {
log.warn("主检测服务异常,切换到备用服务", t);
// 切换到备用服务
DetectionResponse response = backupClient.detectImage(frameData);
// 标记为降级结果
response.setDegraded(true);
return response;
}
@Scheduled(fixedRate = 30000) // 每30秒检查一次
public void healthCheck() {
try {
primaryClient.healthCheck();
log.info("主检测服务健康状态正常");
} catch (Exception e) {
log.error("主检测服务异常,准备切换", e);
switchToBackup();
}
}
}
从实际效果来看,这套方案运行得挺稳定的。检测准确率在白天能达到95%以上,晚上也有90%左右。响应时间方面,单张图片检测平均在50毫秒以内,完全满足实时性要求。资源使用上,每个检测服务实例占用大概2GB内存,CPU使用率在30%-50%之间,还算可以接受。
8. 总结
回过头来看,把YOLO12集成到SpringBoot微服务里,整个过程虽然有些挑战,但收获也不少。最大的感受是,这种架构确实让系统更灵活、更易维护了。
从技术角度看,有几个点值得注意。一是模型服务化之后,升级变得特别简单。之前要更新模型,得每台服务器都操作一遍,现在只需要更新检测服务,然后滚动重启就行。二是监控告警完善了,服务状态、性能指标都能实时看到,出问题能及时发现。三是扩展性好了,流量大的时候加几个实例,流量小的时候减几个,资源利用率高了不少。
不过也有些地方可以继续优化。比如模型推理还可以进一步加速,TensorRT只是其中一种方式,还可以试试其他推理引擎。缓存策略也可以更精细些,现在是用图片MD5做key,但有些场景下同样的物体在不同位置出现,其实可以复用检测结果。还有错误处理,现在虽然有了熔断降级,但智能化程度还不够,比如可以根据错误类型自动选择不同的降级策略。
如果你也在考虑类似的项目,我的建议是先从简单的场景开始,把基础框架搭起来,跑通整个流程。然后再逐步优化性能、完善功能。不要一开始就追求完美,那样容易陷入细节出不来。实际用起来之后,你会发现很多优化点都是业务场景倒逼出来的。
另外就是文档和监控一定要做好。微服务多了之后,如果没有完善的文档和监控,排查问题会非常痛苦。我们在这方面吃过亏,后来花了很大力气补课。
总的来说,AI模型微服务化是个趋势,特别是像目标检测这种通用能力。把它做成服务,不仅自己用着方便,还能开放给其他团队使用,创造更多价值。YOLO12加上SpringBoot这个组合,经过我们实际项目的验证,确实是个不错的选择。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)