pt.scanner.server.data.Contour.java Source code

Java tutorial

Introduction

Here is the source code for pt.scanner.server.data.Contour.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.scanner.server.data;

import com.vividsolutions.jts.geom.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.stream.*;
import org.apache.commons.math3.util.*;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.opencv_core.CvMat;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.MatVector;
import org.bytedeco.javacpp.opencv_core.RNG;
import org.bytedeco.javacpp.opencv_core.RotatedRect;
import org.bytedeco.javacpp.opencv_core.Scalar;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
import org.bytedeco.javacpp.opencv_imgproc.Moments;
import org.slf4j.*;
import static pt.scanner.server.data.Grid.WORLD_REFERENCE;
import pt.scanner.server.util.*;
import static pt.scanner.server.util.Utils.BLACK;
import static pt.scanner.server.util.Utils.point;

public class Contour {

    public static final Integer MAX_CONTOUR = 30;
    private final static AtomicInteger globalIndex = new AtomicInteger();
    private static final Logger log = LoggerFactory.getLogger(Contour.class);
    private final Mat contour;
    private Map<Corner, Coordinate> corners;
    private final Mat image;
    private Integer index;
    private final Map<Position, Set<Line>> intersections = new HashMap<>();
    private final Integer lineCount;
    private final Map<Position, Set<Line>> mainLines = new HashMap<>();
    private final RotatedRect minRect;
    private final Moments moments;
    private Map<Corner, Coordinate> realCorners;
    private final RNG rng = new RNG();

    public Contour(Mat cont, Mat img) {
        this.index = globalIndex.getAndIncrement();
        contour = cont;
        image = img;
        moments = moments(contour);
        minRect = minAreaRect(contour);
        float quadrantLength = FastMath.max(minRect.size().height() / 2f, minRect.size().width() / 2f) * 1.05f;
        Map<Position, Line> guidelines = new HashMap<>();
        Position.quadrants().stream().forEach(p -> mainLines.put(p, new LinkedHashSet<>()));
        Position.quadrants().stream().forEach(
                p -> guidelines.put(p, new Line(getCentroid(), angle() + p.getPosition(), quadrantLength)));
        Mat lines = new Mat(image.size(), CV_8UC4, BLACK);
        // create auxiliary image with contour
        Mat bg = new Mat(image.size(), CV_8UC1, BLACK);
        opencv_core.MatVector v = new opencv_core.MatVector(1);
        v.put(0, contour);
        drawContours(bg, v, 0, new opencv_core.Scalar(255, 255, 255, 255));
        HoughLinesP(bg, lines, 1, FastMath.PI / 180, 30, 2, 20);
        CvMat source = lines.asCvMat();
        Position.quadrants().stream().forEach(p -> intersections.put(p, new LinkedHashSet<>()));
        lineCount = source.cols();
        IntStream.range(0, source.cols())
                .mapToObj(i -> new Line(source.get(0, i, 0), source.get(0, i, 1), source.get(0, i, 2),
                        source.get(0, i, 3)))
                .map(l -> l.expandedLine(image.size()))
                .forEach(l -> Position.quadrants().stream().filter(q -> guidelines.get(q).intersection(l) != null)
                        .forEach(q -> intersections.get(q).add(l)));
        if (log.isDebugEnabled()) {
            Mat debugImg = new Mat(image.size(), CV_8UC3, BLACK);
            cvtColor(image, debugImg, CV_GRAY2BGR);
            Position.quadrants().stream().forEach(q -> {
                intersections.get(q).stream().forEach(l -> line(debugImg, point(l.p0), point(l.p1), q.color()));
                line(debugImg, point(guidelines.get(q).p0), point(guidelines.get(q).p1), q.color());
            });
            MatVector vec = new MatVector(1);
            vec.put(0, contour);
            drawContours(debugImg, vec, 0, new Scalar(255, 255, 255, 255));
            Utils.showImage(debugImg, 1000);
        }
        log.debug("Contour {} constructed - {} lines", index, lines.cols());
    }

    public final float angle() {
        return minRect.angle() + (minRect.angle() > 45f ? -90f : (minRect.angle() < -45f ? 90f : 0));
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Contour other = (Contour) obj;
        return Objects.equals(this.index, other.getIndex());
    }

    public final Coordinate getCentroid() {
        return new Coordinate(moments.m10() / moments.m00(), moments.m01() / moments.m00());
    }

    public Mat getContour() {
        return contour;
    }

    public Coordinate getCorner(Corner p) {
        generateCorners();
        return corners.get(p);
    }

    public Mat getImage() {
        return image;
    }

    public Integer getIndex() {
        return index;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }

    public Integer getLineCount() {
        return lineCount;
    }

    public Map<Corner, Coordinate> getRealCorner(Corner c) {
        generateCorners();
        return realCorners;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 37 * hash + Objects.hashCode(this.index);
        return hash;
    }

    public void joinLines() {
        Position.quadrants().forEach(p -> {
            Line mean = Line.meanLine(intersections.get(p));
            if (mean != null) {
                mainLines.get(p).add(mean);
                mainLines.get(p).forEach(l -> l.addRelated(this));
            }
        });
        intersections.clear();
    }

    public Set<Line> mainLines(Position p) {
        return mainLines.get(p);
    }

    public void sortRelated(int contourIndex, int step, Position pos) {
        List<Contour> contours = mainLines(pos).iterator().next().getRelated().stream().sorted((c1,
                c2) -> (int) (c1.getCentroid().distance(getCentroid()) - c2.getCentroid().distance(getCentroid())))
                .collect(Collectors.toList());
        for (Contour c : contours) {
            c.setIndex(contourIndex);
            contourIndex += step;
        }
    }

    @Override
    public String toString() {
        return String.format("Contour<%s>", index);
    }

    private void generateCorners() {
        if (corners == null) {
            corners = Corner.corners().stream()
                    .filter(c -> !mainLines(c.getHorizontal()).isEmpty() && !mainLines(c.getVertical()).isEmpty()
                            && mainLines(c.getHorizontal()).iterator().next()
                                    .intersection(mainLines(c.getVertical()).iterator().next()) != null)
                    .collect(Collectors.toMap(c -> c, c -> new Coordinate(mainLines(c.getHorizontal()).iterator()
                            .next().intersection(mainLines(c.getVertical()).iterator().next()))));
            realCorners = Corner.corners().stream().filter(c -> !mainLines(c.getHorizontal()).isEmpty())
                    .filter(c -> !mainLines(c.getVertical()).isEmpty())
                    .collect(Collectors.toMap(c -> c, c -> new Coordinate(WORLD_REFERENCE.get(index).get(c))));
        }
    }
}