package com.auction; import org.opencv.core.Mat; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.dnn.Dnn; import org.opencv.dnn.Net; import org.opencv.imgcodecs.Imgcodecs; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import static org.opencv.dnn.Dnn.DNN_BACKEND_OPENCV; import static org.opencv.dnn.Dnn.DNN_TARGET_CPU; /** * Service for performing object detection on images using OpenCV's DNN * module. The DNN module can load pre‑trained models from several * frameworks (Darknet, TensorFlow, ONNX, etc.)【784097309529506†L209-L233】. Here * we load a YOLO model (Darknet) by specifying the configuration and * weights files. For each image we run a forward pass and return a * list of detected class labels. * * If model files are not found, the service operates in disabled mode * and returns empty lists. */ class ObjectDetectionService { private final Net net; private final List classNames; private final boolean enabled; ObjectDetectionService(String cfgPath, String weightsPath, String classNamesPath) throws IOException { // Check if model files exist var cfgFile = Paths.get(cfgPath); var weightsFile = Paths.get(weightsPath); var classNamesFile = Paths.get(classNamesPath); if (!Files.exists(cfgFile) || !Files.exists(weightsFile) || !Files.exists(classNamesFile)) { IO.println("⚠️ Object detection disabled: YOLO model files not found"); IO.println(" Expected files:"); IO.println(" - " + cfgPath); IO.println(" - " + weightsPath); IO.println(" - " + classNamesPath); IO.println(" Scraper will continue without image analysis."); this.enabled = false; this.net = null; this.classNames = new ArrayList<>(); return; } try { // Load network this.net = Dnn.readNetFromDarknet(cfgPath, weightsPath); this.net.setPreferableBackend(DNN_BACKEND_OPENCV); this.net.setPreferableTarget(DNN_TARGET_CPU); // Load class names (one per line) this.classNames = Files.readAllLines(classNamesFile); this.enabled = true; IO.println("✓ Object detection enabled with YOLO"); } catch (Exception e) { System.err.println("⚠️ Object detection disabled: " + e.getMessage()); throw new IOException("Failed to initialize object detection", e); } } /** * Detects objects in the given image file and returns a list of * human‑readable labels. Only detections above a confidence * threshold are returned. For brevity this method omits drawing * bounding boxes. See the OpenCV DNN documentation for details on * post‑processing【784097309529506†L324-L344】. * * @param imagePath absolute path to the image * @return list of detected class names (empty if detection disabled) */ List detectObjects(String imagePath) { if (!enabled) { return new ArrayList<>(); } List labels = new ArrayList<>(); var image = Imgcodecs.imread(imagePath); if (image.empty()) return labels; // Create a 4D blob from the image var blob = Dnn.blobFromImage(image, 1.0 / 255.0, new Size(416, 416), new Scalar(0, 0, 0), true, false); net.setInput(blob); List outs = new ArrayList<>(); var outNames = getOutputLayerNames(net); net.forward(outs, outNames); // Post‑process: for each detection compute score and choose class var confThreshold = 0.5f; for (var out : outs) { for (var i = 0; i < out.rows(); i++) { var data = out.get(i, 0); if (data == null) continue; // The first 5 numbers are bounding box, then class scores var scores = new double[classNames.size()]; System.arraycopy(data, 5, scores, 0, scores.length); var classId = argMax(scores); var confidence = scores[classId]; if (confidence > confThreshold) { var label = classNames.get(classId); if (!labels.contains(label)) { labels.add(label); } } } } return labels; } /** * Returns the indexes of the output layers in the network. YOLO * automatically discovers its output layers; other models may require * manually specifying them【784097309529506†L356-L365】. */ private List getOutputLayerNames(Net net) { List names = new ArrayList<>(); var outLayers = net.getUnconnectedOutLayers().toList(); var layersNames = net.getLayerNames(); for (var i : outLayers) { names.add(layersNames.get(i - 1)); } return names; } /** * Returns the index of the maximum value in the array. */ private int argMax(double[] array) { var best = 0; var max = array[0]; for (var i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; best = i; } } return best; } }