试图让OpenCV在Java中工作,将两张照片缝合在一起的示例代码
我正在尝试在Java中使用OpenCV。我想把两张照片缝在一起。OpenCV是一个C++库,它有一个java包装器。试图让OpenCV在Java中工作,将两张照片缝合在一起的示例代码,java,opencv,opencv-stitching,Java,Opencv,Opencv Stitching,我正在尝试在Java中使用OpenCV。我想把两张照片缝在一起。OpenCV是一个C++库,它有一个java包装器。 我从官方网站下载了带有预构建Windows.dll的OpenCV Java: 我正在使用IntelliJ 2016.1.4 我设置了我的项目并将其指向相关的.jar 我在网上找到了如下代码 它无法开箱即用,因此我修复了一些问题,例如: 专用静态最终积分CV_RANSAC=8;//这只是猜测 我跑了。它失败,在以下行出现错误:“错误:(-5)指定的描述符提取器类型不受支持”:fe=
import org.opencv.calib3d.Calib3d;
import org.opencv.core.*;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.imgproc.Imgproc;
import java.util.LinkedList;
import java.util.List;
import static org.opencv.imgcodecs.Imgcodecs.imread;
import static org.opencv.imgcodecs.Imgcodecs.imwrite;
public class ImageStitching {
static Mat image1;
static Mat image2;
static FeatureDetector fd;
static DescriptorExtractor fe;
static DescriptorMatcher fm;
// Compulsory
static{
try {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
catch (UnsatisfiedLinkError e) {
throw new RuntimeException("Couldn't find \"" + Core.NATIVE_LIBRARY_NAME + ".dll .\n"
+"You need to add something like this to the run configuration \"VM options\":\n"
+"-Djava.library.path=C:\\OpenCvPreBuilt\\opencv\\build\\java\\x64");
}
}
public static void go()
{
//new CvException("hello");
fd = FeatureDetector.create(FeatureDetector.BRISK);
fe = DescriptorExtractor.create(DescriptorExtractor.SURF);
fm = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
//images
image1 = imread("A.jpg");
image2 = imread("B.jpg");
//structures for the keypoints from the 2 images
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
//structures for the computed descriptors
Mat descriptors1 = new Mat();
Mat descriptors2 = new Mat();
//structure for the matches
MatOfDMatch matches = new MatOfDMatch();
//getting the keypoints
fd.detect(image1, keypoints1);
fd.detect(image1, keypoints2);
//getting the descriptors from the keypoints
fe.compute(image1, keypoints1, descriptors1);
fe.compute(image2,keypoints2,descriptors2);
//getting the matches the 2 sets of descriptors
fm.match(descriptors2,descriptors1, matches);
//turn the matches to a list
List<DMatch> matchesList = matches.toList();
Double maxDist = 0.0; //keep track of max distance from the matches
Double minDist = 100.0; //keep track of min distance from the matches
//calculate max & min distances between keypoints
for(int i=0; i<keypoints1.rows();i++){
Double dist = (double) matchesList.get(i).distance;
if (dist<minDist) minDist = dist;
if(dist>maxDist) maxDist=dist;
}
System.out.println("max dist: " + maxDist );
System.out.println("min dist: " + minDist);
//structure for the good matches
LinkedList<DMatch> goodMatches = new LinkedList<DMatch>();
//use only the good matches (i.e. whose distance is less than 3*min_dist)
for(int i=0;i<descriptors1.rows();i++){
if(matchesList.get(i).distance<3*minDist){
goodMatches.addLast(matchesList.get(i));
}
}
//structures to hold points of the good matches (coordinates)
LinkedList<Point> objList = new LinkedList<Point>(); // image1
LinkedList<Point> sceneList = new LinkedList<Point>(); //image 2
List<KeyPoint> keypoints_objectList = keypoints1.toList();
List<KeyPoint> keypoints_sceneList = keypoints2.toList();
//putting the points of the good matches into above structures
for(int i = 0; i<goodMatches.size(); i++){
objList.addLast(keypoints_objectList.get(goodMatches.get(i).queryIdx).pt);
sceneList.addLast(keypoints_sceneList.get(goodMatches.get(i).trainIdx).pt);
}
System.out.println("\nNum. of good matches" +goodMatches.size());
MatOfDMatch gm = new MatOfDMatch();
gm.fromList(goodMatches);
//converting the points into the appropriate data structure
MatOfPoint2f obj = new MatOfPoint2f();
obj.fromList(objList);
MatOfPoint2f scene = new MatOfPoint2f();
scene.fromList(sceneList);
//finding the homography matrix
Mat H = Calib3d.findHomography(obj, scene, CV_RANSAC, 3);
//LinkedList<Point> cornerList = new LinkedList<Point>();
Mat obj_corners = new Mat(4,1,CvType.CV_32FC2);
Mat scene_corners = new Mat(4,1,CvType.CV_32FC2);
obj_corners.put(0,0, new double[]{0,0});
obj_corners.put(0,0, new double[]{image1.cols(),0});
obj_corners.put(0,0,new double[]{image1.cols(),image1.rows()});
obj_corners.put(0,0,new double[]{0,image1.rows()});
Core.perspectiveTransform(obj_corners, scene_corners, H);
//structure to hold the result of the homography matrix
Mat result = new Mat();
//size of the new image - i.e. image 1 + image 2
Size s = new Size(image1.cols()+image2.cols(),image1.rows());
//using the homography matrix to warp the two images
Imgproc.warpPerspective(image1, result, H, s);
int i = image1.cols();
Mat m = new Mat(result,new Rect(i,0,image2.cols(), image2.rows()));
image2.copyTo(m);
Mat img_mat = new Mat();
Features2d.drawMatches(image1, keypoints1, image2, keypoints2, gm, img_mat, new Scalar(254,0,0),new Scalar(254,0,0) , new MatOfByte(), 2);
//creating the output file
boolean imageStitched = imwrite("imageStitched.jpg",result);
boolean imageMatched = imwrite("imageMatched.jpg",img_mat);
}
public static void main(String args[])
{
go();
}
}
import org.opencv.calib3d.calib3d;
导入org.opencv.core.*;
导入org.opencv.features2d.descriptor;
导入org.opencv.features2d.DescriptorMatcher;
导入org.opencv.features2d.FeatureDetector;
导入org.opencv.features2d.features2d;
导入org.opencv.imgproc.imgproc;
导入java.util.LinkedList;
导入java.util.List;
导入静态org.opencv.imgcodecs.imgcodecs.imread;
导入静态org.opencv.imgcodecs.imgcodecs.imwrite;
公共类图像拼接{
静态Mat图像1;
静态Mat图像2;
静态特性检测器;
静态描述符;
静态描述符;
//强制的
静止的{
试一试{
System.loadLibrary(Core.NATIVE\u LIBRARY\u NAME);
}
捕获(未满足链接错误e){
抛出新的运行时异常(“找不到\”“+Core.NATIVE\u LIBRARY\u NAME+”.dll。\n
+“您需要在运行配置\“VM选项\”中添加类似的内容:\n”
+“-Djava.library.path=C:\\opencvprebuild\\opencv\\build\\java\\x64”);
}
}
公共静态void go()
{
//新的例外(“你好”);
fd=FeatureDetector.create(FeatureDetector.BRISK);
fe=描述符RExtractor.create(描述符RExtractor.SURF);
fm=DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
//图像
image1=imread(“A.jpg”);
image2=imread(“B.jpg”);
//两幅图像中关键点的结构
MatOfKeyPoint keypoints1=新的MatOfKeyPoint();
MatOfKeyPoint keypoints2=新的MatOfKeyPoint();
//计算描述符的结构
材料描述符1=新材料();
材料描述符2=新材料();
//比赛的结构
MatOfDMatch matches=新的MatOfDMatch();
//获取关键点
fd.检测(图像1,关键点1);
fd.检测(图像1,关键点2);
//从关键点获取描述符
fe.计算(图像1,关键点1,描述符1);
fe.计算(图像2,关键点2,描述符2);
//获取与2组描述符匹配的
匹配(描述符2,描述符1,匹配);
//将匹配项转到列表
List matchesList=matches.toList();
Double maxDist=0.0;//跟踪与匹配项的最大距离
Double Minist=100.0;//记录与比赛的最小距离
//计算关键点之间的最大和最小距离
对于(inti=0;iSo),让我们再试一次。
我将跳过所有初始化的内容,因为您似乎已经成功加载了库
对于我的环境:
- 我在android上使用OpenCV和您自己链接的下载提供的股票Java包装器
- OpenCV在版本3.4.1中使用
- 我在Android Studio 3.1.3中开发
我尽可能在代码中添加注释,并添加源代码(stackoverflow或其他),以显示我在哪里找到代码
在这一点上,非常感谢这些人帮助我拼凑出一些真正有效的东西
以下方法是将两个图像缝合在一起的方法
(从AsyncTask中提取,因为它需要很多时间):
受保护位图背景(位图…arg0){
//基本代码提取自:http://privateblog.info/sozdanie-panoramy-s-pomoshhyu-opencv-i-java/
// https://stackoverflow.com/questions/36691050/opencv-3-list-of-available-featuredetectorcreate-and-descriptorextractorc
// https://stackoverflow.com/questions/27681389/how-to-multiply-2-matrices-in-java-and-then-use-the-result-as-transformation-mat
//测量持续时间
long startTime=System.nanoTime();
//如果获取的图像数量不正确,请中止。。。
//不支持缝合两个以上的图像。
如果(arg0.length!=2){
返回null;
}
//从给定的参数中获取两个图像
位图bitmap1=arg0[0];
位图bitmap2=arg0[1];
//如果出现问题,请中止。。。
如果(位图1==null | |位图2==null){
返回null;
}
//将两个位图转换为OpenCV mats。。。
Mat img1=新Mat();
Mat img2=新Mat();
bitmapToMat(bitmap1,img1);
Utils.bitmapToMat(bitmap2,img2);
//…然后创建灰度版本
Mat gray_image1=新Mat();
Mat gray_image2=新Mat();
Imgproc.cvt颜色(img1,灰色\u图像1,Imgproc.COLOR\u rgb2灰色);
Imgproc.CVT颜色(img2,灰色图像2,Imgproc.COLOR\U RGB2GRAY);
//在这一点上,大海
protected Bitmap doInBackground(Bitmap...arg0) {
// Base code extracted from: http://privateblog.info/sozdanie-panoramy-s-pomoshhyu-opencv-i-java/
// https://stackoverflow.com/questions/36691050/opencv-3-list-of-available-featuredetectorcreate-and-descriptorextractorc
// https://stackoverflow.com/questions/27681389/how-to-multiply-2-matrices-in-java-and-then-use-the-result-as-transformation-mat
// Measuring the duration
long startTime = System.nanoTime();
// Abort if we got not the right amount of images...
// Stitching more than two images is not supported.
if (arg0.length != 2) {
return null;
}
// Get the two images from the given arguments
Bitmap bitmap1 = arg0[0];
Bitmap bitmap2 = arg0[1];
// If something is wrong, abort...
if (bitmap1 == null || bitmap2 == null) {
return null;
}
// Convert the two bitmaps to OpenCV mats...
Mat img1 = new Mat();
Mat img2 = new Mat();
Utils.bitmapToMat(bitmap1, img1);
Utils.bitmapToMat(bitmap2, img2);
// ...then create greyscale versions
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);
// At this point search for keypoints in both images and compute the matches
MatOfKeyPoint keyPoints1 = new MatOfKeyPoint();
MatOfKeyPoint keyPoints2 = new MatOfKeyPoint();
Mat descriptors1 = new Mat();
Mat descriptors2 = new Mat();
// Since FeatureDetector and Descriptor extractor are marked deprecated and
// crash whatever value they get, use this construct for detecting and computing...
// Source: https://stackoverflow.com/questions/36691050/opencv-3-list-of-available-featuredetectorcreate-and-descriptorextractorc
KAZE kaze = KAZE.create();
kaze.detect(gray_image1, keyPoints1);
kaze.detect(gray_image2, keyPoints2);
kaze.compute(gray_image1, keyPoints1, descriptors1);
kaze.compute(gray_image2, keyPoints2, descriptors2);
MatOfDMatch matches = new MatOfDMatch();
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
matcher.match(descriptors1, descriptors2, matches);
// Calculate min and max distance between the keypoints in the two images.
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;
}
Log.i(this.getClass().getSimpleName(), "Min: " + min_dist);
Log.i(this.getClass().getSimpleName(), "Max: " + max_dist);
// Reduce the list of matching keypoints to a list of good matches...
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);
Log.i(this.getClass().getSimpleName(), "Number of matches: " + listMatches.size());
Log.i(this.getClass().getSimpleName(), "Number of good matches: " + good_matches.size());
// Calculate the homograohy between the two images...
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);
int imageWidth = img2.cols();
int imageHeight = img2.rows();
// To avoid missing some of the possible stitching scenarios, we offset the homography to the middle of a mat which has three time the size of one of the pictures.
// Extracted from this: https://stackoverflow.com/questions/21618044/stitching-2-images-opencv
Mat Offset = new Mat(3, 3, H.type());
Offset.put(0,0, new double[]{1});
Offset.put(0,1, new double[]{0});
Offset.put(0,2, new double[]{imageWidth});
Offset.put(1,0, new double[]{0});
Offset.put(1,1, new double[]{1});
Offset.put(1,2, new double[]{imageHeight});
Offset.put(2,0, new double[]{0});
Offset.put(2,1, new double[]{0});
Offset.put(2,2, new double[]{1});
// Multiply the homography mat with the offset.
Core.gemm(Offset, H, 1, new Mat(), 0, H);
Mat obj_corners = new Mat(4,1,CvType.CV_32FC2);
Mat scene_corners = new Mat(4,1,CvType.CV_32FC2);
obj_corners.put(0,0, new double[]{0,0});
obj_corners.put(0,0, new double[]{imageWidth,0});
obj_corners.put(0,0,new double[]{imageWidth,imageHeight});
obj_corners.put(0,0,new double[]{0,imageHeight});
Core.perspectiveTransform(obj_corners, scene_corners, H);
// The resulting mat will be three times the size (width and height) of one of the source images. (We assume, that both images have the same size.
Size s = new Size(imageWidth *3,imageHeight*3);
Mat img_matches = new Mat(new Size(img1.cols()+img2.cols(),img1.rows()), CvType.CV_32FC2);
// Perform the perspective warp of img1 with the given homography and place it on the large result mat.
Imgproc.warpPerspective(img1, img_matches, H, s);
// Create another mat which is used to hold the second image and place it in the middle of the large sized result mat.
int m_xPos = (int)(img_matches.size().width/2 - img2.size().width/2);
int m_yPos = (int)(img_matches.size().height/2 - img2.size().height/2);
Mat m = new Mat(img_matches,new Rect(m_xPos, m_yPos, img2.cols(), img2.rows()));
// Copy img2 to the mat in the middle of the large result mat
img2.copyTo(m);
// Some debug logging... and some duration logging following...
Log.i(this.getClass().getSimpleName(), "Size of img2: width=" + img2.size().width + "height=" + img2.size().height);
Log.i(this.getClass().getSimpleName(), "Size of m: width=" + m.size().width + "height=" + m.size().height);
Log.i(this.getClass().getSimpleName(), "Size of img_matches: width=" + img_matches.size().width + "height=" + img_matches.size().height);
long elapsedTime = System.nanoTime() - startTime;
elapsedTime = elapsedTime / 1000000; // Milliseconds (1:1000000)
Log.i(this.getClass().getSimpleName(), "Stitching 2 images took " + elapsedTime + "ms");
//loadedImagesText.append("Stitching 2 images took " + elapsedTime + "ms\n");
// The resulting mat is way to big. It holds a lot of empty "transparent" space.
// We will not crop the image, so that only the "region of interest" remains.
startTime = System.nanoTime();
int stepping = 6;
Rect imageBoundingBox3 = findImageBoundingBox2(img_matches, stepping, true);
elapsedTime = System.nanoTime() - startTime;
elapsedTime = elapsedTime / 1000000; // Milliseconds (1:1000000)
Log.i(this.getClass().getSimpleName(), "Resulting rect has tl(x=" + imageBoundingBox3.tl().x + ", y=" + imageBoundingBox3.tl().y +") and br(x=" + imageBoundingBox3.br().x + ", y=" + imageBoundingBox3.br().y +") with stepping="+stepping+" and auto-correct=true\n");
Log.i(this.getClass().getSimpleName(), "Cropping stitched image (v2.1) took " + elapsedTime + "ms");
//loadedImagesText.append("Resulting rect has tl(x=" + imageBoundingBox3.tl().x + ", y=" + imageBoundingBox3.tl().y +") and br(x=" + imageBoundingBox3.br().x + ", y=" + imageBoundingBox3.br().y +") with stepping="+stepping+" and auto-correct=true\n");
//loadedImagesText.append("Cropping stitched image (v2.1) took " + elapsedTime + "ms\n");
// Extract the calculated region of interest from the result mat.
Mat regionOfInterest = img_matches.submat(imageBoundingBox3);
// Convert the end result to a bitmap and we are done!
Bitmap resultBitmap = Bitmap.createBitmap(regionOfInterest.cols(), regionOfInterest.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(regionOfInterest, resultBitmap);
return resultBitmap;
}