2017-03-05 272 views
-1

我得到了相機參數運行後stitching_detailed.cpp。現在我想使用這些檢索的參數來縫合另一組使用下面給出的腳本的圖像。我的腳本構建成功,但是當我運行它時,它會產生運行時錯誤。我的矩陣初始化是否錯誤?我無法找出錯誤。請幫忙。使用相機參數從縫合一組圖像使用縫合不同的圖像集

有沒有其他的方式可以存儲相機一組圖像的參數並將其用於另一組圖像?

//COPYRIGHT LICENSE REMOVED FOR EASE OF PASTING. 
// THIS script is modified from https://github.com/opencv/opencv/blob/master/samples/cpp/stitching_detailed.cpp 
#include <iostream> 
#include <fstream> 
#include <string> 
#include "opencv2/opencv_modules.hpp" 
#include <opencv2/core/utility.hpp> 
#include "opencv2/imgcodecs.hpp" 
#include "opencv2/highgui.hpp" 
#include "opencv2/stitching/detail/autocalib.hpp" 
#include "opencv2/stitching/detail/blenders.hpp" 
#include "opencv2/stitching/detail/timelapsers.hpp" 
#include "opencv2/stitching/detail/camera.hpp" 
#include "opencv2/stitching/detail/exposure_compensate.hpp" 
#include "opencv2/stitching/detail/matchers.hpp" 
#include "opencv2/stitching/detail/motion_estimators.hpp" 
#include "opencv2/stitching/detail/seam_finders.hpp" 
#include "opencv2/stitching/detail/warpers.hpp" 
#include "opencv2/stitching/warpers.hpp" 

#define ENABLE_LOG 1 
#define LOG(msg) std::cout << msg 
#define LOGLN(msg) std::cout << msg << std::endl 

using namespace std; 
using namespace cv; 
using namespace cv::detail; 

void readCameraParamsVector(istream& is, vector<CameraParams> &vec) 
{ 
    typename vector<CameraParams>::size_type size = 0; 
    is.read((char*)&size, sizeof(size)); 
    vec.resize(size); 
    is.read((char*)&vec[0], vec.size() * sizeof(CameraParams)); 
} 

static void printUsage() 
{ 
    cout << 
     "Rotation model images stitcher.\n\n" 
     "stitching_detailed img1 img2 [...imgN] [flags]\n\n" 
     "Flags:\n" 
     " --preview\n" 
     "  Run stitching in the preview mode. Works faster than usual mode,\n" 
     "  but output image will have lower resolution.\n" 
     " --try_cuda (yes|no)\n" 
     "  Try to use CUDA. The default value is 'no'. All default values\n" 
     "  are for CPU mode.\n" 
     "\nMotion Estimation Flags:\n" 
     " --work_megapix <float>\n" 
     "  Resolution for image registration step. The default is 0.6 Mpx.\n" 
     " --features (surf|orb)\n" 
     "  Type of features used for images matching. The default is orb .\n" 
     " --matcher (homography|affine)\n" 
     "  Matcher used for pairwise image matching.\n" 
     " --estimator (homography|affine)\n" 
     "  Type of estimator used for transformation estimation.\n" 
     " --match_conf <float>\n" 
     "  Confidence for feature matching step. The default is 0.65 for surf and 0.3 for orb.\n" 
     " --conf_thresh <float>\n" 
     "  Threshold for two images are from the same panorama confidence.\n" 
     "  The default is 1.0.\n" 
     " --ba (no|reproj|ray|affine)\n" 
     "  Bundle adjustment cost function. The default is ray.\n" 
     " --ba_refine_mask (mask)\n" 
     "  Set refinement mask for bundle adjustment. It looks like 'x_xxx',\n" 
     "  where 'x' means refine respective parameter and '_' means don't\n" 
     "  refine one, and has the following format:\n" 
     "  <fx><skew><ppx><aspect><ppy>. The default mask is 'xxxxx'. If bundle\n" 
     "  adjustment doesn't support estimation of selected parameter then\n" 
     "  the respective flag is ignored.\n" 
     " --wave_correct (no|horiz|vert)\n" 
     "  Perform wave effect correction. The default is 'horiz'.\n" 
     " --save_graph <file_name>\n" 
     "  Save matches graph represented in DOT language to <file_name> file.\n" 
     "  Labels description: Nm is number of matches, Ni is number of inliers,\n" 
     "  C is confidence.\n" 
     "\nCompositing Flags:\n" 
     " --warp (affine|plane|cylindrical|spherical|fisheye|stereographic|compressedPlaneA2B1|compressedPlaneA1.5B1|compressedPlanePortraitA2B1|compressedPlanePortraitA1.5B1|paniniA2B1|paniniA1.5B1|paniniPortraitA2B1|paniniPortraitA1.5B1|mercator|transverseMercator)\n" 
     "  Warp surface type. The default is 'spherical'.\n" 
     " --seam_megapix <float>\n" 
     "  Resolution for seam estimation step. The default is 0.1 Mpx.\n" 
     " --seam (no|voronoi|gc_color|gc_colorgrad)\n" 
     "  Seam estimation method. The default is 'gc_color'.\n" 
     " --compose_megapix <float>\n" 
     "  Resolution for compositing step. Use -1 for original resolution.\n" 
     "  The default is -1.\n" 
     " --expos_comp (no|gain|gain_blocks)\n" 
     "  Exposure compensation method. The default is 'gain_blocks'.\n" 
     " --blend (no|feather|multiband)\n" 
     "  Blending method. The default is 'multiband'.\n" 
     " --blend_strength <float>\n" 
     "  Blending strength from [0,100] range. The default is 5.\n" 
     " --output <result_img>\n" 
     "  The default is 'result.jpg'.\n" 
     " --timelapse (as_is|crop) \n" 
     "  Output warped images separately as frames of a time lapse movie, with 'fixed_' prepended to input file names.\n" 
     " --rangewidth <int>\n" 
     "  uses range_width to limit number of images to match with.\n"; 
} 


// Default command line args 
vector<String> img_names; 
bool preview = false; 
bool try_cuda = false; 
double work_megapix = 0.6; 
double seam_megapix = 0.1; 
double compose_megapix = -1; 
float conf_thresh = 1.f; 
string features_type = "orb"; 
string matcher_type = "homography"; 
string estimator_type = "homography"; 
string ba_cost_func = "ray"; 
string ba_refine_mask = "xxxxx"; 
bool do_wave_correct = true; 
WaveCorrectKind wave_correct = detail::WAVE_CORRECT_HORIZ; 
bool save_graph = false; 
std::string save_graph_to; 
std::string cameraParamFile = "CameraParams.dat"; 
std::string stitchingParamsFileName = "ParamsStitching.dat"; 
string warp_type = "spherical"; 
int expos_comp_type = ExposureCompensator::GAIN_BLOCKS; 
float match_conf = 0.3f; 
string seam_find_type = "gc_color"; 
int blend_type = Blender::MULTI_BAND; 
int timelapse_type = Timelapser::AS_IS; 
float blend_strength = 5; 
string result_name = "result.jpg"; 
bool timelapse = false; 
int range_width = -1; 


static int parseCmdArgs(int argc, char** argv) 
{ 
    if (argc == 1) 
    { 
     printUsage(); 
     return -1; 
    } 
    for (int i = 1; i < argc; ++i) 
    { 
     if (string(argv[i]) == "--help" || string(argv[i]) == "/?") 
     { 
      printUsage(); 
      return -1; 
     } 
     else if (string(argv[i]) == "--preview") 
     { 
      preview = true; 
     } 
     else if (string(argv[i]) == "--try_cuda") 
     { 
      if (string(argv[i + 1]) == "no") 
       try_cuda = false; 
      else if (string(argv[i + 1]) == "yes") 
       try_cuda = true; 
      else 
      { 
       cout << "Bad --try_cuda flag value\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--work_megapix") 
     { 
      work_megapix = atof(argv[i + 1]); 
      i++; 
     } 
     else if (string(argv[i]) == "--seam_megapix") 
     { 
      seam_megapix = atof(argv[i + 1]); 
      i++; 
     } 
     else if (string(argv[i]) == "--compose_megapix") 
     { 
      compose_megapix = atof(argv[i + 1]); 
      i++; 
     } 
     else if (string(argv[i]) == "--result") 
     { 
      result_name = argv[i + 1]; 
      i++; 
     } 
     else if (string(argv[i]) == "--features") 
     { 
      features_type = argv[i + 1]; 
      if (features_type == "orb") 
       match_conf = 0.3f; 
      i++; 
     } 
     else if (string(argv[i]) == "--matcher") 
     { 
      if (string(argv[i + 1]) == "homography" || string(argv[i + 1]) == "affine") 
       matcher_type = argv[i + 1]; 
      else 
      { 
       cout << "Bad --matcher flag value\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--estimator") 
     { 
      if (string(argv[i + 1]) == "homography" || string(argv[i + 1]) == "affine") 
       estimator_type = argv[i + 1]; 
      else 
      { 
       cout << "Bad --estimator flag value\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--match_conf") 
     { 
      match_conf = static_cast<float>(atof(argv[i + 1])); 
      i++; 
     } 
     else if (string(argv[i]) == "--conf_thresh") 
     { 
      conf_thresh = static_cast<float>(atof(argv[i + 1])); 
      i++; 
     } 
     else if (string(argv[i]) == "--ba") 
     { 
      ba_cost_func = argv[i + 1]; 
      i++; 
     } 
     else if (string(argv[i]) == "--ba_refine_mask") 
     { 
      ba_refine_mask = argv[i + 1]; 
      if (ba_refine_mask.size() != 5) 
      { 
       cout << "Incorrect refinement mask length.\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--wave_correct") 
     { 
      if (string(argv[i + 1]) == "no") 
       do_wave_correct = false; 
      else if (string(argv[i + 1]) == "horiz") 
      { 
       do_wave_correct = true; 
       wave_correct = detail::WAVE_CORRECT_HORIZ; 
      } 
      else if (string(argv[i + 1]) == "vert") 
      { 
       do_wave_correct = true; 
       wave_correct = detail::WAVE_CORRECT_VERT; 
      } 
      else 
      { 
       cout << "Bad --wave_correct flag value\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--save_graph") 
     { 
      save_graph = true; 
      save_graph_to = argv[i + 1]; 
      i++; 
     } 
     else if (string(argv[i]) == "--warp") 
     { 
      warp_type = string(argv[i + 1]); 
      i++; 
     } 
     else if (string(argv[i]) == "--expos_comp") 
     { 
      if (string(argv[i + 1]) == "no") 
       expos_comp_type = ExposureCompensator::NO; 
      else if (string(argv[i + 1]) == "gain") 
       expos_comp_type = ExposureCompensator::GAIN; 
      else if (string(argv[i + 1]) == "gain_blocks") 
       expos_comp_type = ExposureCompensator::GAIN_BLOCKS; 
      else 
      { 
       cout << "Bad exposure compensation method\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--seam") 
     { 
      if (string(argv[i + 1]) == "no" || 
       string(argv[i + 1]) == "voronoi" || 
       string(argv[i + 1]) == "gc_color" || 
       string(argv[i + 1]) == "gc_colorgrad" || 
       string(argv[i + 1]) == "dp_color" || 
       string(argv[i + 1]) == "dp_colorgrad") 
       seam_find_type = argv[i + 1]; 
      else 
      { 
       cout << "Bad seam finding method\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--blend") 
     { 
      if (string(argv[i + 1]) == "no") 
       blend_type = Blender::NO; 
      else if (string(argv[i + 1]) == "feather") 
       blend_type = Blender::FEATHER; 
      else if (string(argv[i + 1]) == "multiband") 
       blend_type = Blender::MULTI_BAND; 
      else 
      { 
       cout << "Bad blending method\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--timelapse") 
     { 
      timelapse = true; 

      if (string(argv[i + 1]) == "as_is") 
       timelapse_type = Timelapser::AS_IS; 
      else if (string(argv[i + 1]) == "crop") 
       timelapse_type = Timelapser::CROP; 
      else 
      { 
       cout << "Bad timelapse method\n"; 
       return -1; 
      } 
      i++; 
     } 
     else if (string(argv[i]) == "--rangewidth") 
     { 
      range_width = atoi(argv[i + 1]); 
      i++; 
     } 
     else if (string(argv[i]) == "--blend_strength") 
     { 
      blend_strength = static_cast<float>(atof(argv[i + 1])); 
      i++; 
     } 
     else if (string(argv[i]) == "--output") 
     { 
      result_name = argv[i + 1]; 
      i++; 
     } 
     else 
      img_names.push_back(argv[i]); 
    } 
    if (preview) 
    { 
     compose_megapix = 0.6; 
    } 
    return 0; 
} 


int main(int argc, char* argv[]) 
{ 
    int retval = parseCmdArgs(argc, argv); 
    if (retval) 
     return retval; 

    // Check if have enough images 
    int num_images = static_cast<int>(img_names.size()); 
    if (num_images < 2) 
    { 
     LOGLN("Need more images"); 
     return -1; 
    } 

    double work_scale = 1, seam_scale = 1, compose_scale = 1; 
    float warped_image_scale; 
    bool is_work_scale_set = false, is_seam_scale_set = false, is_compose_scale_set = false; 

    Mat full_img, img; 
    vector<Mat> images(num_images); 
    vector<Size> full_img_sizes(num_images); 
    double seam_work_aspect = 1; 
    vector<CameraParams> cameras; 

    for (int i = 0; i < num_images; ++i) 
    { 
     full_img = imread(img_names[i]); 
     full_img_sizes[i] = full_img.size(); 

     if (full_img.empty()) 
     { 
      LOGLN("Can't open image " << img_names[i]); 
      return -1; 
     } 
     if (work_megapix < 0) 
     { 
      img = full_img; 
      work_scale = 1; 
      is_work_scale_set = true; 
     } 
     else 
     { 
      if (!is_work_scale_set) 
      { 
       work_scale = min(1.0, sqrt(work_megapix * 1e6/full_img.size().area())); 
       is_work_scale_set = true; 
      } 
      resize(full_img, img, Size(), work_scale, work_scale); 
     } 
     if (!is_seam_scale_set) 
     { 
      seam_scale = min(1.0, sqrt(seam_megapix * 1e6/full_img.size().area())); 
      seam_work_aspect = seam_scale/work_scale; 
      is_seam_scale_set = true; 
     } 

     resize(full_img, img, Size(), seam_scale, seam_scale); 
     images[i] = img.clone(); 
    } 

    full_img.release(); 
    img.release(); 

    std::ifstream in(stitchingParamsFileName.c_str(), std::ios::in); 
    in >> warped_image_scale; 
    in.close(); 

    Mat cam1_K = (Mat_<double>(3,3) << 8844.590793591626, 0, 447, 0, 8844.590793591626, 335.5, 0, 0, 1); 
    double cam1_focal = 8844.59 ; 
    double cam1_aspect = 1; 
    double cam1_ppx = 447; 
    double cam1_ppy = 335.5; 
    Mat cam1_R = (Mat_<double>(3,3) << 0.99864292, -0.04946211, 0.016299838, -1.8767452e-009, 0.31298503, 0.94975805, -0.052078638, -0.94846922, 0.31256032); 
    Mat cam1_t = (Mat_<double>(3,1) << 0 , 0, 0); 

    cameras[0].K() = cam1_K; 
    cameras[0].focal = cam1_focal; 
    cameras[0].aspect = cam1_aspect; 
    cameras[0].ppx = cam1_ppx; 
    cameras[0].ppy = cam1_ppy; 
    cameras[0].R = cam1_R; 
    cameras[0].t = cam1_t; 



    Mat cam2_K = (Mat_<double>(3,3) << 8402.297633935312, 0, 447, 0, 8402.297633935312, 335.5, 0, 0, 1); 
    double cam2_focal = 8402.3 ; 
    double cam2_aspect = 1; 
    double cam2_ppx = 447; 
    double cam2_ppy = 335.5; 
    Mat cam2_R = (Mat_<double>(3,3) << 0.99863523, 0.049619142, -0.016299838, 3.7252903e-009, 0.31209099, 0.9500522, .052227825, -0.94875556, 0.31166506); 
    Mat cam2_t = (Mat_<double>(3,1) << 0 , 0, 0); 

    cameras[1].K() = cam2_K; 
    cameras[1].focal = cam2_focal; 
    cameras[1].aspect = cam2_aspect; 
    cameras[1].ppx = cam2_ppx; 
    cameras[1].ppy = cam2_ppy; 
    cameras[1].R = cam2_R; 
    cameras[1].t = cam2_t; 

    LOGLN("Warping images (auxiliary)... "); 

    vector<Point> corners(num_images); 
    vector<UMat> masks_warped(num_images); 
    vector<UMat> images_warped(num_images); 
    vector<Size> sizes(num_images); 
    vector<UMat> masks(num_images); 

    // Prepare images masks 
    for (int i = 0; i < num_images; ++i) 
    { 
     masks[i].create(images[i].size(), CV_8U); 
     masks[i].setTo(Scalar::all(255)); 
    } 

    // Warp images and their masks 

    Ptr<WarperCreator> warper_creator; 
#ifdef HAVE_OPENCV_CUDAWARPING 
    if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0) 
    { 
     if (warp_type == "plane") 
      warper_creator = makePtr<cv::PlaneWarperGpu>(); 
     else if (warp_type == "cylindrical") 
      warper_creator = makePtr<cv::CylindricalWarperGpu>(); 
     else if (warp_type == "spherical") 
      warper_creator = makePtr<cv::SphericalWarperGpu>(); 
    } 
    else 
#endif 
    { 
     if (warp_type == "plane") 
      warper_creator = makePtr<cv::PlaneWarper>(); 
     else if (warp_type == "affine") 
      warper_creator = makePtr<cv::AffineWarper>(); 
     else if (warp_type == "cylindrical") 
      warper_creator = makePtr<cv::CylindricalWarper>(); 
     else if (warp_type == "spherical") 
      warper_creator = makePtr<cv::SphericalWarper>(); 
     else if (warp_type == "fisheye") 
      warper_creator = makePtr<cv::FisheyeWarper>(); 
     else if (warp_type == "stereographic") 
      warper_creator = makePtr<cv::StereographicWarper>(); 
     else if (warp_type == "compressedPlaneA2B1") 
      warper_creator = makePtr<cv::CompressedRectilinearWarper>(2.0f, 1.0f); 
     else if (warp_type == "compressedPlaneA1.5B1") 
      warper_creator = makePtr<cv::CompressedRectilinearWarper>(1.5f, 1.0f); 
     else if (warp_type == "compressedPlanePortraitA2B1") 
      warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(2.0f, 1.0f); 
     else if (warp_type == "compressedPlanePortraitA1.5B1") 
      warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(1.5f, 1.0f); 
     else if (warp_type == "paniniA2B1") 
      warper_creator = makePtr<cv::PaniniWarper>(2.0f, 1.0f); 
     else if (warp_type == "paniniA1.5B1") 
      warper_creator = makePtr<cv::PaniniWarper>(1.5f, 1.0f); 
     else if (warp_type == "paniniPortraitA2B1") 
      warper_creator = makePtr<cv::PaniniPortraitWarper>(2.0f, 1.0f); 
     else if (warp_type == "paniniPortraitA1.5B1") 
      warper_creator = makePtr<cv::PaniniPortraitWarper>(1.5f, 1.0f); 
     else if (warp_type == "mercator") 
      warper_creator = makePtr<cv::MercatorWarper>(); 
     else if (warp_type == "transverseMercator") 
      warper_creator = makePtr<cv::TransverseMercatorWarper>(); 
    } 

    if (!warper_creator) 
    { 
     cout << "Can't create the following warper '" << warp_type << "'\n"; 
     return 1; 
    } 

    Ptr<RotationWarper> warper = warper_creator->create(static_cast<float>(warped_image_scale * seam_work_aspect)); 

    for (int i = 0; i < num_images; ++i) 
    { 
     Mat_<float> K; 
     cameras[i].K().convertTo(K, CV_32F); 
     float swa = (float)seam_work_aspect; 
     K(0,0) *= swa; K(0,2) *= swa; 
     K(1,1) *= swa; K(1,2) *= swa; 

     corners[i] = warper->warp(images[i], K, cameras[i].R, INTER_LINEAR, BORDER_REFLECT, images_warped[i]); 
     sizes[i] = images_warped[i].size(); 

     warper->warp(masks[i], K, cameras[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warped[i]); 
    } 

    vector<UMat> images_warped_f(num_images); 
    for (int i = 0; i < num_images; ++i) 
     images_warped[i].convertTo(images_warped_f[i], CV_32F); 

    LOGLN("Finished warping images"); 

    Ptr<ExposureCompensator> compensator = ExposureCompensator::createDefault(expos_comp_type); 
    compensator->feed(corners, images_warped, masks_warped); 

    Ptr<SeamFinder> seam_finder; 
    if (seam_find_type == "no") 
     seam_finder = makePtr<detail::NoSeamFinder>(); 
    else if (seam_find_type == "voronoi") 
     seam_finder = makePtr<detail::VoronoiSeamFinder>(); 
    else if (seam_find_type == "gc_color") 
    { 
#ifdef HAVE_OPENCV_CUDALEGACY 
     if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0) 
      seam_finder = makePtr<detail::GraphCutSeamFinderGpu>(GraphCutSeamFinderBase::COST_COLOR); 
     else 
#endif 
      seam_finder = makePtr<detail::GraphCutSeamFinder>(GraphCutSeamFinderBase::COST_COLOR); 
    } 
    else if (seam_find_type == "gc_colorgrad") 
    { 
#ifdef HAVE_OPENCV_CUDALEGACY 
     if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0) 
      seam_finder = makePtr<detail::GraphCutSeamFinderGpu>(GraphCutSeamFinderBase::COST_COLOR_GRAD); 
     else 
#endif 
      seam_finder = makePtr<detail::GraphCutSeamFinder>(GraphCutSeamFinderBase::COST_COLOR_GRAD); 
    } 
    else if (seam_find_type == "dp_color") 
     seam_finder = makePtr<detail::DpSeamFinder>(DpSeamFinder::COLOR); 
    else if (seam_find_type == "dp_colorgrad") 
     seam_finder = makePtr<detail::DpSeamFinder>(DpSeamFinder::COLOR_GRAD); 
    if (!seam_finder) 
    { 
     cout << "Can't create the following seam finder '" << seam_find_type << "'\n"; 
     return 1; 
    } 

    seam_finder->find(images_warped_f, corners, masks_warped); 

    // Release unused memory 
    images.clear(); 
    images_warped.clear(); 
    images_warped_f.clear(); 
    masks.clear(); 

    LOGLN("Compositing..."); 
    Mat img_warped, img_warped_s; 
    Mat dilated_mask, seam_mask, mask, mask_warped; 
    Ptr<Blender> blender; 
    Ptr<Timelapser> timelapser; 
    //double compose_seam_aspect = 1; 
    double compose_work_aspect = 1; 

    for (int img_idx = 0; img_idx < num_images; ++img_idx) 
    { 
     LOGLN("Compositing image #" << img_idx +1); 

     // Read image and resize it if necessary 
     img = imread(img_names[img_idx]); 
     if (!is_compose_scale_set) 
     { 
      if (compose_megapix > 0) 
       compose_scale = min(1.0, sqrt(compose_megapix * 1e6/full_img.size().area())); 
      is_compose_scale_set = true; 

      // Compute relative scales 
      //compose_seam_aspect = compose_scale/seam_scale; 
      compose_work_aspect = compose_scale/work_scale; 

      // Update warped image scale 
      warped_image_scale *= static_cast<float>(compose_work_aspect); 
      warper = warper_creator->create(warped_image_scale); 

      // Update corners and sizes 
      for (int i = 0; i < num_images; ++i) 
      { 
       // Update intrinsics 
       cameras[i].focal *= compose_work_aspect; 
       cameras[i].ppx *= compose_work_aspect; 
       cameras[i].ppy *= compose_work_aspect; 

       // Update corner and size 
       Size sz = full_img_sizes[i]; 
       if (std::abs(compose_scale - 1) > 1e-1) 
       { 
        sz.width = cvRound(full_img_sizes[i].width * compose_scale); 
        sz.height = cvRound(full_img_sizes[i].height * compose_scale); 
       } 

       Mat K; 
       cameras[i].K().convertTo(K, CV_32F); 
       Rect roi = warper->warpRoi(sz, K, cameras[i].R); 
       corners[i] = roi.tl(); 
       sizes[i] = roi.size(); 
      } 
     } 
     if (abs(compose_scale - 1) > 1e-1) 
      resize(full_img, img, Size(), compose_scale, compose_scale); 
     else 
      img = full_img; 
     full_img.release(); 
     Size img_size = img.size(); 

     Mat K; 
     cameras[img_idx].K().convertTo(K, CV_32F); 

     // Warp the current image 
     warper->warp(img, K, cameras[img_idx].R, INTER_LINEAR, BORDER_REFLECT, img_warped); 

     // Warp the current image mask 
     mask.create(img_size, CV_8U); 
     mask.setTo(Scalar::all(255)); 
     warper->warp(mask, K, cameras[img_idx].R, INTER_NEAREST, BORDER_CONSTANT, mask_warped); 

     // Compensate exposure 
     compensator->apply(img_idx, corners[img_idx], img_warped, mask_warped); 

     img_warped.convertTo(img_warped_s, CV_16S); 
     img_warped.release(); 
     img.release(); 
     mask.release(); 

     dilate(masks_warped[img_idx], dilated_mask, Mat()); 
     resize(dilated_mask, seam_mask, mask_warped.size()); 
     mask_warped = seam_mask & mask_warped; 

     if (!blender && !timelapse) 
     { 
      blender = Blender::createDefault(blend_type, try_cuda); 
      Size dst_sz = resultRoi(corners, sizes).size(); 
      float blend_width = sqrt(static_cast<float>(dst_sz.area())) * blend_strength/100.f; 
      if (blend_width < 1.f) 
       blender = Blender::createDefault(Blender::NO, try_cuda); 
      else if (blend_type == Blender::MULTI_BAND) 
      { 
       MultiBandBlender* mb = dynamic_cast<MultiBandBlender*>(blender.get()); 
       mb->setNumBands(static_cast<int>(ceil(log(blend_width)/log(2.)) - 1.)); 
       LOGLN("Multi-band blender, number of bands: " << mb->numBands()); 
      } 
      else if (blend_type == Blender::FEATHER) 
      { 
       FeatherBlender* fb = dynamic_cast<FeatherBlender*>(blender.get()); 
       fb->setSharpness(1.f/blend_width); 
       LOGLN("Feather blender, sharpness: " << fb->sharpness()); 
      } 
      blender->prepare(corners, sizes); 
     } 
     else if (!timelapser && timelapse) 
     { 
      timelapser = Timelapser::createDefault(timelapse_type); 
      timelapser->initialize(corners, sizes); 
     } 

     // Blend the current image 
     if (timelapse) 
     { 
      timelapser->process(img_warped_s, Mat::ones(img_warped_s.size(), CV_8UC1), corners[img_idx]); 
      String fixedFileName; 
      size_t pos_s = String(img_names[img_idx]).find_last_of("/\\"); 
      if (pos_s == String::npos) 
      { 
       fixedFileName = "fixed_" + img_names[img_idx]; 
      } 
      else 
      { 
       fixedFileName = "fixed_" + String(img_names[img_idx]).substr(pos_s + 1, String(img_names[img_idx]).length() - pos_s); 
      } 
      imwrite(fixedFileName, timelapser->getDst()); 
     } 
     else 
     { 
      blender->feed(img_warped_s, mask_warped, corners[img_idx]); 
     } 
    } 

    if (!timelapse) 
    { 
     Mat result, result_mask; 
     blender->blend(result, result_mask); 

     LOGLN("Finished Compositing"); 

     imwrite(result_name, result); 
    } 

    LOGLN("Finished Analysis "); 
    return 0; 
} 
+0

「當我運行它,它會給出運行時錯誤」 - 什麼是錯誤信息? – slawekwin

+0

也嘗試隔離發生錯誤的地方,併發布[最小,完整和可驗證的示例](http://stackoverflow.com/help/mcve)而不是整個程序代碼轉儲 – slawekwin

回答

1

我想出了答案。您爲一組圖像編寫YAML文件,然後將這些參數集合用於另一組圖像。

我的代碼的問題是我初始化不同參數的方式,現在它在下面提到的代碼中被修復。

#include <iostream> 
#include <fstream> 
#include <string> 
#include <cstdio> 
#include "opencv2/opencv_modules.hpp" 
#include <opencv2/core/utility.hpp> 
#include "opencv2/imgcodecs.hpp" 
#include "opencv2/highgui.hpp" 
#include "opencv2/stitching/detail/autocalib.hpp" 
#include "opencv2/stitching/detail/blenders.hpp" 
#include "opencv2/stitching/detail/timelapsers.hpp" 
#include "opencv2/stitching/detail/camera.hpp" 
#include "opencv2/stitching/detail/exposure_compensate.hpp" 
#include "opencv2/stitching/detail/matchers.hpp" 
#include "opencv2/stitching/detail/motion_estimators.hpp" 
#include "opencv2/stitching/detail/seam_finders.hpp" 
#include "opencv2/stitching/detail/warpers.hpp" 
#include "opencv2/stitching/warpers.hpp" 
#include <typeinfo> 

#define ENABLE_LOG 1 
#define LOG(msg) std::cout << msg 
#define LOGLN(msg) std::cout << msg << std::endl 

using namespace std; 
using namespace cv; 
using namespace cv::detail; 

// Default command line args 
bool preview = false; 
bool try_cuda = false; 
double work_megapix = 0.6; 
double seam_megapix = 0.1; 
double compose_megapix = -1; 
float conf_thresh = 1.f; 
string features_type = "orb"; 
string matcher_type = "homography"; 
string estimator_type = "homography"; 
string ba_cost_func = "ray"; 
string ba_refine_mask = "xxxxx"; 
bool do_wave_correct = true; 
WaveCorrectKind wave_correct = detail::WAVE_CORRECT_HORIZ; 
std::string stitchingParamsFileName = "ParamsStitching"; 
string warp_type = "plane"; 
float match_conf = 0.3f; 
string result_name = "result.jpg"; 
int range_width = -1; 

//function to get warped and registered image 
//takes input image, mask, corner point, roi, roi mask, output image reference, output image mask reference 
void getWarpedRegisteredImage(InputArray _img, InputArray _mask, Point tl, Rect dst_roi_, Mat dst_mask_, Mat dst_) 
{ 
    int corner_x, corner_y; 
    Mat img = _img.getMat(); 
    Mat mask = _mask.getMat(); 

    CV_Assert(img.type() == CV_16SC3); 
    CV_Assert(mask.type() == CV_8U); 

    //update the corner points for each image 
    corner_x = tl.x - dst_roi_.x; 
    corner_y = tl.y - dst_roi_.y; 

    //update output image and image mask with corners updates 
    for (int y = 0; y < img.rows; ++y) 
    { 
     const Point3_<short> *src_row = img.ptr<Point3_<short> >(y); 
     Point3_<short> *dst_row = dst_.ptr<Point3_<short> >(corner_y + y); 
     const uchar *mask_row = mask.ptr<uchar>(y); 
     uchar *dst_mask_row = dst_mask_.ptr<uchar>(corner_y + y); 

     for (int x = 0; x < img.cols; ++x) 
     { 
      if (mask_row[x]) 
       dst_row[corner_x + x] = src_row[x]; 
      dst_mask_row[corner_x + x] |= mask_row[x]; 
     } 
    } 
} 

vector<Mat> getStitchingParams(vector<Mat> InputImage) 
{ 
#if 0 
    cv::setBreakOnError(true); 
#endif 
    double work_scale = 1, seam_scale = 1, compose_scale = 1; 
    bool is_work_scale_set = false, is_seam_scale_set = false, is_compose_scale_set = false; 

    LOGLN("Finding features..."); 
#if ENABLE_LOG 
    int64 t = getTickCount(); 
#endif 

    Ptr<FeaturesFinder> finder; 
    if (features_type == "surf") 
    { 
#ifdef HAVE_OPENCV_XFEATURES2D 
     if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0) 
      finder = makePtr<SurfFeaturesFinderGpu>(); 
     else 
#endif 
      finder = makePtr<SurfFeaturesFinder>(); 
    } 
    else if (features_type == "orb") 
    { 
     finder = makePtr<OrbFeaturesFinder>(); 
    } 
    else 
    { 
     cout << "Unknown 2D features type: '" << features_type << "'.\n"; 
    } 

    int num_images = (int)InputImage.size(); 
    Mat full_img, img; 
    vector<ImageFeatures> features(num_images); 
    vector<Mat> images(num_images); 
    vector<Size> full_img_sizes(num_images); 
    double seam_work_aspect = 1; 

    for (int i = 0; i < num_images; ++i) 
    { 
     full_img = (Mat)InputImage.at(i); 
     full_img_sizes[i] = full_img.size(); 

     if (work_megapix < 0) 
     { 
      img = full_img; 
      work_scale = 1; 
      is_work_scale_set = true; 
     } 
     else 
     { 
      if (!is_work_scale_set) 
      { 
       work_scale = min(1.0, sqrt(work_megapix * 1e6/full_img.size().area())); 
       is_work_scale_set = true; 
      } 
      resize(full_img, img, Size(), work_scale, work_scale); 
     } 
     if (!is_seam_scale_set) 
     { 
      seam_scale = min(1.0, sqrt(seam_megapix * 1e6/full_img.size().area())); 
      seam_work_aspect = seam_scale/work_scale; 
      is_seam_scale_set = true; 
     } 

     (*finder)(img, features[i]); 
     features[i].img_idx = i; 
     LOGLN("Features in image #" << i+1 << ": " << features[i].keypoints.size()); 

     resize(full_img, img, Size(), seam_scale, seam_scale); 
     images[i] = img.clone(); 
    } 

    finder->collectGarbage(); 
    full_img.release(); 
    img.release(); 

    LOGLN("Finding features, time: " << ((getTickCount() - t)/getTickFrequency()) << " sec"); 
    LOG("Pairwise matching..\n"); 
#if ENABLE_LOG 
    t = getTickCount(); 
#endif 
    vector<MatchesInfo> pairwise_matches; 
    Ptr<FeaturesMatcher> matcher; 
    if (matcher_type == "affine") 
     matcher = makePtr<AffineBestOf2NearestMatcher>(false, try_cuda, match_conf); 
    else if (range_width==-1) 
     matcher = makePtr<BestOf2NearestMatcher>(try_cuda, match_conf); 
    else 
     matcher = makePtr<BestOf2NearestRangeMatcher>(range_width, try_cuda, match_conf); 

    (*matcher)(features, pairwise_matches); 
    matcher->collectGarbage(); 

    LOGLN("Pairwise matching- time: " << ((getTickCount() - t)/getTickFrequency()) << " sec"); 

    // Leave only images we are sure are from the same panorama 
    vector<int> indices = leaveBiggestComponent(features, pairwise_matches, conf_thresh); 

    Ptr<Estimator> estimator; 
    if (estimator_type == "affine") 
     estimator = makePtr<AffineBasedEstimator>(); 
    else 
     estimator = makePtr<HomographyBasedEstimator>(); 

    vector<CameraParams> cameras; 
    if (!(*estimator)(features, pairwise_matches, cameras)) 
    { 
     cout << "Homography estimation failed.\n"; 
    } 

    for (size_t i = 0; i < cameras.size(); ++i) 
    { 
     Mat R; 
     cameras[i].R.convertTo(R, CV_32F); 
     cameras[i].R = R; 
    } 

    //Bundle Adjustment 
    Ptr<detail::BundleAdjusterBase> adjuster; 
    if (ba_cost_func == "reproj") adjuster = makePtr<detail::BundleAdjusterReproj>(); 
    else if (ba_cost_func == "ray") adjuster = makePtr<detail::BundleAdjusterRay>(); 
    else if (ba_cost_func == "affine") adjuster = makePtr<detail::BundleAdjusterAffinePartial>(); 
    else if (ba_cost_func == "no") adjuster = makePtr<NoBundleAdjuster>(); 
    else 
    { 
     cout << "Unknown bundle adjustment cost function: '" << ba_cost_func << "'.\n"; 
    } 
    adjuster->setConfThresh(conf_thresh); 
    Mat_<uchar> refine_mask = Mat::zeros(3, 3, CV_8U); 
    if (ba_refine_mask[0] == 'x') refine_mask(0,0) = 1; 
    if (ba_refine_mask[1] == 'x') refine_mask(0,1) = 1; 
    if (ba_refine_mask[2] == 'x') refine_mask(0,2) = 1; 
    if (ba_refine_mask[3] == 'x') refine_mask(1,1) = 1; 
    if (ba_refine_mask[4] == 'x') refine_mask(1,2) = 1; 
    adjuster->setRefinementMask(refine_mask); 
    if (!(*adjuster)(features, pairwise_matches, cameras)) 
    { 
     cout << "Camera parameters adjusting failed.\n"; 
    } 

    // Find median focal length 
    vector<double> focals; 
    for (size_t i = 0; i < cameras.size(); ++i) 
    { 
     focals.push_back(cameras[i].focal); 
    } 

    sort(focals.begin(), focals.end()); 
    float warped_image_scale; 
    if (focals.size() % 2 == 1) 
     warped_image_scale = static_cast<float>(focals[focals.size()/2]); 
    else 
     warped_image_scale = static_cast<float>(focals[focals.size()/2 - 1] + focals[focals.size()/2]) * 0.5f; 

    if (do_wave_correct) 
    { 
     vector<Mat> rmats; 
     for (size_t i = 0; i < cameras.size(); ++i) 
      rmats.push_back(cameras[i].R.clone()); 
     waveCorrect(rmats, wave_correct); 
     for (size_t i = 0; i < cameras.size(); ++i) 
      cameras[i].R = rmats[i]; 
    } 

    LOGLN("Warping images (auxiliary)... "); 
#if ENABLE_LOG 
    t = getTickCount(); 
#endif 

    std::ofstream out(stitchingParamsFileName.c_str(), std::ios::out); 
    out << warped_image_scale; 
    out.close(); 

    for (int i = 0; i < num_images; ++i) 
    { 
     stringstream camId; 
     camId << i+1; 
     string fileName = "cam" + camId.str() + ".yml"; 
     FileStorage fs(fileName, FileStorage::WRITE); 
     fs << "K" << cameras[i].K(); 
     fs << "R" << cameras[i].R; 
     fs << "t" << cameras[i].t; 
     fs << "ppx" << cameras[i].ppx; 
     fs << "ppy" << cameras[i].ppy; 
     fs << "focal" << cameras[i].focal; 
     fs << "aspect" << cameras[i].aspect; 
     fs.release(); 
    } 

    vector<Point> corners(num_images); 
    vector<UMat> masks_warped(num_images); 
    vector<UMat> images_warped(num_images); 
    vector<Size> sizes(num_images); 
    vector<UMat> masks(num_images); 

    // Prepare images masks for each input image with same size as input image 
    for (int i = 0; i < num_images; ++i) 
    { 
     masks[i].create(images[i].size(), CV_8U); 
     masks[i].setTo(Scalar::all(255)); 
    } 

    // Warp images and their masks 
    Ptr<WarperCreator> warper_creator; 
#ifdef HAVE_OPENCV_CUDAWARPING 
    if (try_cuda && cuda::getCudaEnabledDeviceCount() > 0) 
    { 
     if (warp_type == "plane") 
      warper_creator = makePtr<cv::PlaneWarperGpu>(); 
     else if (warp_type == "cylindrical") 
      warper_creator = makePtr<cv::CylindricalWarperGpu>(); 
     else if (warp_type == "spherical") 
      warper_creator = makePtr<cv::SphericalWarperGpu>(); 
    } 
    else 
#endif 
    { 
     if (warp_type == "plane") 
      warper_creator = makePtr<cv::PlaneWarper>(); 
     else if (warp_type == "affine") 
      warper_creator = makePtr<cv::AffineWarper>(); 
     else if (warp_type == "cylindrical") 
      warper_creator = makePtr<cv::CylindricalWarper>(); 
     else if (warp_type == "spherical") 
      warper_creator = makePtr<cv::SphericalWarper>(); 
     else if (warp_type == "fisheye") 
      warper_creator = makePtr<cv::FisheyeWarper>(); 
     else if (warp_type == "stereographic") 
      warper_creator = makePtr<cv::StereographicWarper>(); 
     else if (warp_type == "compressedPlaneA2B1") 
      warper_creator = makePtr<cv::CompressedRectilinearWarper>(2.0f, 1.0f); 
     else if (warp_type == "compressedPlaneA1.5B1") 
      warper_creator = makePtr<cv::CompressedRectilinearWarper>(1.5f, 1.0f); 
     else if (warp_type == "compressedPlanePortraitA2B1") 
      warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(2.0f, 1.0f); 
     else if (warp_type == "compressedPlanePortraitA1.5B1") 
      warper_creator = makePtr<cv::CompressedRectilinearPortraitWarper>(1.5f, 1.0f); 
     else if (warp_type == "paniniA2B1") 
      warper_creator = makePtr<cv::PaniniWarper>(2.0f, 1.0f); 
     else if (warp_type == "paniniA1.5B1") 
      warper_creator = makePtr<cv::PaniniWarper>(1.5f, 1.0f); 
     else if (warp_type == "paniniPortraitA2B1") 
      warper_creator = makePtr<cv::PaniniPortraitWarper>(2.0f, 1.0f); 
     else if (warp_type == "paniniPortraitA1.5B1") 
      warper_creator = makePtr<cv::PaniniPortraitWarper>(1.5f, 1.0f); 
     else if (warp_type == "mercator") 
      warper_creator = makePtr<cv::MercatorWarper>(); 
     else if (warp_type == "transverseMercator") 
      warper_creator = makePtr<cv::TransverseMercatorWarper>(); 
    } 

    if (!warper_creator) 
    { 
     cout << "Can't create the following warper '" << warp_type << "'\n"; 
    } 

    Ptr<RotationWarper> warper = warper_creator->create(static_cast<float>(warped_image_scale * seam_work_aspect)); 

    for (int i = 0; i < num_images; ++i) 
    { 
     Mat_<float> K; 
     cameras[i].K().convertTo(K, CV_32F); 
     float swa = (float)seam_work_aspect; 
     K(0,0) *= swa; K(0,2) *= swa; 
     K(1,1) *= swa; K(1,2) *= swa; 

     corners[i] = warper->warp(images[i], K, cameras[i].R, INTER_LINEAR, BORDER_REFLECT, images_warped[i]); 
     sizes[i] = images_warped[i].size(); 

     warper->warp(masks[i], K, cameras[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warped[i]); 
    } 

    vector<UMat> images_warped_f(num_images); 
    for (int i = 0; i < num_images; ++i) 
     images_warped[i].convertTo(images_warped_f[i], CV_32F); 

    LOGLN("Warping images - time: " << ((getTickCount() - t)/getTickFrequency()) << " sec"); 

#if ENABLE_LOG 
    t = getTickCount(); 
#endif 

    Mat img_warped, img_warped_s; 
    Mat dilated_mask, seam_mask, mask, mask_warped; 
    //double compose_seam_aspect = 1; 
    double compose_work_aspect = 1; 

    vector<Mat> RegisteredImages, RegisteredImagesMask; 

    for (int img_idx = 0; img_idx < num_images; ++img_idx) 
    { 
     // Read image and resize it if necessary 
     full_img = InputImage.at(img_idx); 
     if (!is_compose_scale_set) 
     { 
      if (compose_megapix > 0) 
       compose_scale = min(1.0, sqrt(compose_megapix * 1e6/full_img.size().area())); 
      is_compose_scale_set = true; 

      // Compute relative scales 
      compose_work_aspect = compose_scale/work_scale; 

      // Update warped image scale 
      warped_image_scale *= static_cast<float>(compose_work_aspect); 
      warper = warper_creator->create(warped_image_scale); 

      // Update corners and sizes 
      for (int i = 0; i < num_images; ++i) 
      { 
       // Update intrinsics 
       cameras[i].focal *= compose_work_aspect; 
       cameras[i].ppx *= compose_work_aspect; 
       cameras[i].ppy *= compose_work_aspect; 

       // Update corner and size 
       Size sz = full_img_sizes[i]; 
       if (std::abs(compose_scale - 1) > 1e-1) 
       { 
        sz.width = cvRound(full_img_sizes[i].width * compose_scale); 
        sz.height = cvRound(full_img_sizes[i].height * compose_scale); 
       } 

       Mat K; 
       cameras[i].K().convertTo(K, CV_32F); 
       Rect roi = warper->warpRoi(sz, K, cameras[i].R); 
       corners[i] = roi.tl(); 
       sizes[i] = roi.size(); 
      } 
     } 
     if (abs(compose_scale - 1) > 1e-1) 
      resize(full_img, img, Size(), compose_scale, compose_scale); 
     else 
      img = full_img; 
     full_img.release(); 
     Size img_size = img.size(); 

     Mat K; 
     cameras[img_idx].K().convertTo(K, CV_32F); 

     // Warp the current image 
     warper->warp(img, K, cameras[img_idx].R, INTER_LINEAR, BORDER_REFLECT, img_warped); 

     // Warp the current image mask 
     mask.create(img_size, CV_8U); 
     mask.setTo(Scalar::all(255)); 
     warper->warp(mask, K, cameras[img_idx].R, INTER_NEAREST, BORDER_CONSTANT, mask_warped); 

     Rect dst_roi, dst_roi_; 

     //create blank image and image mask which will store the warped image 
     dst_roi = resultRoi(corners, sizes); 
     Mat dst_, dst_mask_; 
     dst_.create(dst_roi.size(), CV_16SC3); 
     dst_.setTo(Scalar::all(0)); 
     dst_mask_.create(dst_roi.size(), CV_8U); 
     dst_mask_.setTo(Scalar::all(0)); 
     dst_roi_ = dst_roi; 
     //LOGLN("\nFinal Roi: " << dst_roi_ << "\n"); 

     img_warped.convertTo(img_warped_s, CV_16S); 
     img_warped.release(); 
     img.release(); 
     mask.release(); 

     //save warped and registered image for each camera and also update the new corner 
     getWarpedRegisteredImage(img_warped_s, mask_warped, corners[img_idx], dst_roi_, dst_mask_, dst_); 

     resize(dst_, dst_, Size(1700,920), compose_scale, compose_scale); 
     resize(dst_mask_, dst_mask_, Size(1700,920), compose_scale, compose_scale); 

     RegisteredImages.push_back(dst_); 
     RegisteredImagesMask.push_back(dst_mask_); 
    } 

// for (int img_idx = 0; img_idx < num_images; ++img_idx) 
// { 
//  stringstream camId; 
//  camId << img_idx + 1; 
//  String warpedRegisteredImageFileName, warpedRegisteredImageMaskFileName; 
//  Mat stitchedImage = RegisteredImages[img_idx]; 
//  Mat stitchedImageMask = RegisteredImagesMask[img_idx]; 
//  warpedRegisteredImageFileName = "warpedRegisteredImage_" + camId.str() + ".jpg"; 
//  warpedRegisteredImageMaskFileName = "warpedRegistered_ImageMask_" + camId.str() + ".jpg"; 
//  imwrite(warpedRegisteredImageFileName, stitchedImage); 
//  imwrite(warpedRegisteredImageMaskFileName, stitchedImageMask); 
// } 
// LOGLN("\nFinished Analysis \n"); 

    return RegisteredImages; 
} 

int main(int argc, char* argv[]) 
{ 
    vector<Mat> inputImages, outputImages; 
    String imageName; 
    for (int i = 1; i < argc; ++i) 
    { 
     imageName = String(argv[i]); 
     Mat img = imread(imageName); 
     inputImages.push_back(img); 
    } 
    // Check if have enough images 
    int num_images = static_cast<int>(inputImages.size()); 
    if (num_images < 2) 
    { 
     LOGLN("Need more images"); 
     return -1; 
    } 
    outputImages = getStitchingParams(inputImages); 

    for (int img_idx = 0; img_idx < num_images; ++img_idx) 
    { 
     stringstream camId; 
     camId << img_idx + 1; 
     String warpedRegisteredImageFileName, warpedRegisteredImageMaskFileName; 
     Mat stitchedImage = outputImages[img_idx]; 
     Mat stitchedImageMask = outputImages[img_idx]; 
     warpedRegisteredImageFileName = "warpedRegisteredImage_" + camId.str() + ".jpg"; 
     warpedRegisteredImageMaskFileName = "warpedRegistered_ImageMask_" + camId.str() + ".jpg"; 
     imwrite(warpedRegisteredImageFileName, stitchedImage); 
     imwrite(warpedRegisteredImageMaskFileName, stitchedImageMask); 
    } 

    return 0; 
} 
+0

嗨...我得到了參數如何使用它們來拼接圖像? – pritam001

相關問題