org.lasarobotics.vision.detection.objects.Ellipse.java Source code

Java tutorial

Introduction

Here is the source code for org.lasarobotics.vision.detection.objects.Ellipse.java

Source

/*
 * Copyright (c) 2016 Arthur Pachachura, LASA Robotics, and contributors
 * MIT licensed
 */
package org.lasarobotics.vision.detection.objects;

import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import org.opencv.core.Size;

/**
 * Implements a single ellipse (acts like RotatedRect) with advanced measurement utilities
 */
public class Ellipse extends Detectable implements Comparable<Ellipse> {
    private RotatedRect rect;

    /**
     * Instantiate a null ellipse
     */
    public Ellipse() {
        this.rect = new RotatedRect();
    }

    /**
     * Create an ellipse based on an OpenCV rotated rectangle
     *
     * @param rect OpenCV rotated rectangle which bounds the ellipse
     */
    public Ellipse(RotatedRect rect) {
        this.rect = rect;
    }

    /**
     * Get the OpenCV rectangle which circumscribes the ellipse
     *
     * @return OpenCV rectange
     */
    public RotatedRect getRect() {
        return rect;
    }

    /**
     * Get the size of the ellipse (ignoring inclination)
     *
     * @return Size of the ellipse
     */
    public Size size() {
        return rect.size;
    }

    public double height() {
        return size().height;
    }

    /**
     * Offset the object, translating it by a specific offset point
     *
     * @param offset Point to offset by, e.g. (1, 0) would move object 1 px right
     */
    @Override
    public void offset(Point offset) {
        this.rect = new RotatedRect(new Point(rect.center.x + offset.x, rect.center.y + offset.y), rect.size,
                rect.angle);
    }

    public double width() {
        return size().width;
    }

    /**
     * Get the angle of inclination of the ellipse
     *
     * @return Angle of inclination
     */
    public double angle() {
        return rect.angle;
    }

    /**
     * Get the center of the ellipse
     *
     * @return Center of the ellipse as a point
     */
    public Point center() {
        return rect.center;
    }

    public double left() {
        return center().x - (width() / 2);
    }

    public double right() {
        return center().x + (width() / 2);
    }

    public double top() {
        return center().y - (height() / 2);
    }

    public double bottom() {
        return center().y + (height() / 2);
    }

    /**
     * Gets the area of the ellipse
     *
     * @return Area = semi-major axis * semi-minor axis * PI
     */
    public double area() {
        return semiMajorAxis() * semiMinorAxis() * Math.PI;
    }

    /**
     * Get the major axis of the ellipse
     *
     * @return 2a
     */
    private double majorAxis() {
        return Math.max(height(), width());
    }

    /**
     * Get the minor axis of the ellipse
     *
     * @return 2b
     */
    private double minorAxis() {
        return Math.min(height(), width());
    }

    /**
     * Get the semi-major axis of the ellipse
     *
     * @return a
     */
    private double semiMajorAxis() {
        return majorAxis() / 2;
    }

    /**
     * Get the semi-minor axis of the ellipse
     *
     * @return b
     */
    private double semiMinorAxis() {
        return minorAxis() / 2;
    }

    /**
     * Gets the first flattening ratio for the ellipse
     *
     * @return First flattening ratio = (a-b)/a, where a=semi-major axis and b=semi-minor axis)
     */
    public double flattening() {
        return (semiMajorAxis() - semiMinorAxis()) / semiMajorAxis();
    }

    /**
     * Scale this ellipse by a scaling factor about its center
     *
     * @param factor Scaling factor, 1 for no scale, less than one to contract, greater than one to expand
     */
    public Ellipse scale(double factor) {
        RotatedRect r = rect.clone();
        r.size = new Size(factor * rect.size.width, factor * rect.size.height);
        return new Ellipse(r);
    }

    /**
     * Gets the eccentricity of the ellipse, between 0 (inclusive) and 1 (exclusive)
     *
     * @return e = sqrt(1-(b^2/a^2)), where a=semi-major axis and b=semi-minor axis
     */
    public double eccentricity() {
        return Math.sqrt(1 - (semiMinorAxis() * semiMinorAxis()) / (semiMajorAxis() * semiMajorAxis()));
    }

    /**
     * Returns true if the ellipse is ENTIRELY inside the contour
     *
     * @param contour The contour to test against
     * @return True if the ellipse is entirely inside the contour, false otherwise
     */
    public boolean isInside(Contour contour) {
        //TODO this algorithm checks for entirety; make an isEntirelyInside() and isPartiallyInside()
        //FIXME this is an inaccurate method using only the bounding box of the contour
        //TODO try ray-casting (even-odd) algorithm - use center as point (will show partial matches)
        //TODO also try all 4 points to match (will ensure it is entirely inside)
        return left() >= contour.left() && right() <= contour.right() && top() >= contour.top()
                && bottom() <= contour.bottom();
    }

    /**
     * Transpose this rectangle so that x becomes y and vice versa
     *
     * @return Transposed rectangle instance
     */
    @SuppressWarnings("SuspiciousNameCombination")
    public Ellipse transpose() {
        return new Ellipse(new RotatedRect(new Point(rect.center.y, rect.center.x),
                new Size(rect.size.height, rect.size.width), rect.angle));
    }

    public String getLocationString() {
        return String.format("(%.0f, %.0f)", rect.center.x, rect.center.y);
    }

    /***
     * Compare ellipses by area
     *
     * @param another Another ellipse
     * @return 1 if this is larger, -1 if another is larger, 0 otherwise
     */
    @Override
    public int compareTo(Ellipse another) {
        return this.area() > another.area() ? 1 : this.area() < another.area() ? -1 : 0;
    }
}