Главная > Soft > Создание панорамы с помощью OpenCV и Java

Создание панорамы с помощью OpenCV и Java

OpenCV позволяет не только определять движение, но и создавать панорамы. Данной теме и будет посвящен текущий пример.

Создание панорамы с помощью OpenCV и Java Создание панорамы с помощью OpenCV и Java
Создание панорамы с помощью OpenCV и Java

Для примера будет использоваться библиотека версии 2.4.3. Это связанно с тем, что из-за лицензионных ограничений версия 3.1 не содержит некоторых алгоритмов (типа SURF) для детектирования ключевых точек.

Алгоритм создания панорамы будет состоять из нескольких шагов:

1. Поиск ключевых точек изображений и их описания

2. Сравнение ключевых точек и вычисления расстояния между похожими

3. Определение матрицы гомографии для поворота изображения

4. Совмещение изображений

Ничего сложного.

1. Поиск ключевых точек изображений и их описания

Любое изображение имеет точки, которые уникальны только для этого изображения. Обычно это углы различных объектов и их нельзя спутать ни с чем другим. Такие точки называются ключевыми и для их обнаружения используются специальные алгоритмы детекторов. В нашем случае — SURF (Speeded Up Robust Features).

После обнаружения точек на основе каждого изображения будет получено описание окружения этих точек, что позволит их сравнивать с точками другого изображения

Mat img1 = Highgui.imread("./res/img_12.jpg");
		
Mat gray_image1 = new Mat();

Imgproc.cvtColor(img1, gray_image1, Imgproc.COLOR_RGB2GRAY);

MatOfKeyPoint keyPoints1 = new MatOfKeyPoint();

FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);
detector.detect(gray_image1, keyPoints1);

Mat descriptors1 = new Mat();

DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
extractor.compute(gray_image1, keyPoints1, descriptors1);

Создание панорамы с помощью OpenCV и Java

Features2d.drawKeypoints(img1, keyPoints1, img_matches);
Highgui.imwrite("./out/out.jpg", img_matches);        

2. Сравнение ключевых точек и вычисления расстояния между похожими

После того, как у нас есть ключевые точки и описание окружающей области, можно сравнить их и отобрать те точки, которые ближе всего расположены друг к другу.

MatOfDMatch matches = new MatOfDMatch();
	
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
matcher.match(descriptors1, descriptors2, matches);

double max_dist = 0; double min_dist = 100;
List<DMatch> listMatches = matches.toList();

for( int i = 0; i < listMatches.size(); i++ ) { 
	double dist = listMatches.get(i).distance;
	if( dist < min_dist ) min_dist = dist;
	if( dist > max_dist ) max_dist = dist;
}

LinkedList<DMatch> good_matches = new LinkedList<DMatch>();
MatOfDMatch goodMatches = new MatOfDMatch();
for(int i = 0; i < listMatches.size(); i++){
	if(listMatches.get(i).distance < 5*min_dist){
		good_matches.addLast(listMatches.get(i));
	}
}

goodMatches.fromList(good_matches);

Ниже представлены ключевые точки обоих изображений и проведены линии между схожими точками. На основе этих точек будет строиться итоговая картинка.
Создание панорамы с помощью OpenCV и Java

Features2d.drawMatches(
			img1,
			keyPoints1, 
			img2,
			keyPoints2, 
			goodMatches, 
			img_matches);

Highgui.imwrite("./out/out.jpg", img_matches);        

3. Определение матрицы гомографии для поворота изображения

На данном шаге у нас есть список точек, которые можно использовать для совмещения изображений. Но есть и проблема — точки расположены на разных картинках и не их расположение не совпадает. В этим случае нам нужно получить специальную матрицу гомографии, которая поможет изменить второе изображение так, чтоб расположение его ключевых точек совпало с первым изображением.

Mat img_matches = new Mat(new Size(img1.cols()+img2.cols(),img1.rows()), CvType.CV_32FC2);

LinkedList<Point> imgPoints1List = new LinkedList<Point>();
LinkedList<Point> imgPoints2List = new LinkedList<Point>();
List<KeyPoint> keypoints1List = keyPoints1.toList();
List<KeyPoint> keypoints2List = keyPoints2.toList();

for(int i = 0; i < good_matches.size(); i++){
	imgPoints1List.addLast(keypoints1List.get(good_matches.get(i).queryIdx).pt);
	imgPoints2List.addLast(keypoints2List.get(good_matches.get(i).trainIdx).pt);
}

MatOfPoint2f obj = new MatOfPoint2f();
obj.fromList(imgPoints1List);
MatOfPoint2f scene = new MatOfPoint2f();
scene.fromList(imgPoints2List);

Mat H = Calib3d.findHomography(obj, scene, Calib3d.RANSAC,3);

Создание панорамы с помощью OpenCV и Java

4. Совмещение изображений

После преобразования второго изображения остаётся самое простое — скопировать две картинки вместе.

Size s = new Size(img2.cols() + img1.cols(),img1.rows());

Imgproc.warpPerspective(img1, img_matches, H, s);
Mat m = new Mat(img_matches,new Rect(0,0,img2.cols(), img2.rows()));

img2.copyTo(m);

Highgui.imwrite("./out/out.jpg", img_matches);   

Создание панорамы с помощью OpenCV и Java

public static void main(String[]args) {
	System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	
	Mat img1 = Highgui.imread("./res/img_12.jpg");
	Mat img2 = Highgui.imread("./res/img_11.jpg");		
	
	Mat gray_image1 = new Mat();
	Mat gray_image2 = new Mat();
	
	Imgproc.cvtColor(img1, gray_image1, Imgproc.COLOR_RGB2GRAY);
	Imgproc.cvtColor(img2, gray_image2, Imgproc.COLOR_RGB2GRAY);

	MatOfKeyPoint keyPoints1 = new MatOfKeyPoint();
	MatOfKeyPoint keyPoints2 = new MatOfKeyPoint();
	
	FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);
	detector.detect(gray_image1, keyPoints1);
	detector.detect(gray_image2, keyPoints2);
	
	Mat descriptors1 = new Mat();
	Mat descriptors2 = new Mat();

	DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
	extractor.compute(gray_image1, keyPoints1, descriptors1);
	extractor.compute(gray_image2, keyPoints2, descriptors2);
	
	MatOfDMatch matches = new MatOfDMatch();
	
	DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
	matcher.match(descriptors1, descriptors2, matches);
	
	double max_dist = 0; double min_dist = 100;
	List<DMatch> listMatches = matches.toList();
	
	for( int i = 0; i < listMatches.size(); i++ ) { 
		double dist = listMatches.get(i).distance;
		if( dist < min_dist ) min_dist = dist;
		if( dist > max_dist ) max_dist = dist;
	}
	
	System.out.println("Min: " + min_dist);
	System.out.println("Max: " + max_dist);
	
	LinkedList<DMatch> good_matches = new LinkedList<DMatch>();
	MatOfDMatch goodMatches = new MatOfDMatch();
	for(int i = 0; i < listMatches.size(); i++){
		if(listMatches.get(i).distance < 2*min_dist){
			good_matches.addLast(listMatches.get(i));
		}
	}
	
	goodMatches.fromList(good_matches);
	
	Mat img_matches = new Mat(new Size(img1.cols()+img2.cols(),img1.rows()), CvType.CV_32FC2);

	LinkedList<Point> imgPoints1List = new LinkedList<Point>();
	LinkedList<Point> imgPoints2List = new LinkedList<Point>();
	List<KeyPoint> keypoints1List = keyPoints1.toList();
	List<KeyPoint> keypoints2List = keyPoints2.toList();

	for(int i = 0; i<good_matches.size(); i++){
		imgPoints1List.addLast(keypoints1List.get(good_matches.get(i).queryIdx).pt);
		imgPoints2List.addLast(keypoints2List.get(good_matches.get(i).trainIdx).pt);
	}
	
	MatOfPoint2f obj = new MatOfPoint2f();
	obj.fromList(imgPoints1List);
	MatOfPoint2f scene = new MatOfPoint2f();
	scene.fromList(imgPoints2List);

	Mat H = Calib3d.findHomography(obj, scene, Calib3d.RANSAC,3);

	Size s = new Size(img2.cols() + img1.cols(),img1.rows());

	Imgproc.warpPerspective(img1, img_matches, H, s);
	Mat m = new Mat(img_matches,new Rect(0,0,img2.cols(), img2.rows()));

	img2.copyTo(m);

	Highgui.imwrite("./out/out.jpg", img_matches);        
}
Categories: Soft Tags: ,
  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.