added saving of matches graph into opencv_stitching (in DOT format)
authorAlexey Spizhevoy <no@email>
Mon, 15 Aug 2011 08:19:57 +0000 (08:19 +0000)
committerAlexey Spizhevoy <no@email>
Mon, 15 Aug 2011 08:19:57 +0000 (08:19 +0000)
modules/stitching/main.cpp
modules/stitching/matchers.hpp
modules/stitching/motion_estimators.cpp
modules/stitching/motion_estimators.hpp
modules/stitching/util.hpp

index 76a4c44..573f685 100644 (file)
@@ -48,6 +48,7 @@
 // 3) Automatic Panoramic Image Stitching using Invariant Features. \r
 //    Matthew Brown and David G. Lowe. 2007.\r
 \r
+#include <fstream>\r
 #include "precomp.hpp"\r
 #include "util.hpp"\r
 #include "warpers.hpp"\r
@@ -83,6 +84,10 @@ void printUsage()
         "      Bundle adjustment cost function. The default is 'focal_ray'.\n"\r
         "  --wave_correct (no|yes)\n"\r
         "      Perform wave effect correction. The default is 'yes'.\n"\r
+        "  --save_graph <file_name>\n"\r
+        "      Save matches graph represented in DOT language to <file_name> file.\n"\r
+        "      Labels description: Nm is number of matches, Ni is number of inliers,\n"\r
+        "                          C is confidence.\n"\r
         "\nCompositing Flags:\n"\r
         "  --warp (plane|cylindrical|spherical)\n" \r
         "      Warp surface type. The default is 'spherical'.\n"\r
@@ -114,6 +119,8 @@ double compose_megapix = -1;
 int ba_space = BundleAdjuster::FOCAL_RAY_SPACE;\r
 float conf_thresh = 1.f;\r
 bool wave_correct = true;\r
+bool save_graph = false;\r
+std::string save_graph_to;\r
 int warp_type = Warper::SPHERICAL;\r
 int expos_comp_type = ExposureCompensator::GAIN_BLOCKS;\r
 float match_conf = 0.65f;\r
@@ -209,6 +216,12 @@ int parseCmdArgs(int argc, char** argv)
             }\r
             i++;\r
         }\r
+        else if (string(argv[i]) == "--save_graph")\r
+        {\r
+            save_graph = true;\r
+            save_graph_to = argv[i + 1];\r
+            i++;\r
+        }\r
         else if (string(argv[i]) == "--warp")\r
         {\r
             if (string(argv[i + 1]) == "plane")\r
@@ -378,6 +391,14 @@ int main(int argc, char* argv[])
     matcher.releaseMemory();\r
     LOGLN("Pairwise matching, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");\r
 \r
+    // Check if we should save matches graph\r
+    if (save_graph)\r
+    {\r
+        LOGLN("Saving matches graph...");\r
+        ofstream f(save_graph_to.c_str());\r
+        f << matchesGraphAsString(img_names, pairwise_matches, conf_thresh);\r
+    }\r
+\r
     // Leave only images we are sure are from the same panorama\r
     vector<int> indices = leaveBiggestComponent(features, pairwise_matches, conf_thresh);\r
     vector<Mat> img_subset;\r
index fe7fbe0..f35dcd4 100644 (file)
 // or tort (including negligence or otherwise) arising in any way out of\r
 // the use of this software, even if advised of the possibility of such damage.\r
 //\r
-//M*/
-#ifndef __OPENCV_MATCHERS_HPP__
-#define __OPENCV_MATCHERS_HPP__
-
-#include "precomp.hpp"
-
-struct ImageFeatures
-{    
-    int img_idx;
-    cv::Size img_size;
-    std::vector<cv::KeyPoint> keypoints;
-    cv::Mat descriptors;
-};
-
-
-class FeaturesFinder
-{
-public:
-    virtual ~FeaturesFinder() {}
-    void operator ()(const cv::Mat &image, ImageFeatures &features);
-
-    virtual void releaseMemory() {}
-
-protected:
-    virtual void find(const cv::Mat &image, ImageFeatures &features) = 0;
-};
-
-
-class SurfFeaturesFinder : public FeaturesFinder
-{
-public:
-    SurfFeaturesFinder(bool try_use_gpu = true, double hess_thresh = 300.0, 
-                       int num_octaves = 3, int num_layers = 4, 
-                       int num_octaves_descr = 4, int num_layers_descr = 2);
-
-    void releaseMemory();
-
-protected:
-    void find(const cv::Mat &image, ImageFeatures &features);
-
-    cv::Ptr<FeaturesFinder> impl_;
-};
-
-
-struct MatchesInfo
-{
-    MatchesInfo();
-    MatchesInfo(const MatchesInfo &other);
-    const MatchesInfo& operator =(const MatchesInfo &other);
-
-    int src_img_idx, dst_img_idx;       // Images indices (optional)
-    std::vector<cv::DMatch> matches;
-    std::vector<uchar> inliers_mask;    // Geometrically consistent matches mask
-    int num_inliers;                    // Number of geometrically consistent matches
-    cv::Mat H;                          // Estimated homography
-    double confidence;                  // Confidence two images are from the same panorama
-};
-
-
-class FeaturesMatcher
-{
-public:
-    virtual ~FeaturesMatcher() {}
-
-    void operator ()(const ImageFeatures &features1, const ImageFeatures &features2, 
-                     MatchesInfo& matches_info) { match(features1, features2, matches_info); }
-    void operator ()(const std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches);
-
-    bool isThreadSafe() const { return is_thread_safe_; }
-
-    virtual void releaseMemory() {}
-
-protected:
-    FeaturesMatcher(bool is_thread_safe = false) : is_thread_safe_(is_thread_safe) {}
-
-    virtual void match(const ImageFeatures &features1, const ImageFeatures &features2, 
-                       MatchesInfo& matches_info) = 0;
-
-    bool is_thread_safe_;
-};
-
-
-class BestOf2NearestMatcher : public FeaturesMatcher
-{
-public:
-    BestOf2NearestMatcher(bool try_use_gpu = true, float match_conf = 0.55f, int num_matches_thresh1 = 6, 
-                          int num_matches_thresh2 = 6);
-
-    void releaseMemory();
-
-protected:
-    void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo &matches_info);
-
-    int num_matches_thresh1_;
-    int num_matches_thresh2_;
-    cv::Ptr<FeaturesMatcher> impl_;
-};
-
-#endif // __OPENCV_MATCHERS_HPP__
\ No newline at end of file
+//M*/\r
+#ifndef __OPENCV_MATCHERS_HPP__\r
+#define __OPENCV_MATCHERS_HPP__\r
+\r
+#include "precomp.hpp"\r
+\r
+struct ImageFeatures\r
+{\r
+    int img_idx;\r
+    cv::Size img_size;\r
+    std::vector<cv::KeyPoint> keypoints;\r
+    cv::Mat descriptors;\r
+};\r
+\r
+\r
+class FeaturesFinder\r
+{\r
+public:\r
+    virtual ~FeaturesFinder() {}\r
+    void operator ()(const cv::Mat &image, ImageFeatures &features);\r
+\r
+    virtual void releaseMemory() {}\r
+\r
+protected:\r
+    virtual void find(const cv::Mat &image, ImageFeatures &features) = 0;\r
+};\r
+\r
+\r
+class SurfFeaturesFinder : public FeaturesFinder\r
+{\r
+public:\r
+    SurfFeaturesFinder(bool try_use_gpu = true, double hess_thresh = 300.0, \r
+                       int num_octaves = 3, int num_layers = 4, \r
+                       int num_octaves_descr = 4, int num_layers_descr = 2);\r
+\r
+    void releaseMemory();\r
+\r
+protected:\r
+    void find(const cv::Mat &image, ImageFeatures &features);\r
+\r
+    cv::Ptr<FeaturesFinder> impl_;\r
+};\r
+\r
+\r
+struct MatchesInfo\r
+{\r
+    MatchesInfo();\r
+    MatchesInfo(const MatchesInfo &other);\r
+    const MatchesInfo& operator =(const MatchesInfo &other);\r
+\r
+    int src_img_idx, dst_img_idx;       // Images indices (optional)\r
+    std::vector<cv::DMatch> matches;\r
+    std::vector<uchar> inliers_mask;    // Geometrically consistent matches mask\r
+    int num_inliers;                    // Number of geometrically consistent matches\r
+    cv::Mat H;                          // Estimated homography\r
+    double confidence;                  // Confidence two images are from the same panorama\r
+};\r
+\r
+\r
+class FeaturesMatcher\r
+{\r
+public:\r
+    virtual ~FeaturesMatcher() {}\r
+\r
+    void operator ()(const ImageFeatures &features1, const ImageFeatures &features2, \r
+                     MatchesInfo& matches_info) { match(features1, features2, matches_info); }\r
+    void operator ()(const std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches);\r
+\r
+    bool isThreadSafe() const { return is_thread_safe_; }\r
+\r
+    virtual void releaseMemory() {}\r
+\r
+protected:\r
+    FeaturesMatcher(bool is_thread_safe = false) : is_thread_safe_(is_thread_safe) {}\r
+\r
+    virtual void match(const ImageFeatures &features1, const ImageFeatures &features2, \r
+                       MatchesInfo& matches_info) = 0;\r
+\r
+    bool is_thread_safe_;\r
+};\r
+\r
+\r
+class BestOf2NearestMatcher : public FeaturesMatcher\r
+{\r
+public:\r
+    BestOf2NearestMatcher(bool try_use_gpu = true, float match_conf = 0.55f, int num_matches_thresh1 = 6, \r
+                          int num_matches_thresh2 = 6);\r
+\r
+    void releaseMemory();\r
+\r
+protected:\r
+    void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo &matches_info);\r
+\r
+    int num_matches_thresh1_;\r
+    int num_matches_thresh2_;\r
+    cv::Ptr<FeaturesMatcher> impl_;\r
+};\r
+\r
+#endif // __OPENCV_MATCHERS_HPP__\r
index d354456..cf359f1 100644 (file)
@@ -40,6 +40,7 @@
 //\r
 //M*/\r
 #include <algorithm>\r
+#include <sstream>\r
 #include "autocalib.hpp"\r
 #include "motion_estimators.hpp"\r
 #include "util.hpp"\r
@@ -407,6 +408,70 @@ void waveCorrect(vector<Mat> &rmats)
 \r
 //////////////////////////////////////////////////////////////////////////////\r
 \r
+string matchesGraphAsString(vector<string> &pathes, vector<MatchesInfo> &pairwise_matches,\r
+                            float conf_threshold)\r
+{\r
+    stringstream str;\r
+    str << "graph matches_graph{\n";\r
+\r
+    set<int> added_imgs;\r
+\r
+    // Add matches\r
+    for (size_t i = 0; i < pairwise_matches.size(); ++i)\r
+    {\r
+        if (pairwise_matches[i].src_img_idx < pairwise_matches[i].dst_img_idx &&\r
+            pairwise_matches[i].confidence > conf_threshold)\r
+        {\r
+            string name_src = pathes[pairwise_matches[i].src_img_idx];\r
+            size_t prefix_len = name_src.find_last_of("/\\");\r
+            if (prefix_len != string::npos) prefix_len++; else prefix_len = 0;\r
+            name_src = name_src.substr(prefix_len, name_src.size() - prefix_len);\r
+\r
+            string name_dst = pathes[pairwise_matches[i].dst_img_idx];\r
+            prefix_len = name_dst.find_last_of("/\\");\r
+            if (prefix_len != string::npos) prefix_len++; else prefix_len = 0;\r
+            name_dst = name_dst.substr(prefix_len, name_dst.size() - prefix_len);\r
+\r
+            added_imgs.insert(pairwise_matches[i].src_img_idx);\r
+            added_imgs.insert(pairwise_matches[i].dst_img_idx);\r
+\r
+            str << "\"" << name_src << "\" -- \"" << name_dst << "\""\r
+                << "[label=\"Nm=" << pairwise_matches[i].matches.size()\r
+                << ", Ni=" << pairwise_matches[i].num_inliers\r
+                << ", C=" << pairwise_matches[i].confidence << "\"];\n";\r
+        }\r
+    }\r
+\r
+    // Add unmatched images\r
+    for (size_t i = 0; i < pairwise_matches.size(); ++i)\r
+    {\r
+        if (pairwise_matches[i].src_img_idx < pairwise_matches[i].dst_img_idx)\r
+        {\r
+            if (added_imgs.find(pairwise_matches[i].src_img_idx) == added_imgs.end())\r
+            {\r
+                added_imgs.insert(pairwise_matches[i].src_img_idx);\r
+                string name = pathes[pairwise_matches[i].src_img_idx];\r
+                size_t prefix_len = name.find_last_of("/\\");\r
+                if (prefix_len != string::npos) prefix_len++; else prefix_len = 0;\r
+                name = name.substr(prefix_len, name.size() - prefix_len);\r
+                str << "\"" << name << "\";\n";\r
+            }\r
+            if (added_imgs.find(pairwise_matches[i].dst_img_idx) == added_imgs.end())\r
+            {\r
+                added_imgs.insert(pairwise_matches[i].dst_img_idx);\r
+                string name = pathes[pairwise_matches[i].dst_img_idx];\r
+                size_t prefix_len = name.find_last_of("/\\");\r
+                if (prefix_len != string::npos) prefix_len++; else prefix_len = 0;\r
+                name = name.substr(prefix_len, name.size() - prefix_len);\r
+                str << "\"" << name << "\";\n";\r
+            }\r
+        }\r
+    }\r
+\r
+    str << "}";\r
+    return str.str();\r
+}\r
+\r
 vector<int> leaveBiggestComponent(vector<ImageFeatures> &features,  vector<MatchesInfo> &pairwise_matches, \r
                                   float conf_threshold)\r
 {\r
index 8682a4c..369f725 100644 (file)
 // or tort (including negligence or otherwise) arising in any way out of\r
 // the use of this software, even if advised of the possibility of such damage.\r
 //\r
-//M*/
-#ifndef __OPENCV_MOTION_ESTIMATORS_HPP__
-#define __OPENCV_MOTION_ESTIMATORS_HPP__
-
-#include "precomp.hpp"
-#include "matchers.hpp"
-#include "util.hpp"
-
-struct CameraParams
-{
-    CameraParams();
-    CameraParams(const CameraParams& other);
-    const CameraParams& operator =(const CameraParams& other);
-
-    double focal; // Focal length
-    cv::Mat R; // Rotation
-    cv::Mat t; // Translation
-};
-
-
-class Estimator
-{
-public:
-    void operator ()(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, 
-                     std::vector<CameraParams> &cameras)
-    {
-        estimate(features, pairwise_matches, cameras);
-    }
-
-protected:
-    virtual void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, 
-                          std::vector<CameraParams> &cameras) = 0;
-};
-
-
-class HomographyBasedEstimator : public Estimator
-{
-public:
-    HomographyBasedEstimator() : is_focals_estimated_(false) {}
-    bool isFocalsEstimated() const { return is_focals_estimated_; }
-
-private:   
-    void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, 
-                  std::vector<CameraParams> &cameras);
-
-    bool is_focals_estimated_;
-};
-
-
-class BundleAdjuster : public Estimator
-{
-public:
-    enum { RAY_SPACE, FOCAL_RAY_SPACE };
-
-    BundleAdjuster(int cost_space = FOCAL_RAY_SPACE, float conf_thresh = 1.f) 
-        : cost_space_(cost_space), conf_thresh_(conf_thresh) {}
-
-private:
-    void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, 
-                  std::vector<CameraParams> &cameras);
-
-    void calcError(cv::Mat &err);
-    void calcJacobian();
-
-    int num_images_;
-    int total_num_matches_;
-    const ImageFeatures *features_;
-    const MatchesInfo *pairwise_matches_;
-    cv::Mat cameras_;
-    std::vector<std::pair<int,int> > edges_;
-
-    int cost_space_;
-    float conf_thresh_;
-    cv::Mat err_, err1_, err2_;
-    cv::Mat J_;
-};
-
-
-void waveCorrect(std::vector<cv::Mat> &rmats);
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Auxiliary functions
-
-std::vector<int> leaveBiggestComponent(std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches, 
-                                       float conf_threshold);
-
-void findMaxSpanningTree(int num_images, const std::vector<MatchesInfo> &pairwise_matches, 
-                         Graph &span_tree, std::vector<int> &centers);
-
-#endif // __OPENCV_MOTION_ESTIMATORS_HPP__
+//M*/\r
+#ifndef __OPENCV_MOTION_ESTIMATORS_HPP__\r
+#define __OPENCV_MOTION_ESTIMATORS_HPP__\r
+\r
+#include "precomp.hpp"\r
+#include "matchers.hpp"\r
+#include "util.hpp"\r
+\r
+struct CameraParams\r
+{\r
+    CameraParams();\r
+    CameraParams(const CameraParams& other);\r
+    const CameraParams& operator =(const CameraParams& other);\r
+\r
+    double focal; // Focal length\r
+    cv::Mat R; // Rotation\r
+    cv::Mat t; // Translation\r
+};\r
+\r
+\r
+class Estimator\r
+{\r
+public:\r
+    void operator ()(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, \r
+                     std::vector<CameraParams> &cameras)\r
+    {\r
+        estimate(features, pairwise_matches, cameras);\r
+    }\r
+\r
+protected:\r
+    virtual void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, \r
+                          std::vector<CameraParams> &cameras) = 0;\r
+};\r
+\r
+\r
+class HomographyBasedEstimator : public Estimator\r
+{\r
+public:\r
+    HomographyBasedEstimator() : is_focals_estimated_(false) {}\r
+    bool isFocalsEstimated() const { return is_focals_estimated_; }\r
+\r
+private:   \r
+    void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, \r
+                  std::vector<CameraParams> &cameras);\r
+\r
+    bool is_focals_estimated_;\r
+};\r
+\r
+\r
+class BundleAdjuster : public Estimator\r
+{\r
+public:\r
+    enum { RAY_SPACE, FOCAL_RAY_SPACE };\r
+\r
+    BundleAdjuster(int cost_space = FOCAL_RAY_SPACE, float conf_thresh = 1.f) \r
+        : cost_space_(cost_space), conf_thresh_(conf_thresh) {}\r
+\r
+private:\r
+    void estimate(const std::vector<ImageFeatures> &features, const std::vector<MatchesInfo> &pairwise_matches, \r
+                  std::vector<CameraParams> &cameras);\r
+\r
+    void calcError(cv::Mat &err);\r
+    void calcJacobian();\r
+\r
+    int num_images_;\r
+    int total_num_matches_;\r
+    const ImageFeatures *features_;\r
+    const MatchesInfo *pairwise_matches_;\r
+    cv::Mat cameras_;\r
+    std::vector<std::pair<int,int> > edges_;\r
+\r
+    int cost_space_;\r
+    float conf_thresh_;\r
+    cv::Mat err_, err1_, err2_;\r
+    cv::Mat J_;\r
+};\r
+\r
+\r
+void waveCorrect(std::vector<cv::Mat> &rmats);\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// Auxiliary functions\r
+\r
+// Returns matches graph representation in DOT language\r
+std::string matchesGraphAsString(std::vector<std::string> &pathes, std::vector<MatchesInfo> &pairwise_matches,\r
+                                 float conf_threshold);\r
+\r
+std::vector<int> leaveBiggestComponent(std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches, \r
+                                       float conf_threshold);\r
+\r
+void findMaxSpanningTree(int num_images, const std::vector<MatchesInfo> &pairwise_matches, \r
+                         Graph &span_tree, std::vector<int> &centers);\r
+\r
+#endif // __OPENCV_MOTION_ESTIMATORS_HPP__\r
index 6bec221..4c2db0d 100644 (file)
@@ -108,8 +108,9 @@ bool overlapRoi(cv::Point tl1, cv::Point tl2, cv::Size sz1, cv::Size sz2, cv::Re
 cv::Rect resultRoi(const std::vector<cv::Point> &corners, const std::vector<cv::Mat> &images);\r
 cv::Rect resultRoi(const std::vector<cv::Point> &corners, const std::vector<cv::Size> &sizes);\r
 cv::Point resultTl(const std::vector<cv::Point> &corners);\r
-void selectRandomSubset(int count, int size, std::vector<int> &subset);\r
 \r
+// Returns random 'count' element subset of the {0,1,...,size-1} set\r
+void selectRandomSubset(int count, int size, std::vector<int> &subset);\r
 \r
 #include "util_inl.hpp"\r
 \r