/** * Этот метод выполняет обнаружение объектов. * * <p>В результате получаем множество возможных положений объектов. * * @return double наибольший наблюдаемый отклик. */ private double detect(double scale) { LOG.debug("Detecting, scale = " + scale); // Выбираем каскад Хаара final HaarCascade cascade = getHaarCascade(scale); // Создаем кластеризатор KMeansClusterer kmeans = new KMeansClusterer(params.k); kmeans.setThreshold(params.threshold); // Шаг по осям будет зависеть от размера final int d = Math.max(1, (int) (scale / 4.0)); // Haar Detection for (int y = ROI.getY1(); y < ROI.getY2() - cascade.height; y += d) { for (int x = ROI.getX1(); x < ROI.getX2() - cascade.width; x += d) { final double r = HaarCascadeEvaluator.evaluateHaarCascade(cascade, integralImage, x, y); if (r > 0.0) { kmeans.addPoint(x + cascade.width / 2, y + cascade.height / 2); } } } // Кластеризуем отклики kmeans.cluster(); // Группируем кластеры kmeans.group(); // Сохраняем обнаруженные возможные положения объектов double maxScore = 0; for (Cluster c : kmeans.getClusters()) { // Объектом становится отклик который ближе других к центру кластера final ClusterPoint p = kmeans.getNearestPoint(c); // Создаем и сохраняем объект objects.add( new HaarObject( new Rectangle( p.getPoint().getX() - cascade.width / 2, p.getPoint().getY() - cascade.height / 2, cascade.width, cascade.height), c.getM())); // Обновляем рекорд if (c.getM() > maxScore) { maxScore = c.getM(); } } LOG.debug("Found " + objects.size() + " candidates, maximum score = " + maxScore); return maxScore; }
/** @see ImageProcessor#process() */ @Override public void process() { LOG.debug("Processing..."); if (integralImage == null) { throw new RuntimeException("Integral image undefined!"); } // Cleanup objects.clear(); // Если ROI не определен, придется анализировать все изображение if (ROI == null) { ROI = new Rectangle(0, 0, integralImage.width - 1, integralImage.height - 1); } // Используется для оптимизации (alpha-pruning) double a = 0; // Обнаружение ведем от максимального масштаба к минимальному double scale = Math.min(ROI.getWidth() / params.cascade.width, ROI.getHeight() / params.cascade.height) * params.maxScale; do { final double r = detect(scale); if (params.detectBiggest) { // Pruning if (r > 0) { if (r > a) { a = r; } else break; } } } while ((scale -= params.scaleStep) >= params.minScale); if (params.detectBiggest) { // Сохраняем положение объекта с максимальным откликом ArrayList<HaarObject> t = new ArrayList<>(); HaarObject mainObject = getMainObject(); if (mainObject != null) { LOG.debug("Main object has score = " + mainObject.score); t.add(mainObject); } objects = t; } else { // Группируем обнаруженные возможные положения объектов groupByIntersection(); } // Если надо выполняем трассировку откликов if (params.trace) { for (HaarObject object : objects) { final Rectangle r = object.rectangle; final HaarCascade cascade = getHaarCascade(r.getWidth() / (double) params.cascade.width); object.trace = HaarCascadeEvaluator.traceHaarCascade(cascade, integralImage, r.getX(), r.getY()); } } LOG.debug("Done."); }