/** * Этот метод выполняет шаг группирования. * * @return boolean TRUE, если объединение объектов произошло. */ private boolean groupByIntersectionStep() { int i = 0; while (i < objects.size() - 1) { int j = i + 1; while (j < objects.size()) { // Есть два прямоугольника (объекта) Rectangle a = objects.get(i).rectangle; Rectangle b = objects.get(j).rectangle; // Проверяем, пересекаются ли они if (a.intersect(b)) { // Рассчитываем площадь пересечения final double s = a.intersection(b).getSquare(); // Нас интересует относительная величина final double f = Math.max(a.getSquare() / s, b.getSquare() / s); // Проверяем порог if (f > params.intersectionThreshold) { // Выбираем победителя if (objects.get(i).score > objects.get(j).score) { // Победил A objects.remove(j); } else { // Победил B objects.remove(i); } return true; } } j++; } i++; } return false; }
/** * Этот метод выполняет обнаружение объектов. * * <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."); }