Line cross motion detection with OpenCV and Java

Before you start you can check out Compiling/Building OpenCV under Ubuntu/Debian

Create a package called detector and inside create class LineCrossDetector:

package detector;

import javafx.scene.shape.Rectangle;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;

import javax.swing.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.ArrayList;
import java.util.List;

public class LineCrossDetector {

    private Point lineStartPoint;
    private Point lineEndPoint;

    public static void main(String[] args) {
        // load the OpenCV library
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        // initialize and assign the detector
        LineCrossDetector detector = new LineCrossDetector();
        // start detecting
        detector.detect();
    }

    private void detect() {
        // initialize and assign the basic image container
        Mat mat = new Mat();
        // initialize and assign which camera to use, on most laptops index - 0 is the main laptop camera
        VideoCapture camera = new VideoCapture(0);
        // get the resolution of the the camera
        final Size frameSize = new Size(camera.get(Videoio.CAP_PROP_FRAME_WIDTH), camera.get(Videoio.CAP_PROP_FRAME_HEIGHT));

        JFrame frame = new JFrame();
        // Make X close the window
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        JLabel panel = new JLabel();
        frame.setContentPane(panel);
        frame.setSize((int) frameSize.width, (int) frameSize.height);
        frame.setVisible(true);

        // when the program stops (Pressed X button, closed from the IDE and etc) makes sure that the image container and the video capture are released
        Runtime.getRuntime().addShutdownHook(new ShutdownThread(mat, camera));

        Mat outerBox;
        Mat renderedImage;
        Mat diffFrame = null;
        Mat tempFrame = null;

        lineStartPoint = new Point(0, 200);
        lineEndPoint = new Point(frameSize.width, 200);

        boolean initialized = false;
        // while the camera is opened and is "reading" video
        while (camera.isOpened() && camera.read(mat)) {
            Imgproc.resize(mat, mat, frameSize);
            renderedImage = mat.clone();
            outerBox = new Mat(mat.size(), CvType.CV_8UC1);
            Imgproc.cvtColor(mat, outerBox, Imgproc.COLOR_BGR2GRAY);
            Imgproc.GaussianBlur(outerBox, outerBox, new Size(3, 3), 0);

            Imgproc.line(renderedImage, lineStartPoint, lineEndPoint, new Scalar(255, 12, 0), 1);

            if (initialized) {
                Core.subtract(outerBox, tempFrame, diffFrame);
                Imgproc.adaptiveThreshold(diffFrame, diffFrame, 255,
                        Imgproc.ADAPTIVE_THRESH_MEAN_C,
                        Imgproc.THRESH_BINARY_INV, 5, 2);
                if (isLineIntersected(diffFrame)) {
                    // when they are intersected print a message
                    System.out.println("The line was crossed");
                }
            }

            if (!initialized) {
                diffFrame = outerBox.clone();
                initialized = true;
            }

            ImageIcon image = new ImageIcon(mat2BufferedImage(renderedImage));
            panel.setIcon(image);
            panel.repaint();
            tempFrame = outerBox.clone();
        }
    }

    /**
     * Detects whether the drawn line is intersected
     * @param diffFrame
     * @return
     */
    private boolean isLineIntersected(Mat diffFrame) {
        Mat mat = new Mat();
        Mat diffFrameClone = diffFrame.clone();
        List<MatOfPoint> contours = new ArrayList<>();
        Imgproc.findContours(diffFrameClone, contours, mat, Imgproc.RETR_LIST,
                Imgproc.CHAIN_APPROX_SIMPLE);

        double maxArea = 50;

        for (MatOfPoint contour : contours) {
            double contourArea = Imgproc.contourArea(contour);
            if (contourArea > maxArea) {
                Rect rect = Imgproc.boundingRect(contour);
                Rectangle rectangle = new Rectangle(rect.x + rect.width / 4, rect.y + rect.height / 4, rect.width - rect.width / 2, rect.height - rect.height / 2 - rect.height / 3);
                boolean intersected = isIntersected(new Point(rectangle.getX(), rectangle.getY()), new Point(rectangle.getX(), rectangle.getY() + rectangle.getHeight()), lineStartPoint, lineEndPoint);
                if (intersected) {
                    mat.release();
                    diffFrameClone.release();
                    return true;
                }

            }
        }
        mat.release();
        diffFrameClone.release();
        return false;
    }

    private BufferedImage mat2BufferedImage(Mat m) {
        int type = BufferedImage.TYPE_BYTE_GRAY;
        if (m.channels() > 1) {
            type = BufferedImage.TYPE_3BYTE_BGR;
        }
        int bufferSize = m.channels() * m.cols() * m.rows();
        byte[] b = new byte[bufferSize];
        m.get(0, 0, b); // get all the pixels
        BufferedImage img = new BufferedImage(m.cols(), m.rows(), type);
        final byte[] targetPixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
        System.arraycopy(b, 0, targetPixels, 0, b.length);
        return img;
    }

    private boolean isIntersected(Point start1, Point end1, Point start2, Point end2) {
        Point dir1 = new Point(end1.x - start1.x, end1.y - start1.y);
        Point dir2 = new Point(end2.x - start2.x, end2.y - start2.y);

        double a1 = -dir1.y;
        double b1 = +dir1.y;
        double d1 = -(a1 * start1.x + b1 * start1.y);

        double a2 = -dir2.y;
        double b2 = +dir2.x;
        double d2 = -(a2 * start2.x + b2 * start2.y);

        double line2_start = a2 * start1.x + b2 * start1.y + d2;
        double line2_end = a2 * end1.x + b2 * end1.y + d2;

        double line1_start = a1 * start2.x + b1 * start2.y + d1;
        double line1_end = a1 * end2.x + b1 * end2.y + d1;

        return !(line2_start * line2_end >= 0 || line1_start * line1_end >= 0);
    }

    private class ShutdownThread extends Thread {

        private Mat mat;
        private VideoCapture videoCapture;

        public ShutdownThread(Mat mat, VideoCapture videoCapture) {
            this.mat = mat;
            this.videoCapture = videoCapture;
        }

        @Override
        public void run() {
            super.run();
            mat.release();
            videoCapture.release();
        }
    }
}



If you have any questions or suggestions, feel free to leave a comment.