├── .gitignore ├── NISwGSP_line.sln ├── NISwGSP_line ├── Configure.cpp ├── Configure.h ├── Debugger │ ├── ColorMap.cpp │ ├── ColorMap.h │ ├── ErrorController.cpp │ ├── ErrorController.h │ ├── ImageDebugger.cpp │ ├── ImageDebugger.h │ ├── TimeCalculator.cpp │ └── TimeCalculator.h ├── Feature │ ├── FeatureController.cpp │ ├── FeatureController.h │ ├── ImageData.cpp │ ├── ImageData.h │ ├── MultiImages.cpp │ └── MultiImages.h ├── Mesh │ ├── Mesh2D.cpp │ ├── Mesh2D.h │ ├── MeshGrid.cpp │ ├── MeshGrid.h │ ├── MeshOptimization.cpp │ └── MeshOptimization.h ├── NISwGSP_line.vcxproj ├── NISwGSP_line.vcxproj.filters ├── NISwGSP_line.vcxproj.user ├── Stitching │ ├── APAP_Stitching.cpp │ ├── APAP_Stitching.h │ ├── NISwGSP_Stitching.cpp │ ├── NISwGSP_Stitching.h │ ├── Parameter.cpp │ └── Parameter.h ├── Util │ ├── Blending.cpp │ ├── Blending.h │ ├── InputParser.cpp │ ├── InputParser.h │ ├── Statistics.cpp │ ├── Statistics.h │ ├── Transform.cpp │ └── Transform.h └── main.cpp ├── README.md └── readme.docx /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /NISwGSP_line.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.1022 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NISwGSP_line", "NISwGSP_line\NISwGSP_line.vcxproj", "{0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Debug|x64.ActiveCfg = Debug|x64 17 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Debug|x64.Build.0 = Debug|x64 18 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Debug|x86.ActiveCfg = Debug|Win32 19 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Debug|x86.Build.0 = Debug|Win32 20 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Release|x64.ActiveCfg = Release|x64 21 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Release|x64.Build.0 = Release|x64 22 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Release|x86.ActiveCfg = Release|Win32 23 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {6437E64B-8489-4B51-BCA1-BA6AA3CA2327} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /NISwGSP_line/Configure.cpp: -------------------------------------------------------------------------------- 1 | #include "Configure.h" 2 | 3 | bool LINE_INTERACTIVE = false; -------------------------------------------------------------------------------- /NISwGSP_line/Configure.h: -------------------------------------------------------------------------------- 1 | // 2 | // Configure.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Configure__ 10 | #define __UglyMan_Stitiching__Configure__ 11 | 12 | #include "./Debugger/ErrorController.h" 13 | #include "./Debugger/TimeCalculator.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | using namespace std; 23 | 24 | #define MAXFLOAT (std::numeric_limits::max)() 25 | 26 | template 27 | unique_ptr make_unique(Args&&... args) { 28 | return unique_ptr(new T(forward(args)...)); 29 | } 30 | 31 | #include "opencv2/core/core.hpp" 32 | #include "opencv2/highgui/highgui.hpp" 33 | #include "opencv2/imgproc/imgproc.hpp" 34 | //using namespace cv; 35 | 36 | 37 | 38 | #include 39 | #include 40 | using namespace Eigen; 41 | 42 | #include "vl/sift.h" 43 | 44 | /******************************/ 45 | /******* you may adjust *******/ 46 | /******************************/ 47 | 48 | /* Line interactive */ 49 | extern bool LINE_INTERACTIVE; 50 | 51 | /*** data setting ***/ 52 | const int GRID_SIZE = 40; 53 | const int DOWN_SAMPLE_IMAGE_SIZE = 800 * 600; 54 | 55 | /*** APAP ***/ 56 | const double APAP_GAMMA = 0.0015; 57 | const double APAP_SIGMA = 8.5; 58 | 59 | /*** matching method ***/ 60 | const string FEATURE_RATIO_TEST_THRESHOLD_STRING = "15e-1"; 61 | const double FEATURE_RATIO_TEST_THRESHOLD = atof(FEATURE_RATIO_TEST_THRESHOLD_STRING.c_str()); 62 | 63 | /*** homography based ***/ 64 | const double GLOBAL_HOMOGRAPHY_MAX_INLIERS_DIST = 5.; 65 | const double LOCAL_HOMOGRAPHY_MAX_INLIERS_DIST = 3.; 66 | const int LOCAL_HOMOGRAPHY_MIN_FEATURES_COUNT = 40; 67 | 68 | /*** vlfeat sift ***/ 69 | const int SIFT_LEVEL_COUNT = 3; 70 | const int SIFT_MINIMUM_OCTAVE_INDEX = 0; 71 | const double SIFT_PEAK_THRESH = 0.; 72 | const double SIFT_EDGE_THRESH = 10.; 73 | 74 | /*** init feature ***/ 75 | const double INLIER_TOLERANT_STD_DISTANCE = 4.25; /* mean + 4.25 * std */ 76 | 77 | /*** sRANSAC ***/ 78 | const double GLOBAL_TRUE_PROBABILITY = 0.225; 79 | const double LOCAL_TRUE_PROBABILITY = 0.2; 80 | const double OPENCV_DEFAULT_CONFIDENCE = 0.995; 81 | 82 | /*** sparse linear system ***/ 83 | const double STRONG_CONSTRAINT = 1e4; 84 | 85 | /*** bundle adjustment ***/ 86 | const int CRITERIA_MAX_COUNT = 1000; 87 | const double CRITERIA_EPSILON = DBL_EPSILON; 88 | 89 | /*** 2D Method ***/ 90 | const double TOLERANT_ANGLE = 1.5; 91 | 92 | /*** 3D Method ***/ 93 | const double LAMBDA_GAMMA = 10; 94 | 95 | /******************************/ 96 | /******************************/ 97 | /******************************/ 98 | 99 | /*** rotation method setting ***/ 100 | enum GLOBAL_ROTATION_METHODS { 101 | GLOBAL_ROTATION_2D_METHOD = 0, GLOBAL_ROTATION_3D_METHOD, GLOBAL_ROTATION_METHODS_SIZE 102 | }; 103 | const string GLOBAL_ROTATION_METHODS_NAME[GLOBAL_ROTATION_METHODS_SIZE] = { 104 | "[2D]", "[3D]" 105 | }; 106 | 107 | /* blending method setting */ 108 | enum BLENDING_METHODS { 109 | BLEND_AVERAGE = 0, BLEND_LINEAR, BLEND_METHODS_SIZE 110 | }; 111 | const string BLENDING_METHODS_NAME[BLEND_METHODS_SIZE] = { 112 | "[BLEND_AVERAGE]", "[BLEND_LINEAR]" 113 | }; 114 | 115 | 116 | /* type */ 117 | typedef float FLOAT_TYPE; 118 | typedef cv::Size_ Size2; 119 | typedef cv::Point_ Point2; 120 | typedef cv::Rect_ Rect2; 121 | 122 | const int DIMENSION_2D = 2; 123 | const int HOMOGRAPHY_VARIABLES_COUNT = 9; 124 | 125 | /* AutoStitch */ 126 | enum AUTO_STITCH_WAVE_CORRECTS { WAVE_X = 0, WAVE_H, WAVE_V }; 127 | const AUTO_STITCH_WAVE_CORRECTS WAVE_CORRECT = WAVE_H; 128 | const string AUTO_STITCH_WAVE_CORRECTS_NAME[] = {"", "[WAVE_H]", "[WAVE_V]"}; 129 | 130 | #endif /* defined(__UglyMan_Stitiching__Configure__) */ 131 | -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ColorMap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ColorMap.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "ColorMap.h" 10 | 11 | Scalar getBlueToRedScalar(double v, double vmin, double vmax) { 12 | Scalar c = {1.0, 1.0, 1.0, 1.0}; // white 13 | double dv; 14 | 15 | if (v < vmin) 16 | v = vmin; 17 | if (v > vmax) 18 | v = vmax; 19 | dv = vmax - vmin; 20 | 21 | if (v < (vmin + 0.25 * dv)) { 22 | c[2] = 0; 23 | c[1] = 4 * (v - vmin) / dv; 24 | } else if (v < (vmin + 0.5 * dv)) { 25 | c[2] = 0; 26 | c[0] = 1 + 4 * (vmin + 0.25 * dv - v) / dv; 27 | } else if (v < (vmin + 0.75 * dv)) { 28 | c[2] = 4 * (v - vmin - 0.5 * dv) / dv; 29 | c[0] = 0; 30 | } else { 31 | c[1] = 1 + 4 * (vmin + 0.75 * dv - v) / dv; 32 | c[0] = 0; 33 | } 34 | return c; 35 | } 36 | -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ColorMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorMap.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__ColorMap__ 10 | #define __UglyMan_Stitiching__ColorMap__ 11 | 12 | #include "opencv2/core/core.hpp" 13 | #include "opencv2/highgui/highgui.hpp" 14 | #include "opencv2/imgproc/imgproc.hpp" 15 | 16 | using namespace cv; 17 | 18 | Scalar getBlueToRedScalar(double v, double vmin = -1, double vmax = 1); 19 | 20 | #endif /* defined(__UglyMan_Stitiching_ColorMap__) */ 21 | -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ErrorController.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorController.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "ErrorController.h" 10 | 11 | void printError(const string _error) { 12 | cerr << "[ERROR] " << _error << endl; 13 | } -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ErrorController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorController.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__ErrorController__ 10 | #define __UglyMan_Stitiching__ErrorController__ 11 | 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | void printError(const string _error); 18 | 19 | #endif /* defined(__UglyMan_Stitiching__ErrorController__) */ 20 | -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ImageDebugger.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDebugger.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "ImageDebugger.h" 10 | 11 | cv::Mat getImageOfFeaturePairs(const cv::Mat & img1, 12 | const cv::Mat & img2, 13 | const vector & f1, 14 | const vector & f2, 15 | const string & _type) { 16 | assert(f1.size() == f2.size()); 17 | assert(img1.type() == img2.type()); 18 | 19 | int CIRCLE_RADIUS = 5, CIRCLE_THICKNESS = 1; 20 | if (_type == "matching_points") { 21 | CIRCLE_RADIUS = 3, CIRCLE_THICKNESS = -1; 22 | } 23 | const int LINE_THICKNESS = 1; 24 | const int RGB_8U_RANGE = 256; 25 | 26 | cv::Mat result = cv::Mat::zeros(max(img1.rows, img2.rows), img1.cols + img2.cols, CV_8UC3); 27 | cv::Mat left (result, cv::Rect(0, 0, img1.cols, img1.rows)); 28 | cv::Mat right(result, cv::Rect(img1.cols, 0, img2.cols, img2.rows)); 29 | 30 | cv::Mat img1_8UC3, img2_8UC3; 31 | 32 | if(img1.type() == CV_8UC3) { 33 | img1_8UC3 = img1; 34 | img2_8UC3 = img2; 35 | } else { 36 | img1.convertTo(img1_8UC3, CV_8UC3); 37 | img2.convertTo(img2_8UC3, CV_8UC3); 38 | } 39 | img1_8UC3.copyTo(left); 40 | img2_8UC3.copyTo(right); 41 | 42 | for(int i = 0; i < f1.size(); ++i) { 43 | cv::Scalar color(rand() % RGB_8U_RANGE, rand() % RGB_8U_RANGE, rand() % RGB_8U_RANGE); 44 | circle(result, f1[i], CIRCLE_RADIUS, color, CIRCLE_THICKNESS, cv::LINE_AA); 45 | line(result, f1[i], f2[i] + Point2(img1.cols, 0), color, LINE_THICKNESS, cv::LINE_AA); 46 | circle(result, f2[i] + Point2(img1.cols, 0), CIRCLE_RADIUS, color, CIRCLE_THICKNESS, cv::LINE_AA); 47 | } 48 | return result; 49 | } -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/ImageDebugger.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDebugger.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__ImageDebugger__ 10 | #define __UglyMan_Stitiching__ImageDebugger__ 11 | 12 | #include "../Configure.h" 13 | 14 | cv::Mat getImageOfFeaturePairs(const cv::Mat & img1, 15 | const cv::Mat & img2, 16 | const vector & f1, 17 | const vector & f2, 18 | const string & _type = "feature_pairs"); 19 | 20 | #endif /* defined(__UglyMan_Stitiching__ImageDebugger__) */ 21 | -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/TimeCalculator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TimeCalculator.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "TimeCalculator.h" 10 | 11 | void TimeCalculator::start() { 12 | begin_time = omp_get_wtime(); 13 | } 14 | double TimeCalculator::end(const string output) const { 15 | double result = omp_get_wtime() - begin_time; 16 | if(output.empty() == false) { 17 | printf("[TIME] %.4fs : %s\n", result, output.c_str()); 18 | } 19 | return result; 20 | } -------------------------------------------------------------------------------- /NISwGSP_line/Debugger/TimeCalculator.h: -------------------------------------------------------------------------------- 1 | // 2 | // TimeCalculator.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__TimeCalculator__ 10 | #define __UglyMan_Stitiching__TimeCalculator__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include /* wall-clock time */ 16 | 17 | using namespace::std; 18 | 19 | class TimeCalculator { 20 | public: 21 | void start(); 22 | double end(const string output) const; 23 | private: 24 | double begin_time; 25 | }; 26 | 27 | #endif /* defined(__UglyMan_Stitiching__TimeCalculator__) */ 28 | -------------------------------------------------------------------------------- /NISwGSP_line/Feature/FeatureController.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FeatureController.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "FeatureController.h" 10 | 11 | void FeatureDescriptor::addDescriptor(const cv::Mat & _descriptor) { 12 | data.emplace_back(_descriptor); 13 | } 14 | 15 | double FeatureDescriptor::getDistance(const FeatureDescriptor & _descriptor1, 16 | const FeatureDescriptor & _descriptor2, 17 | const double _threshold) { 18 | const vector & data1 = _descriptor1.data; 19 | const vector & data2 = _descriptor2.data; 20 | double result = MAXFLOAT; 21 | for(int i = 0; i < data1.size(); ++i) { 22 | for(int j = 0; j < data2.size(); ++j) { 23 | double distance = 0; 24 | for(int k = 0; k < SIFT_DESCRIPTOR_DIM; ++k) { 25 | distance += ((data1[i].at(k) - data2[j].at(k)) * 26 | (data1[i].at(k) - data2[j].at(k))); 27 | 28 | /* at(k) == at(0, k) */ 29 | 30 | if(distance >= _threshold) { 31 | break; 32 | } 33 | } 34 | result = min(result, distance); 35 | } 36 | } 37 | return result; 38 | } 39 | 40 | void FeatureController::detect(const cv::Mat & _grey_img, 41 | vector & _feature_points, 42 | vector & _feature_descriptors) { 43 | #ifndef NDEBUG 44 | if(_feature_points.empty() == false) { 45 | _feature_points.clear(); 46 | printError("F(detect) feature points is not empty"); 47 | } 48 | if(_feature_descriptors.empty() == false) { 49 | _feature_descriptors.clear(); 50 | printError("F(detect) feature descriptors is not empty"); 51 | } 52 | #endif 53 | cv::Mat grey_img_float = _grey_img.clone(); 54 | grey_img_float.convertTo(grey_img_float, CV_32FC1); 55 | 56 | const int width = _grey_img.cols; 57 | const int height = _grey_img.rows; 58 | 59 | VlSiftFilt * vlSift = vl_sift_new(width, height, 60 | log2(min(width, height)), 61 | SIFT_LEVEL_COUNT, 62 | SIFT_MINIMUM_OCTAVE_INDEX); 63 | vl_sift_set_peak_thresh(vlSift, SIFT_PEAK_THRESH); 64 | vl_sift_set_edge_thresh(vlSift, SIFT_EDGE_THRESH); 65 | 66 | if(vl_sift_process_first_octave(vlSift, (vl_sift_pix const *) grey_img_float.data) != VL_ERR_EOF) { 67 | do { 68 | vl_sift_detect(vlSift); 69 | for(int i = 0; i < vlSift->nkeys; ++i) { 70 | double angles[4]; 71 | _feature_points.emplace_back(vlSift->keys[i].x, vlSift->keys[i].y); 72 | FeatureDescriptor descriptor; 73 | int angleCount = vl_sift_calc_keypoint_orientations(vlSift, angles, &vlSift->keys[i]); 74 | for(int j = 0; j < angleCount; ++j) { 75 | cv::Mat descriptor_array(1, SIFT_DESCRIPTOR_DIM, CV_32FC1); 76 | vl_sift_calc_keypoint_descriptor(vlSift, (vl_sift_pix *) descriptor_array.data, &vlSift->keys[i], angles[j]); 77 | descriptor.addDescriptor(descriptor_array); 78 | } 79 | _feature_descriptors.emplace_back(descriptor); 80 | } 81 | } while (vl_sift_process_next_octave(vlSift) != VL_ERR_EOF); 82 | } 83 | vl_sift_delete(vlSift); 84 | } -------------------------------------------------------------------------------- /NISwGSP_line/Feature/FeatureController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FeatureController.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__FeatureController__ 10 | #define __UglyMan_Stitiching__FeatureController__ 11 | 12 | #include "../Configure.h" 13 | 14 | const int SIFT_DESCRIPTOR_DIM = 128; 15 | 16 | class FeatureDescriptor { 17 | public: 18 | void addDescriptor(const cv::Mat & _descriptor); 19 | static double getDistance(const FeatureDescriptor & _descriptor1, 20 | const FeatureDescriptor & _descriptor2, 21 | const double _threshold); 22 | vector data; 23 | }; 24 | 25 | class FeatureController { 26 | public: 27 | static void detect(const cv::Mat & _grey_img, 28 | vector & _feature_points, 29 | vector & _feature_descriptors); 30 | 31 | private: 32 | }; 33 | 34 | #endif /* defined(__UglyMan_Stitiching__FeatureController__) */ 35 | -------------------------------------------------------------------------------- /NISwGSP_line/Feature/ImageData.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ImageData.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "ImageData.h" 10 | 11 | LineData::LineData(const Point2 & _a, 12 | const Point2 & _b, 13 | const double _width, 14 | const double _length) { 15 | data[0] = _a; 16 | data[1] = _b; 17 | width = _width; 18 | length = _length; 19 | } 20 | 21 | const bool LINES_FILTER_NONE(const double _data, 22 | const Statistics & _statistics) { 23 | return true; 24 | }; 25 | 26 | const bool LINES_FILTER_WIDTH (const double _data, 27 | const Statistics & _statistics) { 28 | return _data >= MAX(2.f, (_statistics.min + _statistics.mean) / 2.f); 29 | return true; 30 | }; 31 | 32 | const bool LINES_FILTER_LENGTH(const double _data, 33 | const Statistics & _statistics) { 34 | return _data >= MAX(10.f, _statistics.mean); 35 | return true; 36 | }; 37 | 38 | 39 | ImageData::ImageData(const string & _file_dir, 40 | const string & _file_full_name, 41 | LINES_FILTER_FUNC * _width_filter, 42 | LINES_FILTER_FUNC * _length_filter, 43 | const string * _debug_dir) { 44 | 45 | file_dir = &_file_dir; 46 | std::size_t found = _file_full_name.find_last_of("."); 47 | assert(found != std::string::npos); 48 | file_name = _file_full_name.substr(0, found); 49 | file_extension = _file_full_name.substr(found); 50 | debug_dir = _debug_dir; 51 | 52 | grey_img = cv::Mat(); 53 | 54 | width_filter = _width_filter; 55 | length_filter = _length_filter; 56 | 57 | img = cv::imread(*file_dir + file_name + file_extension); 58 | rgba_img = cv::imread(*file_dir + file_name + file_extension, cv::IMREAD_UNCHANGED); 59 | 60 | float original_img_size = img.rows * img.cols; 61 | 62 | if(original_img_size > DOWN_SAMPLE_IMAGE_SIZE) { 63 | float scale = sqrt(DOWN_SAMPLE_IMAGE_SIZE / original_img_size); 64 | resize(img, img, cv::Size(), scale, scale); 65 | resize(rgba_img, rgba_img, cv::Size(), scale, scale); 66 | } 67 | 68 | assert(rgba_img.channels() >= 3); 69 | if(rgba_img.channels() == 3) { 70 | cvtColor(rgba_img, rgba_img, CV_BGR2BGRA); 71 | } 72 | vector channels; 73 | split(rgba_img, channels); 74 | alpha_mask = channels[3]; 75 | mesh_2d = std::make_unique(img.cols, img.rows); 76 | } 77 | 78 | const cv::Mat & ImageData::getGreyImage() const { 79 | if(grey_img.empty()) { 80 | cvtColor(img, grey_img, CV_BGR2GRAY); 81 | } 82 | return grey_img; 83 | } 84 | 85 | const vector & ImageData::getLines() const { 86 | if(img_lines.empty()) { 87 | const cv::Mat & grey_image = getGreyImage(); 88 | cv::Ptr ls = cv::createLineSegmentDetector(cv::LSD_REFINE_STD); 89 | 90 | vector lines; 91 | vector lines_width, lines_prec, lines_nfa; 92 | ls->detect(grey_image, lines, lines_width, lines_prec, lines_nfa); 93 | 94 | vector lines_length; 95 | vector lines_points[2]; 96 | 97 | const int line_count = (int)lines.size(); 98 | 99 | lines_length.reserve(line_count); 100 | lines_points[0].reserve(line_count); 101 | lines_points[1].reserve(line_count); 102 | 103 | for(int i = 0; i < line_count; ++i) { 104 | lines_points[0].emplace_back(lines[i][0], lines[i][1]); 105 | lines_points[1].emplace_back(lines[i][2], lines[i][3]); 106 | lines_length.emplace_back(norm(lines_points[1][i] - lines_points[0][i])); 107 | } 108 | 109 | const Statistics width_statistics(lines_width), length_statistics(lines_length); 110 | for(int i = 0; i < line_count; ++i) { 111 | if( width_filter( lines_width[i], width_statistics) && 112 | length_filter(lines_length[i], length_statistics)) { 113 | img_lines.emplace_back(lines_points[0][i], 114 | lines_points[1][i], 115 | lines_width[i], 116 | lines_length[i]); 117 | } 118 | } 119 | #ifndef NDEBUG 120 | //vector draw_lines; 121 | //draw_lines.reserve(img_lines.size()); 122 | //for(int i = 0; i < img_lines.size(); ++i) { 123 | // draw_lines.emplace_back(img_lines[i].data[0].x, img_lines[i].data[0].y, 124 | // img_lines[i].data[1].x, img_lines[i].data[1].y); 125 | //} 126 | //cv::Mat canvas = cv::Mat::zeros(grey_image.rows, grey_image.cols, grey_image.type()); 127 | //ls->drawSegments(canvas, draw_lines); 128 | //imwrite(*debug_dir + "line-result-" + file_name + file_extension, canvas); 129 | #endif 130 | } 131 | return img_lines; 132 | } 133 | 134 | const vector & ImageData::getFeaturePoints() const { 135 | if(feature_points.empty()) { 136 | FeatureController::detect(getGreyImage(), feature_points, feature_descriptors); 137 | } 138 | return feature_points; 139 | } 140 | const vector & ImageData::getFeatureDescriptors() const { 141 | if(feature_descriptors.empty()) { 142 | FeatureController::detect(getGreyImage(), feature_points, feature_descriptors); 143 | } 144 | return feature_descriptors; 145 | } 146 | 147 | void ImageData::clear() { 148 | img.release(); 149 | grey_img.release(); 150 | img_lines.clear(); 151 | feature_points.clear(); 152 | feature_descriptors.clear(); 153 | } -------------------------------------------------------------------------------- /NISwGSP_line/Feature/ImageData.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageData.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__ImageData__ 10 | #define __UglyMan_Stitiching__ImageData__ 11 | 12 | #include 13 | #include "../Util/Statistics.h" 14 | #include "../Feature/FeatureController.h" 15 | #include "../Mesh/MeshGrid.h" 16 | 17 | class LineData { 18 | public: 19 | LineData(const Point2 & _a, 20 | const Point2 & _b, 21 | const double _width, 22 | const double _length); 23 | Point2 data[2]; 24 | double width, length; 25 | private: 26 | }; 27 | 28 | typedef const bool (LINES_FILTER_FUNC)(const double _data, \ 29 | const Statistics & _statistics); 30 | 31 | LINES_FILTER_FUNC LINES_FILTER_NONE; 32 | LINES_FILTER_FUNC LINES_FILTER_WIDTH; 33 | LINES_FILTER_FUNC LINES_FILTER_LENGTH; 34 | 35 | 36 | class ImageData { 37 | public: 38 | string file_name, file_extension; 39 | const string * file_dir, * debug_dir; 40 | ImageData(const string & _file_dir, 41 | const string & _file_full_name, 42 | LINES_FILTER_FUNC * _width_filter, 43 | LINES_FILTER_FUNC * _length_filter, 44 | const string * _debug_dir = NULL); 45 | 46 | const cv::Mat & getGreyImage() const; 47 | const vector & getLines() const; 48 | const vector & getFeaturePoints() const; 49 | const vector & getFeatureDescriptors() const; 50 | 51 | void clear(); 52 | 53 | cv::Mat img, rgba_img, alpha_mask; 54 | unique_ptr mesh_2d; 55 | 56 | private: 57 | LINES_FILTER_FUNC * width_filter, * length_filter; 58 | 59 | mutable cv::Mat grey_img; 60 | mutable vector img_lines; 61 | mutable vector feature_points; 62 | mutable vector feature_descriptors; 63 | }; 64 | 65 | #endif /* defined(__UglyMan_Stitiching__ImageData__) */ 66 | -------------------------------------------------------------------------------- /NISwGSP_line/Feature/MultiImages.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breadcake/NISwGSP_line/cb055bd4decde3e0ba9ccdf97e584728ea5aa2ae/NISwGSP_line/Feature/MultiImages.cpp -------------------------------------------------------------------------------- /NISwGSP_line/Feature/MultiImages.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiImages.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__MultiImages__ 10 | #define __UglyMan_Stitiching__MultiImages__ 11 | 12 | #include 13 | 14 | #include "../Configure.h" 15 | #include "../Stitching/Parameter.h" 16 | #include "../Feature/ImageData.h" 17 | #include "../Util/Statistics.h" 18 | #include "../Debugger/ImageDebugger.h" 19 | #include "../Stitching/APAP_Stitching.h" 20 | #include "../Util/Blending.h" 21 | #include "../Debugger/ColorMap.h" 22 | 23 | #include /* CV_RANSAC */ 24 | #include /* ImageFeatures, MatchesInfo */ 25 | #include /* CameraParams */ 26 | #include /* BundleAdjusterBase */ 27 | 28 | const int PAIR_COUNT = 2; 29 | 30 | class FeatureDistance { 31 | public: 32 | double distance; 33 | int feature_index[PAIR_COUNT]; 34 | FeatureDistance() { 35 | feature_index[0] = feature_index[1] = -1; 36 | distance = MAXFLOAT; 37 | } 38 | FeatureDistance(const double _distance, 39 | const int _p_1, 40 | const int _feature_index_1, 41 | const int _feature_index_2) { 42 | distance = _distance; 43 | feature_index[ _p_1] = _feature_index_1; 44 | feature_index[!_p_1] = _feature_index_2; 45 | } 46 | bool operator < (const FeatureDistance & fd) const { 47 | return distance > fd.distance; 48 | } 49 | private: 50 | }; 51 | 52 | class SimilarityElements { 53 | public: 54 | double scale; 55 | double theta; 56 | SimilarityElements(const double _scale, 57 | const double _theta) { 58 | scale = _scale; 59 | theta = _theta; 60 | } 61 | private: 62 | }; 63 | 64 | class MultiImages { 65 | public: 66 | MultiImages(const string & _file_name, 67 | LINES_FILTER_FUNC * _width_filter = &LINES_FILTER_NONE, 68 | LINES_FILTER_FUNC * _length_filter = &LINES_FILTER_NONE); 69 | 70 | const vector & getImagesFeaturesByMatchingPoints() const; 71 | const vector & getPairwiseMatchesByMatchingPoints() const; 72 | const vector & getCameraParams() const; 73 | 74 | const vector > & getImagesFeaturesMaskByMatchingPoints() const; 75 | 76 | const vector > > > & getFeaturePairs() const; 77 | const vector > > & getFeatureMatches() const; 78 | 79 | const vector > > & getAPAPOverlapMask() const; 80 | const vector > > & getAPAPHomographies() const; 81 | const vector > > & getAPAPMatchingPoints() const; 82 | 83 | const vector > & getInterpolateVerticesOfMatchingPoints() const; 84 | 85 | const vector & getImagesVerticesStartIndex() const; 86 | const vector & getImagesSimilarityElements(const enum GLOBAL_ROTATION_METHODS & _global_rotation_method) const; 87 | const vector > > & getImagesRelativeRotationRange() const; 88 | 89 | const vector > & getImagesGridSpaceMatchingPointsWeight(const double _global_weight_gamma) const; 90 | 91 | const vector & getImagesLinesProject(const int _from, const int _to) const; 92 | 93 | const vector & getImages() const; 94 | 95 | FLOAT_TYPE getImagesMinimumLineDistortionRotation(const int _from, const int _to) const; 96 | 97 | const vector > > & getLineSamplePoints() const; 98 | const vector > > & getLineMeshInterpolation() const; 99 | const vector > > & getLineTermU() const; 100 | 101 | Mat textureMapping(const vector > & _vertices, 102 | const Size2 & _target_size, 103 | const BLENDING_METHODS & _blend_method) const; 104 | 105 | Mat textureMapping(const vector > & _vertices, 106 | const Size2 & _target_size, 107 | const BLENDING_METHODS & _blend_method, 108 | vector & _warp_images) const; 109 | 110 | void writeResultWithMesh(const Mat & _result, 111 | const vector > & _vertices, 112 | const string & _postfix, 113 | const bool _only_border) const; 114 | 115 | vector images_data; 116 | Parameter parameter; 117 | private: 118 | /*** Debugger ***/ 119 | void writeImageOfFeaturePairs(const string & _name, 120 | const pair & _index_pair, 121 | const vector > & _pairs) const; 122 | 123 | void writeImageOfMatchingPoints(const string & _name, 124 | const pair & _match_pair, 125 | const vector & m1_fpts, const vector & m2_fpts, 126 | const vector & mask) const; 127 | /****************/ 128 | 129 | void doFeatureMatching() const; 130 | void initialFeaturePairsSpace() const; 131 | 132 | vector > getInitialFeaturePairs(const pair & _match_pair) const; 133 | 134 | vector > getFeaturePairsBySequentialRANSAC(const pair & _match_pair, 135 | const vector & _X, 136 | const vector & _Y, 137 | const vector > & _initial_indices) const; 138 | 139 | mutable vector images_features; 140 | mutable vector pairwise_matches; 141 | mutable vector camera_params; 142 | 143 | mutable vector > images_features_mask; 144 | 145 | mutable vector > > > feature_pairs; 146 | mutable vector > > feature_matches; /* [m1][m2][j], img1 j_th matches */ 147 | 148 | mutable vector > > apap_overlap_mask; 149 | mutable vector > > apap_homographies; 150 | mutable vector > > apap_matching_points; 151 | 152 | mutable vector > mesh_interpolate_vertex_of_feature_pts; 153 | mutable vector > mesh_interpolate_vertex_of_matching_pts; 154 | 155 | mutable vector images_vertices_start_index; 156 | mutable vector images_similarity_elements_2D; 157 | mutable vector images_similarity_elements_3D; 158 | mutable vector > > images_relative_rotation_range; 159 | 160 | mutable vector > images_polygon_space_matching_pts_weight; 161 | 162 | /* Line */ 163 | mutable vector > images_minimum_line_distortion_rotation; 164 | mutable vector > > images_lines_projects; /* [m1][m2] img1 lines project on img2 */ 165 | 166 | mutable vector images; 167 | 168 | mutable vector > > line_sample_points; 169 | mutable vector > > line_mesh_interpolation; 170 | mutable vector > > line_term_u; 171 | }; 172 | 173 | #endif /* defined(__UglyMan_Stitiching__MultiImages__) */ 174 | -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/Mesh2D.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Mesh2D.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "Mesh2D.h" 10 | 11 | Mesh2D::Mesh2D(const int _cols, const int _rows) { 12 | nw = _cols / GRID_SIZE + (_cols % GRID_SIZE != 0); 13 | nh = _rows / GRID_SIZE + (_rows % GRID_SIZE != 0); 14 | lw = _cols / (double)nw; 15 | lh = _rows / (double)nh; 16 | } 17 | Mesh2D::~Mesh2D() { 18 | 19 | } 20 | 21 | const vector & Mesh2D::getPolygonsCenter() const { 22 | if(polygons_center.empty()) { 23 | const vector & vertices = getVertices(); 24 | const vector & polygons_indices = getPolygonsIndices(); 25 | polygons_center.reserve(polygons_indices.size()); 26 | for(int i = 0; i < polygons_indices.size(); ++i) { 27 | Point2 center(0, 0); 28 | for(int j = 0; j < polygons_indices[i].indices.size(); ++j) { 29 | center += vertices[polygons_indices[i].indices[j]]; 30 | } 31 | polygons_center.emplace_back(center / (FLOAT_TYPE)polygons_indices[i].indices.size()); 32 | } 33 | } 34 | return polygons_center; 35 | } 36 | 37 | template 38 | int Mesh2D::getGridIndexOfPoint(const cv::Point_ & _p) const { 39 | cv::Point2i grid_p(_p.x / lw, _p.y / lh); 40 | grid_p.x = grid_p.x - (grid_p.x == nw); 41 | grid_p.y = grid_p.y - (grid_p.y == nh); 42 | return grid_p.x + grid_p.y * nw; 43 | } 44 | 45 | template int Mesh2D::getGridIndexOfPoint< float>(const cv::Point_< float> & _p) const; 46 | template int Mesh2D::getGridIndexOfPoint(const cv::Point_ & _p) const; 47 | -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/Mesh2D.h: -------------------------------------------------------------------------------- 1 | // 2 | // Mesh2D.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Mesh2D__ 10 | #define __UglyMan_Stitiching__Mesh2D__ 11 | 12 | #include "../Configure.h" 13 | #include "../Util/Transform.h" 14 | 15 | const int EDGE_VERTEX_SIZE = 2; 16 | 17 | class Edge { 18 | public: 19 | Edge(const int _e1, const int _e2) { 20 | indices[0] = _e1; 21 | indices[1] = _e2; 22 | } 23 | int indices[EDGE_VERTEX_SIZE]; 24 | private: 25 | }; 26 | 27 | class Indices { /* 3 or 4 */ 28 | public: 29 | vector indices; 30 | Indices() { 31 | 32 | } 33 | Indices(const int _i0, const int _i1, const int _i2) { 34 | indices.emplace_back(_i0); 35 | indices.emplace_back(_i1); 36 | indices.emplace_back(_i2); 37 | } 38 | Indices(const int _i0, const int _i1, const int _i2, const int _i3) { 39 | indices.emplace_back(_i0); 40 | indices.emplace_back(_i1); 41 | indices.emplace_back(_i2); 42 | indices.emplace_back(_i3); 43 | } 44 | private: 45 | }; 46 | 47 | class InterpolateVertex { 48 | public: 49 | int polygon; 50 | vector weights; 51 | InterpolateVertex() { 52 | polygon = -1; 53 | } 54 | InterpolateVertex(const InterpolateVertex & _iv) { 55 | polygon = _iv.polygon; 56 | weights = _iv.weights; 57 | } 58 | InterpolateVertex(const int _polygon, 59 | const double _w0, const double _w1, const double _w2) { 60 | polygon = _polygon; 61 | weights.emplace_back(_w0); 62 | weights.emplace_back(_w1); 63 | weights.emplace_back(_w2); 64 | } 65 | InterpolateVertex(const int _polygon, 66 | const vector & _weights) { 67 | polygon = _polygon; 68 | weights = _weights; 69 | } 70 | private: 71 | }; 72 | 73 | class Mesh2D { 74 | public: 75 | int nw, nh; 76 | double lw, lh; 77 | Mesh2D(const int _cols, const int _rows); 78 | virtual ~Mesh2D(); 79 | virtual const vector & getVertices() const = 0; 80 | virtual const vector & getEdges() const = 0; 81 | virtual const vector & getPolygonsIndices() const = 0; 82 | virtual const vector & getPolygonsNeighbors() const = 0; 83 | virtual const vector & getPolygonsEdges() const = 0; 84 | virtual const vector & getVertexStructures() const = 0; 85 | virtual const vector & getEdgeStructures() const = 0; /* grid neighbor */ 86 | virtual const vector & getTriangulationIndices() const = 0; 87 | virtual const int & getPolygonVerticesCount() const = 0; 88 | virtual const vector & getBoundaryVertexIndices() const = 0; /* clockwise order */ 89 | virtual const vector & getBoundaryEdgeIndices() const = 0; 90 | 91 | virtual InterpolateVertex getInterpolateVertex(const cv::Point_ & _p) const = 0; 92 | virtual InterpolateVertex getInterpolateVertex(const cv::Point_ & _p) const = 0; 93 | 94 | virtual const vector & getPolygonsCenter() const; 95 | 96 | template 97 | int getGridIndexOfPoint(const cv::Point_ & _p) const; 98 | 99 | protected: 100 | mutable vector vertices; 101 | mutable vector polygons_center; 102 | mutable vector edges; 103 | mutable vector polygons_indices; 104 | mutable vector polygons_neighbors; 105 | mutable vector polygons_edges; 106 | mutable vector vertex_structures; 107 | mutable vector edge_structures; 108 | mutable vector triangulation_indices; 109 | mutable vector boundary_vertex_indices; 110 | mutable vector boundary_edge_indices; 111 | }; 112 | 113 | #endif /* defined(__UglyMan_Stitiching__Mesh2D__) */ 114 | -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/MeshGrid.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MeshGrid.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "MeshGrid.h" 10 | 11 | const int GRID_VERTEX_SIZE = 4; 12 | 13 | MeshGrid::MeshGrid(const int _cols, const int _rows) : Mesh2D(_cols, _rows) { 14 | 15 | } 16 | 17 | const vector & MeshGrid::getVertices() const { 18 | if(vertices.empty()) { 19 | const int memory = (nh + 1) * (nw + 1); 20 | vertices.reserve(memory); 21 | for(int h = 0; h <= nh; ++h) { 22 | for(int w = 0; w <= nw; ++w) { 23 | vertices.emplace_back(w * lw, h * lh); 24 | } 25 | } 26 | assert(memory == vertices.size()); 27 | } 28 | return vertices; 29 | } 30 | 31 | const vector & MeshGrid::getEdges() const { 32 | if(edges.empty()) { 33 | const vector nexts = { cv::Point2i(1, 0), cv::Point2i(0, 1) }; 34 | const int memory = DIMENSION_2D * nh * nw + nh + nw; 35 | edges.reserve(memory); 36 | for(int h = 0; h <= nh; ++h) { 37 | for(int w = 0; w <= nw; ++w) { 38 | const cv::Point2i p1(w, h); 39 | for(int n = 0; n < nexts.size(); ++n) { 40 | const cv::Point2i p2 = p1 + nexts[n]; 41 | if(p2.x >= 0 && p2.y >= 0 && p2.x <= nw && p2.y <= nh) { 42 | edges.emplace_back(p1.x + p1.y * (nw + 1), 43 | p2.x + p2.y * (nw + 1)); 44 | } 45 | } 46 | } 47 | } 48 | assert(memory == edges.size()); 49 | } 50 | return edges; 51 | } 52 | const vector & MeshGrid::getPolygonsIndices() const { 53 | if(polygons_indices.empty()) { 54 | const cv::Point2i nexts[GRID_VERTEX_SIZE] = { 55 | cv::Point2i(0, 0), cv::Point2i(1, 0), cv::Point2i(1, 1), cv::Point2i(0, 1) 56 | }; 57 | const int memory = nh * nw; 58 | polygons_indices.resize(memory); 59 | int index = 0; 60 | for(int h = 0; h < nh; ++h) { 61 | for(int w = 0; w < nw; ++w) { 62 | const cv::Point2i p1(w, h); 63 | polygons_indices[index].indices.reserve(GRID_VERTEX_SIZE); 64 | for(int n = 0; n < GRID_VERTEX_SIZE; ++n) { 65 | const cv::Point2i p2 = p1 + nexts[n]; 66 | polygons_indices[index].indices.emplace_back(p2.x + p2.y * (nw + 1)); 67 | } 68 | ++index; 69 | } 70 | } 71 | assert(memory == polygons_indices.size()); 72 | } 73 | return polygons_indices; 74 | } 75 | const vector & MeshGrid::getPolygonsNeighbors() const { 76 | if(polygons_neighbors.empty()) { 77 | const vector nexts = { 78 | cv::Point2i(1, 0), cv::Point2i(0, 1), cv::Point2i(-1, 0), cv::Point2i(0, -1) 79 | }; 80 | const int memory = nh * nw; 81 | polygons_neighbors.resize(memory); 82 | int index = 0; 83 | for(int h = 0; h < nh; ++h) { 84 | for(int w = 0; w < nw; ++w) { 85 | const cv::Point2i p1(w, h); 86 | for(int n = 0; n < nexts.size(); ++n) { 87 | const cv::Point2i p2 = p1 + nexts[n]; 88 | if(p2.x >= 0 && p2.y >= 0 && p2.x < nw && p2.y < nh) { 89 | polygons_neighbors[index].indices.emplace_back(p2.x + p2.y * nw); 90 | } 91 | } 92 | ++index; 93 | } 94 | } 95 | assert(memory == polygons_neighbors.size()); 96 | } 97 | return polygons_neighbors; 98 | } 99 | const vector & MeshGrid::getPolygonsEdges() const { 100 | if(polygons_edges.empty()) { 101 | const vector nexts = { 102 | 0, 1, 3, nw * 2 + 1 103 | }; 104 | const int memory = nh * nw; 105 | polygons_edges.resize(memory); 106 | int index = 0, e_index = 0;; 107 | for(int h = 0; h < nh; ++h) { 108 | for(int w = 0; w < nw; ++w) { 109 | for(int n = 0; n < nexts.size(); ++n) { 110 | polygons_edges[index].indices.emplace_back(e_index + nexts[n]); 111 | } 112 | polygons_edges[index].indices.back() = polygons_edges[index].indices.back() - (h == nh-1) * w; 113 | ++index; 114 | e_index += 2; 115 | } 116 | polygons_edges[index - 1].indices[2] = polygons_edges[index - 1].indices[2] - 1; 117 | e_index += 1; 118 | } 119 | assert(memory == polygons_edges.size()); 120 | } 121 | return polygons_edges; 122 | } 123 | 124 | const vector & MeshGrid::getVertexStructures() const { 125 | if(vertex_structures.empty()) { 126 | const vector nexts = { 127 | cv::Point2i(1, 0), cv::Point2i(0, 1), cv::Point2i(-1, 0), cv::Point2i(0, -1) 128 | }; 129 | const int memory = (nh + 1) * (nw + 1); 130 | vertex_structures.resize(memory); 131 | int index = 0; 132 | for(int h = 0; h <= nh; ++h) { 133 | for(int w = 0; w <= nw; ++w) { 134 | cv::Point2i p1(w, h); 135 | for(int n = 0; n < nexts.size(); ++n) { 136 | cv::Point2i p2 = p1 + nexts[n]; 137 | if(p2.x >= 0 && p2.y >= 0 && p2.x <= nw && p2.y <= nh) { 138 | vertex_structures[index].indices.emplace_back(p2.x + p2.y * (nw + 1)); 139 | } 140 | } 141 | ++index; 142 | } 143 | } 144 | assert(memory == vertex_structures.size()); 145 | } 146 | return vertex_structures; 147 | } 148 | const vector & MeshGrid::getEdgeStructures() const { 149 | if(edge_structures.empty()) { 150 | const vector nexts = { cv::Point2i(1, 0), cv::Point2i( 0, 1) }; 151 | const vector grid_neighbor = { cv::Point2i(0, -1), cv::Point2i(-1, 0) }; 152 | const int memory = DIMENSION_2D * nh * nw + nh + nw; 153 | edge_structures.resize(memory); 154 | int index = 0; 155 | for(int h = 0; h <= nh; ++h) { 156 | for(int w = 0; w <= nw; ++w) { 157 | cv::Point2i p1(w, h); 158 | for(int n = 0; n < nexts.size(); ++n) { 159 | cv::Point2i p2 = p1 + nexts[n]; 160 | if(p2.x >= 0 && p2.y >= 0 && p2.x <= nw && p2.y <= nh) { 161 | for(int j = 0; j < grid_neighbor.size(); ++j) { 162 | cv::Point2i p3 = p1 + grid_neighbor[n] * j; 163 | if(p3.x >= 0 && p3.y >= 0 && p3.x < nw && p3.y < nh) { 164 | edge_structures[index].indices.emplace_back(p3.x + p3.y * nw); 165 | } 166 | } 167 | ++index; 168 | } 169 | } 170 | } 171 | } 172 | assert(memory == edge_structures.size()); 173 | } 174 | return edge_structures; 175 | } 176 | 177 | const vector & MeshGrid::getTriangulationIndices() const { 178 | if(triangulation_indices.empty()) { 179 | triangulation_indices.emplace_back(0, 1, 2); 180 | triangulation_indices.emplace_back(0, 2, 3); 181 | } 182 | return triangulation_indices; 183 | } 184 | 185 | const int & MeshGrid::getPolygonVerticesCount() const { 186 | return GRID_VERTEX_SIZE; 187 | } 188 | 189 | const vector & MeshGrid::getBoundaryVertexIndices() const { 190 | if(boundary_vertex_indices.empty()) { 191 | const int memory = DIMENSION_2D * (nw + nh) + 1; 192 | boundary_vertex_indices.reserve(memory); 193 | for(int tw = 0; tw < nw; ++tw) { 194 | boundary_vertex_indices.emplace_back(tw); 195 | } 196 | const int right_bottom = nw * nh + nw + nh; 197 | for(int rh = nw; rh < right_bottom; rh += (nw + 1)) { 198 | boundary_vertex_indices.emplace_back(rh); 199 | } 200 | const int left_bottom = nh * (nw + 1); 201 | for(int bw = right_bottom; bw > left_bottom; --bw) { 202 | boundary_vertex_indices.emplace_back(bw); 203 | } 204 | for(int lh = left_bottom; lh >= 0; lh -= (nw + 1)) { 205 | boundary_vertex_indices.emplace_back(lh); 206 | } 207 | assert(memory == boundary_vertex_indices.size()); 208 | } 209 | return boundary_vertex_indices; 210 | } 211 | 212 | const vector & MeshGrid::getBoundaryEdgeIndices() const { 213 | if(boundary_edge_indices.empty()) { 214 | const int memory = DIMENSION_2D * (nh + nw); 215 | boundary_edge_indices.reserve(memory); 216 | const int bottom_shift = DIMENSION_2D * nh * nw + nh; 217 | for(int w = 0; w < nw; ++w) { 218 | boundary_edge_indices.emplace_back(2 * w); 219 | boundary_edge_indices.emplace_back(bottom_shift + w); 220 | } 221 | const int dh = 2 * nw + 1; 222 | for(int h = 0; h < nh; ++h) { 223 | int tmp = h * dh; 224 | boundary_edge_indices.emplace_back(tmp + 1); 225 | boundary_edge_indices.emplace_back(tmp + dh - 1); 226 | } 227 | assert(memory == boundary_edge_indices.size()); 228 | } 229 | return boundary_edge_indices; 230 | } 231 | 232 | template 233 | InterpolateVertex MeshGrid::getInterpolateVertexTemplate(const cv::Point_ & _p) const { 234 | const vector & vertices = getVertices(); 235 | const vector & grids = getPolygonsIndices(); 236 | 237 | const int grid_index = getGridIndexOfPoint(_p); 238 | 239 | const Indices & g = grids[grid_index]; 240 | 241 | const vector diagonal_indices = {2, 3, 0, 1}; /* 0 1 2 3 242 | -> 243 | 3 2 1 0 */ 244 | assert(g.indices.size() == GRID_VERTEX_SIZE); 245 | 246 | vector weights(GRID_VERTEX_SIZE); 247 | double sum_inv = 0; 248 | for(int i = 0; i < diagonal_indices.size(); ++i) { 249 | Point2 tmp(_p.x - vertices[g.indices[diagonal_indices[i]]].x, 250 | _p.y - vertices[g.indices[diagonal_indices[i]]].y); 251 | weights[i] = fabs(tmp.x * tmp.y); 252 | sum_inv += weights[i]; 253 | } 254 | sum_inv = 1. / sum_inv; 255 | for(int i = 0; i < GRID_VERTEX_SIZE; ++i) { 256 | weights[i] = weights[i] * sum_inv; 257 | } 258 | return InterpolateVertex(grid_index, weights); 259 | } 260 | 261 | InterpolateVertex MeshGrid::getInterpolateVertex(const cv::Point_ & _p) const { 262 | return getInterpolateVertexTemplate(_p); 263 | } 264 | InterpolateVertex MeshGrid::getInterpolateVertex(const cv::Point_ & _p) const { 265 | return getInterpolateVertexTemplate(_p); 266 | } 267 | 268 | template InterpolateVertex MeshGrid::getInterpolateVertexTemplate< float>(const cv::Point_< float> & _p) const; 269 | template InterpolateVertex MeshGrid::getInterpolateVertexTemplate(const cv::Point_ & _p) const; 270 | -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/MeshGrid.h: -------------------------------------------------------------------------------- 1 | // 2 | // MeshGrid.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__MeshGrid__ 10 | #define __UglyMan_Stitiching__MeshGrid__ 11 | 12 | #include "Mesh2D.h" 13 | 14 | class MeshGrid : public Mesh2D { 15 | public: 16 | MeshGrid(const int _cols, const int _rows); 17 | const vector & getVertices() const; 18 | const vector & getEdges() const; 19 | const vector & getPolygonsIndices() const; 20 | const vector & getPolygonsNeighbors() const; 21 | const vector & getPolygonsEdges() const; 22 | const vector & getVertexStructures() const; 23 | const vector & getEdgeStructures() const; 24 | const vector & getTriangulationIndices() const; 25 | const int & getPolygonVerticesCount() const; 26 | const vector & getBoundaryVertexIndices() const; 27 | const vector & getBoundaryEdgeIndices() const; 28 | 29 | InterpolateVertex getInterpolateVertex(const cv::Point_ & _p) const; 30 | InterpolateVertex getInterpolateVertex(const cv::Point_ & _p) const; 31 | 32 | template 33 | InterpolateVertex getInterpolateVertexTemplate(const cv::Point_ & _p) const; 34 | private: 35 | 36 | }; 37 | 38 | #endif /* defined(__UglyMan_Stitiching__MeshGrid__) */ 39 | -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/MeshOptimization.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MeshOptimization.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "MeshOptimization.h" 10 | 11 | MeshOptimization::MeshOptimization(const MultiImages & _multi_images) { 12 | multi_images = &_multi_images; 13 | 14 | alignment_weight = 0; 15 | local_similarity_weight = 0; 16 | global_similarity_weight_beta = global_similarity_weight_gamma = 0; 17 | global_rotation_method = GLOBAL_ROTATION_METHODS_SIZE; 18 | 19 | alignment_equation = make_pair(0, 0); 20 | local_similarity_equation = make_pair(0, 0); 21 | global_similarity_equation = make_pair(0, 0); 22 | } 23 | 24 | void MeshOptimization::setWeightToAlignmentTerm(const double _weight) { 25 | alignment_weight = _weight; 26 | } 27 | 28 | void MeshOptimization::setWeightToLocalSimilarityTerm(const double _weight) { 29 | local_similarity_weight = _weight; 30 | } 31 | 32 | void MeshOptimization::setWeightToGlobalSimilarityTerm(const double _weight_beta, 33 | const double _weight_gamma, 34 | const enum GLOBAL_ROTATION_METHODS _global_rotation_method) { 35 | global_similarity_weight_beta = _weight_beta; 36 | global_similarity_weight_gamma = _weight_gamma; 37 | global_rotation_method = _global_rotation_method; 38 | } 39 | 40 | void MeshOptimization::setWeightToLinePreservingTerm(const double _weight) { 41 | line_preserving_weight = _weight; 42 | } 43 | 44 | const MultiImages & MeshOptimization::getMultiImages() const { 45 | return *multi_images; 46 | } 47 | 48 | double MeshOptimization::getAlignmentTermWeight() const { 49 | return alignment_weight; 50 | } 51 | double MeshOptimization::getLocalSimilarityTermWeight() const { 52 | return local_similarity_weight; 53 | } 54 | double MeshOptimization::getGlobalSimilarityTermWeightBeta() const { 55 | return global_similarity_weight_beta; 56 | } 57 | double MeshOptimization::getGlobalSimilarityTermWeightGamma() const { 58 | return global_similarity_weight_gamma; 59 | } 60 | 61 | enum GLOBAL_ROTATION_METHODS MeshOptimization::getGlobalRotationMethod() const { 62 | return global_rotation_method; 63 | } 64 | 65 | void MeshOptimization::reserveData(vector > & _triplets, 66 | vector > & _b_vector, 67 | const int _start_index) { 68 | int equation = _start_index; 69 | const bool alignment_term = alignment_weight; 70 | const bool local_similarity_term = local_similarity_weight; 71 | const bool global_similarity_term = (global_similarity_weight_beta || global_similarity_weight_gamma); 72 | int edge_count = (local_similarity_term || global_similarity_term) ? getEdgesCount() : 0; 73 | int similarity_equation_count = (edge_count) ? edge_count * DIMENSION_2D : 0; 74 | int edge_neighbor_vertices_count = (similarity_equation_count) ? getEdgeNeighborVerticesCount() : 0; 75 | 76 | alignment_equation.first = equation; 77 | alignment_equation.second = (alignment_term) ? getAlignmentTermEquationsCount() : 0; 78 | equation += alignment_equation.second; 79 | 80 | local_similarity_equation.first = equation; 81 | local_similarity_equation.second = (local_similarity_term) ? similarity_equation_count : 0; 82 | equation += local_similarity_equation.second; 83 | 84 | global_similarity_equation.first = equation; 85 | global_similarity_equation.second = (global_similarity_term) ? similarity_equation_count : 0; 86 | 87 | _triplets.reserve(alignment_equation.second * 8 + 88 | (local_similarity_term) * (edge_neighbor_vertices_count * 8 + edge_count * 4) + 89 | (global_similarity_term) * (edge_neighbor_vertices_count * 8) + 90 | _start_index); 91 | _b_vector.reserve((global_similarity_term) * edge_neighbor_vertices_count * 4 + 92 | _start_index); 93 | } 94 | 95 | void MeshOptimization::reserveData_line(vector > & _triplets, 96 | vector > & _b_vector, 97 | const int _start_index) { 98 | int equation = _start_index; 99 | const bool alignment_term = alignment_weight; 100 | const bool local_similarity_term = local_similarity_weight; 101 | const bool global_similarity_term = (global_similarity_weight_beta || global_similarity_weight_gamma); 102 | const bool line_preserving_term = line_preserving_weight; 103 | 104 | int edge_count = (local_similarity_term || global_similarity_term) ? getEdgesCount() : 0; 105 | int similarity_equation_count = (edge_count) ? edge_count * DIMENSION_2D : 0; 106 | int edge_neighbor_vertices_count = (similarity_equation_count) ? getEdgeNeighborVerticesCount() : 0; 107 | 108 | int line_preserving_equation_count = (line_preserving_weight) ? getLinePreservingTermEquationCount() : 0; 109 | //cout << "MeshOptimization:line_preserving_equation_count=" << line_preserving_equation_count << endl; 110 | 111 | alignment_equation.first = equation; 112 | alignment_equation.second = (alignment_term) ? getAlignmentTermEquationsCount() : 0; 113 | equation += alignment_equation.second; 114 | 115 | local_similarity_equation.first = equation; 116 | local_similarity_equation.second = (local_similarity_term) ? similarity_equation_count : 0; 117 | equation += local_similarity_equation.second; 118 | 119 | global_similarity_equation.first = equation; 120 | global_similarity_equation.second = (global_similarity_term) ? similarity_equation_count : 0; 121 | equation += global_similarity_equation.second; 122 | 123 | line_preserving_equation.first = equation; 124 | line_preserving_equation.second = (line_preserving_term) ? line_preserving_equation_count : 0; 125 | 126 | _triplets.reserve(alignment_equation.second * 8 + 127 | (local_similarity_term) * (edge_neighbor_vertices_count * 8 + edge_count * 4) + 128 | (global_similarity_term) * (edge_neighbor_vertices_count * 8) + 129 | (line_preserving_term) * (line_preserving_equation_count * 12) + 130 | _start_index); 131 | _b_vector.reserve((global_similarity_term)* edge_neighbor_vertices_count * 4 + 132 | _start_index); 133 | } 134 | 135 | void MeshOptimization::prepareAlignmentTerm(vector > & _triplets) const { 136 | if(alignment_equation.second) { 137 | const int equation = alignment_equation.first; 138 | 139 | const vector > & mesh_interpolate_vertex_of_matching_pts = multi_images->getInterpolateVerticesOfMatchingPoints(); 140 | const vector & pairwise_matches = multi_images->getPairwiseMatchesByMatchingPoints(); 141 | const vector > & images_match_graph_pair_list = multi_images->parameter.getImagesMatchGraphPairList(); 142 | const vector & images_vertices_start_index = multi_images->getImagesVerticesStartIndex(); 143 | 144 | int eq_count = 0; 145 | for(int i = 0; i < images_match_graph_pair_list.size(); ++i) { 146 | const pair & match_pair = images_match_graph_pair_list[i]; 147 | const int & m1 = match_pair.first, & m2 = match_pair.second; 148 | const int pm_index = m1 * (int)multi_images->images_data.size() + m2; 149 | const vector & polygons_indices_1 = multi_images->images_data[m1].mesh_2d->getPolygonsIndices(); 150 | const vector & polygons_indices_2 = multi_images->images_data[m2].mesh_2d->getPolygonsIndices(); 151 | 152 | for(int j = 0; j < pairwise_matches[pm_index].matches.size(); ++j) { 153 | const DMatch & D_Match = pairwise_matches[pm_index].matches[j]; 154 | 155 | for(int dim = 0; dim < DIMENSION_2D; ++dim) { 156 | for(int k = 0; k < multi_images->images_data[m1].mesh_2d->getPolygonVerticesCount(); ++k) { 157 | _triplets.emplace_back(equation + eq_count + dim, 158 | images_vertices_start_index[m1] + dim + 159 | DIMENSION_2D * (polygons_indices_1[mesh_interpolate_vertex_of_matching_pts[m1][D_Match.queryIdx].polygon].indices[k]), 160 | alignment_weight * mesh_interpolate_vertex_of_matching_pts[m1][D_Match.queryIdx].weights[k]); 161 | } 162 | for(int k = 0; k < multi_images->images_data[m2].mesh_2d->getPolygonVerticesCount(); ++k) { 163 | _triplets.emplace_back(equation + eq_count + dim, 164 | images_vertices_start_index[m2] + dim + 165 | DIMENSION_2D * (polygons_indices_2[mesh_interpolate_vertex_of_matching_pts[m2][D_Match.trainIdx].polygon].indices[k]), 166 | -alignment_weight * mesh_interpolate_vertex_of_matching_pts[m2][D_Match.trainIdx].weights[k]); 167 | } 168 | } 169 | eq_count += DIMENSION_2D; 170 | } 171 | } 172 | assert(eq_count == alignment_equation.second); 173 | } 174 | } 175 | 176 | void MeshOptimization::prepareSimilarityTerm(vector > & _triplets, 177 | vector > & _b_vector) const { 178 | const bool local_similarity_term = local_similarity_equation.second; 179 | const bool global_similarity_term = global_similarity_equation.second; 180 | if(local_similarity_term || global_similarity_term) { 181 | const vector & images_vertices_start_index = multi_images->getImagesVerticesStartIndex(); 182 | const vector > & images_grid_space_matching_pts_weight = multi_images->getImagesGridSpaceMatchingPointsWeight(global_similarity_weight_gamma); 183 | const vector & images_similarity_elements = multi_images->getImagesSimilarityElements(global_rotation_method); 184 | int eq_count = 0, eq_count_rotation = 0; 185 | for(int i = 0; i < multi_images->images_data.size(); ++i) { 186 | const vector & edges = multi_images->images_data[i].mesh_2d->getEdges(); 187 | const vector & vertices = multi_images->images_data[i].mesh_2d->getVertices(); 188 | const vector & v_neighbors = multi_images->images_data[i].mesh_2d->getVertexStructures(); 189 | const vector & e_neighbors = multi_images->images_data[i].mesh_2d->getEdgeStructures(); 190 | 191 | const double similarity[DIMENSION_2D] = { 192 | images_similarity_elements[i].scale * cos(images_similarity_elements[i].theta), 193 | images_similarity_elements[i].scale * sin(images_similarity_elements[i].theta) 194 | }; 195 | 196 | for(int j = 0; j < edges.size(); ++j) { 197 | const int & ind_e1 = edges[j].indices[0]; 198 | const int & ind_e2 = edges[j].indices[1]; 199 | const Point2 & src = multi_images->images_data[i].mesh_2d->getVertices()[ind_e1]; 200 | const Point2 & dst = multi_images->images_data[i].mesh_2d->getVertices()[ind_e2]; 201 | set point_ind_set; 202 | for(int e = 0; e < EDGE_VERTEX_SIZE; ++e) { 203 | for(int v = 0; v < v_neighbors[edges[j].indices[e]].indices.size(); ++v) { 204 | int v_index = v_neighbors[edges[j].indices[e]].indices[v]; 205 | if(v_index != ind_e1) { 206 | point_ind_set.insert(v_index); 207 | } 208 | } 209 | } 210 | Mat Et, E_Main(DIMENSION_2D, DIMENSION_2D, CV_64FC1), E((int)point_ind_set.size() * DIMENSION_2D, DIMENSION_2D, CV_64FC1); 211 | set::const_iterator it = point_ind_set.begin(); 212 | for(int p = 0; it != point_ind_set.end(); ++p, ++it) { 213 | Point2 e = vertices[*it] - src; 214 | E.at(DIMENSION_2D * p , 0) = e.x; 215 | E.at(DIMENSION_2D * p , 1) = e.y; 216 | E.at(DIMENSION_2D * p + 1, 0) = e.y; 217 | E.at(DIMENSION_2D * p + 1, 1) = -e.x; 218 | } 219 | transpose(E, Et); 220 | Point2 e_main = dst - src; 221 | E_Main.at(0, 0) = e_main.x; 222 | E_Main.at(0, 1) = e_main.y; 223 | E_Main.at(1, 0) = e_main.y; 224 | E_Main.at(1, 1) = -e_main.x; 225 | 226 | Mat G_W = (Et * E).inv(DECOMP_SVD) * Et; 227 | Mat L_W = - E_Main * G_W; 228 | 229 | double _global_similarity_weight = global_similarity_weight_beta; 230 | if(global_similarity_weight_gamma) { 231 | double sum_weight = 0; 232 | for(int p = 0; p < e_neighbors[j].indices.size(); ++p) { 233 | sum_weight += images_grid_space_matching_pts_weight[i][e_neighbors[j].indices[p]]; 234 | } 235 | _global_similarity_weight = _global_similarity_weight + global_similarity_weight_gamma * (sum_weight / e_neighbors[j].indices.size()); 236 | } 237 | double _local_similarity_weight = 1; 238 | it = point_ind_set.begin(); 239 | for(int p = 0; it != point_ind_set.end(); ++p, ++it) { 240 | for(int xy = 0; xy < DIMENSION_2D; ++xy) { 241 | for(int dim = 0; dim < DIMENSION_2D; ++dim) { 242 | if(local_similarity_term) { 243 | _triplets.emplace_back(local_similarity_equation.first + eq_count + dim, 244 | images_vertices_start_index[i] + DIMENSION_2D * (*it) + xy, 245 | _local_similarity_weight * 246 | local_similarity_weight * L_W.at(dim, DIMENSION_2D * p + xy)); 247 | _triplets.emplace_back(local_similarity_equation.first + eq_count + dim, 248 | images_vertices_start_index[i] + DIMENSION_2D * ind_e1 + xy, 249 | _local_similarity_weight * 250 | -local_similarity_weight * L_W.at(dim, DIMENSION_2D * p + xy)); 251 | } 252 | if(global_similarity_term) { 253 | _triplets.emplace_back(global_similarity_equation.first + eq_count + dim, 254 | images_vertices_start_index[i] + DIMENSION_2D * (*it) + xy, 255 | _global_similarity_weight * G_W.at(dim, DIMENSION_2D * p + xy)); 256 | _triplets.emplace_back(global_similarity_equation.first + eq_count + dim, 257 | images_vertices_start_index[i] + DIMENSION_2D * ind_e1 + xy, 258 | -_global_similarity_weight * G_W.at(dim, DIMENSION_2D * p + xy)); 259 | _b_vector.emplace_back(global_similarity_equation.first + eq_count + dim, _global_similarity_weight * similarity[dim]); 260 | } 261 | } 262 | } 263 | } 264 | if(local_similarity_term) { 265 | _triplets.emplace_back(local_similarity_equation.first + eq_count , images_vertices_start_index[i] + DIMENSION_2D * ind_e2 , 266 | _local_similarity_weight * local_similarity_weight); 267 | _triplets.emplace_back(local_similarity_equation.first + eq_count + 1, images_vertices_start_index[i] + DIMENSION_2D * ind_e2 + 1, 268 | _local_similarity_weight * local_similarity_weight); 269 | _triplets.emplace_back(local_similarity_equation.first + eq_count , images_vertices_start_index[i] + DIMENSION_2D * ind_e1 , 270 | _local_similarity_weight * -local_similarity_weight); 271 | _triplets.emplace_back(local_similarity_equation.first + eq_count + 1, images_vertices_start_index[i] + DIMENSION_2D * ind_e1 + 1, 272 | _local_similarity_weight * -local_similarity_weight); 273 | } 274 | eq_count += DIMENSION_2D; 275 | ++eq_count_rotation; 276 | } 277 | } 278 | assert(! local_similarity_term || eq_count == local_similarity_equation.second); 279 | assert(!global_similarity_term || eq_count == global_similarity_equation.second); 280 | } 281 | } 282 | 283 | void MeshOptimization::prepareLinePreservingTerm(vector > & _triplets, 284 | vector > & _b_vector) const { 285 | if (line_preserving_equation.second) { 286 | const vector > > & line_interpolation = multi_images->getLineMeshInterpolation(); 287 | const vector > > & line_term_u = multi_images->getLineTermU(); 288 | const vector & images_vertices_start_index = multi_images->getImagesVerticesStartIndex(); 289 | 290 | int eq_count = 0; 291 | for (int i = 0; i < multi_images->images_data.size(); i++) { 292 | const vector & poly_vertices_index = multi_images->images_data[i].mesh_2d->getPolygonsIndices(); 293 | for (int j = 0; j < line_interpolation[i].size(); j++) { 294 | for (int k = 1; k < line_interpolation[i][j].size() - 1; k++) { 295 | for (int dim = 0; dim < DIMENSION_2D; ++dim) { 296 | for (int p = 0; p < multi_images->images_data[i].mesh_2d->getPolygonVerticesCount(); ++p) { 297 | _triplets.emplace_back(line_preserving_equation.first + eq_count + dim, 298 | images_vertices_start_index[i] + dim + DIMENSION_2D * (poly_vertices_index[line_interpolation[i][j][k].polygon].indices[p]), 299 | line_interpolation[i][j][k].weights[p] * line_preserving_weight); 300 | _triplets.emplace_back(line_preserving_equation.first + eq_count + dim, 301 | images_vertices_start_index[i] + dim + DIMENSION_2D * (poly_vertices_index[line_interpolation[i][j].front().polygon].indices[p]), 302 | -(1 - line_term_u[i][j][k]) * line_interpolation[i][j].front().weights[p] * line_preserving_weight); 303 | _triplets.emplace_back(line_preserving_equation.first + eq_count + dim, 304 | images_vertices_start_index[i] + dim + DIMENSION_2D * (poly_vertices_index[line_interpolation[i][j].back().polygon].indices[p]), 305 | -line_term_u[i][j][k] * line_interpolation[i][j].back().weights[p] * line_preserving_weight); 306 | } 307 | } 308 | eq_count += DIMENSION_2D; 309 | } 310 | } 311 | } 312 | assert(eq_count == line_preserving_equation.second); 313 | } 314 | } 315 | 316 | int MeshOptimization::getAlignmentTermEquationsCount() const { 317 | int result = 0; 318 | const vector > & images_match_graph_pair_list = multi_images->parameter.getImagesMatchGraphPairList(); 319 | const vector & pairwise_matches = multi_images->getPairwiseMatchesByMatchingPoints(); 320 | for(int i = 0; i < images_match_graph_pair_list.size(); ++i) { 321 | const pair & match_pair = images_match_graph_pair_list[i]; 322 | const int & m1 = match_pair.first, & m2 = match_pair.second; 323 | const int pm_index = m1 * (int)multi_images->images_data.size() + m2; 324 | result += pairwise_matches[pm_index].matches.size(); 325 | } 326 | return result * DIMENSION_2D; 327 | } 328 | 329 | int MeshOptimization::getVerticesCount() const { 330 | int result = 0; 331 | for(int i = 0; i < multi_images->images_data.size(); ++i) { 332 | result += multi_images->images_data[i].mesh_2d->getVertices().size(); 333 | } 334 | return result * DIMENSION_2D; 335 | } 336 | 337 | int MeshOptimization::getEdgesCount() const { 338 | int result = 0; 339 | for(int i = 0; i < multi_images->images_data.size(); ++i) { 340 | result += multi_images->images_data[i].mesh_2d->getEdges().size(); 341 | } 342 | return result; 343 | } 344 | 345 | int MeshOptimization::getEdgeNeighborVerticesCount() const { 346 | int result = 0; 347 | for(int i = 0; i < multi_images->images_data.size(); ++i) { 348 | const vector & edges = multi_images->images_data[i].mesh_2d->getEdges(); 349 | const vector & v_neighbors = multi_images->images_data[i].mesh_2d->getVertexStructures(); 350 | for(int j = 0; j < edges.size(); ++j) { 351 | for(int e = 0; e < EDGE_VERTEX_SIZE; ++e) { 352 | result += v_neighbors[edges[j].indices[e]].indices.size(); 353 | } 354 | } 355 | result -= edges.size(); 356 | } 357 | return result; 358 | } 359 | 360 | int MeshOptimization::getLinePreservingTermEquationCount() const { 361 | int result = 0; 362 | const vector > > & line_interpolation = multi_images->getLineMeshInterpolation(); 363 | for (int i = 0; i < line_interpolation.size(); i++)//pic 364 | { 365 | for (int j = 0; j < line_interpolation[i].size(); j++)//lines 366 | { 367 | result += line_interpolation[i][j].size() - 2; 368 | } 369 | } 370 | return result * DIMENSION_2D; 371 | } 372 | 373 | vector > MeshOptimization::getImageVerticesBySolving(vector > & _triplets, 374 | const vector > & _b_vector) const { 375 | //const int equations = global_similarity_equation.first + global_similarity_equation.second; 376 | const int equations = line_preserving_equation.first + line_preserving_equation.second; 377 | 378 | LeastSquaresConjugateGradient > lscg; 379 | SparseMatrix A(equations, getVerticesCount()); 380 | VectorXd b = VectorXd::Zero(equations), x; 381 | 382 | #ifndef NDEBUG 383 | TimeCalculator timer; 384 | timer.start(); 385 | cout << "A = [" << equations << ", " << getVerticesCount() << "]" << endl; 386 | #endif 387 | A.setFromTriplets(_triplets.begin(), _triplets.end()); 388 | for(int i = 0; i < _b_vector.size(); ++i) { 389 | b[_b_vector[i].first] = _b_vector[i].second; 390 | } 391 | #ifndef NDEBUG 392 | timer.end("Initial A matrix"); 393 | timer.start(); 394 | #endif 395 | lscg.compute(A); 396 | x = lscg.solve(b); 397 | #ifndef NDEBUG 398 | timer.end("Solve Ax = b"); 399 | cout << "#Iterations: " << lscg.iterations() << endl; 400 | cout << "Estimated error: " << lscg.error() << endl; 401 | #endif 402 | 403 | vector > vertices; 404 | vertices.resize(multi_images->images_data.size()); 405 | for(int i = 0, x_index = 0; i < vertices.size(); ++i) { 406 | int count = (int)multi_images->images_data[i].mesh_2d->getVertices().size() * DIMENSION_2D; 407 | vertices[i].reserve(count); 408 | for(int j = 0; j < count; j += DIMENSION_2D) { 409 | vertices[i].emplace_back(x[x_index + j ], 410 | x[x_index + j + 1]); 411 | } 412 | x_index += count; 413 | } 414 | return vertices; 415 | } -------------------------------------------------------------------------------- /NISwGSP_line/Mesh/MeshOptimization.h: -------------------------------------------------------------------------------- 1 | // 2 | // MeshOptimization.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__MeshOptimization__ 10 | #define __UglyMan_Stitiching__MeshOptimization__ 11 | 12 | #include "../Feature/MultiImages.h" 13 | #include "../Util/Blending.h" 14 | 15 | class MeshOptimization { 16 | public: 17 | MeshOptimization(const MultiImages & _multi_images); 18 | 19 | virtual Mat solve(const BLENDING_METHODS & _blend_method) = 0; 20 | protected: 21 | void setWeightToAlignmentTerm(const double _weight); 22 | void setWeightToLocalSimilarityTerm(const double _weight); 23 | void setWeightToGlobalSimilarityTerm(const double _weight_beta, 24 | const double _weight_gamma, 25 | const enum GLOBAL_ROTATION_METHODS _global_rotation_method); 26 | void setWeightToLinePreservingTerm(const double _weight); 27 | 28 | const MultiImages & getMultiImages() const; 29 | 30 | double getAlignmentTermWeight() const; 31 | double getLocalSimilarityTermWeight() const; 32 | double getGlobalSimilarityTermWeightBeta() const; 33 | double getGlobalSimilarityTermWeightGamma() const; 34 | enum GLOBAL_ROTATION_METHODS getGlobalRotationMethod() const; 35 | 36 | void reserveData(vector > & _triplets, 37 | vector > & _b_vector, 38 | const int _start_index); 39 | 40 | void reserveData_line(vector > & _triplets, 41 | vector > & _b_vector, 42 | const int _start_index); 43 | 44 | void prepareAlignmentTerm(vector > & _triplets) const; 45 | void prepareSimilarityTerm(vector > & _triplets, 46 | vector > & _b_vector) const; 47 | void prepareLinePreservingTerm(vector > & _triplets, 48 | vector > & _b_vector) const; 49 | 50 | vector > getImageVerticesBySolving(vector > & _triplets, 51 | const vector > & _b_vector) const; 52 | 53 | private: 54 | 55 | int getAlignmentTermEquationsCount() const; 56 | 57 | int getVerticesCount() const; 58 | int getEdgesCount() const; 59 | int getEdgeNeighborVerticesCount() const; 60 | 61 | int getLinePreservingTermEquationCount() const; 62 | 63 | const MultiImages * multi_images; 64 | 65 | double alignment_weight; 66 | double local_similarity_weight; 67 | double global_similarity_weight_beta, global_similarity_weight_gamma; 68 | double line_preserving_weight; 69 | 70 | pair alignment_equation; /* begin, count */ 71 | pair local_similarity_equation; 72 | pair global_similarity_equation; 73 | pair line_preserving_equation; 74 | enum GLOBAL_ROTATION_METHODS global_rotation_method; 75 | }; 76 | 77 | #endif /* defined(__UglyMan_Stitiching__MeshOptimization__) */ 78 | -------------------------------------------------------------------------------- /NISwGSP_line/NISwGSP_line.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {0BDD4D0D-E8C2-4ED9-9650-1760043C81F4} 24 | NISwGSPline 25 | 10.0.17763.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | D:\3rd_party\dirent\include;D:\3rd_party\eigen3;D:\3rd_party\vlfeat-0.9.20;D:\OpenCV\opencv-3.4.5\build\include;$(IncludePath) 74 | D:\3rd_party\vlfeat-0.9.20\bin\win64;D:\OpenCV\opencv-3.4.5\build\x64\vc15\lib;$(LibraryPath) 75 | 76 | 77 | 78 | Level3 79 | Disabled 80 | true 81 | true 82 | 83 | 84 | Console 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | true 92 | true 93 | 94 | 95 | Console 96 | 97 | 98 | 99 | 100 | Level3 101 | MaxSpeed 102 | true 103 | true 104 | true 105 | true 106 | 107 | 108 | Console 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | MaxSpeed 117 | true 118 | true 119 | true 120 | true 121 | 122 | 123 | Console 124 | true 125 | true 126 | vl.lib;opencv_world345.lib;%(AdditionalDependencies) 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /NISwGSP_line/NISwGSP_line.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;ipp;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 | {6c7774e5-990c-4ba8-afe2-d2f3f62b6575} 18 | 19 | 20 | {f046c73b-2b85-4aa7-aa53-96d109b171bc} 21 | 22 | 23 | {37234937-2d4b-43d5-8409-8dc82eaff416} 24 | 25 | 26 | {9a48d118-0603-4db9-8f71-217d26874769} 27 | 28 | 29 | {4ec04f1d-f201-4f91-ba83-4b25bae9a74a} 30 | 31 | 32 | 33 | 34 | 源文件\Debugger 35 | 36 | 37 | 源文件\Debugger 38 | 39 | 40 | 源文件\Debugger 41 | 42 | 43 | 源文件\Debugger 44 | 45 | 46 | 源文件\Feature 47 | 48 | 49 | 源文件\Feature 50 | 51 | 52 | 源文件\Feature 53 | 54 | 55 | 源文件\Mesh 56 | 57 | 58 | 源文件\Mesh 59 | 60 | 61 | 源文件\Mesh 62 | 63 | 64 | 源文件\Stitching 65 | 66 | 67 | 源文件\Stitching 68 | 69 | 70 | 源文件\Stitching 71 | 72 | 73 | 源文件\Util 74 | 75 | 76 | 源文件\Util 77 | 78 | 79 | 源文件\Util 80 | 81 | 82 | 源文件\Util 83 | 84 | 85 | 源文件 86 | 87 | 88 | 源文件 89 | 90 | 91 | 92 | 93 | 源文件\Debugger 94 | 95 | 96 | 源文件\Debugger 97 | 98 | 99 | 源文件\Debugger 100 | 101 | 102 | 源文件\Debugger 103 | 104 | 105 | 源文件\Feature 106 | 107 | 108 | 源文件\Feature 109 | 110 | 111 | 源文件\Feature 112 | 113 | 114 | 源文件\Mesh 115 | 116 | 117 | 源文件\Mesh 118 | 119 | 120 | 源文件\Mesh 121 | 122 | 123 | 源文件\Stitching 124 | 125 | 126 | 源文件\Stitching 127 | 128 | 129 | 源文件\Stitching 130 | 131 | 132 | 源文件\Util 133 | 134 | 135 | 源文件\Util 136 | 137 | 138 | 源文件\Util 139 | 140 | 141 | 源文件\Util 142 | 143 | 144 | 源文件 145 | 146 | 147 | -------------------------------------------------------------------------------- /NISwGSP_line/NISwGSP_line.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/APAP_Stitching.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // APAP_Stitching.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "APAP_Stitching.h" 10 | 11 | void APAP_Stitching::apap_project(const vector & _p_src, 12 | const vector & _p_dst, 13 | const vector & _src, 14 | vector & _dst, 15 | vector & _homographies) { 16 | vector nf1, nf2, cf1, cf2; 17 | cv::Mat N1, N2, C1, C2; 18 | N1 = getNormalize2DPts(_p_src, nf1); 19 | N2 = getNormalize2DPts(_p_dst, nf2); 20 | C1 = getConditionerFromPts(nf1); 21 | C2 = getConditionerFromPts(nf2); 22 | cf1.reserve(nf1.size()); 23 | cf2.reserve(nf2.size()); 24 | for(int i = 0; i < nf1.size(); ++i) { 25 | cf1.emplace_back(nf1[i].x * C1.at(0, 0) + C1.at(0, 2), 26 | nf1[i].y * C1.at(1, 1) + C1.at(1, 2)); 27 | 28 | cf2.emplace_back(nf2[i].x * C2.at(0, 0) + C2.at(0, 2), 29 | nf2[i].y * C2.at(1, 1) + C2.at(1, 2)); 30 | } 31 | double sigma_inv_2 = 1. / (APAP_SIGMA * APAP_SIGMA), gamma = APAP_GAMMA; 32 | MatrixXd A = MatrixXd::Zero(cf1.size() * DIMENSION_2D, 33 | HOMOGRAPHY_VARIABLES_COUNT); 34 | 35 | #ifndef NDEBUG 36 | if(_dst.empty() == false) { 37 | _dst.clear(); 38 | printError("F(apap_project) dst is not empty"); 39 | } 40 | if(_homographies.empty() == false) { 41 | _homographies.clear(); 42 | printError("F(apap_project) homographies is not empty"); 43 | } 44 | #endif 45 | _dst.reserve(_src.size()); 46 | _homographies.reserve(_src.size()); 47 | for(int i = 0; i < _src.size(); ++i) { 48 | for(int j = 0; j < _p_src.size(); ++j) { 49 | Point2 d = _src[i] - _p_src[j]; 50 | double www = MAX(gamma, exp(-sqrt(d.x * d.x + d.y * d.y) * sigma_inv_2)); 51 | A(2*j , 0) = www * cf1[j].x; 52 | A(2*j , 1) = www * cf1[j].y; 53 | A(2*j , 2) = www * 1; 54 | A(2*j , 6) = www * -cf2[j].x * cf1[j].x; 55 | A(2*j , 7) = www * -cf2[j].x * cf1[j].y; 56 | A(2*j , 8) = www * -cf2[j].x; 57 | 58 | A(2*j+1, 3) = www * cf1[j].x; 59 | A(2*j+1, 4) = www * cf1[j].y; 60 | A(2*j+1, 5) = www * 1; 61 | A(2*j+1, 6) = www * -cf2[j].y * cf1[j].x; 62 | A(2*j+1, 7) = www * -cf2[j].y * cf1[j].y; 63 | A(2*j+1, 8) = www * -cf2[j].y; 64 | } 65 | JacobiSVD jacobi_svd(A, ComputeThinV); 66 | MatrixXd V = jacobi_svd.matrixV(); 67 | cv::Mat H(3, 3, CV_64FC1); 68 | for(int j = 0; j < V.rows(); ++j) { 69 | H.at(j / 3, j % 3) = V(j, V.rows() - 1); 70 | } 71 | H = C2.inv() * H * C1; 72 | H = N2.inv() * H * N1; 73 | 74 | _dst.emplace_back(applyTransform3x3(_src[i].x, _src[i].y, H)); 75 | _homographies.emplace_back(H); 76 | } 77 | } -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/APAP_Stitching.h: -------------------------------------------------------------------------------- 1 | // 2 | // APAP_Stitching.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__APAP_Stitching__ 10 | #define __UglyMan_Stitiching__APAP_Stitching__ 11 | 12 | #include "../Configure.h" 13 | #include "../Util/Transform.h" 14 | 15 | class APAP_Stitching { 16 | public: 17 | static void apap_project(const vector & _p_src, 18 | const vector & _p_dst, 19 | const vector & _src, 20 | vector & _dst, 21 | vector & _homographies); 22 | private: 23 | }; 24 | 25 | #endif /* defined(__UglyMan_Stitiching__APAP_Stitching__) */ 26 | -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/NISwGSP_Stitching.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // NISwGSP_Stitching.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "NISwGSP_Stitching.h" 10 | 11 | NISwGSP_Stitching::NISwGSP_Stitching(const MultiImages & _multi_images) : MeshOptimization(_multi_images) { 12 | 13 | } 14 | 15 | void NISwGSP_Stitching::setWeightToAlignmentTerm(const double _weight) { 16 | MeshOptimization::setWeightToAlignmentTerm(_weight); 17 | } 18 | 19 | void NISwGSP_Stitching::setWeightToLocalSimilarityTerm(const double _weight) { 20 | MeshOptimization::setWeightToLocalSimilarityTerm(_weight); 21 | } 22 | 23 | void NISwGSP_Stitching::setWeightToGlobalSimilarityTerm(const double _weight_beta, 24 | const double _weight_gamma, 25 | const enum GLOBAL_ROTATION_METHODS _global_rotation_method) { 26 | MeshOptimization::setWeightToGlobalSimilarityTerm(_weight_beta, _weight_gamma, _global_rotation_method); 27 | } 28 | 29 | void NISwGSP_Stitching::setWeightToLinePreservingTerm(const double _weight) { 30 | MeshOptimization::setWeightToLinePreservingTerm(_weight); 31 | } 32 | 33 | Mat NISwGSP_Stitching::solve(const BLENDING_METHODS & _blend_method) { 34 | const MultiImages & multi_images = getMultiImages(); 35 | 36 | vector > triplets; 37 | vector > b_vector; 38 | 39 | reserveData(triplets, b_vector, DIMENSION_2D); 40 | 41 | triplets.emplace_back(0, 0, STRONG_CONSTRAINT); 42 | triplets.emplace_back(1, 1, STRONG_CONSTRAINT); 43 | b_vector.emplace_back(0, STRONG_CONSTRAINT); 44 | b_vector.emplace_back(1, STRONG_CONSTRAINT); 45 | 46 | prepareAlignmentTerm(triplets); 47 | prepareSimilarityTerm(triplets, b_vector); 48 | 49 | vector > original_vertices; 50 | 51 | original_vertices = getImageVerticesBySolving(triplets, b_vector); 52 | 53 | Size2 target_size = normalizeVertices(original_vertices); 54 | 55 | Mat result = multi_images.textureMapping(original_vertices, target_size, _blend_method); 56 | #ifndef NDEBUG 57 | multi_images.writeResultWithMesh(result, original_vertices, "-[NISwGSP]" + 58 | GLOBAL_ROTATION_METHODS_NAME[getGlobalRotationMethod()] + 59 | BLENDING_METHODS_NAME[_blend_method] + 60 | "[Mesh]", false); 61 | multi_images.writeResultWithMesh(result, original_vertices, "-[NISwGSP]" + 62 | GLOBAL_ROTATION_METHODS_NAME[getGlobalRotationMethod()] + 63 | BLENDING_METHODS_NAME[_blend_method] + 64 | "[Border]", true); 65 | #endif 66 | return result; 67 | } 68 | 69 | Mat NISwGSP_Stitching::solve_line(const BLENDING_METHODS & _blend_method) { 70 | const MultiImages & multi_images = getMultiImages(); 71 | 72 | vector > triplets; 73 | vector > b_vector; 74 | 75 | reserveData_line(triplets, b_vector, DIMENSION_2D); 76 | 77 | triplets.emplace_back(0, 0, STRONG_CONSTRAINT); 78 | triplets.emplace_back(1, 1, STRONG_CONSTRAINT); 79 | b_vector.emplace_back(0, STRONG_CONSTRAINT); 80 | b_vector.emplace_back(1, STRONG_CONSTRAINT); 81 | 82 | prepareAlignmentTerm(triplets); 83 | prepareSimilarityTerm(triplets, b_vector); 84 | prepareLinePreservingTerm(triplets, b_vector); 85 | 86 | vector > original_vertices; 87 | 88 | original_vertices = getImageVerticesBySolving(triplets, b_vector); 89 | 90 | Size2 target_size = normalizeVertices(original_vertices); 91 | 92 | Mat result = multi_images.textureMapping(original_vertices, target_size, _blend_method); 93 | #ifndef NDEBUG 94 | multi_images.writeResultWithMesh(result, original_vertices, "-[NISwGSP]" + 95 | GLOBAL_ROTATION_METHODS_NAME[getGlobalRotationMethod()] + 96 | BLENDING_METHODS_NAME[_blend_method] + 97 | "[Mesh]", false); 98 | multi_images.writeResultWithMesh(result, original_vertices, "-[NISwGSP]" + 99 | GLOBAL_ROTATION_METHODS_NAME[getGlobalRotationMethod()] + 100 | BLENDING_METHODS_NAME[_blend_method] + 101 | "[Border]", true); 102 | #endif 103 | return result; 104 | } 105 | 106 | void NISwGSP_Stitching::writeImage(const Mat & _image, const string _post_name) const { 107 | const MultiImages & multi_images = getMultiImages(); 108 | const Parameter & parameter = multi_images.parameter; 109 | string file_name = parameter.file_name; 110 | 111 | imwrite(parameter.result_dir + file_name + "-" + 112 | "[NISwGSP]" + 113 | GLOBAL_ROTATION_METHODS_NAME[getGlobalRotationMethod()] + 114 | _post_name + 115 | ".png", _image); 116 | } -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/NISwGSP_Stitching.h: -------------------------------------------------------------------------------- 1 | // 2 | // NISwGSP_Stitching.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__NISwGSP_Stitching__ 10 | #define __UglyMan_Stitiching__NISwGSP_Stitching__ 11 | 12 | #include "../Mesh/MeshOptimization.h" 13 | 14 | class NISwGSP_Stitching : public MeshOptimization { 15 | public: 16 | NISwGSP_Stitching(const MultiImages & _multi_images); 17 | 18 | void setWeightToAlignmentTerm(const double _weight); 19 | void setWeightToLocalSimilarityTerm(const double _weight); 20 | void setWeightToGlobalSimilarityTerm(const double _weight_beta, 21 | const double _weight_gamma, 22 | const enum GLOBAL_ROTATION_METHODS _global_rotation_method); 23 | void setWeightToLinePreservingTerm(const double _weight); 24 | 25 | void writeImage(const Mat & _image, const string _post_name) const; 26 | 27 | Mat solve(const BLENDING_METHODS & _blend_method); 28 | Mat solve_line(const BLENDING_METHODS & _blend_method); 29 | private: 30 | }; 31 | 32 | #endif /* defined(__UglyMan_Stitiching__NISwGSP_Stitching__) */ 33 | -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/Parameter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Parameter.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | #include 9 | #include "Parameter.h" 10 | 11 | vector getImageFileFullNamesInDir(const string & dir_name) { 12 | DIR *dir; 13 | struct dirent *ent; 14 | vector result; 15 | 16 | const vector image_formats = { 17 | ".bmp", ".dib", 18 | ".jpeg", ".jpg", ".jpe", ".JPG", 19 | ".jp2", 20 | ".png", ".PNG" 21 | ".pbm", ".pgm", ".ppm", 22 | ".sr", ".ras", 23 | ".tiff", ".tif" }; 24 | 25 | if((dir = opendir (dir_name.c_str())) != NULL) { 26 | while((ent = readdir (dir)) != NULL) { 27 | string file = string(ent->d_name); 28 | for(int i = 0; i < image_formats.size(); ++i) { 29 | if(file.length() > image_formats[i].length() && 30 | image_formats[i].compare(file.substr(file.length() - image_formats[i].length(), 31 | image_formats[i].length())) == 0) { 32 | result.emplace_back(file); 33 | } 34 | } 35 | } 36 | closedir(dir); 37 | } else { 38 | printError("F(getImageFileFullNamesInDir) could not open directory"); 39 | } 40 | return result; 41 | } 42 | 43 | bool isFileExist (const string & name) { 44 | struct stat buffer; 45 | return (stat (name.c_str(), &buffer) == 0); 46 | } 47 | 48 | Parameter::Parameter(const string & _file_name) { 49 | 50 | file_name = _file_name; 51 | file_dir = "./input-data/" + _file_name + "/"; 52 | result_dir = "./input-data/0_results/" + _file_name + "-result/"; 53 | 54 | _mkdir("./input-data/0_results/"); 55 | _mkdir(result_dir.c_str()); 56 | // mkdir("./input-42-data/0_results/", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 57 | // mkdir(result_dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 58 | #ifndef NDEBUG 59 | debug_dir = "./input-data/1_debugs/" + _file_name + "-result/"; 60 | _mkdir("./input-data/1_debugs/"); 61 | _mkdir(debug_dir.c_str()); 62 | // mkdir("./input-42-data/1_debugs/", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 63 | // mkdir(debug_dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 64 | #endif 65 | 66 | stitching_parse_file_name = file_dir + _file_name + "-STITCH-GRAPH.txt"; 67 | 68 | image_file_full_names = getImageFileFullNamesInDir(file_dir); 69 | 70 | /*** configure ***/ 71 | grid_size = GRID_SIZE; 72 | down_sample_image_size = DOWN_SAMPLE_IMAGE_SIZE; 73 | 74 | /*** by file ***/ 75 | 76 | if(isFileExist(stitching_parse_file_name)) { 77 | const InputParser input_parser(stitching_parse_file_name); 78 | 79 | global_homography_max_inliers_dist = input_parser.get("*global_homography_max_inliers_dist" , &GLOBAL_HOMOGRAPHY_MAX_INLIERS_DIST); 80 | local_homogrpahy_max_inliers_dist = input_parser.get( "*local_homogrpahy_max_inliers_dist" , &LOCAL_HOMOGRAPHY_MAX_INLIERS_DIST); 81 | local_homography_min_features_count = input_parser.get< int>( "*local_homography_min_features_count", &LOCAL_HOMOGRAPHY_MIN_FEATURES_COUNT); 82 | 83 | images_count = input_parser.get< int>("images_count"); 84 | center_image_index = input_parser.get< int>("center_image_index"); 85 | center_image_rotation_angle = input_parser.get("center_image_rotation_angle"); 86 | 87 | /*** check ***/ 88 | 89 | assert(image_file_full_names.size() == images_count); 90 | assert(center_image_index >= 0 && center_image_index < images_count); 91 | /*************/ 92 | 93 | images_match_graph_manually.resize(images_count); 94 | for(int i = 0; i < images_count; ++i) { 95 | images_match_graph_manually[i].resize(images_count, false); 96 | vector labels = input_parser.getVec("matching_graph_image_edges-"+to_string(i), false); 97 | for(int j = 0; j < labels.size(); ++j) { 98 | images_match_graph_manually[i][labels[j]] = true; 99 | } 100 | } 101 | 102 | /*** check ***/ 103 | queue que; 104 | vector label(images_count, false); 105 | que.push(center_image_index); 106 | while(que.empty() == false) { 107 | int n = que.front(); 108 | que.pop(); 109 | label[n] = true; 110 | for(int i = 0; i < images_count; ++i) { 111 | if(!label[i] && (images_match_graph_manually[n][i] || images_match_graph_manually[i][n])) { 112 | que.push(i); 113 | } 114 | } 115 | } 116 | assert(std::all_of(label.begin(), label.end(), [](bool i){return i;})); 117 | 118 | /*************/ 119 | 120 | #ifndef NDEBUG 121 | cout << "center_image_index = " << center_image_index << endl; 122 | cout << "center_image_rotation_angle = " << center_image_rotation_angle << endl; 123 | cout << "images_count = " << images_count << endl; 124 | cout << "images_match_graph_manually = " << endl; 125 | for(int i = 0; i < images_match_graph_manually.size(); ++i) { 126 | for(int j = 0; j < images_match_graph_manually[i].size(); ++j) { 127 | cout << images_match_graph_manually[i][j] << " "; 128 | } 129 | cout << endl; 130 | } 131 | #endif 132 | } 133 | } 134 | 135 | const vector > & Parameter::getImagesMatchGraph() const { 136 | if(images_match_graph_manually.empty()) { 137 | printError("F(getImagesMatchGraph) image match graph verification [2] didn't be implemented yet"); 138 | return images_match_graph_automatically; /* TODO */ 139 | } 140 | return images_match_graph_manually; 141 | } 142 | 143 | const vector > & Parameter::getImagesMatchGraphPairList() const { 144 | if(images_match_graph_pair_list.empty()) { 145 | const vector > & images_match_graph = getImagesMatchGraph(); 146 | for(int i = 0; i < images_match_graph.size(); ++i) { 147 | for(int j = 0; j < images_match_graph[i].size(); ++j) { 148 | if(images_match_graph[i][j]) { 149 | images_match_graph_pair_list.emplace_back(i, j); 150 | } 151 | } 152 | } 153 | } 154 | return images_match_graph_pair_list; 155 | } -------------------------------------------------------------------------------- /NISwGSP_line/Stitching/Parameter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Parameter.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Parameter__ 10 | #define __UglyMan_Stitiching__Parameter__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "../Configure.h" 17 | #include "../Util/InputParser.h" 18 | 19 | class Parameter { 20 | public: 21 | Parameter(const string & _file_name); 22 | 23 | string file_name, file_dir; 24 | string stitching_parse_file_name; 25 | 26 | string result_dir, debug_dir; 27 | vector image_file_full_names; 28 | /* configure */ 29 | int grid_size; 30 | int down_sample_image_size; 31 | 32 | /* stitching file */ 33 | double global_homography_max_inliers_dist; 34 | double local_homogrpahy_max_inliers_dist; 35 | int local_homography_min_features_count; 36 | 37 | int images_count; 38 | 39 | int center_image_index; 40 | double center_image_rotation_angle; 41 | 42 | const vector > & getImagesMatchGraph() const; 43 | const vector > & getImagesMatchGraphPairList() const; 44 | private: 45 | mutable vector > images_match_graph_manually; 46 | mutable vector > images_match_graph_automatically; /* TODO */ 47 | mutable vector > images_match_graph_pair_list; 48 | }; 49 | 50 | #endif /* defined(__UglyMan_Stitiching__Parameter__) */ 51 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/Blending.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Blending.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "Blending.h" 10 | 11 | cv::Mat getMatOfLinearBlendWeight(const cv::Mat & image) { 12 | cv::Mat result(image.size(), CV_32FC1, cv::Scalar::all(0)); 13 | for(int y = 0; y < result.rows; ++y) { 14 | int w_y = min(y + 1, result.rows - y); 15 | for(int x = 0; x < result.cols; ++x) { 16 | result.at(y, x) = min(x + 1, result.cols - x) * w_y; 17 | } 18 | } 19 | return result; 20 | } 21 | 22 | vector getMatsLinearBlendWeight(const vector & images) { 23 | vector result; 24 | result.reserve(images.size()); 25 | for(int i = 0; i < images.size(); ++i) { 26 | result.emplace_back(getMatOfLinearBlendWeight(images[i])); 27 | } 28 | return result; 29 | } 30 | 31 | cv::Mat Blending(const vector & images, 32 | const vector & origins, 33 | const Size2 target_size, 34 | const vector & weight_mask, 35 | const bool ignore_weight_mask) { 36 | 37 | cv::Mat result = cv::Mat::zeros(round(target_size.height), round(target_size.width), CV_8UC4); 38 | 39 | vector rects; 40 | rects.reserve(origins.size()); 41 | for(int i = 0; i < origins.size(); ++i) { 42 | rects.emplace_back(origins[i], images[i].size()); 43 | } 44 | for(int y = 0; y < result.rows; ++y) { 45 | for(int x = 0; x < result.cols; ++x) { 46 | cv::Point2i p(x, y); 47 | cv::Vec3f pixel_sum(0, 0, 0); 48 | float weight_sum = 0.f; 49 | for(int i = 0; i < rects.size(); ++i) { 50 | cv::Point2i pv(round(x - origins[i].x), round(y - origins[i].y)); 51 | if(pv.x >= 0 && pv.x < images[i].cols && 52 | pv.y >= 0 && pv.y < images[i].rows) { 53 | cv::Vec4b v = images[i].at(pv); 54 | cv::Vec3f value = cv::Vec3f(v[0], v[1], v[2]); 55 | if(ignore_weight_mask) { 56 | if(v[3] > 127) { 57 | pixel_sum += value; 58 | weight_sum += 1.f; 59 | } 60 | } else { 61 | float weight = weight_mask[i].at(pv); 62 | pixel_sum += weight * value; 63 | weight_sum += weight; 64 | } 65 | } 66 | } 67 | if(weight_sum) { 68 | pixel_sum /= weight_sum; 69 | result.at(p) = cv::Vec4b(round(pixel_sum[0]), round(pixel_sum[1]), round(pixel_sum[2]), 255); 70 | } 71 | } 72 | } 73 | return result; 74 | } -------------------------------------------------------------------------------- /NISwGSP_line/Util/Blending.h: -------------------------------------------------------------------------------- 1 | // 2 | // Blending.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Blending__ 10 | #define __UglyMan_Stitiching__Blending__ 11 | 12 | #include "../Configure.h" 13 | 14 | cv::Mat getMatOfLinearBlendWeight(const cv::Mat & image); 15 | 16 | vector getMatsLinearBlendWeight(const vector & images); 17 | 18 | cv::Mat Blending(const vector & images, 19 | const vector & origins, 20 | const Size2 target_size, 21 | const vector & weight_mask, 22 | const bool ignore_weight_mask = true); 23 | 24 | #endif /* defined(__UglyMan_Stitiching__Blending__) */ 25 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/InputParser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // InputParser.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "InputParser.h" 10 | 11 | class StrTok { 12 | public: 13 | StrTok(const string & str_input, 14 | const string & str_delim = " ,") { 15 | string::size_type nHead, nTail; 16 | nHead = str_input.find_first_not_of(str_delim, 0); 17 | nTail = str_input.find_first_of(str_delim, nHead); 18 | 19 | while(nHead != string::npos || nTail != string::npos) { 20 | tokened.emplace_back(str_input.substr(nHead, nTail - nHead)); 21 | nHead = str_input.find_first_not_of(str_delim, nTail); 22 | nTail = str_input.find_first_of(str_delim, nHead); 23 | } 24 | } 25 | std::vector tokened; 26 | }; 27 | 28 | InputParser::InputParser(const string & file_name) { 29 | ifstream file(file_name); 30 | string line; 31 | 32 | while(getline(file, line)) { 33 | StrTok st(line, " {|}\n\r\t"); 34 | data[st.tokened[0]] = st.tokened[1]; 35 | } 36 | file.close(); 37 | } 38 | 39 | template 40 | T transfer(const string & str) { 41 | T result; 42 | try { 43 | if(std::is_same::value) { 44 | result = stoi(str); 45 | } else if(std::is_same::value) { 46 | result = stof(str); 47 | } else if(std::is_same::value) { 48 | result = stod(str); 49 | } else { 50 | printError("F(transfer) transfer function"); 51 | } 52 | } catch (const invalid_argument & inv_argument) { 53 | printError("F(transfer) invalid argument: " + string(inv_argument.what())); 54 | } 55 | return result; 56 | } 57 | 58 | template<> 59 | string transfer(const string & str) { 60 | return str; 61 | } 62 | 63 | template 64 | T InputParser::get(const string & key, const T * ptr) const { 65 | T result; 66 | if(data.find(key) == data.end()) { 67 | if(ptr == NULL) { 68 | printError("F(get) key error: " + key); 69 | } else { 70 | result = *ptr; 71 | } 72 | } else { 73 | const string value = data.at(key); 74 | result = transfer(value); 75 | } 76 | return result; 77 | } 78 | 79 | template 80 | vector InputParser::getVec(const string & key, 81 | const bool sure_exist) const { 82 | vector result; 83 | if(data.find(key) == data.end()) { 84 | if(sure_exist) { 85 | printError("F(getVec) key error: " + key); 86 | } 87 | } else { 88 | const string value = data.at(key); 89 | StrTok st(value, " (,)\n\r\t"); 90 | result.reserve(st.tokened.size()); 91 | for(int i = 0; i < st.tokened.size(); ++i) { 92 | result.emplace_back(transfer(st.tokened[i])); 93 | } 94 | } 95 | return result; 96 | } 97 | 98 | template int InputParser::get< int>(const string & key, const int * ptr) const; 99 | template float InputParser::get< float>(const string & key, const float * ptr) const; 100 | template double InputParser::get(const string & key, const double * ptr) const; 101 | template string InputParser::get(const string & key, const string * ptr) const; 102 | 103 | template vector< int> InputParser::getVec< int>(const string & key, const bool sure_exist) const; 104 | template vector< float> InputParser::getVec< float>(const string & key, const bool sure_exist) const; 105 | template vector InputParser::getVec(const string & key, const bool sure_exist) const; 106 | template vector InputParser::getVec(const string & key, const bool sure_exist) const; 107 | 108 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/InputParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // InputParser.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__InputParser__ 10 | #define __UglyMan_Stitiching__InputParser__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../Debugger/ErrorController.h" 19 | 20 | using namespace std; 21 | 22 | class InputParser { 23 | public: 24 | template 25 | T get(const string & key, const T * ptr = NULL) const; 26 | 27 | template 28 | vector getVec(const string & key, 29 | const bool sure_exist = true) const; 30 | 31 | InputParser(const string & file_name); 32 | private: 33 | map data; 34 | 35 | }; 36 | 37 | #endif /* defined(__UglyMan_Stitiching__InputParser__) */ 38 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/Statistics.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Statistics.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include "Statistics.h" 10 | 11 | template 12 | void Statistics::getMeanAndVariance(const vector & _vec, 13 | double & _mean, double & _var) { 14 | _mean = 0, _var = 0; 15 | 16 | const int count = (int)_vec.size(); 17 | for(int i = 0; i < count; ++i) { 18 | _mean += _vec[i]; 19 | } _mean /= count; 20 | 21 | for(int i = 0; i < count; ++i) { 22 | _var += (_vec[i] * _vec[i]); 23 | } 24 | _var = (_var / count) - (_mean * _mean); 25 | 26 | } 27 | 28 | template 29 | void Statistics::getMeanAndSTD(const vector & _vec, 30 | double & _mean, double & _std) { 31 | getMeanAndVariance(_vec, _mean, _std); 32 | _std = sqrt(_std); 33 | 34 | } 35 | 36 | template 37 | void Statistics::getMin(const vector & _vec, double & _min) { 38 | _min = *std::min_element(_vec.begin(), _vec.end()); 39 | } 40 | 41 | template 42 | void Statistics::getMax(const vector & _vec, double & _max) { 43 | _max = *std::max_element(_vec.begin(), _vec.end()); 44 | } 45 | 46 | template 47 | void Statistics::getMinAndMax(const vector & _vec, double & _min, double & _max) { 48 | getMin(_vec, _min); 49 | getMax(_vec, _max); 50 | } 51 | 52 | template 53 | void Statistics::getMedianWithCopyData(const vector & _vec, double & _median) { 54 | vector v = _vec; 55 | getMedianWithoutCopyData(v, _median); 56 | } 57 | 58 | template 59 | void Statistics::getMedianWithoutCopyData(vector & _vec, double & _median) { 60 | size_t n = _vec.size() / 2; 61 | std::nth_element(_vec.begin(), _vec.begin() + n, _vec.end()); 62 | _median = _vec[n]; 63 | if((_vec.size() & 1) == 0) { 64 | std::nth_element(_vec.begin(), _vec.begin() + n - 1, _vec.end()); 65 | _median = (_median + _vec[n-1]) * 0.5; 66 | } 67 | } 68 | 69 | template 70 | Statistics::Statistics(const vector & _vec) { 71 | getMeanAndVariance(_vec, mean, var); 72 | std = sqrt(var); 73 | getMinAndMax(_vec, min, max); 74 | } 75 | 76 | template void Statistics::getMeanAndVariance< int>(const vector< int> & _vec, double & _mean, double & _var); 77 | template void Statistics::getMeanAndVariance< float>(const vector< float> & _vec, double & _mean, double & _var); 78 | template void Statistics::getMeanAndVariance(const vector & _vec, double & _mean, double & _var); 79 | 80 | template void Statistics::getMeanAndSTD< int>(const vector< int> & _vec, double & _mean, double & _std); 81 | template void Statistics::getMeanAndSTD< float>(const vector< float> & _vec, double & _mean, double & _std); 82 | template void Statistics::getMeanAndSTD(const vector & _vec, double & _mean, double & _std); 83 | 84 | template void Statistics::getMin< int>(const vector< int> & _vec, double & _min); 85 | template void Statistics::getMin< float>(const vector< float> & _vec, double & _min); 86 | template void Statistics::getMin(const vector & _vec, double & _min); 87 | 88 | template void Statistics::getMax< int>(const vector< int> & _vec, double & _max); 89 | template void Statistics::getMax< float>(const vector< float> & _vec, double & _max); 90 | template void Statistics::getMax(const vector & _vec, double & _max); 91 | 92 | template void Statistics::getMinAndMax< int>(const vector< int> & _vec, double & _min, double & _max); 93 | template void Statistics::getMinAndMax< float>(const vector< float> & _vec, double & _min, double & _max); 94 | template void Statistics::getMinAndMax(const vector & _vec, double & _min, double & _max); 95 | 96 | template void Statistics::getMedianWithCopyData< int>(const vector< int> & _vec, double & _median); 97 | template void Statistics::getMedianWithCopyData< float>(const vector< float> & _vec, double & _median); 98 | template void Statistics::getMedianWithCopyData(const vector & _vec, double & _median); 99 | 100 | template void Statistics::getMedianWithoutCopyData< int>(vector< int> & _vec, double & _median); 101 | template void Statistics::getMedianWithoutCopyData< float>(vector< float> & _vec, double & _median); 102 | template void Statistics::getMedianWithoutCopyData(vector & _vec, double & _median); 103 | 104 | template Statistics::Statistics(const vector< int> & _vec); 105 | template Statistics::Statistics(const vector< float> & _vec); 106 | template Statistics::Statistics(const vector & _vec); 107 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/Statistics.h: -------------------------------------------------------------------------------- 1 | // 2 | // Statistics.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Statistics__ 10 | #define __UglyMan_Stitiching__Statistics__ 11 | 12 | #include 13 | #include "../Debugger/ErrorController.h" 14 | #include "../Configure.h" 15 | 16 | class Statistics { 17 | public: 18 | template 19 | static void getMeanAndVariance(const vector & _vec, double & _mean, double & _var); 20 | 21 | template 22 | static void getMeanAndSTD(const vector & _vec, double & _mean, double & _std); 23 | 24 | template 25 | static void getMin(const vector & _vec, double & _min); 26 | 27 | template 28 | static void getMax(const vector & _vec, double & _max); 29 | 30 | template 31 | static void getMinAndMax(const vector & _vec, double & _min, double & _max); 32 | 33 | template 34 | static void getMedianWithCopyData(const vector & _vec, double & _median); 35 | 36 | template 37 | static void getMedianWithoutCopyData(vector & _vec, double & _median); 38 | 39 | template 40 | Statistics(const vector & _vec); 41 | 42 | double mean, var, std, min, max; 43 | private: 44 | 45 | }; 46 | 47 | #endif /* defined(__UglyMan_Stitiching__Statistics__) */ 48 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/Transform.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Transform.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #define _USE_MATH_DEFINES // for C++ 10 | #include 11 | 12 | #include "Transform.h" 13 | 14 | cv::Mat getConditionerFromPts(const vector & pts) { 15 | cv::Mat pts_ref(pts); 16 | cv::Scalar mean_pts, std_pts; 17 | meanStdDev(pts_ref, mean_pts, std_pts); 18 | 19 | std_pts = (std_pts.mul(std_pts) * pts_ref.rows / (double)(pts_ref.rows - 1)); 20 | sqrt(std_pts, std_pts); 21 | 22 | std_pts.val[0] = std_pts.val[0] + (std_pts.val[0] == 0); 23 | std_pts.val[1] = std_pts.val[1] + (std_pts.val[1] == 0); 24 | 25 | cv::Mat result(3, 3, CV_64FC1); 26 | result.at(0, 0) = sqrt(2) / (double)std_pts.val[0]; 27 | result.at(0, 1) = 0; 28 | result.at(0, 2) = -(sqrt(2) / (double)std_pts.val[0]) * (double)mean_pts.val[0]; 29 | 30 | result.at(1, 0) = 0; 31 | result.at(1, 1) = sqrt(2) / (double)std_pts.val[1]; 32 | result.at(1, 2) = -(sqrt(2) / (double)std_pts.val[1]) * (double)mean_pts.val[1]; 33 | 34 | result.at(2, 0) = 0; 35 | result.at(2, 1) = 0; 36 | result.at(2, 2) = 1; 37 | 38 | return result; 39 | } 40 | cv::Mat getNormalize2DPts(const vector & pts, vector & newpts) { 41 | cv::Mat pts_ref(pts), npts; 42 | cv::Scalar mean_p = mean(pts_ref); 43 | npts = pts_ref - mean_p; 44 | cv::Mat dist = npts.mul(npts); 45 | dist = dist.reshape(1); 46 | sqrt(dist.col(0) + dist.col(1), dist); 47 | double scale = sqrt(2) / mean(dist).val[0]; 48 | 49 | cv::Mat result(3, 3, CV_64FC1); 50 | result.at(0, 0) = scale; 51 | result.at(0, 1) = 0; 52 | result.at(0, 2) = -scale * (double)mean_p.val[0]; 53 | 54 | result.at(1, 0) = 0; 55 | result.at(1, 1) = scale; 56 | result.at(1, 2) = -scale * (double)mean_p.val[1]; 57 | 58 | result.at(2, 0) = 0; 59 | result.at(2, 1) = 0; 60 | result.at(2, 2) = 1; 61 | 62 | #ifndef NDEBUG 63 | if(newpts.empty() == false) { 64 | newpts.clear(); 65 | printError("F(getNormalize2DPts) newpts is not empty"); 66 | } 67 | #endif 68 | newpts.reserve(pts.size()); 69 | for(int i = 0; i < pts.size(); ++i) { 70 | newpts.emplace_back(pts[i].x * result.at(0, 0) + result.at(0, 2), 71 | pts[i].y * result.at(1, 1) + result.at(1, 2)); 72 | } 73 | 74 | return result; 75 | } 76 | 77 | template 78 | T normalizeAngle(T x) { 79 | x = fmod(x + 180, 360); 80 | if (x < 0) { 81 | x += 360; 82 | } 83 | return x - 180; 84 | } 85 | 86 | template 87 | cv::Point_ applyTransform3x3(T x, T y, const cv::Mat & matT) { 88 | double denom = 1. / (matT.at(2, 0) * x + matT.at(2, 1) * y + matT.at(2, 2)); 89 | return cv::Point_((matT.at(0, 0) * x + matT.at(0, 1) * y + matT.at(0, 2)) * denom, 90 | (matT.at(1, 0) * x + matT.at(1, 1) * y + matT.at(1, 2)) * denom); 91 | } 92 | 93 | template 94 | cv::Point_ applyTransform2x3(T x, T y, const cv::Mat & matT) { 95 | return cv::Point_((matT.at(0, 0) * x + matT.at(0, 1) * y + matT.at(0, 2)), 96 | (matT.at(1, 0) * x + matT.at(1, 1) * y + matT.at(1, 2))); 97 | } 98 | 99 | template 100 | cv::Size_ normalizeVertices(vector > > & vertices) { 101 | T min_x = std::numeric_limits::max(), max_x = -std::numeric_limits::max(); 102 | T min_y = std::numeric_limits::max(), max_y = -std::numeric_limits::max(); 103 | for(int i = 0; i < vertices.size(); ++i) { 104 | for(int j = 0; j < vertices[i].size(); ++j) { 105 | min_x = min(min_x, vertices[i][j].x); 106 | min_y = min(min_y, vertices[i][j].y); 107 | max_x = max(max_x, vertices[i][j].x); 108 | max_y = max(max_y, vertices[i][j].y); 109 | } 110 | } 111 | for(int i = 0; i < vertices.size(); ++i) { 112 | for(int j = 0; j < vertices[i].size(); ++j) { 113 | vertices[i][j].x = (vertices[i][j].x - min_x); 114 | vertices[i][j].y = (vertices[i][j].y - min_y); 115 | } 116 | } 117 | return cv::Size_(max_x - min_x, max_y - min_y); 118 | } 119 | 120 | template 121 | cv::Rect_ getVerticesRects(const vector > & vertices) { 122 | vector > > tmp(1, vertices); 123 | return getVerticesRects(tmp).front(); 124 | } 125 | 126 | template 127 | vector > getVerticesRects(const vector > > & vertices) { 128 | vector > result; 129 | result.reserve(vertices.size()); 130 | for(int i = 0; i < vertices.size(); ++i) { 131 | T min_ix = MAXFLOAT, max_ix = -MAXFLOAT; 132 | T min_iy = MAXFLOAT, max_iy = -MAXFLOAT; 133 | for(int j = 0; j < vertices[i].size(); ++j) { 134 | min_ix = min(min_ix, vertices[i][j].x); 135 | max_ix = max(max_ix, vertices[i][j].x); 136 | min_iy = min(min_iy, vertices[i][j].y); 137 | max_iy = max(max_iy, vertices[i][j].y); 138 | } 139 | result.emplace_back(min_ix, min_iy, 140 | max_ix - min_ix, max_iy - min_iy); 141 | } 142 | return result; 143 | } 144 | 145 | template 146 | T getSubpix(const cv::Mat & img, const cv::Point2f & pt) { 147 | cv::Mat patch; 148 | cv::getRectSubPix(img, cv::Size(1,1), pt, patch); 149 | return patch.at(0,0); 150 | } 151 | 152 | template 153 | cv::Vec getSubpix(const cv::Mat & img, const cv::Point2f & pt) { 154 | cv::Mat patch; 155 | cv::getRectSubPix(img, cv::Size(1,1), pt, patch); 156 | return patch.at >(0,0); 157 | } 158 | 159 | template 160 | cv::Vec getEulerZXYRadians(const cv::Mat_ & rot_matrix) { 161 | const T r00 = rot_matrix.template at(0, 0); 162 | const T r01 = rot_matrix.template at(0, 1); 163 | const T r02 = rot_matrix.template at(0, 2); 164 | const T r10 = rot_matrix.template at(1, 0); 165 | const T r11 = rot_matrix.template at(1, 1); 166 | const T r12 = rot_matrix.template at(1, 2); 167 | const T r22 = rot_matrix.template at(2, 2); 168 | 169 | cv::Vec result; 170 | if(r12 < 1) { 171 | if(r12 > -1) { 172 | result[0] = asin(-r12); 173 | result[1] = atan2(r02, r22); 174 | result[2] = atan2(r10, r11); 175 | } else { 176 | result[0] = M_PI_2; 177 | result[1] = -atan2(-r01, r00); 178 | result[2] = 0.; 179 | } 180 | } else { 181 | result[0] = -M_PI_2; 182 | result[1] = -atan2(-r01, r00); 183 | result[2] = 0.; 184 | } 185 | return result; 186 | } 187 | 188 | template 189 | bool isEdgeIntersection(const cv::Point_ & src_1, const cv::Point_ & dst_1, 190 | const cv::Point_ & src_2, const cv::Point_ & dst_2, 191 | double * scale_1, double * scale_2) { 192 | const cv::Point_ s1 = dst_1 - src_1, s2 = dst_2 - src_2; 193 | const double denom = -s2.x * s1.y + s1.x * s2.y; 194 | 195 | if(denom < std::numeric_limits::epsilon() && 196 | denom > -std::numeric_limits::epsilon()) { 197 | return false; 198 | } 199 | 200 | double tmp_scale_1 = ( s2.x * (src_1.y - src_2.y) - s2.y * (src_1.x - src_2.x)) / denom; 201 | double tmp_scale_2 = (-s1.y * (src_1.x - src_2.x) + s1.x * (src_1.y - src_2.y)) / denom; 202 | 203 | if(scale_1) *scale_1 = tmp_scale_1; 204 | if(scale_2) *scale_2 = tmp_scale_2; 205 | 206 | return (tmp_scale_1 >= 0 && tmp_scale_1 <= 1 && 207 | tmp_scale_2 >= 0 && tmp_scale_2 <= 1); 208 | } 209 | 210 | template 211 | bool isRotationInTheRange(const T rotation, const T min_rotation, const T max_rotation) { 212 | const cv::Point_ b(cos(rotation), sin(rotation)); 213 | const cv::Point_ a(cos(min_rotation), sin(min_rotation)); 214 | const cv::Point_ c(cos(max_rotation), sin(max_rotation)); 215 | const T direction_a_b = a.x * b.y - a.y * b.x; 216 | const T direction_a_c = a.x * c.y - a.y * c.x; 217 | const T direction_b_c = b.x * c.y - b.y * c.x; 218 | 219 | return (direction_a_b * direction_a_c >= 0) && (direction_a_b * direction_b_c >= 0); 220 | } 221 | 222 | template float normalizeAngle< float>( float x); 223 | template double normalizeAngle(double x); 224 | 225 | template cv::Point_< float> applyTransform3x3< float>( float x, float y, const cv::Mat & matT); 226 | template cv::Point_ applyTransform3x3(double x, double y, const cv::Mat & matT); 227 | 228 | template cv::Point_< float> applyTransform2x3< float>( float x, float y, const cv::Mat & matT); 229 | template cv::Point_ applyTransform2x3(double x, double y, const cv::Mat & matT); 230 | 231 | template cv::Size_< int> normalizeVertices< int>(vector > > & vertices); 232 | template cv::Size_< float> normalizeVertices< float>(vector > > & vertices); 233 | template cv::Size_ normalizeVertices(vector > > & vertices); 234 | 235 | template cv::Rect_< float> getVerticesRects< float>(const vector > & vertices); 236 | template cv::Rect_ getVerticesRects(const vector > & vertices); 237 | 238 | template vector > getVerticesRects< float>(const vector > > & vertices); 239 | template vector > getVerticesRects(const vector > > & vertices); 240 | 241 | template float getSubpix< float>(const cv::Mat & img, const cv::Point2f & pt); 242 | template cv::Vec< uchar, 1> getSubpix(const cv::Mat & img, const cv::Point2f & pt); 243 | template cv::Vec< uchar, 3> getSubpix(const cv::Mat & img, const cv::Point2f & pt); 244 | 245 | template cv::Vec< float, 3> getEulerZXYRadians< float>(const cv::Mat_< float> & rot_matrix); 246 | template cv::Vec getEulerZXYRadians(const cv::Mat_ & rot_matrix); 247 | 248 | template bool isEdgeIntersection< float>(const cv::Point_< float> & src_1, const cv::Point_< float> & dst_1, 249 | const cv::Point_< float> & src_2, const cv::Point_< float> & dst_2, 250 | double * scale_1, double * scale_2); 251 | 252 | template bool isEdgeIntersection(const cv::Point_ & src_1, const cv::Point_ & dst_1, 253 | const cv::Point_ & src_2, const cv::Point_ & dst_2, 254 | double * scale_1, double * scale_2); 255 | 256 | template bool isRotationInTheRange< float>(const float rotation, const float min_rotation, const float max_rotation); 257 | template bool isRotationInTheRange(const double rotation, const double min_rotation, const double max_rotation); 258 | 259 | 260 | -------------------------------------------------------------------------------- /NISwGSP_line/Util/Transform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Transform.h 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #ifndef __UglyMan_Stitiching__Transform__ 10 | #define __UglyMan_Stitiching__Transform__ 11 | 12 | #include "../Configure.h" 13 | 14 | cv::Mat getConditionerFromPts(const vector & pts); 15 | 16 | cv::Mat getNormalize2DPts(const vector & pts, 17 | vector & newpts); 18 | 19 | template 20 | T normalizeAngle(T x); 21 | 22 | template 23 | cv::Point_ applyTransform3x3(T x, T y, const cv::Mat & matT); 24 | 25 | template 26 | cv::Point_ applyTransform2x3(T x, T y, const cv::Mat & matT); 27 | 28 | template 29 | cv::Size_ normalizeVertices(vector > > & vertices); 30 | 31 | template 32 | cv::Rect_ getVerticesRects(const vector > & vertices); 33 | 34 | template 35 | vector > getVerticesRects(const vector > > & vertices); 36 | 37 | template 38 | T getSubpix(const cv::Mat & img, const cv::Point2f & pt); 39 | 40 | template 41 | cv::Vec getSubpix(const cv::Mat & img, const cv::Point2f & pt); 42 | 43 | template 44 | cv::Vec getEulerZXYRadians(const cv::Mat_ & rot_matrix); 45 | 46 | template 47 | bool isEdgeIntersection(const cv::Point_ & src_1, const cv::Point_ & dst_1, 48 | const cv::Point_ & src_2, const cv::Point_ & dst_2, 49 | double * scale_1 = NULL, double * scale_2 = NULL); 50 | template 51 | bool isRotationInTheRange(const T rotation, const T min_rotation, const T max_rotation); 52 | 53 | #endif /* defined(__UglyMan_Stitiching__Transform__) */ 54 | -------------------------------------------------------------------------------- /NISwGSP_line/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // UglyMan_Stitching 4 | // 5 | // Created by uglyman.nothinglo on 2015/8/15. 6 | // Copyright (c) 2015 nothinglo. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "./Stitching/NISwGSP_Stitching.h" 11 | #include "./Debugger/TimeCalculator.h" 12 | 13 | using namespace std; 14 | 15 | int main(int argc, const char * argv[]) { 16 | Eigen::initParallel(); /* remember to turn off "Hardware Multi-Threading */ 17 | cout << "nThreads = " << Eigen::nbThreads() << endl; 18 | cout << "[#Images : " << argc - 1 << "]" << endl; 19 | 20 | cout << "Turn on interactive mode? Press 'y'." << endl; 21 | string flag; 22 | getline(cin, flag); 23 | if (flag == "Y" || flag == "y") { LINE_INTERACTIVE = true; } 24 | 25 | TimeCalculator timer; 26 | for(int i = 1; i < argc; ++i) { 27 | cout << "i = " << i << ", [Images : " << argv[i] << "]" << endl; 28 | MultiImages multi_images(argv[i], LINES_FILTER_WIDTH, LINES_FILTER_LENGTH); 29 | 30 | timer.start(); 31 | /* 2D */ 32 | NISwGSP_Stitching niswgsp(multi_images); 33 | niswgsp.setWeightToAlignmentTerm(1); 34 | niswgsp.setWeightToLocalSimilarityTerm(0.75); 35 | niswgsp.setWeightToGlobalSimilarityTerm(6, 20, GLOBAL_ROTATION_2D_METHOD); 36 | niswgsp.setWeightToLinePreservingTerm(1.1); 37 | niswgsp.writeImage(niswgsp.solve_line(BLEND_AVERAGE), BLENDING_METHODS_NAME[BLEND_AVERAGE]); 38 | niswgsp.writeImage(niswgsp.solve_line(BLEND_LINEAR), BLENDING_METHODS_NAME[BLEND_LINEAR]); 39 | /* 3D */ 40 | /*niswgsp.setWeightToAlignmentTerm(1); 41 | niswgsp.setWeightToLocalSimilarityTerm(0.75); 42 | niswgsp.setWeightToGlobalSimilarityTerm(6, 20, GLOBAL_ROTATION_3D_METHOD); 43 | niswgsp.setWeightToLinePreservingTerm(1.1); 44 | niswgsp.writeImage(niswgsp.solve_line(BLEND_AVERAGE), BLENDING_METHODS_NAME[BLEND_AVERAGE]); 45 | niswgsp.writeImage(niswgsp.solve_line(BLEND_LINEAR), BLENDING_METHODS_NAME[BLEND_LINEAR]);*/ 46 | timer.end("[NISwGSP] " + multi_images.parameter.file_name); 47 | } 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NISwGSP_line 2 | 3 | A line preserving term is added to the NISwGSP algorithm to protect the typical line structure from being distorted by the mesh deformation process. 4 | 5 | Reference: 6 | 7 | He C, Zhou J . Mesh-based image stitching algorithm with linear structure protection[J]. Journal of Image and Graphics, 2018, 23(7): 973-983. 8 | 9 | 直线检测与采样结果: 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 运行结果: 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
NISwGSP
NISwGSP_line
25 | NISwGSP
NISwGSP_line
NISwGSP
NISwGSP_line(交互方式下)
NISwGSP
NISwGSP_line(交互方式下)
37 | -------------------------------------------------------------------------------- /readme.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breadcake/NISwGSP_line/cb055bd4decde3e0ba9ccdf97e584728ea5aa2ae/readme.docx --------------------------------------------------------------------------------