├── ImageStitching ├── main.cpp ├── stdafx.h ├── stdafx.cpp ├── res │ ├── pano1.jpg │ └── pano2.jpg ├── targetver.h ├── FileReading.cpp ├── dataset1 │ ├── 1.bmp │ ├── 2.bmp │ ├── 3.bmp │ └── 4.bmp ├── dataset2 │ ├── 1.JPG │ ├── 10.JPG │ ├── 11.JPG │ ├── 12.JPG │ ├── 13.JPG │ ├── 14.JPG │ ├── 15.JPG │ ├── 16.JPG │ ├── 17.JPG │ ├── 18.JPG │ ├── 2.JPG │ ├── 3.JPG │ ├── 4.JPG │ ├── 5.JPG │ ├── 6.JPG │ ├── 7.JPG │ ├── 8.JPG │ └── 9.JPG ├── Projection.h ├── Blend.h ├── FileReading.h ├── Stitching.h ├── Interpolation.cpp ├── Feature.h ├── Warping.h ├── ReadMe.txt ├── Match.h ├── Projection.cpp ├── Interpolation.h ├── Feature.cpp ├── ImageStitching.vcxproj.filters ├── Warping.cpp ├── Blend.cpp ├── ImageStitching.vcxproj ├── Stitching.cpp └── Match.cpp ├── README.md ├── ImageStitching.sln ├── .gitattributes └── .gitignore /ImageStitching/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/main.cpp -------------------------------------------------------------------------------- /ImageStitching/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/stdafx.h -------------------------------------------------------------------------------- /ImageStitching/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/stdafx.cpp -------------------------------------------------------------------------------- /ImageStitching/res/pano1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/res/pano1.jpg -------------------------------------------------------------------------------- /ImageStitching/res/pano2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/res/pano2.jpg -------------------------------------------------------------------------------- /ImageStitching/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/targetver.h -------------------------------------------------------------------------------- /ImageStitching/FileReading.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/FileReading.cpp -------------------------------------------------------------------------------- /ImageStitching/dataset1/1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset1/1.bmp -------------------------------------------------------------------------------- /ImageStitching/dataset1/2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset1/2.bmp -------------------------------------------------------------------------------- /ImageStitching/dataset1/3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset1/3.bmp -------------------------------------------------------------------------------- /ImageStitching/dataset1/4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset1/4.bmp -------------------------------------------------------------------------------- /ImageStitching/dataset2/1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/1.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/10.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/10.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/11.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/11.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/12.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/12.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/13.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/13.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/14.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/14.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/15.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/15.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/16.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/16.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/17.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/17.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/18.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/18.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/2.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/3.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/4.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/5.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/5.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/6.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/6.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/7.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/7.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/8.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/8.JPG -------------------------------------------------------------------------------- /ImageStitching/dataset2/9.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmazingZhen/ImageStitching/HEAD/ImageStitching/dataset2/9.JPG -------------------------------------------------------------------------------- /ImageStitching/Projection.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROJECTION_H_ 2 | #define _PROJECTION_H_ 3 | 4 | #include "CImg.h" 5 | 6 | #define ANGLE 15.0 7 | #define PI 3.1415926 8 | 9 | using namespace cimg_library; 10 | 11 | CImg cylinderProjection(const CImg &src); 12 | 13 | #endif -------------------------------------------------------------------------------- /ImageStitching/Blend.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLEND_H_ 2 | #define _BLEND_H_ 3 | 4 | #include 5 | #include "CImg.h" 6 | #include 7 | 8 | using namespace std; 9 | using namespace cimg_library; 10 | 11 | CImg blendTwoImages(const CImg &a, const CImg &b); 12 | 13 | #endif -------------------------------------------------------------------------------- /ImageStitching/FileReading.h: -------------------------------------------------------------------------------- 1 | #ifndef _FILEREADING_H_ 2 | #define _FILEREADING_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | void getAllFiles(string path, vector &files); 12 | void getAllFormatFiles(string path, vector& files, string format); 13 | 14 | #endif -------------------------------------------------------------------------------- /ImageStitching/Stitching.h: -------------------------------------------------------------------------------- 1 | #ifndef _STITCHING_H_ 2 | #define _STITCHING_H_ 3 | 4 | #include "Feature.h" 5 | #include "Match.h" 6 | #include "Warping.h" 7 | #include "Blend.h" 8 | 9 | #define MAX_STITCHING_NUM 20 10 | 11 | CImg get_gray_image(const CImg &srcImg); 12 | CImg stitching(vector> &src_imgs); 13 | 14 | #endif -------------------------------------------------------------------------------- /ImageStitching/Interpolation.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Interpolation.h" 3 | 4 | double sinxx(double value) { 5 | if (value < 0) 6 | value = -value; 7 | 8 | if (value < 1.0) { 9 | double temp = value * value; 10 | return 0.5 * temp * value - temp + 2.0 / 3.0; 11 | } 12 | else if (value < 2.0) { 13 | value = 2.0 - value; 14 | value *= value * value; 15 | return value / 6.0; 16 | } 17 | else { 18 | return 0.0; 19 | } 20 | } -------------------------------------------------------------------------------- /ImageStitching/Feature.h: -------------------------------------------------------------------------------- 1 | #ifndef _FEATURE_H_ 2 | #define _FEATURE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "CImg.h" 8 | 9 | #define RESIZE_SIZE 500.0 10 | 11 | using namespace cimg_library; 12 | using namespace std; 13 | 14 | extern "C" { 15 | #include "vl/generic.h" 16 | #include "vl/sift.h" 17 | } 18 | 19 | map, VlSiftKeypoint> getFeatureFromImage(const CImg &src); 20 | 21 | #endif -------------------------------------------------------------------------------- /ImageStitching/Warping.h: -------------------------------------------------------------------------------- 1 | #ifndef _WARPING_H_ 2 | #define _WARPING_H_ 3 | 4 | #include 5 | #include "Match.h" 6 | #include "Interpolation.h" 7 | 8 | using namespace std; 9 | 10 | float getMaxXAfterWarping(const CImg &src, Parameters H); 11 | float getMinXAfterWarping(const CImg &src, Parameters H); 12 | float getMaxYAferWarping(const CImg &src, Parameters H); 13 | float getMinYAfterWarping(const CImg &src, Parameters H); 14 | 15 | int getWidthAfterWarping(const CImg &src, Parameters H); 16 | int getHeightAfterWarping(const CImg &src, Parameters H); 17 | 18 | void warpingImageByHomography(const CImg &src, CImg &dst, Parameters H, float offset_x, float offset_y); 19 | void movingImageByOffset(const CImg &src, CImg &dst, int offset_x, int offset_y); 20 | 21 | 22 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageStitching 2 | A solution for images stitchinging based on Sift, kd-tree, RANSAC and Multi-band Blending. 3 | Based on [CImg Library](http://cimg.eu/) and [Vlfeat library](http://www.vlfeat.org/). 4 | 5 | ## Input 6 | - A set of images, supporting disordered images. 7 | - Two sample input sets, "dataset1" and "dataset2". 8 | 9 | ## Output 10 | - A stitched image. 11 | - ![result1](https://github.com/AmazingZhen/ImageStitching/blob/master/ImageStitching/res/pano1.jpg?raw=true) 12 | - ![result2](https://github.com/AmazingZhen/ImageStitching/blob/master/ImageStitching/res/pano2.jpg?raw=true) 13 | 14 | ## Algorithmic process 15 | - Image registration 16 | + Cylinder projection 17 | + Feature extraction 18 | + Feature matching 19 | + RANSAC and least squares for homography 20 | - Image blending 21 | + Image wrapping 22 | + Multi-band blending 23 | 24 | ## Areas for improvement 25 | - Compensate explosion error. 26 | -------------------------------------------------------------------------------- /ImageStitching.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageStitching", "ImageStitching\ImageStitching.vcxproj", "{2249AB88-07F1-4BDD-9054-195024D764C9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2249AB88-07F1-4BDD-9054-195024D764C9}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {2249AB88-07F1-4BDD-9054-195024D764C9}.Debug|Win32.Build.0 = Debug|Win32 16 | {2249AB88-07F1-4BDD-9054-195024D764C9}.Release|Win32.ActiveCfg = Release|Win32 17 | {2249AB88-07F1-4BDD-9054-195024D764C9}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /ImageStitching/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | 控制台应用程序:ImageStitching 项目概述 3 | ======================================================================== 4 | 5 | 应用程序向导已为您创建了此 ImageStitching 应用程序。 6 | 7 | 本文件概要介绍组成 ImageStitching 应用程序的每个文件的内容。 8 | 9 | 10 | ImageStitching.vcxproj 11 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 12 | 13 | ImageStitching.vcxproj.filters 14 | 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。 15 | 16 | ImageStitching.cpp 17 | 这是主应用程序源文件。 18 | 19 | ///////////////////////////////////////////////////////////////////////////// 20 | 其他标准文件: 21 | 22 | StdAfx.h, StdAfx.cpp 23 | 这些文件用于生成名为 ImageStitching.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 24 | 25 | ///////////////////////////////////////////////////////////////////////////// 26 | 其他注释: 27 | 28 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。 29 | 30 | ///////////////////////////////////////////////////////////////////////////// 31 | -------------------------------------------------------------------------------- /ImageStitching/Match.h: -------------------------------------------------------------------------------- 1 | #ifndef _MATCH_H 2 | #define _MATCH_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "CImg.h" 9 | 10 | using namespace cimg_library; 11 | using namespace std; 12 | 13 | #define NUM_OF_PAIR 4 14 | #define CONFIDENCE 0.99 15 | #define INLINER_RATIO 0.5 16 | #define RANSAC_THRESHOLD 4.0 17 | 18 | extern "C" { 19 | #include "vl/generic.h" 20 | #include "vl/sift.h" 21 | #include "vl/kdtree.h" 22 | } 23 | 24 | struct point_pair { 25 | VlSiftKeypoint a; 26 | VlSiftKeypoint b; 27 | point_pair(VlSiftKeypoint _a, VlSiftKeypoint _b) { 28 | a = _a; 29 | b = _b; 30 | } 31 | }; 32 | 33 | struct point_pairs { 34 | vector pairs; 35 | int src; 36 | int dst; 37 | point_pairs(const vector &_pairs, int s, int d) { 38 | pairs = _pairs; 39 | src = s; 40 | dst = d; 41 | } 42 | }; 43 | 44 | using namespace std; 45 | 46 | struct Parameters { 47 | float c1; 48 | float c2; 49 | float c3; 50 | float c4; 51 | float c5; 52 | float c6; 53 | float c7; 54 | float c8; 55 | Parameters(float _c1, float _c2, float _c3, float _c4, float _c5, float _c6, float _c7, float _c8) { 56 | c1 = _c1; 57 | c2 = _c2; 58 | c3 = _c3; 59 | c4 = _c4; 60 | c5 = _c5; 61 | c6 = _c6; 62 | c7 = _c7; 63 | c8 = _c8; 64 | } 65 | void print() { 66 | cout << c1 << " " << c2 << " " << c3 << " " << c4 << endl; 67 | cout << c5 << " " << c6 << " " << c7 << " " << c8 << endl; 68 | } 69 | }; 70 | 71 | float getXAfterWarping(float x, float y, Parameters H); 72 | float getYAfterWarping(float x, float y, Parameters H); 73 | 74 | vector getPointPairsFromFeature(const map, VlSiftKeypoint> &feature_a, const map, VlSiftKeypoint> &feature_b); 75 | Parameters getHomographyFromPoingPairs(const vector &pairs); 76 | Parameters RANSAC(const vector &pairs); 77 | 78 | #endif -------------------------------------------------------------------------------- /ImageStitching/Projection.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Projection.h" 3 | #include 4 | #include "Interpolation.h" 5 | 6 | CImg cylinderProjection(const CImg &src) { 7 | int projection_width, projection_height; 8 | CImg res(src.width(), src.height(), 1, src.spectrum(), 0); 9 | float r; 10 | 11 | if (src.width() > src.height()) { 12 | projection_width = src.height(); 13 | projection_height = src.width(); 14 | 15 | r = (projection_width / 2.0) / tan(ANGLE * PI / 180.0); 16 | 17 | for (int i = 0; i < res.width(); i++) { 18 | for (int j = 0; j < res.height(); j++) { 19 | float dst_x = j - projection_width / 2; 20 | float dst_y = i - projection_height / 2; 21 | 22 | float k = r / sqrt(r * r + dst_x * dst_x); 23 | float src_x = dst_x / k; 24 | float src_y = dst_y / k; 25 | 26 | if (src_x + projection_width / 2 >= 0 && src_x + projection_width / 2 < src.height() 27 | && src_y + projection_height / 2 >= 0 && src_y + projection_height / 2 < src.width()) { 28 | for (int k = 0; k < res.spectrum(); k++) { 29 | res(i, j, k) = bilinear_interpolation(src, src_y + projection_height / 2, src_x + projection_width / 2, k); 30 | } 31 | } 32 | } 33 | } 34 | 35 | } 36 | else { 37 | projection_width = src.width(); 38 | projection_height = src.height(); 39 | 40 | r = (projection_width / 2.0) / tan(ANGLE * PI / 180.0); 41 | 42 | for (int i = 0; i < res.width(); i++) { 43 | for (int j = 0; j < res.height(); j++) { 44 | float dst_x = i - projection_width / 2; 45 | float dst_y = j - projection_height / 2; 46 | 47 | float k = r / sqrt(r * r + dst_x * dst_x); 48 | float src_x = dst_x / k; 49 | float src_y = dst_y / k; 50 | 51 | if (src_x + projection_width / 2 >= 0 && src_x + projection_width / 2 < src.width() 52 | && src_y + projection_height / 2 >= 0 && src_y + projection_height / 2 < src.height()) { 53 | for (int k = 0; k < res.spectrum(); k++) { 54 | res(i, j, k) = bilinear_interpolation(src, src_x + projection_width / 2, src_y + projection_height / 2, k); 55 | } 56 | } 57 | } 58 | } 59 | 60 | } 61 | 62 | return res; 63 | } -------------------------------------------------------------------------------- /ImageStitching/Interpolation.h: -------------------------------------------------------------------------------- 1 | #ifndef _INTERPOLATION_H_ 2 | #define _INTERPOLATION_H_ 3 | 4 | #include "CImg.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace cimg_library; 11 | using namespace std; 12 | 13 | double sinxx(double value); 14 | 15 | template 16 | T bilinear_interpolation(const CImg& image, float x, float y, int channel) { 17 | assert(x >= 0 && x < image.width()); 18 | assert(y >= 0 && y < image.height()); 19 | assert(channel <= image.spectrum()); 20 | 21 | int x_pos = floor(x); 22 | float x_u = x - x_pos; 23 | int xb = (x_pos < image.width() - 1) ? x_pos + 1 : x_pos; 24 | 25 | int y_pos = floor(y); 26 | float y_v = y - y_pos; 27 | int yb = (y_pos < image.height() - 1) ? y_pos + 1 : y_pos; 28 | 29 | float P1 = image(x_pos, y_pos, channel) * (1 - x_u) + image(xb, y_pos, channel) * x_u; 30 | float P2 = image(x_pos, yb, channel) * (1 - x_u) + image(xb, yb, channel) * x_u; 31 | 32 | return P1 * (1 - y_v) + P2 * y_v; 33 | } 34 | 35 | template 36 | T bicubic_interpolation(const CImg& image, float x, float y, int channel) { 37 | assert(x >= 0 && x < image.width()); 38 | assert(y >= 0 && y < image.height()); 39 | assert(channel <= image.spectrum()); 40 | 41 | int x_pos = floor(x); 42 | float x_u = x - x_pos; 43 | int y_pos = floor(y); 44 | float y_v = y - y_pos; 45 | 46 | int xa = (x_pos > 0) ? x_pos - 1 : x_pos; 47 | int xc = (x_pos < image.width() - 1) ? x_pos + 1 : x_pos; 48 | int xd = (x_pos < image.width() - 2) ? x_pos + 2 : xc; 49 | 50 | int ya = (y_pos > 0) ? y_pos - 1 : y_pos; 51 | int yc = (y_pos < image.height() - 1) ? y_pos + 1 : y_pos; 52 | int yd = (y_pos < image.height() - 2) ? y_pos + 2 : yc; 53 | 54 | CImg A(4, 1, 1, 1, sinxx(x_u + 1), sinxx(x_u), sinxx(x_u - 1), sinxx(x_u - 2)); 55 | CImg C(1, 4, 1, 1, sinxx(y_v + 1), sinxx(y_v), sinxx(y_v - 1), sinxx(y_v - 2)); 56 | 57 | CImg B(4, 4, 1, 1, image(xa, ya, channel), image(xa, y_pos, channel), image(xa, yc, channel), image(xa, yd, channel), 58 | image(x_pos, ya, channel), image(x_pos, y_pos, channel), image(x_pos, yc, channel), image(x_pos, yd, channel), 59 | image(xc, ya, channel), image(xc, y_pos, channel), image(xc, yc, channel), image(xc, yd, channel), 60 | image(xd, ya, channel), image(xd, y_pos, channel), image(xd, yc, channel), image(xd, yd, channel) 61 | ); 62 | 63 | CImg res = A*B*C; 64 | return res(0, 0); 65 | } 66 | 67 | #endif -------------------------------------------------------------------------------- /ImageStitching/Feature.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Feature.h" 3 | 4 | map, VlSiftKeypoint> getFeatureFromImage(const CImg &input) { 5 | assert(input.spectrum() == 1); 6 | 7 | CImg src(input); 8 | 9 | float resize_factor; 10 | if (input.width() < input.height()) { 11 | resize_factor = RESIZE_SIZE / input.width(); 12 | } 13 | else { 14 | resize_factor = RESIZE_SIZE / input.height(); 15 | } 16 | 17 | if (resize_factor >= 1) { 18 | resize_factor = 1; 19 | } 20 | else { 21 | src.resize(src.width() * resize_factor, src.height() * resize_factor, src.depth(), src.spectrum(), 3); 22 | } 23 | 24 | vl_sift_pix *imageData = new vl_sift_pix[src.width()*src.height()]; 25 | 26 | for (int i = 0; i < src.width(); i++) { 27 | for (int j = 0; j < src.height(); j++) { 28 | imageData[j*src.width() + i] = src(i, j, 0); 29 | } 30 | } 31 | 32 | // Initialize a SIFT filter object. 33 | int noctaves = 4, nlevels = 2, o_min = 0; 34 | VlSiftFilt *siftFilt = NULL; 35 | siftFilt = vl_sift_new(src.width(), src.height(), noctaves, nlevels, o_min); 36 | 37 | map, VlSiftKeypoint> features; 38 | 39 | // Compute the first octave of the DOG scale space. 40 | if (vl_sift_process_first_octave(siftFilt, imageData) != VL_ERR_EOF) { 41 | while (true) { 42 | // Run the SIFT detector to get the keypoints. 43 | vl_sift_detect(siftFilt); 44 | 45 | VlSiftKeypoint *pKeyPoint = siftFilt->keys; 46 | // For each keypoint: 47 | for (int i = 0; i < siftFilt->nkeys; i++) { 48 | VlSiftKeypoint tempKeyPoint = *pKeyPoint; 49 | 50 | // Get the keypoint orientation(s). 51 | double angles[4]; 52 | int angleCount = vl_sift_calc_keypoint_orientations(siftFilt, angles, &tempKeyPoint); 53 | 54 | // For each orientation: 55 | for (int j = 0; j < angleCount; j++) { 56 | double tempAngle = angles[j]; 57 | vl_sift_pix descriptors[128]; 58 | 59 | // Get the keypoint descriptor 60 | vl_sift_calc_keypoint_descriptor(siftFilt, descriptors, &tempKeyPoint, tempAngle); 61 | 62 | vector des; 63 | int k = 0; 64 | while (k < 128) { 65 | des.push_back(descriptors[k]); 66 | k++; 67 | } 68 | 69 | tempKeyPoint.x /= resize_factor; 70 | tempKeyPoint.y /= resize_factor; 71 | tempKeyPoint.ix = tempKeyPoint.x; 72 | tempKeyPoint.iy = tempKeyPoint.y; 73 | 74 | features.insert(pair, VlSiftKeypoint>(des, tempKeyPoint)); 75 | } 76 | 77 | pKeyPoint++; 78 | } 79 | if (vl_sift_process_next_octave(siftFilt) == VL_ERR_EOF) { 80 | break; 81 | } 82 | } 83 | } 84 | 85 | vl_sift_delete(siftFilt); 86 | 87 | delete[] imageData; 88 | imageData = NULL; 89 | 90 | return features; 91 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ImageStitching/ImageStitching.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 头文件 35 | 36 | 37 | 源文件 38 | 39 | 40 | 头文件 41 | 42 | 43 | 头文件 44 | 45 | 46 | 头文件 47 | 48 | 49 | 头文件 50 | 51 | 52 | 头文件 53 | 54 | 55 | 56 | 57 | 源文件 58 | 59 | 60 | 源文件 61 | 62 | 63 | 源文件 64 | 65 | 66 | 源文件 67 | 68 | 69 | 源文件 70 | 71 | 72 | 源文件 73 | 74 | 75 | 源文件 76 | 77 | 78 | 源文件 79 | 80 | 81 | 源文件 82 | 83 | 84 | 源文件 85 | 86 | 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml -------------------------------------------------------------------------------- /ImageStitching/Warping.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Warping.h" 3 | 4 | float getMaxXAfterWarping(const CImg &src, Parameters H) { 5 | float max_x = getXAfterWarping(0, 0, H); 6 | 7 | if (getXAfterWarping(src.width() - 1, 0, H) > max_x) { 8 | max_x = getXAfterWarping(src.width() - 1, 0, H); 9 | } 10 | if (getXAfterWarping(0, src.height() - 1, H) > max_x) { 11 | max_x = getXAfterWarping(0, src.height() - 1, H); 12 | } 13 | if (getXAfterWarping(src.width() - 1, src.height() - 1, H) > max_x) { 14 | max_x = getXAfterWarping(src.width() - 1, src.height() - 1, H); 15 | } 16 | 17 | return max_x; 18 | } 19 | 20 | float getMinXAfterWarping(const CImg &src, Parameters H) { 21 | float min_x = getXAfterWarping(0, 0, H); 22 | 23 | if (getXAfterWarping(src.width() - 1, 0, H) < min_x) { 24 | min_x = getXAfterWarping(src.width() - 1, 0, H); 25 | } 26 | if (getXAfterWarping(0, src.height() - 1, H) < min_x) { 27 | min_x = getXAfterWarping(0, src.height() - 1, H); 28 | } 29 | if (getXAfterWarping(src.width() - 1, src.height() - 1, H) < min_x) { 30 | min_x = getXAfterWarping(src.width() - 1, src.height() - 1, H); 31 | } 32 | 33 | return min_x; 34 | } 35 | 36 | float getMaxYAferWarping(const CImg &src, Parameters H) { 37 | float max_y = getYAfterWarping(0, 0, H); 38 | 39 | if (getYAfterWarping(src.width() - 1, 0, H) > max_y) { 40 | max_y = getYAfterWarping(src.width() - 1, 0, H); 41 | } 42 | if (getYAfterWarping(0, src.height() - 1, H) > max_y) { 43 | max_y = getYAfterWarping(0, src.height() - 1, H); 44 | } 45 | if (getYAfterWarping(src.width() - 1, src.height() - 1, H) > max_y) { 46 | max_y = getYAfterWarping(src.width() - 1, src.height() - 1, H); 47 | } 48 | 49 | return max_y; 50 | } 51 | 52 | float getMinYAfterWarping(const CImg &src, Parameters H) { 53 | float min_y = getYAfterWarping(0, 0, H); 54 | 55 | if (getYAfterWarping(src.width() - 1, 0, H) < min_y) { 56 | min_y = getYAfterWarping(src.width() - 1, 0, H); 57 | } 58 | if (getYAfterWarping(0, src.height() - 1, H) < min_y) { 59 | min_y = getYAfterWarping(0, src.height() - 1, H); 60 | } 61 | if (getYAfterWarping(src.width() - 1, src.height() - 1, H) < min_y) { 62 | min_y = getYAfterWarping(src.width() - 1, src.height() - 1, H); 63 | } 64 | 65 | return min_y; 66 | } 67 | 68 | int getWidthAfterWarping(const CImg &src, Parameters H) { 69 | int max = getMaxXAfterWarping(src, H); 70 | int min = getMinXAfterWarping(src, H); 71 | 72 | return max - min; 73 | } 74 | 75 | int getHeightAfterWarping(const CImg &src, Parameters H) { 76 | int max = getMaxYAferWarping(src, H); 77 | int min = getMinYAfterWarping(src, H); 78 | 79 | return max - min; 80 | } 81 | 82 | void warpingImageByHomography(const CImg &src, CImg &dst, Parameters H, float offset_x, float offset_y) { 83 | for (int dst_x = 0; dst_x < dst.width(); dst_x++) { 84 | for (int dst_y = 0; dst_y < dst.height(); dst_y++) { 85 | int src_x = getXAfterWarping(dst_x + offset_x, dst_y + offset_y, H); 86 | int src_y = getYAfterWarping(dst_x + offset_x, dst_y + offset_y, H); 87 | 88 | if (src_x >= 0 && src_x < src.width() && src_y >= 0 && src_y < src.height()) { 89 | for (int k = 0; k < src.spectrum(); k++) { 90 | dst(dst_x, dst_y, k) = bilinear_interpolation(src, src_x, src_y, k); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | void movingImageByOffset(const CImg &src, CImg &dst, int offset_x, int offset_y) { 98 | for (int dst_x = 0; dst_x < dst.width(); dst_x++) { 99 | for (int dst_y = 0; dst_y < dst.height(); dst_y++) { 100 | int src_x = dst_x + offset_x; 101 | int src_y = dst_y + offset_y; 102 | 103 | if (src_x >= 0 && src_x < src.width() && src_y >= 0 && src_y < src.height()) { 104 | for (int k = 0; k < src.spectrum(); k++) { 105 | dst(dst_x, dst_y, k) = src(src_x, src_y, k); 106 | } 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /ImageStitching/Blend.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Blend.h" 3 | #include 4 | #include 5 | 6 | bool isEmpty(const CImg &img, int x, int y) { 7 | assert(img.spectrum() == 3); 8 | 9 | return (img(x, y, 0) == 0 && img(x, y, 1) == 0 && img(x, y, 2) == 0); 10 | } 11 | 12 | CImg blendTwoImages(const CImg &a, const CImg &b) { 13 | assert(a.width() == b.width() && a.height() == b.height() && a.spectrum() == b.spectrum()); 14 | 15 | // Find the center point of a and overlapping part. 16 | double sum_a_x = 0; 17 | double sum_a_y = 0; 18 | int a_n = 0; 19 | //double sum_b_x = 0; 20 | //double sum_b_y = 0; 21 | //int b_n = 0; 22 | double sum_overlap_x = 0; 23 | double sum_overlap_y = 0; 24 | int overlap_n = 0; 25 | if (a.width() > a.height()) { 26 | for (int x = 0; x < a.width(); x++) { 27 | if (!isEmpty(a, x, a.height() / 2)) { 28 | sum_a_x += x; 29 | a_n++; 30 | } 31 | 32 | //if (!isEmpty(b, x, b.height() / 2)) { 33 | // sum_b_x += x; 34 | // b_n++; 35 | //} 36 | 37 | if (!isEmpty(a, x, a.height() / 2) && !isEmpty(b, x, a.height() / 2)) { 38 | sum_overlap_x += x; 39 | overlap_n++; 40 | } 41 | } 42 | } 43 | else { 44 | for (int y = 0; y < a.height(); y++) { 45 | if (!isEmpty(a, a.width() / 2, y)) { 46 | sum_a_y += y; 47 | a_n++; 48 | } 49 | 50 | if (!isEmpty(a, a.width() / 2, y) && !isEmpty(b, b.width() / 2, y)) { 51 | sum_overlap_y += y; 52 | overlap_n++; 53 | } 54 | } 55 | } 56 | 57 | int min_len = (a.width() < a.height()) ? a.width() : a.height(); 58 | 59 | int n_level = floor(log2(min_len)); 60 | 61 | vector > a_pyramid(n_level); 62 | vector > b_pyramid(n_level); 63 | vector > mask(n_level); 64 | 65 | // Initialize the base. 66 | a_pyramid[0] = a; 67 | b_pyramid[0] = b; 68 | mask[0] = CImg(a.width(), a.height(), 1, 1, 0); 69 | 70 | if (a.width() > a.height()) { 71 | if (sum_a_x / a_n < sum_overlap_x / overlap_n) { 72 | for (int x = 0; x < sum_overlap_x / overlap_n; x++) { 73 | for (int y = 0; y < a.height(); y++) { 74 | mask[0](x, y) = 1; 75 | } 76 | } 77 | } 78 | else { 79 | for (int x = sum_overlap_x / overlap_n + 1; x < a.width(); x++) { 80 | for (int y = 0; y < a.height(); y++) { 81 | mask[0](x, y) = 1; 82 | } 83 | } 84 | } 85 | } 86 | else { 87 | if (sum_a_y / a_n < sum_overlap_y / overlap_n) { 88 | for (int x = 0; x < a.width(); x++) { 89 | for (int y = 0; y < sum_overlap_y / overlap_n; y++) { 90 | mask[0](x, y) = 1; 91 | } 92 | } 93 | } 94 | else { 95 | for (int x = 0; x < a.width(); x++) { 96 | for (int y = sum_overlap_y / overlap_n; y < a.height(); y++) { 97 | mask[0](x, y) = 1; 98 | } 99 | } 100 | } 101 | } 102 | 103 | // Down sampling a and b, building Gaussian pyramids. 104 | for (int i = 1; i < n_level; i++) { 105 | a_pyramid[i] = a_pyramid[i - 1].get_blur(2).get_resize(a_pyramid[i - 1].width() / 2, a_pyramid[i - 1].height() / 2, 1, a_pyramid[i - 1].spectrum(), 3); 106 | b_pyramid[i] = b_pyramid[i - 1].get_blur(2).get_resize(b_pyramid[i - 1].width() / 2, b_pyramid[i - 1].height() / 2, 1, b_pyramid[i - 1].spectrum(), 3); 107 | 108 | mask[i] = mask[i - 1].get_blur(2).get_resize(mask[i - 1].width() / 2, mask[i - 1].height() / 2, 1, mask[i - 1].spectrum(), 3); 109 | } 110 | 111 | // Building Laplacian pyramids. 112 | for (int i = 0; i < n_level - 1; i++) { 113 | a_pyramid[i] = a_pyramid[i] - a_pyramid[i + 1].get_resize(a_pyramid[i].width(), a_pyramid[i].height(), 1, a_pyramid[i].spectrum(), 3); 114 | b_pyramid[i] = b_pyramid[i] - b_pyramid[i + 1].get_resize(b_pyramid[i].width(), b_pyramid[i].height(), 1, b_pyramid[i].spectrum(), 3); 115 | } 116 | 117 | vector > blend_pyramid(n_level); 118 | 119 | for (int i = 0; i < n_level; i++) { 120 | blend_pyramid[i] = CImg(a_pyramid[i].width(), a_pyramid[i].height(), 1, a_pyramid[i].spectrum(), 0); 121 | 122 | for (int x = 0; x < blend_pyramid[i].width(); x++) { 123 | for (int y = 0; y < blend_pyramid[i].height(); y++) { 124 | for (int k = 0; k < blend_pyramid[i].spectrum(); k++) { 125 | blend_pyramid[i](x, y, k) = a_pyramid[i](x, y, k) * mask[i](x, y) + b_pyramid[i](x, y, k) * (1.0 - mask[i](x, y)); 126 | 127 | } 128 | } 129 | } 130 | } 131 | 132 | CImg res = blend_pyramid[n_level - 1]; 133 | for (int i = n_level - 2; i >= 0; i--) { 134 | res.resize(blend_pyramid[i].width(), blend_pyramid[i].height(), 1, blend_pyramid[i].spectrum(), 3); 135 | 136 | for (int x = 0; x < blend_pyramid[i].width(); x++) { 137 | for (int y = 0; y < blend_pyramid[i].height(); y++) { 138 | for (int k = 0; k < blend_pyramid[i].spectrum(); k++) { 139 | float t = res(x, y, k) + blend_pyramid[i](x, y, k); 140 | 141 | if (t > 255) { 142 | t = 255; 143 | } 144 | else if (t < 0) { 145 | t = 0; 146 | } 147 | 148 | res(x, y, k) = t; 149 | } 150 | } 151 | } 152 | } 153 | 154 | return res; 155 | } 156 | -------------------------------------------------------------------------------- /ImageStitching/ImageStitching.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {2249AB88-07F1-4BDD-9054-195024D764C9} 15 | Win32Proj 16 | ImageStitching 17 | 18 | 19 | 20 | Application 21 | true 22 | v120 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v120 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | Use 51 | Level3 52 | Disabled 53 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 54 | true 55 | D:\vlfeat;%(AdditionalIncludeDirectories) 56 | 57 | 58 | Console 59 | true 60 | D:\vlfeat\bin\win32;%(AdditionalLibraryDirectories) 61 | vl.lib;%(AdditionalDependencies) 62 | 63 | 64 | 65 | 66 | Level3 67 | Use 68 | MaxSpeed 69 | true 70 | true 71 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 72 | true 73 | D:\vlfeat;%(AdditionalIncludeDirectories) 74 | 75 | 76 | Console 77 | true 78 | true 79 | true 80 | D:\vlfeat\bin\win32;%(AdditionalLibraryDirectories) 81 | vl.lib;%(AdditionalDependencies) 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Create 110 | Create 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /ImageStitching/Stitching.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Stitching.h" 3 | #include "Projection.h" 4 | #include 5 | 6 | CImg get_gray_image(const CImg &srcImg) { 7 | if (srcImg.spectrum() == 1) { 8 | return srcImg; 9 | } 10 | int width = srcImg.width(); 11 | int height = srcImg.height(); 12 | int depth = srcImg.depth(); 13 | CImg grayImg(width, height, depth, 1); 14 | float r, g, b, gr; 15 | for (int i = 0; i < width; i++) { 16 | for (int j = 0; j < height; j++) { 17 | r = srcImg(i, j, 0, 0); 18 | g = srcImg(i, j, 0, 1); 19 | b = srcImg(i, j, 0, 2); 20 | gr = 0.299*(r)+0.587*(g)+0.114*(b); 21 | grayImg(i, j, 0, 0) = gr; 22 | } 23 | } 24 | return grayImg; 25 | } 26 | 27 | int getMiddleIndex(vector> matching_index) { 28 | int one_side = 0; 29 | 30 | for (int i = 0; i < matching_index.size(); i++) { 31 | if (matching_index[i].size() == 1) { 32 | one_side = i; 33 | break; 34 | } 35 | } 36 | 37 | int middle_index = one_side; 38 | int pre_middle_index = -1; 39 | int n = matching_index.size() / 2; 40 | 41 | while (n--) { 42 | for (int i = 0; i < matching_index[middle_index].size(); i++) { 43 | if (matching_index[middle_index][i] != pre_middle_index) { 44 | pre_middle_index = middle_index; 45 | middle_index = matching_index[middle_index][i]; 46 | 47 | break; 48 | } 49 | } 50 | } 51 | 52 | return middle_index; 53 | } 54 | 55 | void updateFeaturesByHomography(map, VlSiftKeypoint> &feature, Parameters H, float offset_x, float offset_y) { 56 | for (auto iter = feature.begin(); iter != feature.end(); iter++) { 57 | float cur_x = iter->second.x; 58 | float cur_y = iter->second.y; 59 | iter->second.x = getXAfterWarping(cur_x, cur_y, H) - offset_x; 60 | iter->second.y = getYAfterWarping(cur_x, cur_y, H) - offset_y; 61 | iter->second.ix = int(iter->second.x); 62 | iter->second.iy = int(iter->second.y); 63 | } 64 | } 65 | 66 | void updateFeaturesByOffset(map, VlSiftKeypoint> &feature, int offset_x, int offset_y) { 67 | for (auto iter = feature.begin(); iter != feature.end(); iter++) { 68 | iter->second.x -= offset_x; 69 | iter->second.y -= offset_y; 70 | iter->second.ix = int(iter->second.x); 71 | iter->second.iy = int(iter->second.y); 72 | } 73 | } 74 | 75 | CImg stitching(vector> &src_imgs) { 76 | // Used to save each image's features and corresponding coordinates. 77 | vector, VlSiftKeypoint>> features(src_imgs.size()); 78 | 79 | for (int i = 0; i < src_imgs.size(); i++) { 80 | cout << "Preprocessing input image " << i << " ..." << endl; 81 | 82 | cout << "Cylinder projection started." << endl; 83 | src_imgs[i] = cylinderProjection(src_imgs[i]); 84 | cout << "Cylinder projection finished." << endl; 85 | 86 | CImg gray = get_gray_image(src_imgs[i]); 87 | 88 | cout << "Extracting SIFT feature started." << endl; 89 | features[i] = getFeatureFromImage(gray); 90 | cout << "Extracting SIFT feature finished." << endl; 91 | 92 | cout << "Preprocessing input image " << i << " finished." << endl << endl; 93 | } 94 | 95 | // Used to record the image's adjacent images. 96 | bool need_stitching[MAX_STITCHING_NUM][MAX_STITCHING_NUM] = { false }; 97 | 98 | // Used to record each image's adjacent images. 99 | vector> matching_index(src_imgs.size()); 100 | 101 | cout << "Finding adjacent images ..." << endl; 102 | 103 | for (int i = 0; i < src_imgs.size(); i++) { 104 | for (int j = 0; j < src_imgs.size(); j++) { 105 | if (i == j) 106 | continue; 107 | 108 | vector pairs = getPointPairsFromFeature(features[i], features[j]); 109 | if (pairs.size() >= 20) { 110 | need_stitching[i][j] = true; 111 | 112 | cout << "Image " << i << " and " << j << " are adjacent." << endl; 113 | matching_index[i].push_back(j); 114 | } 115 | 116 | } 117 | } 118 | 119 | cout << endl; 120 | 121 | cout << "Stitching begins." << endl << endl; 122 | // Stitching begins. 123 | 124 | // Stitching from middle to have better visual effect. 125 | int start_index = getMiddleIndex(matching_index); 126 | //int start_index = 3; 127 | 128 | // Used to record the previous stitched image. 129 | int prev_dst_index = start_index; 130 | 131 | queue unstitched_index; 132 | unstitched_index.push(start_index); 133 | 134 | CImg cur_stitched_img = src_imgs[start_index]; 135 | 136 | while (!unstitched_index.empty()) { 137 | int src_index = unstitched_index.front(); 138 | unstitched_index.pop(); 139 | 140 | for (int j = matching_index[src_index].size() - 1; j >= 0; j--) { 141 | int dst_index = matching_index[src_index][j]; 142 | 143 | if (need_stitching[src_index][dst_index] == false) { 144 | continue; 145 | } 146 | else { 147 | need_stitching[src_index][dst_index] = false; 148 | need_stitching[dst_index][src_index] = false; 149 | unstitched_index.push(dst_index); 150 | } 151 | 152 | // Matching features using best-bin kd-tree. 153 | vector src_to_dst_pairs = getPointPairsFromFeature(features[src_index], features[dst_index]); 154 | vector dst_to_src_pairs = getPointPairsFromFeature(features[dst_index], features[src_index]); 155 | 156 | if (src_to_dst_pairs.size() > dst_to_src_pairs.size()) { 157 | dst_to_src_pairs.clear(); 158 | for (int i = 0; i < src_to_dst_pairs.size(); i++) { 159 | point_pair temp(src_to_dst_pairs[i].b, src_to_dst_pairs[i].a); 160 | dst_to_src_pairs.push_back(temp); 161 | } 162 | } 163 | else { 164 | src_to_dst_pairs.clear(); 165 | for (int i = 0; i < dst_to_src_pairs.size(); i++) { 166 | point_pair temp(dst_to_src_pairs[i].b, dst_to_src_pairs[i].a); 167 | src_to_dst_pairs.push_back(temp); 168 | } 169 | } 170 | 171 | // Finding homography by RANSAC. 172 | Parameters forward_H = RANSAC(dst_to_src_pairs); 173 | Parameters backward_H = RANSAC(src_to_dst_pairs); 174 | 175 | // Calculate the size of the image after stitching. 176 | float min_x = getMinXAfterWarping(src_imgs[dst_index], forward_H); 177 | min_x = (min_x < 0) ? min_x : 0; 178 | float min_y = getMinYAfterWarping(src_imgs[dst_index], forward_H); 179 | min_y = (min_y < 0) ? min_y : 0; 180 | float max_x = getMaxXAfterWarping(src_imgs[dst_index], forward_H); 181 | max_x = (max_x >= cur_stitched_img.width()) ? max_x : cur_stitched_img.width(); 182 | float max_y = getMaxYAferWarping(src_imgs[dst_index], forward_H); 183 | max_y = (max_y >= cur_stitched_img.height()) ? max_y : cur_stitched_img.height(); 184 | 185 | int new_width = ceil(max_x - min_x); 186 | int new_height = ceil(max_y - min_y); 187 | 188 | CImg a(new_width, new_height, 1, src_imgs[dst_index].spectrum(), 0); 189 | CImg b(new_width, new_height, 1, src_imgs[dst_index].spectrum(), 0); 190 | 191 | // Warping the dst image into the coordinate space of currently stitched image. 192 | warpingImageByHomography(src_imgs[dst_index], a, backward_H, min_x, min_y); 193 | // Move stitched image according to min_x and min_y as translation. 194 | movingImageByOffset(cur_stitched_img, b, min_x, min_y); 195 | 196 | // Update features coordinates according to homography. 197 | updateFeaturesByHomography(features[dst_index], forward_H, min_x, min_y); 198 | // Update features coordinates according to min_x and min_y. 199 | updateFeaturesByOffset(features[prev_dst_index], min_x, min_y); 200 | 201 | // Blending two images. 202 | cur_stitched_img = blendTwoImages(a, b); 203 | prev_dst_index = dst_index; 204 | 205 | cout << "Image " << dst_index << " has stitched." << endl << endl; 206 | } 207 | } 208 | 209 | return cur_stitched_img; 210 | } -------------------------------------------------------------------------------- /ImageStitching/Match.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Match.h" 3 | #include "Feature.h" 4 | #include 5 | 6 | float getXAfterWarping(float x, float y, Parameters H) { 7 | return H.c1 * x + H.c2 * y + H.c3 * x * y + H.c4; 8 | } 9 | 10 | float getYAfterWarping(float x, float y, Parameters H) { 11 | return H.c5 * x + H.c6 * y + H.c7 * x * y + H.c8; 12 | } 13 | 14 | vector getPointPairsFromFeature(const map, VlSiftKeypoint> &feature_a, const map, VlSiftKeypoint> &feature_b) { 15 | VlKDForest* forest = vl_kdforest_new(VL_TYPE_FLOAT, 128, 1, VlDistanceL1); 16 | 17 | float *data = new float[128 * feature_a.size()]; 18 | int k = 0; 19 | for (auto it = feature_a.begin(); it != feature_a.end(); it++) { 20 | const vector &descriptors = it->first; 21 | assert(descriptors.size() == 128); 22 | 23 | for (int i = 0; i < 128; i++) { 24 | data[i + 128 * k] = descriptors[i]; 25 | } 26 | k++; 27 | } 28 | 29 | vl_kdforest_build(forest, feature_a.size(), data); 30 | 31 | vector res; 32 | 33 | VlKDForestSearcher* searcher = vl_kdforest_new_searcher(forest); 34 | VlKDForestNeighbor neighbours[2]; 35 | 36 | for (auto it = feature_b.begin(); it != feature_b.end(); it++){ 37 | float *temp_data = new float[128]; 38 | 39 | for (int i = 0; i < 128; i++) { 40 | temp_data[i] = (it->first)[i]; 41 | } 42 | 43 | int nvisited = vl_kdforestsearcher_query(searcher, neighbours, 2, temp_data); 44 | 45 | float ratio = neighbours[0].distance / neighbours[1].distance; 46 | if (ratio < 0.5) { 47 | vector des(128); 48 | for (int j = 0; j < 128; j++) { 49 | des[j] = data[j + neighbours[0].index * 128]; 50 | } 51 | 52 | VlSiftKeypoint left = feature_a.find(des)->second; 53 | VlSiftKeypoint right = it->second; 54 | res.push_back(point_pair(left, right)); 55 | } 56 | 57 | delete[] temp_data; 58 | temp_data = NULL; 59 | } 60 | 61 | vl_kdforestsearcher_delete(searcher); 62 | vl_kdforest_delete(forest); 63 | 64 | delete[] data; 65 | data = NULL; 66 | 67 | return res; 68 | } 69 | 70 | Parameters getHomographyFromPoingPairs(const vector &pair) { 71 | assert(pair.size() == 4); 72 | 73 | float u0 = pair[0].a.x, v0 = pair[0].a.y; 74 | float u1 = pair[1].a.x, v1 = pair[1].a.y; 75 | float u2 = pair[2].a.x, v2 = pair[2].a.y; 76 | float u3 = pair[3].a.x, v3 = pair[3].a.y; 77 | 78 | float x0 = pair[0].b.x, y0 = pair[0].b.y; 79 | float x1 = pair[1].b.x, y1 = pair[1].b.y; 80 | float x2 = pair[2].b.x, y2 = pair[2].b.y; 81 | float x3 = pair[3].b.x, y3 = pair[3].b.y; 82 | 83 | float c1, c2, c3, c4, c5, c6, c7, c8; 84 | 85 | c1 = -(u0*v0*v1*x2 - u0*v0*v2*x1 - u0*v0*v1*x3 + u0*v0*v3*x1 - u1*v0*v1*x2 + u1*v1*v2*x0 + u0*v0*v2*x3 - u0*v0*v3*x2 + u1*v0*v1*x3 - u1*v1*v3*x0 + u2*v0*v2*x1 - u2*v1*v2*x0 86 | - u1*v1*v2*x3 + u1*v1*v3*x2 - u2*v0*v2*x3 + u2*v2*v3*x0 - u3*v0*v3*x1 + u3*v1*v3*x0 + u2*v1*v2*x3 - u2*v2*v3*x1 + u3*v0*v3*x2 - u3*v2*v3*x0 - u3*v1*v3*x2 + u3*v2*v3*x1) 87 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 88 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 89 | 90 | c2 = (u0*u1*v0*x2 - u0*u2*v0*x1 - u0*u1*v0*x3 - u0*u1*v1*x2 + u0*u3*v0*x1 + u1*u2*v1*x0 + u0*u1*v1*x3 + u0*u2*v0*x3 + u0*u2*v2*x1 - u0*u3*v0*x2 - u1*u2*v2*x0 - u1*u3*v1*x0 91 | - u0*u2*v2*x3 - u0*u3*v3*x1 - u1*u2*v1*x3 + u1*u3*v1*x2 + u1*u3*v3*x0 + u2*u3*v2*x0 + u0*u3*v3*x2 + u1*u2*v2*x3 - u2*u3*v2*x1 - u2*u3*v3*x0 - u1*u3*v3*x2 + u2*u3*v3*x1) 92 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 93 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 94 | 95 | c3 = (u0*v1*x2 - u0*v2*x1 - u1*v0*x2 + u1*v2*x0 + u2*v0*x1 - u2*v1*x0 - u0*v1*x3 + u0*v3*x1 + u1*v0*x3 - u1*v3*x0 - u3*v0*x1 + u3*v1*x0 96 | + u0*v2*x3 - u0*v3*x2 - u2*v0*x3 + u2*v3*x0 + u3*v0*x2 - u3*v2*x0 - u1*v2*x3 + u1*v3*x2 + u2*v1*x3 - u2*v3*x1 - u3*v1*x2 + u3*v2*x1) 97 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 98 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 99 | 100 | c4 = (u0*u1*v0*v2*x3 - u0*u1*v0*v3*x2 - u0*u2*v0*v1*x3 + u0*u2*v0*v3*x1 + u0*u3*v0*v1*x2 - u0*u3*v0*v2*x1 - u0*u1*v1*v2*x3 + u0*u1*v1*v3*x2 + u1*u2*v0*v1*x3 - u1*u2*v1*v3*x0 - u1*u3*v0*v1*x2 + u1*u3*v1*v2*x0 101 | + u0*u2*v1*v2*x3 - u0*u2*v2*v3*x1 - u1*u2*v0*v2*x3 + u1*u2*v2*v3*x0 + u2*u3*v0*v2*x1 - u2*u3*v1*v2*x0 - u0*u3*v1*v3*x2 + u0*u3*v2*v3*x1 + u1*u3*v0*v3*x2 - u1*u3*v2*v3*x0 - u2*u3*v0*v3*x1 + u2*u3*v1*v3*x0) 102 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 103 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 104 | 105 | c5 = -(u0*v0*v1*y2 - u0*v0*v2*y1 - u0*v0*v1*y3 + u0*v0*v3*y1 - u1*v0*v1*y2 + u1*v1*v2*y0 + u0*v0*v2*y3 - u0*v0*v3*y2 + u1*v0*v1*y3 - u1*v1*v3*y0 + u2*v0*v2*y1 - u2*v1*v2*y0 106 | - u1*v1*v2*y3 + u1*v1*v3*y2 - u2*v0*v2*y3 + u2*v2*v3*y0 - u3*v0*v3*y1 + u3*v1*v3*y0 + u2*v1*v2*y3 - u2*v2*v3*y1 + u3*v0*v3*y2 - u3*v2*v3*y0 - u3*v1*v3*y2 + u3*v2*v3*y1) 107 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 108 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 109 | 110 | c6 = (u0*u1*v0*y2 - u0*u2*v0*y1 - u0*u1*v0*y3 - u0*u1*v1*y2 + u0*u3*v0*y1 + u1*u2*v1*y0 + u0*u1*v1*y3 + u0*u2*v0*y3 + u0*u2*v2*y1 - u0*u3*v0*y2 - u1*u2*v2*y0 - u1*u3*v1*y0 111 | - u0*u2*v2*y3 - u0*u3*v3*y1 - u1*u2*v1*y3 + u1*u3*v1*y2 + u1*u3*v3*y0 + u2*u3*v2*y0 + u0*u3*v3*y2 + u1*u2*v2*y3 - u2*u3*v2*y1 - u2*u3*v3*y0 - u1*u3*v3*y2 + u2*u3*v3*y1) 112 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 113 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 114 | 115 | c7 = (u0*v1*y2 - u0*v2*y1 - u1*v0*y2 + u1*v2*y0 + u2*v0*y1 - u2*v1*y0 - u0*v1*y3 + u0*v3*y1 + u1*v0*y3 - u1*v3*y0 - u3*v0*y1 + u3*v1*y0 116 | + u0*v2*y3 - u0*v3*y2 - u2*v0*y3 + u2*v3*y0 + u3*v0*y2 - u3*v2*y0 - u1*v2*y3 + u1*v3*y2 + u2*v1*y3 - u2*v3*y1 - u3*v1*y2 + u3*v2*y1) 117 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 118 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 119 | 120 | c8 = (u0*u1*v0*v2*y3 - u0*u1*v0*v3*y2 - u0*u2*v0*v1*y3 + u0*u2*v0*v3*y1 + u0*u3*v0*v1*y2 - u0*u3*v0*v2*y1 - u0*u1*v1*v2*y3 + u0*u1*v1*v3*y2 + u1*u2*v0*v1*y3 - u1*u2*v1*v3*y0 - u1*u3*v0*v1*y2 + u1*u3*v1*v2*y0 121 | + u0*u2*v1*v2*y3 - u0*u2*v2*v3*y1 - u1*u2*v0*v2*y3 + u1*u2*v2*v3*y0 + u2*u3*v0*v2*y1 - u2*u3*v1*v2*y0 - u0*u3*v1*v3*y2 + u0*u3*v2*v3*y1 + u1*u3*v0*v3*y2 - u1*u3*v2*v3*y0 - u2*u3*v0*v3*y1 + u2*u3*v1*v3*y0) 122 | / (u0*u1*v0*v2 - u0*u2*v0*v1 - u0*u1*v0*v3 - u0*u1*v1*v2 + u0*u3*v0*v1 + u1*u2*v0*v1 + u0*u1*v1*v3 + u0*u2*v0*v3 + u0*u2*v1*v2 - u0*u3*v0*v2 - u1*u2*v0*v2 - u1*u3*v0*v1 123 | - u0*u2*v2*v3 - u0*u3*v1*v3 - u1*u2*v1*v3 + u1*u3*v0*v3 + u1*u3*v1*v2 + u2*u3*v0*v2 + u0*u3*v2*v3 + u1*u2*v2*v3 - u2*u3*v0*v3 - u2*u3*v1*v2 - u1*u3*v2*v3 + u2*u3*v1*v3); 124 | 125 | return Parameters(c1, c2, c3, c4, c5, c6, c7, c8); 126 | } 127 | 128 | int numberOfIterations(float p, float w, int num) { 129 | return ceil(log(1 - p) / log(1 - pow(w, num))); 130 | } 131 | 132 | int random(int min, int max) { 133 | assert(max > min); 134 | 135 | return rand() % (max - min + 1) + min; 136 | } 137 | 138 | vector getIndexsOfInliner(const vector &pairs, Parameters H, set seleted_indexs) { 139 | vector inliner_indexs; 140 | 141 | for (int i = 0; i < pairs.size(); i++) { 142 | if (seleted_indexs.find(i) != seleted_indexs.end()) { 143 | continue; 144 | } 145 | 146 | float real_x = pairs[i].b.x; 147 | float real_y = pairs[i].b.y; 148 | 149 | float x = getXAfterWarping(pairs[i].a.x, pairs[i].a.y, H); 150 | float y = getYAfterWarping(pairs[i].a.x, pairs[i].a.y, H); 151 | 152 | float distance = sqrt((x - real_x) * (x - real_x) + (y - real_y) * (y - real_y)); 153 | if (distance < RANSAC_THRESHOLD) { 154 | inliner_indexs.push_back(i); 155 | } 156 | } 157 | 158 | return inliner_indexs; 159 | } 160 | 161 | Parameters leastSquaresSolution(const vector pairs, vector inliner_indexs) { 162 | int calc_size = inliner_indexs.size(); 163 | 164 | CImg A(4, calc_size, 1, 1, 0); 165 | CImg b(1, calc_size, 1, 1, 0); 166 | 167 | for (int i = 0; i < calc_size; i++) { 168 | int cur_index = inliner_indexs[i]; 169 | 170 | A(0, i) = pairs[cur_index].a.x; 171 | A(1, i) = pairs[cur_index].a.y; 172 | A(2, i) = pairs[cur_index].a.x * pairs[cur_index].a.y; 173 | A(3, i) = 1; 174 | 175 | b(0, i) = pairs[cur_index].b.x; 176 | } 177 | 178 | CImg x1 = b.get_solve(A); 179 | 180 | for (int i = 0; i < calc_size; i++) { 181 | int cur_index = inliner_indexs[i]; 182 | 183 | b(0, i) = pairs[cur_index].b.y; 184 | } 185 | 186 | CImg x2 = b.get_solve(A); 187 | 188 | return Parameters(x1(0, 0), x1(0, 1), x1(0, 2), x1(0, 3), x2(0, 0), x2(0, 1), x2(0, 2), x2(0, 3)); 189 | 190 | } 191 | 192 | Parameters RANSAC(const vector &pairs) { 193 | assert(pairs.size() >= NUM_OF_PAIR); 194 | 195 | srand(time(0)); 196 | 197 | int iterations = numberOfIterations(CONFIDENCE, INLINER_RATIO, NUM_OF_PAIR); 198 | 199 | vector max_inliner_indexs; 200 | 201 | while (iterations--) { 202 | vector random_pairs; 203 | set seleted_indexs; 204 | 205 | for (int i = 0; i < NUM_OF_PAIR; i++) { 206 | int index = random(0, pairs.size() - 1); 207 | while (seleted_indexs.find(index) != seleted_indexs.end()) { 208 | index = random(0, pairs.size() - 1); 209 | } 210 | seleted_indexs.insert(index); 211 | 212 | random_pairs.push_back(pairs[index]); 213 | } 214 | 215 | Parameters H = getHomographyFromPoingPairs(random_pairs); 216 | 217 | vector cur_inliner_indexs = getIndexsOfInliner(pairs, H, seleted_indexs); 218 | if (cur_inliner_indexs.size() > max_inliner_indexs.size()) { 219 | max_inliner_indexs = cur_inliner_indexs; 220 | } 221 | } 222 | 223 | Parameters t = leastSquaresSolution(pairs, max_inliner_indexs); 224 | 225 | return t; 226 | 227 | } --------------------------------------------------------------------------------