图像拼接

来源:互联网 发布:安装人工智能计算器 编辑:程序博客网 时间:2024/05/20 01:47


1、主要工作

尝试对所给的52JPEG图像进行拼接,看其是否可行。

2、工作具体内容

2.1 利用所给程序验证拼接可行性

这里利用stitching.cpp以及stitching_detailed.cpp基于openCV 2.4.9版本进行试验,发现无法完成拼接。

stitching.cpp

#include <iostream>#include <fstream>#include "opencv2/highgui/highgui.hpp"#include "opencv2/stitching/stitcher.hpp"using namespace std;using namespace cv;bool try_use_gpu = false;vector<Mat> imgs;string result_name = "result.jpg";void printUsage();int parseCmdArgs(int argc, char** argv);int main(int argc, char* argv[]){    int retval = parseCmdArgs(argc, argv);    if (retval) return -1;    Mat pano;    Stitcher stitcher = Stitcher::createDefault(try_use_gpu);    Stitcher::Status status = stitcher.stitch(imgs, pano);    if (status != Stitcher::OK)    {        cout << "Can't stitch images, error code = " << int(status) << endl;        return -1;    }    imwrite(result_name, pano);    return 0;}void printUsage(){    cout <<        "Rotation model images stitcher.\n\n"        "stitching img1 img2 [...imgN]\n\n"        "Flags:\n"        "  --try_use_gpu (yes|no)\n"        "      Try to use GPU. The default value is 'no'. All default values\n"        "      are for CPU mode.\n"        "  --output <result_img>\n"        "      The default is 'result.jpg'.\n";}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]) == "--try_use_gpu")        {            if (string(argv[i + 1]) == "no")                try_use_gpu = false;            else if (string(argv[i + 1]) == "yes")                try_use_gpu = true;            else            {                cout << "Bad --try_use_gpu flag value\n";                return -1;            }            i++;        }        else if (string(argv[i]) == "--output")        {            result_name = argv[i + 1];            i++;        }        else        {            Mat img = imread(argv[i]);            if (img.empty())            {                cout << "Can't read image '" << argv[i] << "'\n";                return -1;            }            imgs.push_back(img);        }    }    return 0;}


 

2.1.1 stitching.cpp运行结果

 stitching_detailed.cpp

 

#include <iostream>#include <fstream>#include <string>#include "opencv2/opencv_modules.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/stitching/detail/autocalib.hpp"#include "opencv2/stitching/detail/blenders.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/util.hpp"#include "opencv2/stitching/detail/warpers.hpp"#include "opencv2/stitching/warpers.hpp"using namespace std;using namespace cv;using namespace cv::detail;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_gpu (yes|no)\n"        "      Try to use GPU. 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 surf.\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 (reproj|ray)\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 (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";}// Default command line argsvector<string> img_names;bool preview = false;bool try_gpu = false;double work_megapix = 0.6;double seam_megapix = 0.1;double compose_megapix = -1;float conf_thresh = 1.f;string features_type = "surf";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;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;float blend_strength = 5;string result_name = "result.jpg";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_gpu")        {            if (string(argv[i + 1]) == "no")                try_gpu = false;            else if (string(argv[i + 1]) == "yes")                try_gpu = true;            else            {                cout << "Bad --try_gpu 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]) == "--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]) == "--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[]){#if ENABLE_LOG    int64 app_start_time = getTickCount();#endif    cv::setBreakOnError(true);    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;    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")    {#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU)        if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0)            finder = new SurfFeaturesFinderGpu();        else#endif            finder = new SurfFeaturesFinder();    }    else if (features_type == "orb")    {        finder = new OrbFeaturesFinder();    }    else    {        cout << "Unknown 2D features type: '" << features_type << "'.\n";        return -1;    }    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 = 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;        }        (*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");#if ENABLE_LOG    t = getTickCount();#endif    vector<MatchesInfo> pairwise_matches;    BestOf2NearestMatcher matcher(try_gpu, match_conf);    matcher(features, pairwise_matches);    matcher.collectGarbage();    LOGLN("Pairwise matching, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");    // Check if we should save matches graph    if (save_graph)    {        LOGLN("Saving matches graph...");        ofstream f(save_graph_to.c_str());        f << matchesGraphAsString(img_names, pairwise_matches, conf_thresh);    }    // Leave only images we are sure are from the same panorama    vector<int> indices = leaveBiggestComponent(features, pairwise_matches, conf_thresh);    vector<Mat> img_subset;    vector<string> img_names_subset;    vector<Size> full_img_sizes_subset;    for (size_t i = 0; i < indices.size(); ++i)    {        img_names_subset.push_back(img_names[indices[i]]);        img_subset.push_back(images[indices[i]]);        full_img_sizes_subset.push_back(full_img_sizes[indices[i]]);    }    images = img_subset;    img_names = img_names_subset;    full_img_sizes = full_img_sizes_subset;    // Check if we still have enough images    num_images = static_cast<int>(img_names.size());    if (num_images < 2)    {        LOGLN("Need more images");        return -1;    }    HomographyBasedEstimator estimator;    vector<CameraParams> cameras;    estimator(features, pairwise_matches, cameras);    for (size_t i = 0; i < cameras.size(); ++i)    {        Mat R;        cameras[i].R.convertTo(R, CV_32F);        cameras[i].R = R;        LOGLN("Initial intrinsics #" << indices[i]+1 << ":\n" << cameras[i].K());    }    Ptr<detail::BundleAdjusterBase> adjuster;    if (ba_cost_func == "reproj") adjuster = new detail::BundleAdjusterReproj();    else if (ba_cost_func == "ray") adjuster = new detail::BundleAdjusterRay();    else    {        cout << "Unknown bundle adjustment cost function: '" << ba_cost_func << "'.\n";        return -1;    }    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);    (*adjuster)(features, pairwise_matches, cameras);    // Find median focal length    vector<double> focals;    for (size_t i = 0; i < cameras.size(); ++i)    {        LOGLN("Camera #" << indices[i]+1 << ":\n" << cameras[i].K());        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);        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    vector<Point> corners(num_images);    vector<Mat> masks_warped(num_images);    vector<Mat> images_warped(num_images);    vector<Size> sizes(num_images);    vector<Mat> masks(num_images);    // Preapre 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;#if defined(HAVE_OPENCV_GPU)    if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0)    {        if (warp_type == "plane") warper_creator = new cv::PlaneWarperGpu();        else if (warp_type == "cylindrical") warper_creator = new cv::CylindricalWarperGpu();        else if (warp_type == "spherical") warper_creator = new cv::SphericalWarperGpu();    }    else#endif    {        if (warp_type == "plane") warper_creator = new cv::PlaneWarper();        else if (warp_type == "cylindrical") warper_creator = new cv::CylindricalWarper();        else if (warp_type == "spherical") warper_creator = new cv::SphericalWarper();        else if (warp_type == "fisheye") warper_creator = new cv::FisheyeWarper();        else if (warp_type == "stereographic") warper_creator = new cv::StereographicWarper();        else if (warp_type == "compressedPlaneA2B1") warper_creator = new cv::CompressedRectilinearWarper(2, 1);        else if (warp_type == "compressedPlaneA1.5B1") warper_creator = new cv::CompressedRectilinearWarper(1.5, 1);        else if (warp_type == "compressedPlanePortraitA2B1") warper_creator = new cv::CompressedRectilinearPortraitWarper(2, 1);        else if (warp_type == "compressedPlanePortraitA1.5B1") warper_creator = new cv::CompressedRectilinearPortraitWarper(1.5, 1);        else if (warp_type == "paniniA2B1") warper_creator = new cv::PaniniWarper(2, 1);        else if (warp_type == "paniniA1.5B1") warper_creator = new cv::PaniniWarper(1.5, 1);        else if (warp_type == "paniniPortraitA2B1") warper_creator = new cv::PaniniPortraitWarper(2, 1);        else if (warp_type == "paniniPortraitA1.5B1") warper_creator = new cv::PaniniPortraitWarper(1.5, 1);        else if (warp_type == "mercator") warper_creator = new cv::MercatorWarper();        else if (warp_type == "transverseMercator") warper_creator = new cv::TransverseMercatorWarper();    }    if (warper_creator.empty())    {        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<Mat> 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");    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 = new detail::NoSeamFinder();    else if (seam_find_type == "voronoi")        seam_finder = new detail::VoronoiSeamFinder();    else if (seam_find_type == "gc_color")    {#if defined(HAVE_OPENCV_GPU)        if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0)            seam_finder = new detail::GraphCutSeamFinderGpu(GraphCutSeamFinderBase::COST_COLOR);        else#endif            seam_finder = new detail::GraphCutSeamFinder(GraphCutSeamFinderBase::COST_COLOR);    }    else if (seam_find_type == "gc_colorgrad")    {#if defined(HAVE_OPENCV_GPU)        if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0)            seam_finder = new detail::GraphCutSeamFinderGpu(GraphCutSeamFinderBase::COST_COLOR_GRAD);        else#endif            seam_finder = new detail::GraphCutSeamFinder(GraphCutSeamFinderBase::COST_COLOR_GRAD);    }    else if (seam_find_type == "dp_color")        seam_finder = new detail::DpSeamFinder(DpSeamFinder::COLOR);    else if (seam_find_type == "dp_colorgrad")        seam_finder = new detail::DpSeamFinder(DpSeamFinder::COLOR_GRAD);    if (seam_finder.empty())    {        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...");#if ENABLE_LOG    t = getTickCount();#endif    Mat img_warped, img_warped_s;    Mat dilated_mask, seam_mask, mask, mask_warped;    Ptr<Blender> blender;    //double compose_seam_aspect = 1;    double compose_work_aspect = 1;    for (int img_idx = 0; img_idx < num_images; ++img_idx)    {        LOGLN("Compositing image #" << indices[img_idx]+1);        // Read image and resize it if necessary        full_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.empty())        {            blender = Blender::createDefault(blend_type, try_gpu);            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_gpu);            else if (blend_type == Blender::MULTI_BAND)            {                MultiBandBlender* mb = dynamic_cast<MultiBandBlender*>(static_cast<Blender*>(blender));                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*>(static_cast<Blender*>(blender));                fb->setSharpness(1.f/blend_width);                LOGLN("Feather blender, sharpness: " << fb->sharpness());            }            blender->prepare(corners, sizes);        }        // Blend the current image        blender->feed(img_warped_s, mask_warped, corners[img_idx]);    }    Mat result, result_mask;    blender->blend(result, result_mask);    LOGLN("Compositing, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");    imwrite(result_name, result);    LOGLN("Finished, total time: " << ((getTickCount() - app_start_time) / getTickFrequency()) << " sec");    return 0;}


2.1.1 stitching.cpp运行结果

2.2 利用手动选择匹配特征点的拼接程序验证

在上述方案无效的情况下,利用可以手动选择特征点的程序imagemosaic_main进行拼接,为了提高拼接成高的概率,采用了图像细节较为丰富的0046.png0047.png

imagemosaic_main

#include "imagemosaic_main.h"using namespace cv;using namespace std;RNG rng(12345678);int main(){Mat src1, src2; char* imagename1 = "0046.PNG";char* imagename2 = "0047.PNG"; /*char* imagename1 = "BoundingBox+原图-C-0m4s.jpg"; char* imagename2 = "BoundingBox+原图-CT-g1-0m4s_undistoredImg.jpg";*/src1 = imread(imagename1, CV_LOAD_IMAGE_COLOR);src2 = imread(imagename2, CV_LOAD_IMAGE_COLOR);if (src1.empty()){fprintf(stderr, "Can not load image %s\n", imagename1);return -1;}if (src2.empty()){fprintf(stderr, "Can not load image %s\n", imagename2);return -1;}//sift特征检测SiftFeatureDetector  siftdtc;vector<KeyPoint>kp1, kp2;siftdtc.detect(src1, kp1);Mat outimg1;drawKeypoints(src1, kp1, outimg1);namedWindow("image1 keypoints", 0);resizeWindow("image1 keypoints", 480 * src1.cols / src1.rows, 480);imshow("image1 keypoints", outimg1);waitKey();KeyPoint kp;/*vector<KeyPoint>::iterator itvc;for (itvc = kp1.begin();itvc != kp1.end();itvc++){cout << "angle:" << itvc->angle << "\t" << itvc->class_id << "\t" << itvc->octave << "\t" << itvc->pt << "\t" << itvc->response << endl;}*/siftdtc.detect(src2, kp2);Mat outimg2;drawKeypoints(src2, kp2, outimg2);namedWindow("image2 keypoints", 0);resizeWindow("image2 keypoints", 480 * src2.cols / src2.rows, 480);imshow("image2 keypoints", outimg2);waitKey();SiftDescriptorExtractor extractor;Mat descriptor1, descriptor2;BruteForceMatcher<L2<float>> matcher;vector<DMatch> matches;Mat img_matches;extractor.compute(src1, kp1, descriptor1);extractor.compute(src2, kp2, descriptor2);//imshow("desc", descriptor1);//cout << endl << descriptor1 << endl;matcher.match(descriptor1, descriptor2, matches);drawMatches(src1, kp1, src2, kp2, matches, img_matches);namedWindow("matches", 0);resizeWindow("matches", 480 * img_matches.cols / img_matches.rows, 480);imshow("matches", img_matches);//imwrite("matches", img_matches);waitKey();// findhomographymatrix// vector<Point2f> imgpts1, imgpts2;// for (unsigned int i = 0;i < matches.size();i++)// {// imgpts1.push_back(kp1[matches[i].queryIdx].pt);// imgpts2.push_back(kp2[matches[i].trainIdx].pt);// }//手工标注特征点对vector<Point2f> imgpts1{ Point2f(815, 270), Point2f(866, 557), Point2f(987, 681), Point2f(933, 869), Point2f(691, 734), Point2f(568, 1181), Point2f(490, 1237), Point2f(652, 1583), Point2f(924, 1505), Point2f(951, 1084), Point2f(529, 1533) };vector<Point2f>imgpts2{ Point2f(304, 76), Point2f(373, 338), Point2f(469, 439), Point2f(428, 606), Point2f(204, 478), Point2f(61, 874), Point2f(48, 924), Point2f(182, 1215), Point2f(371, 1117), Point2f(386, 757), Point2f(131, 1218) };//findhomographyvector<uchar> status(imgpts2.size());Mat H = cv::findHomography(imgpts1,imgpts2,CV_RANSAC,3.0,status);Mat pano(Size(src1.cols + src2.cols, (src1.rows + src2.rows)/2), src1.type());src1.copyTo(pano(Rect(0, 0, src1.cols, src1.rows)));src2.copyTo(pano(Rect(src1.cols, 0, src2.cols, src2.rows))); for (int i = 0; i < imgpts1.size();i++) { if (status[i]==1) {Point2f p1 = imgpts1[i];Point2f p2 = imgpts2[i] + Point2f(src1.cols, 0);Scalar color = Scalar(rng.next() % 256, rng.next() % 256, rng.next() % 256);circle(pano, p1, 3, color, 5);circle(pano, p2, 3, color, 5);line(pano,p1,p2,color,2); } }namedWindow("drawMatches", 0);resizeWindow("drawMatches", 480 * pano.cols / pano.rows, 480);imshow("drawMatches", pano);waitKey();Mat dst = Mat::zeros(src1.rows, 1.5*src1.cols, CV_8UC3);cv::warpPerspective(src1, dst, H, dst.size());namedWindow("dst", 0);resizeWindow("dst", 480 * dst.cols / dst.rows, 480);imshow("dst", dst);Mat image_mosaic = Mat::zeros(src1.rows, 1.5*src1.cols,CV_8UC3);Mat src2_ext = Mat::zeros(src1.rows, (1.5*src1.cols - src1.cols), CV_8UC3);copyMakeBorder(src2, src2, 0, 0, 0, (1.5*src1.cols - src1.cols), BORDER_CONSTANT, Scalar(0, 0, 0));addWeighted(src2, 0.5, dst, 0.5,0.0, image_mosaic, -1);namedWindow("image_mosaic", 0);resizeWindow("image_mosaic", 480 * image_mosaic.cols / image_mosaic.rows, 480);imshow("image_mosaic", image_mosaic);//imwrite("image_mosaic", image_mosaic);waitKey();return 0;}


 

2.2.1 0046.png

 

2.2.2 0047.png

拼接程序imagemosaic_main.cpp基于openCV 2.4.9运行,手动选择特征点代码如下所示:

 

一共选取11组像素对进行匹配。拼接结果如下图所示:

 

2.2.3 拼接结果

可见拼接效果并不理想。

 

2.3 利用拼接软件进行拼接

为了检验图片的可拼接性,使用图像拼接软件对其进行拼接。

这里我们共采用3款拼接软件对图像进行拼接,分别为AutostitchPTGuiHugin

2.3.1 Autostitch

官方网站:http://matthewalunbrown.com/autostitch/autostitch.html

此软件使用最为简单,功能也较为简易,只可实现自动拼接,无法实现手动选取特征匹配点。

软件界面如下图所示:

 

2.3.1.1 Autostitch软件界面

我们采用采用了图像细节较为丰富的0046.png0047.png进行拼接。发现软件无法完成拼接。

 

2.3.1.2 Autostitch拼接失败

2.3.2 Hugin

官方网站:http://hugin.sourceforge.net/

此软件功能较为全面,可实现自动拼接以及手动选取特征匹配点进行拼接。

这里对0046.png0047.png两张图像手动选取20个特征点,过程及拼接结果如下所示:

 

2.3.2.1 Hugin手动选择特征点匹配

 

2.3.2.2 Hugin拼接结果

2.3.3 PTGui

官方网站:http://www.ptgui.com/

此软件此软件功能也较为全面,可实现自动拼接以及手动选取特征匹配点进行拼接。

这里对0046.png0047.png两张图像手动选取20个特征点,过程及拼接结果如下所示:

 

2.3.3.1 PTGui手动选择特征点匹配

 

2.3.3.2 PTGui拼接结果

2.4 结果分析

由上述结果可看出,无论是利用拼接程序中的自动拼接程序、手动选取特征点程序或是拼接软件中的自动拼接还是手动选取特征点都无法对图像进行效果良好的拼接。

观察平时进行图像全景拼接效果较好的图像均是静态目标,且移动镜头进行拍摄,图像之间变化较小,进而拼接。

而该组图像存在高速移动的物体(列车),且每张图像之间目标有较为大的移动,且图像边缘形拉伸较为剧烈,从而对拼接带来了较大的困难性。

0 0
原创粉丝点击