学习template算法(template matching)以及改进(二)

为了实现跨越在不同两张图片内的模板和该两张搜索图片的模板匹配,现在先尝试使用图像拼接的方法,获得两个图像的拼接后的全景图像,然后再看看在全景图像中使用原来的matchTemplate算法是否可以匹配出其中模板图像的位置。

参考文章https://blog.csdn.net/dcrmg/article/details/52629856

Sift和Surf算法实现两幅图像拼接的过程是一样的,主要分为4大部分:

  1. 对每幅图进行特征点提取
  2. 特征点配对,找到两幅图像中匹配点的位置
  3. 通过配对点,生成变换矩阵,并对图像1应用变换矩阵生成对图像2的映射图像
  4. 图像2拼接到映射图像上,完成拼接
  5. 对重叠边界进行特殊处理

一、基于stitching的图像拼接+模板匹配

参考https://blog.csdn.net/czl389/article/details/60767654 ,
代码stitching.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;//
#define ENABLE_LOG 1// Default command line args
vector<string> img_names;
bool preview = false;
bool try_gpu = true;
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";int main(int argc, char* argv[])
{//读入图像double ttt = getTickCount();img_names.push_back("ref0.jpg");img_names.push_back("ref1.jpg");#if ENABLE_LOGint64 app_start_time = getTickCount();
#endifcv::setBreakOnError(true);/*int retval = parseCmdArgs(argc, argv);if (retval)return retval;*/// Check if have enough imagesint 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_LOGint64 t = getTickCount();
#endifPtr<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
#endiffinder = 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_LOGt = getTickCount();
#endifvector<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 graphif (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 panoramavector<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 imagesnum_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 lengthvector<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]);elsewarped_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_LOGt = getTickCount();
#endifvector<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 masksfor (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 masksPtr<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
#endifseam_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
#endifseam_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 memoryimages.clear();images_warped.clear();images_warped_f.clear();masks.clear();LOGLN("Compositing...");
#if ENABLE_LOGt = getTickCount();
#endifMat 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 necessaryfull_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 scalewarped_image_scale *= static_cast<float>(compose_work_aspect);warper = warper_creator->create(warped_image_scale);// Update corners and sizesfor (int i = 0; i < num_images; ++i){// Update intrinsicscameras[i].focal *= compose_work_aspect;cameras[i].ppx *= compose_work_aspect;cameras[i].ppy *= compose_work_aspect;// Update corner and sizeSize 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);elseimg = full_img;full_img.release();Size img_size = img.size();Mat K;cameras[img_idx].K().convertTo(K, CV_32F);// Warp the current imagewarper->warp(img, K, cameras[img_idx].R, INTER_LINEAR, BORDER_REFLECT, img_warped);// Warp the current image maskmask.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 exposurecompensator->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 imageblender->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);result.convertTo(result,CV_8UC1);namedWindow("stitch", WINDOW_NORMAL);imshow("stitch",result);/*----------------my coding-------------*/Mat image_template = imread("local.jpg");Mat image_matched;Mat resized_image_template;resize(image_template, resized_image_template, Size(), 0.4, 0.4, INTER_AREA);matchTemplate(result, resized_image_template, image_matched, TM_CCORR_NORMED);//find the best positiondouble minVal, maxVal;Point minLoc, maxLoc;minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);Mat roi_image;roi_image = result( Range(maxLoc.y, maxLoc.y + resized_image_template.rows + 1), Range(maxLoc.x,  maxLoc.x + resized_image_template.cols + 1) );namedWindow("Template in SearchImage", WINDOW_NORMAL);imshow("Template in SearchImage", roi_image);imwrite("found.jpg", roi_image);           //save the result image!/*----------------------end--------------------*/ttt = ((double)getTickCount() - ttt) / getTickFrequency();cout << "总的拼接时间:" << ttt << endl;waitKey(0);LOGLN("Finished, total time: " << ((getTickCount() - app_start_time) / getTickFrequency()) << " sec");return 0;
}

结果如下:

对比之前的local模板图像:

可见效果不错:)


二、基于SURF的图像拼接+模板匹配

以下步骤参考文章https://blog.csdn.net/yaningli/article/details/78551615
但代码可能与其中不同

(1)特征点提取与匹配

feature.cpp的特征点匹配结果:

参考文章http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.html
使用feature2-show matchedpoints.cpp的结果为

关于opencv中的二维特征点的匹配方法和其适用的特征提取结果特征描述符descriptor,见文章https://blog.csdn.net/holybin/article/details/40926315

另外,注意将其中的

drawMatches( leftGray, leftKeyPoints, rightGray, rightKeyPoints, GoodMatchPoints, img_matches);

换成

drawMatches(leftImg, leftKeyPoints, rightImg, rightKeyPoints, GoodMatchPoints, img_matches);

之后,结果如下图:

(2)图像配准

学习template算法(template matching)以及改进(二)相关推荐

  1. 2017年深度学习优化算法最新进展:改进SGD和Adam方法

    2017年深度学习优化算法最新进展:如何改进SGD和Adam方法 转载的文章,把个人觉得比较好的摘录了一下 AMSGrad 这个前期比sgd快,不能收敛到最优. sgdr 余弦退火的方案比较好 最近的 ...

  2. 学习template算法以及改进(一)

    学习template算法(template matching)以及改进(一) 参考https://en.wikipedia.org/wiki/Template_matching#Examples_of ...

  3. 模板方法模式 Template method 行为型 设计模式(二十六)

    模板方法模式 Template method 上图为网上百度的一份简历模板截图 相信大家都有求职的经历,那么必然需要简历,写简历的时候,很可能你会网上检索一份简历模板,使用此模板的格式,然后替换为你的 ...

  4. 深度强化学习主流算法介绍(二):DPG系列

    之前的文章可以看这里 深度强化学习主流算法介绍(一):DQN系列 相关论文在这里 开始介绍DPG之前,先回顾下DQN系列 DQN直接训练一个Q Network 去估计每个离散动作的Q值,使用时选择Q值 ...

  5. 基于MVS的三维重建算法学习笔记(四)— 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读

    基于MVS的三维重建算法学习笔记(四)- 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读 声明 SGM概述 Cost Calculation(像素代价计算)--M ...

  6. 2017年深度学习优化算法最新进展:如何改进SGD和Adam方法?

    2017年深度学习优化算法最新进展:如何改进SGD和Adam方法? 深度学习的基本目标,就是寻找一个泛化能力强的最小值,模型的快速性和可靠性也是一个加分点. 随机梯度下降(SGD)方法是1951年由R ...

  7. 设计模式学习笔记——模板(Template)模式

    设计模式学习笔记--模板(Template)模式 @(设计模式)[设计模式, 模板模式, template, 模板方法] 设计模式学习笔记模板Template模式 基本介绍 模板案例 类图 实现代码 ...

  8. SpringBoot(二)Error resolving template “xxx”, template might not exist or might not be accessible解决办法

    SpringBoot 7 SpringBoot整合持久层技术 7.1 整合JDBC 7.1.1 修改pom文件 <?xml version="1.0" encoding=&q ...

  9. 强化学习经典算法笔记(十二):近端策略优化算法(PPO)实现,基于A2C(下)

    强化学习经典算法笔记(十二):近端策略优化算法(PPO)实现,基于A2C 本篇实现一个基于A2C框架的PPO算法,应用于连续动作空间任务. import torch import torch.nn a ...

最新文章

  1. redis mysql 集群_mysql_redis
  2. Oracle体系结构之密码文件管理
  3. 【性能优化实战】java嵌入式开发pos
  4. oracle和mysql登录方式_使用普通方式和连接池方式获取Oracle和Mysql链接
  5. 如何将结婚当作项目来管理
  6. app.vue中引用图片src=“../assets/logo.png“报错未找到图片
  7. JAVA——改变观感
  8. python非数值型数据_Python机器学习实战:如何处理非数值特征
  9. python anaconda和pycharm_Python 、Pycharm、Anaconda三者的区别与联系、安装过程及注意事项...
  10. Hbase API实现倒序查询
  11. mplayer+科大讯飞离线语音包
  12. dnf 卸载软件linux,Linux软件安装与卸载的基本概念
  13. python绘制emoji_使用Emoji表情拼成汉字
  14. 全世界最全牛人博客,你可以学习到太多!
  15. 计算机主板纽扣电池缺电,主板的纽扣电池没电了怎么更换
  16. C++ HOOK PC微信实现无限制多开,理论支持所有版本,本文使用微信 3.0.0.47
  17. windows10安装MySQL8.0
  18. 如何判断JS中变量的类型
  19. JAVA面试系列:你了解系统可用性吗?
  20. 二叉排序树实现英文文章单词统计

热门文章

  1. Nodejs模块初始化
  2. Sql Server2008中自定义函数调用存储过程解决方案
  3. Elasticsearch aggregations API
  4. 每天备份NAS上的www目录到一块单独的硬盘上
  5. PHP的学习--在sublime中使用XDebug(Ubuntu)
  6. 我们活在世界上,不是为了求人们原谅。
  7. 河外动态:疑似UFO的编队在太阳附近徘徊
  8. WinAPI: waveOutPause - 暂停播放
  9. 对标Postman的ApiPost创始人:用户,是ApiPost唯一的信仰
  10. 接口测试和性能测试的区别