Perspektivní napasování fotek

Vyfotíme placatou scénu (například černou mřížku namalovanou na stole) dvakrát z různých úhlů. Pak o něco později chceme přesně zjistit, odkud jsme kterou fotku fotili.

Postup:

  1. najdeme na první fotce (aspoň) čtyři výrazné body,
  2. najdeme ty samé body na druhé fotce,
  3. spočítáme, jaká geometrická transformace vyrobí z jedněch bodů ty druhé,
  4. dopočítáme, z jakého místa jsme fotili druhou fotku.
Buďto potřebujeme vědět všechny podrobnosti o použité kameře, anebo vědět, odkud byla jedna z fotek pořízená. Rozsekneme to tak, že si prostě vymyslíme pozici kamery na první fotce.

Pokročilý kód

V následujícím kódu máte připravené hledání významných bodů a několik dalších užitečných funkcí, aby se s nimi hezky zacházelo. Podrobněji jsou to:

HelloCV.java

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.KeyPoint;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.opencv.video.Video;
import org.opencv.features2d.FeatureDetector;
import org.opencv.imgcodecs.Imgcodecs;

public class HelloCV {
	public static void main(String[] args) {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		Mat left = Imgcodecs.imread("a.jpg");
		if (left.total() == 0) {
			System.out.println("Fotku se nepovedlo přečíst.");
		}
		MatOfPoint2f leftPoints = Util.detectPoints(left);
		for (int i = 0; i < points.rows(); i++) {
			Point p = new Point(points.get(i, 0)[0], points.get(i, 0)[1]);
			Imgproc.circle(img, p, 3, new Scalar(0, 255, 255));
		}
		JFrame window = new JFrame();
		Util.imshow(img, window);
	}
}

class Util {

	public static MatOfPoint2f trackPoints(MatOfPoint2f leftPoints, Mat leftImg, Mat rightImg) {
		MatOfPoint2f result = new MatOfPoint2f();
		MatOfByte status = new MatOfByte();
		MatOfFloat pointError = new MatOfFloat();
		Video.calcOpticalFlowPyrLK(leftImg, rightImg, leftPoints, result, status, pointError);
		Point[] leftArray = leftPoints.toArray();
		Point[] rightArray = result.toArray();
		ArrayList<Point> goodLeftPoints = new ArrayList<Point>();
		ArrayList<Point> goodRightPoints = new ArrayList<Point>();
		for (int i=0; i<leftPoints.rows(); ++i) {
			if (status.get(i,  0)[0] == 1 && pointError.get(i,  0)[0] < 10) {
				goodLeftPoints.add(leftArray[i]);
				goodRightPoints.add(rightArray[i]);
			}
		}
		leftPoints.fromList(goodLeftPoints);
		result.fromList(goodRightPoints);
		return result;
	}
	
	public static void imshow(Mat img, JFrame frame) {
		MatOfByte matOfByte = new MatOfByte();
		Imgcodecs.imencode(".png", img, matOfByte);
		byte[] byteArray = matOfByte.toArray();
		BufferedImage bufImage = null;
		try {
			InputStream in = new ByteArrayInputStream(byteArray);
			bufImage = ImageIO.read(in);
			frame.getContentPane().removeAll();
			frame.getContentPane().add(new JLabel(new ImageIcon(bufImage)));
			frame.pack();
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static MatOfPoint2f detectPoints(Mat img) {
		FeatureDetector detector = FeatureDetector.create(FeatureDetector.HARRIS);
		MatOfKeyPoint points = new MatOfKeyPoint();
		detector.detect(img, points);
		return toPoint2f(points);
	}
	
	public static MatOfPoint2f toPoint2f(MatOfKeyPoint mat) {
		MatOfPoint2f result = new MatOfPoint2f();
		KeyPoint[] inarr = mat.toArray();
		Point[] outarr = new Point[inarr.length];
		for (int i = 0; i < inarr.length; ++i) {
			outarr[i] = inarr[i].pt;
		}
		result.fromArray(outarr);
		return result;
	}
}