I am learning how to stitch two images together using the below link but whatever I do to calculate the homography and warpPerspective, two images won't stitch together.
https://learnopencv.com/feature-based-image-alignment-using-opencv-c-python/
Below is the source code for image stitching
Include Section
#include <opencv2/features2d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/core/types.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <vector>
#include <iostream>
Global Variables
using namespace std;
using namespace cv;
const float inlier_threshold = 2.5f; // Distance threshold to identify inliers
const float nn_match_ratio = 0.8f; // Nearest neighbor matching ratio
Main Function
int main(void)
{
puts("opening");
Mat img1 = imread("uttower_right.jpg", IMREAD_GRAYSCALE); // To be Aligned
Mat img2 = imread("large2_uttower_left.jpg", IMREAD_GRAYSCALE); // Reference
Mat img3 = Mat(img2.rows, img2.cols, CV_8UC1);
//img2.copyTo(img3);
Mat homography;
vector<KeyPoint> kpts1, kpts2;
Mat desc1, desc2;
puts("Have opened");
Ptr<AKAZE> akaze = AKAZE::create();
akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
akaze->detectAndCompute(img2, noArray(), kpts2, desc2);
puts("have commputed akaze");
BFMatcher matcher(NORM_HAMMING);
vector< vector<DMatch> > nn_matches;
matcher.knnMatch(desc1, desc2, nn_matches, 2);
puts("Have done match");
vector<KeyPoint> matched1, matched2;
vector<Point2f> inliers1, inliers2;
for (size_t i = 0; i < nn_matches.size(); i++) {
DMatch first = nn_matches[i][0];
float dist1 = nn_matches[i][0].distance;
float dist2 = nn_matches[i][1].distance;
if (dist1 < nn_match_ratio * dist2) {
matched1.push_back(kpts1[first.queryIdx]);
matched2.push_back(kpts2[first.trainIdx]);
inliers1.push_back(kpts1[first.queryIdx].pt);
inliers2.push_back(kpts1[first.trainIdx].pt);
}
}
printf("Matches %d %d\n", matched1.size(), matched2.size());
homography = findHomography(inliers1, inliers2, RANSAC);
warpPerspective(img1, img3, homography, img2.size());
//Display input and output
imshow("Input1", img1);
imshow("Input2", img2);
imshow("Input3", img3);
waitKey(0);
return 0;
}
Images used
Related
Code :
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
// Mat faceImage = imread("/Users/softech66/Downloads/images 2/unvese_space.png");
// Mat glassBGR = imread("/Users/softech66/Downloads/images 2/sun.png");
// /Users/softech66/Downloads/images 2/sun_mask.bmp
// resize(glassBGR,glassBGR,Size(300,100));
// check each pixel of glass and if its white(255,255,255) then change it with face image pixels
Mat img2 = imread("/Users/softech66/Downloads/images 2/sun.png",0);
Mat img4 = imread("/Users/softech66/Downloads/images 2/univese_space.png",0);
Mat img3 = imread("/Users/softech66/Downloads/images 2/sun_mask.bmp",0);
Mat img;
cvtColor(imread("/Users/softech66/Downloads/images 2/sun.png", IMREAD_COLOR), img , cv::COLOR_RGB2RGBA);
Mat mask = imread("/Users/softech66/Downloads/images 2/sun_mask.bmp", IMREAD_GRAYSCALE);
for(int r = 0; r < img.rows; r++){
for(int c = 0; c < img.cols; c++){
uchar alpha = 0;
if(r < mask.rows && c < mask.cols)
alpha = mask.at<uchar>(r, c);
img.at<Vec4b>(r, c)[3] = alpha;
}
}
imwrite("result.png", img);
Mat roi=img4(Rect(0,0,img.cols,img2.rows));
Mat mask1(roi.rows,roi.cols,roi.depth(),Scalar(1));
img.copyTo(roi,mask1);
// img4.copyTo(img);
imshow("img3", img4);
// imshow("img33", img4);
waitKey(0);
}
The images above are come in pairs, as you need to use the corresponding “mask” image to let OpenCV to only copy the circle region into the universe space image. Note for the mask image, the extension name is “.bmp”. This is because the bmp format provide higher precision for the bit storage. But it makes no difference for OpenCV to load them by using “imread(..., 0)” in the same manner with the second parameter “0” to indicate a single channel or grey image to be load.
Moon Mask
Earth Mask
Sun Mask
Universe Space
Moon image
Earth image
Sun image
Result image
Cross post here
I note the function FlannBasedMatcher::match have a parameter mask, so I give a try with following code:
#include<opencv.hpp>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/nonfree/nonfree.hpp"
using namespace cv;
using namespace std;
int main() {
Mat rightImg = imread("right.jpg", 0);
Mat leanImg = imread("lean.jpg", 0);
if (!rightImg.data || !leanImg.data) {
cout << "Fail to read your image. Please check your path.\n";
return -1;
}
resize(leanImg, leanImg, rightImg.size());
int minHessian = 400;
SurfFeatureDetector detector(minHessian);
vector<KeyPoint> keypoints_right, keypoints_lean;
detector.detect(rightImg, keypoints_right);
detector.detect(leanImg, keypoints_lean);
Mat med_right, med_lean;
drawKeypoints(rightImg, keypoints_right, med_right);
drawKeypoints(leanImg, keypoints_lean, med_lean);
SurfDescriptorExtractor extractor;
Mat descriptors_right, descriptors_lean;
extractor.compute(rightImg, keypoints_right, descriptors_right);
extractor.compute(leanImg, keypoints_lean, descriptors_lean);
FlannBasedMatcher matcher;
vector< DMatch > matches;
Mat mask(descriptors_right.rows, descriptors_lean.rows, CV_8UC1, Scalar(0));
Mat target(rightImg.size(), CV_8UC1, Scalar(255));
ellipse(target, Point(rightImg.cols / 2, rightImg.rows / 2), Size(rightImg.cols / 2, rightImg.rows / 2), 0, 0, 360, Scalar(0), CV_FILLED);
for (int i = 0; i < mask.rows; i++) {
uchar* pixrow = mask.ptr<uchar>(i);
for (int j = 0; j < mask.cols; j++) {
if (target.at<uchar>(keypoints_right[i].pt) == 255)
pixrow[j] = 255;
}
}
matcher.match(descriptors_right, descriptors_lean, matches/*, mask*/);//use it or not to test
Mat img_matches;
drawMatches(rightImg, keypoints_right, leanImg, keypoints_lean,
matches, img_matches, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
return 0;
}
And this is my right.jpg and lean.jpg. I don't care those points in the center of right.jpg. So I make a mask for it. But I note I will get a same result totally whether I use the mask in the function FlannBasedMatcher::match. You can use the mask or not to reproduce it. Do I have missed something or the OpenCV have a bug in my 2.4.13? Can anyone tell me how to use the mask in the FlannBasedMatcher::match? I think it is a usefull parameter..
From the docs: "FlannBasedMatcher does not support masking permissible matches of descriptor sets because flann::Index does not support this." See the DescriptorMatcher::isMaskSupported method for a way to test if the matcher supports masking.
Using this code to find matches between images:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main(int argc, char *argv[]) {
//cv::initModule_nonfree();
//initModule_features2d();
Mat img_1 = imread("C:/Users/Dan/Desktop/0.jpg", 1);
Mat img_2 = imread("C:/Users/Dan/Desktop/0.jpg", 1);
cv::Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
//-- Step 1: Detect the keypoints:
std::vector<KeyPoint> keypoints_1, keypoints_2;
f2d->detect(img_1, keypoints_1);
f2d->detect(img_2, keypoints_2);
//-- Step 2: Calculate descriptors (feature vectors)
Mat descriptors_1, descriptors_2;
f2d->compute(img_1, keypoints_1, descriptors_1);
f2d->compute(img_2, keypoints_2, descriptors_2);
Mat out0;
drawKeypoints(img_1, keypoints_1, out0);
imshow("KeyPoint0.jpg", out0);
//-- Step 3: Matching descriptor vectors using BFMatcher :
BFMatcher matcher;
std::vector< DMatch > matches;
matcher.match(descriptors_1, descriptors_2, matches);
Mat img_matches = Mat::zeros( img_1.size(), CV_8UC3 );
drawMatches(img_1,keypoints_1,img_2,keypoints_2,matches,img_matches);
imshow("matches", img_matches);
waitKey(0); // Keep window there until user presses 'q' to quit.
return 0;
}
Since OpenCV 3.1 functions were changed, I looked for example code using SURF or SIFT, but could not find any.
How to modify this code so it will draw contours around detected objects similar to OpenCV version?
You will need to use findHomography to get the transformation that relates your training image (img_1) to the image to be detected (img_2)
Then you can simply do a perspectiveTransform on a bounding box of your training image (at origin) using the homography obtained, to place the correct bounding box on the detected image
Related code taken from ORB detection example
Mat inlier_mask, homography;
vector<KeyPoint> inliers1, inliers2;
vector<DMatch> inlier_matches;
if(matched1.size() >= 4) {
homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);
}
for(unsigned i = 0; i < matched1.size(); i++) {
if(inlier_mask.at<uchar>(i)) {
int new_i = static_cast<int>(inliers1.size());
inliers1.push_back(matched1[i]);
inliers2.push_back(matched2[i]);
inlier_matches.push_back(DMatch(new_i, new_i, 0));
}
}
stats.inliers = (int)inliers1.size();
stats.ratio = stats.inliers * 1.0 / stats.matches;
vector<Point2f> new_bb;
perspectiveTransform(object_bb, new_bb, homography);
Mat frame_with_bb = frame.clone();
if(stats.inliers >= bb_min_inliers) {
drawBoundingBox(frame_with_bb, new_bb);
}
Mat res;
drawMatches(first_frame, inliers1, frame_with_bb, inliers2,
inlier_matches, res,
Scalar(255, 0, 0), Scalar(255, 0, 0));
I tried to extract SIFT key points. It is working fine for a sample image I downloaded (height 400px width 247px horizontal and vertical resolutions 300dpi). Below image shows the extracted points.
Then I tried to apply the same code to a image that was taken and edited by me (height 443px width 541px horizontal and vertical resolutions 72dpi).
To create the above image I rotated the original image then removed its background and resized it using Photoshop, but my code, for that image doesn't extract features like in the first image.
See the result :
It just extract very few points. I expect a result as in the first case.
For the second case when I'm using the original image without any edit the program gives points as the first case.
Here is the simple code I have used
#include<opencv\cv.h>
#include<opencv\highgui.h>
#include<opencv2\nonfree\nonfree.hpp>
using namespace cv;
int main(){
Mat src, descriptors,dest;
vector<KeyPoint> keypoints;
src = imread(". . .");
cvtColor(src, src, CV_BGR2GRAY);
SIFT sift;
sift(src, src, keypoints, descriptors, false);
drawKeypoints(src, keypoints, dest);
imshow("Sift", dest);
cvWaitKey(0);
return 0;
}
What I'm doing wrong here? what do I need to do to get a result like in the first case to my own image after resizing ?
Thank you!
Try set nfeatures parameter (may be other parameters also need adjustment) in SIFT constructor.
Here is constructor definition from reference:
SIFT::SIFT(int nfeatures=0, int nOctaveLayers=3, double contrastThreshold=0.04, double edgeThreshold=10, double sigma=1.6)
Your code will be:
#include<opencv\cv.h>
#include<opencv\highgui.h>
#include<opencv2\nonfree\nonfree.hpp>
using namespace cv;
using namespace std;
int main(){
Mat src, descriptors,dest;
vector<KeyPoint> keypoints;
src = imread("D:\\ImagesForTest\\leaf.jpg");
cvtColor(src, src, CV_BGR2GRAY);
SIFT sift(2000,3,0.004);
sift(src, src, keypoints, descriptors, false);
drawKeypoints(src, keypoints, dest);
imshow("Sift", dest);
cvWaitKey(0);
return 0;
}
The result:
Dense sampling example:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include "opencv2/nonfree/nonfree.hpp"
int main(int argc, char* argv[])
{
cv::initModule_nonfree();
cv::namedWindow("result");
cv::Mat bgr_img = cv::imread("D:\\ImagesForTest\\lena.jpg");
if (bgr_img.empty())
{
exit(EXIT_FAILURE);
}
cv::Mat gray_img;
cv::cvtColor(bgr_img, gray_img, cv::COLOR_BGR2GRAY);
cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
cv::DenseFeatureDetector detector(12.0f, 1, 0.1f, 10);
std::vector<cv::KeyPoint> keypoints;
detector.detect(gray_img, keypoints);
std::vector<cv::KeyPoint>::iterator itk;
for (itk = keypoints.begin(); itk != keypoints.end(); ++itk)
{
std::cout << itk->pt << std::endl;
cv::circle(bgr_img, itk->pt, itk->size, cv::Scalar(0,255,255), 1, CV_AA);
cv::circle(bgr_img, itk->pt, 1, cv::Scalar(0,255,0), -1);
}
cv::Ptr<cv::DescriptorExtractor> descriptorExtractor = cv::DescriptorExtractor::create("SURF");
cv::Mat descriptors;
descriptorExtractor->compute( gray_img, keypoints, descriptors);
// SIFT returns large negative values when it goes off the edge of the image.
descriptors.setTo(0, descriptors<0);
imshow("result",bgr_img);
cv::waitKey();
return 0;
}
The result:
Following code is used to calculate the normalized gradient at all the pixels of image. But on using imshow on calculated gradient, instead of showing gradient for provided image its showing gradient of provided image 4 times (side by side).
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/core/core.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
Mat mat2gray(const Mat& src)
{
Mat dst;
normalize(src, dst, 0.0, 1.0, NORM_MINMAX);
return dst;
}
Mat setImage(Mat srcImage){
//GaussianBlur(srcImage,srcImage,Size(3,3),0.5,0.5);
Mat avgImage = Mat::zeros(srcImage.rows,srcImage.cols,CV_32F);
Mat gradient = Mat::zeros(srcImage.rows,srcImage.cols,CV_32F);
Mat norMagnitude = Mat::zeros(srcImage.rows,srcImage.cols,CV_32F);
Mat orientation = Mat::zeros(srcImage.rows,srcImage.cols,CV_32F);
//Mat_<uchar> srcImagetemp = srcImage;
float dx,dy;
for(int i=0;i<srcImage.rows-1;i++){
for(int j=0;j<srcImage.cols-1;j++){
dx=srcImage.at<float>(i,j+1)-srcImage.at<float>(i,j);
dy=srcImage.at<float>(i+1,j)-srcImage.at<float>(i,j);
gradient.at<float>(i,j)=sqrt(dx*dx+dy*dy);
orientation.at<float>(i,j)=atan2(dy,dx);
//cout<<gradient.at<float>(i,j)<<endl;
}
}
GaussianBlur(gradient,avgImage,Size(7,7),3,3);
for(int i=0;i<srcImage.rows;i++){
for(int j=0;j<srcImage.cols;j++){
norMagnitude.at<float>(i,j)=gradient.at<float>(i,j)/max(avgImage.at<float>(i,j),float(4));
//cout<<norMagnitude.at<float>(i,j)<<endl;
}
}
imshow("b",(gradient));
waitKey();
return norMagnitude;
}
int main(int argc,char **argv){
Mat image=imread(argv[1]);
cvtColor( image,image, CV_BGR2GRAY );
Mat newImage=setImage(image);
imshow("a",(newImage));
waitKey();
}
Your incoming source image is of type CV_8UC1, and yet you read it as floats:
dx=srcImage.at<float>(i,j+1)-srcImage.at<float>(i,j);
dy=srcImage.at<float>(i+1,j)-srcImage.at<float>(i,j);
If running under the debugger, this should have thrown an assertion, which would have highlighted the problem.
Try changing those lines to use unsigned char as follows:
dx=(float)(srcImage.at<unsigned char>(i,j+1)-srcImage.at<unsigned char>(i,j));
dy=(float)(srcImage.at<unsigned char>(i+1,j)-srcImage.at<unsigned char>(i,j));