├── README.md ├── README.md~ ├── Samples ├── airView1.png ├── airView2.png ├── airViewStitched.jpg ├── city1.png ├── city2.png ├── cityStitched.jpg ├── hut1.png ├── hut2.png ├── hutStitched.jpg ├── lake1.png ├── lake2.png ├── lakeStitched.jpg ├── monkeys1.png ├── monkeys2.png └── monkeysStitched.jpg ├── appendimages.m ├── match ├── match.m ├── match.m~ ├── sift ├── sift.m ├── siftWin32.exe └── stitch.m /README.md: -------------------------------------------------------------------------------- 1 | Matlab-ImageStitching 2 | ===================== 3 | 4 | This is a project I did in my junior year for a computer vision class. 5 | 6 | **Running**: call the stitch function with the two images you want stiched. Eg: *stitch('Samples/monkeys1.png', 'Samples/monkeys2.png')* 7 | 8 | **Approach**: 9 | * The local features of the two images are detected and described with the SIFT algorithm ( http://en.wikipedia.org/wiki/Scale-invariant_feature_transform ) 10 | * The two sets of SIFT featurs are then analyzed for similar datapoints which are likely to refer to the same image feature 11 | * If insufficient matching points have been found, then we quit as there isn't enough overlapping area to stitch the images 12 | * Otherwise, the RANSAC toolbox is used to apply a homographic transformation to one of the images such that the two images can be superimposed, and the resulting panorama view is outputed. 13 | 14 | **Samples**: 15 | * The Samples folder shows several starting images and their stitched result. 16 | * Circles represent the features we matched on from the first image and crosses represent the corresponding features from the second image. 17 | * The quality of the match can be visually inspected by the amount of crosses which ended up on top of their circle. 18 | * Notice how in the case of the *airView* image very few matches are detected but the information is still sufficient to generate a good stitching. This shows that quality of matching points if significantly more important than quantity. 19 | -------------------------------------------------------------------------------- /README.md~: -------------------------------------------------------------------------------- 1 | Matlab-ImageStitching 2 | ===================== 3 | 4 | This is a project I did in my junior year for a computer vision class. 5 | 6 | **Running**: call the stitch function with the two images you want stiched. Eg: *stitch('Samples/monkeys1.png', 'Samples/monkeys2.png')* 7 | -------------------------------------------------------------------------------- /Samples/airView1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/airView1.png -------------------------------------------------------------------------------- /Samples/airView2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/airView2.png -------------------------------------------------------------------------------- /Samples/airViewStitched.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/airViewStitched.jpg -------------------------------------------------------------------------------- /Samples/city1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/city1.png -------------------------------------------------------------------------------- /Samples/city2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/city2.png -------------------------------------------------------------------------------- /Samples/cityStitched.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/cityStitched.jpg -------------------------------------------------------------------------------- /Samples/hut1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/hut1.png -------------------------------------------------------------------------------- /Samples/hut2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/hut2.png -------------------------------------------------------------------------------- /Samples/hutStitched.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/hutStitched.jpg -------------------------------------------------------------------------------- /Samples/lake1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/lake1.png -------------------------------------------------------------------------------- /Samples/lake2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/lake2.png -------------------------------------------------------------------------------- /Samples/lakeStitched.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/lakeStitched.jpg -------------------------------------------------------------------------------- /Samples/monkeys1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/monkeys1.png -------------------------------------------------------------------------------- /Samples/monkeys2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/monkeys2.png -------------------------------------------------------------------------------- /Samples/monkeysStitched.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/Samples/monkeysStitched.jpg -------------------------------------------------------------------------------- /appendimages.m: -------------------------------------------------------------------------------- 1 | % im = appendimages(image1, image2) 2 | % 3 | % Return a new image that appends the two images side-by-side. 4 | 5 | function im = appendimages(image1, image2) 6 | 7 | % Select the image with the fewest rows and fill in enough empty rows 8 | % to make it the same height as the other image. 9 | rows1 = size(image1,1); 10 | rows2 = size(image2,1); 11 | 12 | if (rows1 < rows2) 13 | image1(rows2,1) = 0; 14 | else 15 | image2(rows1,1) = 0; 16 | end 17 | 18 | % Now append both images side-by-side. 19 | im = [image1 image2]; 20 | -------------------------------------------------------------------------------- /match: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/match -------------------------------------------------------------------------------- /match.m: -------------------------------------------------------------------------------- 1 | % num = match(image1, image2) 2 | % 3 | % This function reads two images, finds their SIFT features, and 4 | % displays lines connecting the matched keypoints. A match is accepted 5 | % only if its distance is less than distRatio times the distance to the 6 | % second closest match. 7 | % It returns the number of matches displayed. 8 | % 9 | % Example: match('scene.pgm','book.pgm'); 10 | 11 | function [rez,nr] = match(image1, image2) 12 | 13 | % Find SIFT keypoints for each image 14 | [im1, des1, loc1] = sift(image1); 15 | [im2, des2, loc2] = sift(image2); 16 | 17 | % For efficiency in Matlab, it is cheaper to compute dot products between 18 | % unit vectors rather than Euclidean distances. Note that the ratio of 19 | % angles (acos of dot products of unit vectors) is a close approximation 20 | % to the ratio of Euclidean distances for small angles. 21 | % 22 | % distRatio: Only keep matches in which the ratio of vector angles from the 23 | % nearest to second nearest neighbor is less than distRatio. 24 | distRatio = 0.6; 25 | 26 | % For each descriptor in the first image, select its match to second image. 27 | des2t = des2'; % Precompute matrix transpose 28 | for i = 1 : size(des1,1) 29 | dotprods = des1(i,:) * des2t; % Computes vector of dot products 30 | [vals,indx] = sort(acos(dotprods)); % Take inverse cosine and sort results 31 | 32 | % Check if nearest neighbor has angle less than distRatio times 2nd. 33 | if (vals(1) < distRatio * vals(2)) 34 | match(i) = indx(1); 35 | else 36 | match(i) = 0; 37 | end 38 | end 39 | 40 | cols1 = size(im1,2); 41 | rez = []; 42 | for i = 1: size(des1,1) 43 | if (match(i) > 0) 44 | rez = [rez; loc1(i,2), loc1(i,1), loc2(match(i),2), loc2(match(i),1)]; 45 | end 46 | end 47 | 48 | num = sum(match > 0); 49 | fprintf('Found %d matches.\n', num); 50 | nr = size(des1,1) + size(des2,1); 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /match.m~: -------------------------------------------------------------------------------- 1 | % num = match(image1, image2) 2 | % 3 | % This function reads two images, finds their SIFT features, and 4 | % displays lines connecting the matched keypoints. A match is accepted 5 | % only if its distance is less than distRatio times the distance to the 6 | % second closest match. 7 | % It returns the number of matches displayed. 8 | % 9 | % Example: match('scene.pgm','book.pgm'); 10 | 11 | function [rez,nr] = match(image1, image2) 12 | 13 | % Find SIFT keypoints for each image 14 | [im1, des1, loc1] = sift(image1); 15 | [im2, des2, loc2] = sift(image2); 16 | 17 | % For efficiency in Matlab, it is cheaper to compute dot products between 18 | % unit vectors rather than Euclidean distances. Note that the ratio of 19 | % angles (acos of dot products of unit vectors) is a close approximation 20 | % to the ratio of Euclidean distances for small angles. 21 | % 22 | % distRatio: Only keep matches in which the ratio of vector angles from the 23 | % nearest to second nearest neighbor is less than distRatio. 24 | distRatio = 0.6; 25 | 26 | % For each descriptor in the first image, select its match to second image. 27 | des2t = des2'; % Precompute matrix transpose 28 | for i = 1 : size(des1,1) 29 | dotprods = des1(i,:) * des2t; % Computes vector of dot products 30 | [vals,indx] = sort(acos(dotprods)); % Take inverse cosine and sort results 31 | 32 | % Check if nearest neighbor has angle less than distRatio times 2nd. 33 | if (vals(1) < distRatio * vals(2)) 34 | match(i) = indx(1); 35 | else 36 | match(i) = 0; 37 | end 38 | end 39 | 40 | % Create a new image showing the two images side by side. 41 | im3 = appendimages(im1,im2); 42 | 43 | % Show a figure with lines joining the accepted matches. 44 | %figure('Position', [100 100 size(im3,2) size(im3,1)]); 45 | %colormap('gray'); 46 | %imagesc(im3); 47 | %hold on; 48 | cols1 = size(im1,2); 49 | rez = []; 50 | for i = 1: size(des1,1) 51 | if (match(i) > 0) 52 | % line([loc1(i,2) loc2(match(i),2)+cols1], ... 53 | % [loc1(i,1) loc2(match(i),1)], 'Color', 'c'); 54 | rez = [rez; loc1(i,2), loc1(i,1), loc2(match(i),2), loc2(match(i),1)]; 55 | end 56 | end 57 | %hold off; 58 | num = sum(match > 0); 59 | fprintf('Found %d matches.\n', num); 60 | nr = size(des1,1) + size(des2,1); 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /sift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/sift -------------------------------------------------------------------------------- /sift.m: -------------------------------------------------------------------------------- 1 | % [image, descriptors, locs] = sift(imageFile) 2 | % 3 | % This function reads an image and returns its SIFT keypoints. 4 | % Input parameters: 5 | % imageFile: the file name for the image. 6 | % 7 | % Returned: 8 | % image: the image array in double format 9 | % descriptors: a K-by-128 matrix, where each row gives an invariant 10 | % descriptor for one of the K keypoints. The descriptor is a vector 11 | % of 128 values normalized to unit length. 12 | % locs: K-by-4 matrix, in which each row has the 4 values for a 13 | % keypoint location (row, column, scale, orientation). The 14 | % orientation is in the range [-PI, PI] radians. 15 | % 16 | % Credits: Thanks for initial version of this program to D. Alvaro and 17 | % J.J. Guerrero, Universidad de Zaragoza (modified by D. Lowe) 18 | 19 | function [image, descriptors, locs] = sift(imageFile) 20 | 21 | % Load image 22 | image = imread(imageFile); 23 | 24 | % If you have the Image Processing Toolbox, you can uncomment the following 25 | % lines to allow input of color images, which will be converted to grayscale. 26 | % if isrgb(image) 27 | % image = rgb2gray(image); 28 | % end 29 | 30 | [rows, cols] = size(image); 31 | 32 | % Convert into PGM imagefile, readable by "keypoints" executable 33 | f = fopen('tmp.pgm', 'w'); 34 | if f == -1 35 | error('Could not create file tmp.pgm.'); 36 | end 37 | fprintf(f, 'P5\n%d\n%d\n255\n', cols, rows); 38 | fwrite(f, image', 'uint8'); 39 | fclose(f); 40 | 41 | % Call keypoints executable 42 | if isunix 43 | command = '!./sift '; 44 | else 45 | command = '!siftWin32 '; 46 | end 47 | command = [command ' tmp.key']; 48 | eval(command); 49 | 50 | % Open tmp.key and check its header 51 | g = fopen('tmp.key', 'r'); 52 | if g == -1 53 | error('Could not open file tmp.key.'); 54 | end 55 | [header, count] = fscanf(g, '%d %d', [1 2]); 56 | if count ~= 2 57 | error('Invalid keypoint file beginning.'); 58 | end 59 | num = header(1); 60 | len = header(2); 61 | if len ~= 128 62 | error('Keypoint descriptor length invalid (should be 128).'); 63 | end 64 | 65 | % Creates the two output matrices (use known size for efficiency) 66 | locs = double(zeros(num, 4)); 67 | descriptors = double(zeros(num, 128)); 68 | 69 | % Parse tmp.key 70 | for i = 1:num 71 | [vector, count] = fscanf(g, '%f %f %f %f', [1 4]); %row col scale ori 72 | if count ~= 4 73 | error('Invalid keypoint file format'); 74 | end 75 | locs(i, :) = vector(1, :); 76 | 77 | [descrip, count] = fscanf(g, '%d', [1 len]); 78 | if (count ~= 128) 79 | error('Invalid keypoint file value.'); 80 | end 81 | % Normalize each input vector to unit length 82 | descrip = descrip / sqrt(sum(descrip.^2)); 83 | descriptors(i, :) = descrip(1, :); 84 | end 85 | fclose(g); 86 | 87 | 88 | -------------------------------------------------------------------------------- /siftWin32.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazvanRanca/Matlab-ImageStitching/d4134b66cee06dd2c180c961f66f9d17e406e968/siftWin32.exe -------------------------------------------------------------------------------- /stitch.m: -------------------------------------------------------------------------------- 1 | function stitch (im2,im1) 2 | 3 | [r,nr] = match(im1,im2); 4 | 5 | if (size(r,1) < nr/100) 6 | fprintf('There are not enough matching keypoints\n'); 7 | else 8 | % set RANSAC path 9 | cd RANSAC-Toolbox 10 | SetPathLocal 11 | cd .. 12 | % set RANSAC options 13 | options.epsilon = 1e-6; 14 | options.P_inlier = 1-1e-4; 15 | options.sigma = 1; 16 | options.validateMSS_fun = @validateMSS_homography; 17 | options.est_fun = @estimate_homography; 18 | options.man_fun = @error_homography; 19 | options.mode = 'RANSAC'; 20 | options.Ps = []; 21 | options.notify_iters = []; 22 | options.min_iters = 1000; 23 | options.fix_seed = false; 24 | options.reestimate = true; 25 | options.stabilize = false; 26 | 27 | [results, options] = RANSAC(r', options); 28 | 29 | H = (reshape(results.Theta, 3, 3))'; 30 | 31 | T = maketform('projective', H) 32 | 33 | [rim1, xdata, ydata] = imtransform(imread(im1), T); 34 | 35 | rp = r(:,1:2); 36 | [NX,NY] = tformfwd(T,rp); 37 | 38 | 39 | OX = r(:,3); 40 | OY = r(:,4); 41 | 42 | xdata 43 | ydata 44 | rim2 = imread(im2); 45 | s2 = size(rim2); 46 | s1 = size(rim1); 47 | 48 | if(ydata(1) < 0) 49 | rim2 = padarray(rim2, [round(-1*ydata(1)) 0],'pre'); 50 | OY = OY - ydata(1); 51 | NY = NY - ydata(1); 52 | %rp(:,2) = rp(:,2) - ydata(1); 53 | end 54 | 55 | if(xdata(1) < 0) 56 | rim2 = padarray(rim2, [0 round(-1*xdata(1))],'pre'); 57 | OX = OX - xdata(1); 58 | NX = NX - xdata(1); 59 | %rp(:,1) = rp(:,1) - xdata(1); 60 | end 61 | 62 | if(ydata(2) > s2(2)) 63 | rim2 = padarray(rim2, [round(ydata(2)-s2(2)) 0],'post'); 64 | %rp(:,2) = rp(:,2) + (ydata(2)-s(2)); 65 | end 66 | 67 | if(xdata(2) > s2(1)) 68 | rim2 = padarray(rim2, [0 round(xdata(2)-s2(2))],'post'); 69 | %rp(:,1) = rp(:,1) + (xdata(2)-s(2)); 70 | 71 | end 72 | 73 | s2 = size(rim2); 74 | 75 | %np(:,2) = np(:,2) + s2(2)-s1(2); 76 | %op(:,1) = op(:,1) + ss(1); 77 | 78 | 79 | if(ydata(1) > 0) 80 | rim1 = padarray(rim1, [round(ydata(1)) 0],'pre'); 81 | end 82 | 83 | if(xdata(1) > 0) 84 | rim1 = padarray(rim1, [0 round(xdata(1))],'pre'); 85 | end 86 | 87 | s1 = size(rim1); 88 | 89 | s1 90 | s2 91 | 92 | if(s1(2) < s2(2)) 93 | rim1 = padarray(rim1, [0 s2(2)-s1(2)],'post'); 94 | end 95 | 96 | if(s1(1) < s2(1)) 97 | rim1 = padarray(rim1, [s2(1)-s1(1) 0],'post'); 98 | end 99 | 100 | for i=1:s2(1) 101 | for j=1:s2(2) 102 | if (rim1(i,j) == 0) 103 | rim1(i,j) = rim2(i,j); 104 | end 105 | end 106 | end 107 | 108 | imshow(rim1); 109 | hold on; 110 | 111 | s = size(NX) 112 | 113 | for i=1:s(1) 114 | plot(NX(i),NY(i),'bo'); 115 | plot(OX(i),OY(i),'gx'); 116 | end 117 | 118 | hold off; 119 | delete('tmp.key', 'tmp.pgm') 120 | end 121 | --------------------------------------------------------------------------------