在张正友标定法的时候,可以使用单应矩阵来计算标定板平面和像素平面之间的变换关系,其本身包含了相机的内参和标定板与相机的外参矩阵。而在射影几何中,单应矩阵更多地用来表征两个平面之间的变换关系。对于标定时
令世界点的,并将中间部分协作M,则有
如果存在两个不同的相机,或者相同相机在不同位置派到同一个平面,则有
这表明,存在一组关系,可以实现两个像素平面的互相变换,具体的计算和标定时类似。
因为最后一行的1的缘故,H矩阵仍然只有8个自由度,所以只需要四组对应点即可计算出。
使用四组对应点即可实现
可以方便将普通视图转换为鸟瞰图
因为单应矩阵可以进行视角转换,则可以将不同角度拍摄的图像转换到相同的视角,从而实现图像的拼接
平面二维标记图案长用来做AR展示,根据marker不同视角下的图像可以方便地得到虚拟物体的位置姿态并进行显示
令
整理可得
整理成矩阵形式
所以只需要四组点即可求解出单应矩阵
但是再实际中,我们计算的关键点对会包含噪声,甚至出现误匹配的现象,所以只使用4组点来计算单应矩阵会出现很大的误差。因此一般会使用多于四组点来计算。另外直接法求解很难得到最优解,所以实际中会使用其他优化方法进行求解,比如SVD、LM等算法。
在opencv中有现成的函数可以进行调用
Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )
从函数中看,只需要输入对应的匹配点,指定具体计算方法即可输出结果。
输入:两张图片

输出:将图二拼接到图一
代码:virtual_billboard.cpp
#include <opencv/cv.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
class ImageMosic{
public:
ImageMosic(cv::Mat img_src,cv::Mat img_target){
img_src_ = img_src.clone();
img_target_ = img_target.clone();
SetSrcPts();
SetTargetPts();
CalcHAndPerspective();
}
private:
struct UserData{
cv::Mat img;
std::vector<cv::Point2f> pts;
};
void SetSrcPts()
{
cv::Size src_size = img_src_.size();
src_pts_.push_back(cv::Point2f(0, 0));
src_pts_.push_back(cv::Point2f(src_size.width-1, 0));
src_pts_.push_back(cv::Point2f(src_size.width-1, src_size.height-1));
src_pts_.push_back(cv::Point2f(0, src_size.height-1));
}
static void mouseHandler(int event, int x, int y, int flags,void* data_ptr)
{
if (event ==cv::EVENT_LBUTTONDOWN)
{
UserData *data = (UserData *)data_ptr;
cv::circle( data->img, cv::Point(x, y), 3, cv::Scalar(0, 255, 255), 5);
cv::imshow("Image_target", data->img);
if (data->pts.size() < 4)
{
data->pts.push_back(cv::Point2f(x,y));
}
}
}
void SetTargetPts(){
//show the image
cv::imshow("Image_target", img_target_);
std::cout << "Click on four corners of a billboard and then press ENTER" << std::endl;
//set the callback function for any mouse event
UserData user_data;
user_data.img = img_target_;
cv::setMouseCallback("Image_target", mouseHandler, &user_data);
cv::waitKey(0);
target_pts_ = user_data.pts;
}
void CalcHAndPerspective(){
assert(target_pts_.size() == 4);
cv::Mat H = cv::findHomography(src_pts_, target_pts_, 0); //计算单应矩阵
cv::warpPerspective(img_src_, img_src_perspective_, H, img_target_.size()); //透视变换
cv::Point pts_dst[4] = {target_pts_[0], target_pts_[1], target_pts_[2], target_pts_[3]};
cv::fillConvexPoly(img_target_, pts_dst, 4, cv::Scalar(0));
img_target_ = img_target_ + img_src_perspective_;
cv::imshow("Image_target", img_target_);
cv::waitKey(0);
}
private:
cv::Mat img_src_;
cv::Mat img_src_perspective_;
cv::Mat img_target_;
std::vector<cv::Point2f> src_pts_;
std::vector<cv::Point2f> target_pts_;
};
int main(){
cv::Mat img_src = cv::imread("../images/cvlife.jpg");
cv::Mat img_target = cv::imread("../images/ad.jpg");
ImageMosic img_mosaic(img_src, img_target);
return 0;
}
CMakelists.txt
cmake_minimum_required(VERSION 1.0)
project(virtual_billboard)
find_package(OpenCV)
add_executable(virtual-billboard src/virtual_billboard.cpp)
target_link_libraries(virtual-billboard ${OpenCV_LIBS})
操作步骤:在图片上从左上角顺时针选4个点,按enter结束

结果:

与本质矩阵类似,单应矩阵需要进行分解,才能得到旋转平移矩阵。分解的方法包含数值法[2][3]和解析法[4]。与本质矩阵类似,单应矩阵的分解也会返回4组旋转平移矩阵,并且同时可以解算出他们分别对应的场景点所在平面的法向量。如果已知成像的地图点的深度全为正值(即在相机前方),则可以排除两组。最后剩余两组解,需要更多的先验信息进行判断。通常我们可以通过假设已知场景平面的法向量来解决,如场景平面与相机平面平行,那么法向量的理论值为。
单应性在SLAM中有重要意义。当特征点共面或相机发生纯旋转时,基础矩阵自由度下降,出现退化。现实中数据总会包含一些噪声,这是继续使用八点法求解基础矩阵,其多余出来的自由度将会主要由噪声决定。为了避免退化现象造成影响,通常我们会同时估计基础矩阵F和单应矩阵H,选择重投影误差较小的那个作为最终的运动估计矩阵。
[1]单应性矩阵的理解及求解_机器视觉001的博客-CSDN博客_单应性矩阵
[2]Faugeras O D, Lustman F. Motion and structure from motion in a piecewise planar environment[J]. International Journal of Pattern Recognition and Artificial Intelligence, 1988, 2(03): 485-508.
[3]Zhang Z, Hanson A R. 3D reconstruction based on homography mapping[J]. Proc. ARPA96, 1996: 1007-1012.
[4]Malis E, Vargas M. Deeper understanding of the homography decomposition for vision-based control[D]. INRIA, 2007.
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin