圖像拼接(一):柱面投影+模板匹配+漸入漸出融合

這種拼接方法的假設前提是:待拼接的兩幅圖像之間的變換模型是平移模型,即兩幅圖像同名點位置之間只相差兩個未知量:ΔxΔx 和ΔyΔy,自由度為2,模型收得最緊。所以只有所有圖像都是用同一水平線或者同一已知傾斜角的攝像機拍攝時,這種方法才適用。?
整個過程為:首先對輸入的兩幅圖像做柱面投影;然后通過模板匹配求取ΔxΔx 和ΔyΔy;最后采用漸入漸出的融合方式拼接兩幅圖像。

柱面投影
為了保證拼接后的視覺一致性,所以需要將待拼接的圖像分別投影到一個標準的坐標系下,然后再進行圖像拼接。由于柱面坐標的變換比較簡單并且投影圖像與其投影到圓柱表面的位置無關,用其描述的柱面全景圖像可在水平方向上滿足360度環視,具有較好的視覺效果,所以可采用柱面投影完成圖像拼接。?
下面為柱面投影采用的公式,x′x′和y′y′為柱面投影后的圖像坐標,xx和yy為圖像原來的坐標,widthwidth和heightheight為圖像寬高,ff為相機焦距,我的理解是:因為widthwidth和heightheight都是在圖像坐標系下,所以這個ff是相對于圖像的,我是根據圖像大小以及視場角最做的估計。?
x′=f?atan(x?0.5?widthf)+f?atan(0.5?widthf)
x′=f?atan(x?0.5?widthf)+f?atan(0.5?widthf)
y′=f?(y?0.5?height)(x?0.5?width)2+f2??????????????????√+0.5?height
y′=f?(y?0.5?height)(x?0.5?width)2+f2+0.5?height

下面是柱面校正的代碼,事無巨細都是自己寫的,因為沒有找到上述公式的反演公式,直接正向插值了,好在出來的效果還不錯。
/**柱面投影函數
?*參數列表中imgIn為輸入圖像,f為焦距
?*返回值為柱面投影后的圖像
*/
Mat cylinder(Mat imgIn, int f)
{
? ? int colNum, rowNum;
? ? colNum = 2 * f*atan(0.5*imgIn.cols / f);//柱面圖像寬
? ? rowNum = 0.5*imgIn.rows*f / sqrt(pow(f, 2)) + 0.5*imgIn.rows;//柱面圖像高

? ? Mat imgOut = Mat::zeros(rowNum, colNum, CV_8UC1);
? ? Mat_<uchar> im1(imgIn);
? ? Mat_<uchar> im2(imgOut);

? ? //正向插值
? ? int x1(0), y1(0);
? ? for (int i = 0; i < imgIn.rows; i++)
? ? ? ? for (int j = 0; j < imgIn.cols; j++)
? ? ? ? {
? ? ? ? ? ? x1 = f*atan((j - 0.5*imgIn.cols) / f) + f*atan(0.5*imgIn.cols / f);
? ? ? ? ? ? y1 = f*(i - 0.5*imgIn.rows) / sqrt(pow(j - 0.5*imgIn.cols, 2) + pow(f, 2)) + 0.5*imgIn.rows;
? ? ? ? ? ? if (x1 >= 0 && x1 < colNum&&y1 >= 0 && y1<rowNum)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? im2(y1, x1) = im1(i, j);
? ? ? ? ? ? }
? ? ? ? }
? ? return imgOut;
}
?
模板匹配
通過模板匹配的方法求取平移變換參數

/**求平移量
?*參數表為輸入兩幅圖像(有一定重疊區域)
?*返回值為點類型,存儲x,y方向的偏移量
*/
Point2i getOffset(Mat img, Mat img1)
{
? ? Mat templ(img1, Rect(0, 0.4*img1.rows, 0.2*img1.cols, 0.2*img1.rows));
? ? Mat result(img.cols - templ.cols + 1, img.rows - templ.rows + 1, CV_8UC1);//result存放匹配位置信息
? ? matchTemplate(img, templ, result, CV_TM_CCORR_NORMED);
? ? normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
? ? double minVal; double maxVal; Point minLoc; Point maxLoc; Point matchLoc;
? ? minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
? ? matchLoc = maxLoc;//獲得最佳匹配位置
? ? int dx = matchLoc.x;
? ? int dy = matchLoc.y - 0.4*img1.rows;//右圖像相對左圖像的位移
? ? Point2i a(dx, dy);
? ? return a;
}
?
線性融合
采用漸入漸出融合,其實就是在重疊區域,對兩幅圖像的像素,線性地分配權值。公式:img=d?img1+(1?d)?img2img=d?img1+(1?d)?img2;其中img為融合后的圖像,img1和img2為待拼接的兩幅圖像。d為重疊區域中某個像素點到邊界的距離。

/*漸入漸出拼接
?*參數列表中,img1,img2為待拼接的兩幅圖像,a為偏移量
?*返回值為拼接后的圖像
*/
Mat linearStitch(Mat img, Mat img1, Point2i a)
{
? ? int d = img.cols - a.x;//過渡區寬度
? ? int ms = img.rows - abs(a.y);//拼接圖行數
? ? int ns = img.cols + a.x;//拼接圖列數
? ? Mat stitch = Mat::zeros(ms, ns, CV_8UC1);
? ? //拼接
? ? Mat_<uchar> ims(stitch);
? ? Mat_<uchar> im(img);
? ? Mat_<uchar> im1(img1);

? ? if (a.y >= 0)
? ? {
? ? ? ? Mat roi1(stitch, Rect(0, 0, a.x, ms));
? ? ? ? img(Range(a.y, img.rows), Range(0, a.x)).copyTo(roi1);
? ? ? ? Mat roi2(stitch, Rect(img.cols, 0, a.x, ms));
? ? ? ? img1(Range(0, ms), Range(d, img1.cols)).copyTo(roi2);
? ? ? ? for (int i = 0; i < ms; i++)
? ? ? ? ? ? for (int j = a.x; j < img.cols; j++)
? ? ? ? ? ? ? ? ims(i, j) = uchar((img.cols - j) / float(d)*im(i + a.y, j) + (j - a.x) / float(d)*im1(i, j - a.x));

? ? }
? ? else
? ? {
? ? ? ? Mat roi1(stitch, Rect(0, 0, a.x, ms));
? ? ? ? img(Range(0, ms), Range(0, a.x)).copyTo(roi1);
? ? ? ? Mat roi2(stitch, Rect(img.cols, 0, a.x, ms));
? ? ? ? img1(Range(-a.y, img.rows), Range(d, img1.cols)).copyTo(roi2);
? ? ? ? for (int i = 0; i < ms; i++)
? ? ? ? ? ? for (int j = a.x; j < img.cols; j++)
? ? ? ? ? ? ? ? ims(i, j) = uchar((img.cols - j) / float(d)*im(i, j) + (j - a.x) / float(d)*im1(i + abs(a.y), j - a.x));
? ? }


? ? return stitch;
}
?
實驗效果
寫一個包含主函數的文件調用上述方法:

#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include<iostream>
#include<time.h>

int main()
{
? ? Mat img = imread("frame1.jpg", 0);//左圖像
? ? Mat img1 = imread("frame2.jpg", 0);//右圖像
? ? imshow("源圖像-左", img);
? ? imshow("源圖像-右", img1);
? ? double t = (double)getTickCount();
? ? //柱形投影
? ? double t3 = (double)getTickCount();
? ? img = cylinder(img,1000);
? ? img1 = cylinder(img1, 1000);
? ? t3 = ((double)getTickCount() - t3) / getTickFrequency();
? ? //匹配
? ? double t1 = (double)getTickCount();
? ? Point2i a = getOffset(img, img1);
? ? t1 = ((double)getTickCount() - t1) / getTickFrequency();
? ? //拼接
? ? double t2 = (double)getTickCount();
? ? Mat stitch = linearStitch(img, img1, a);
? ? t2 = ((double)getTickCount() - t2) / getTickFrequency();
? ? t = ((double)getTickCount() - t) / getTickFrequency();

? ? cout << "各階段耗時:"<< endl;
? ? cout << "柱面投影:" << t3 << '\n' << "模板匹配:" << t1 << '\n' << "漸入漸出拼接:" << t2 << endl;
? ? cout << "總時間:" << t << endl;

? ? imshow("柱面校正-左圖像", img);
? ? imshow("柱面校正-右圖像", img1);
? ? imshow("拼接結果", stitch);
? ? imwrite("rectify.jpg", img);
? ? imwrite("rectify1.jpg", img1);
? ? imwrite("stitch.jpg", stitch);
? ? waitKey(0);
? ? return 0;
}
?
首先讀取兩幅源圖像:?

柱面投影之后:?


?
最后的拼接結果:?

拼接時間效率:?
測試環境為Intel(R) Core(TM) i3-2350M CPU @ 2.3GHz;Win 10+VS2013;源圖像像素分辨率為640*480。?


可以看出整體效果還行,但細節很差,比如重疊區域的墻上電線出現了重影。時間效率方面,自己寫的柱面投影函數耗時明顯,有待優化
---------------------?
作者:czl389?
來源:CSDN?
原文:https://blog.csdn.net/czl389/article/details/54599253?
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444662.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444662.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444662.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

圖像拼接(二):OpenCV同時打開兩個攝像頭捕獲視頻

使用OpenCV實現同時打開兩個USB攝像頭&#xff0c;并實時顯示視頻。如果未檢測有兩個攝像頭&#xff0c;程序會結束并發出“攝像頭未安裝好”的警告。這里推薦一個小巧的攝像頭視頻捕捉軟件&#xff1a;amcap&#xff0c;使用它可以方便的檢查每個攝像頭是否能正常工作。 捕獲…

Git(10)-merge

Merge1. 無沖突合并2. 有沖突合并-手動解決3. git diff in merge4. 廢棄合并5. 合并策略merge相關的操作的命令 git checkout master git merge alternate # 解決沖突 ..... git add file_1 git commit -m "Add slternate line 5, 6" git reset --hard HEAD # b…

elasticsearch的Linux下安裝報錯問題解決

1.啟動報錯如下: vim /etc/security/limits.conf 然后修改如下 * soft nofile 65536 * hard nofile 65536sudo vi /etc/pam.d/common-session 添加 session required pam_limits.so sudo vi /etc/pam.d/common-session-noninteractive 添加 session required pam_limits.so…

leetcode120. 三角形最小路徑和

給定一個三角形&#xff0c;找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。 例如&#xff0c;給定三角形&#xff1a; [ [2], [3,4], [6,5,7], [4,1,8,3] ] 自頂向下的最小路徑和為 11&#xff08;即&#xff0c;2 3 5 1 11&#xff0…

Elasticsearchan相關插件和工具安裝

1、下載elasticsearch-head的源碼包 地址&#xff1a;https://github.com/mobz/elasticsearch-head/releases 2、安裝node運行環境 地址&#xff1a;https://nodejs.org/en/download/ 3、安裝完node之后編譯elasticsearch-head 執行npm install -g grunt-cli編譯源碼 執行…

Git(11)-cherry-pick、reset、rebase

更改提交&#xff0c;版本回退1.get reset 重置HEAD指針的指向2.git cherry-pick3.git revert4.git commit --amend修改提交5.git rebase 變基提交5.1 git rebase --onto5.2rebase 產生沖突&#xff0c;解決沖突/終止變基5.3git rebase -i6. rebase Vs mergegit 提供了【修改】…

Elasticsearch集群節點配置詳解

注意&#xff1a;如果是在局域網中運行elasticsearch集群也是很簡單的&#xff0c;只要cluster.name設置一致&#xff0c;并且機器在同一網段下&#xff0c;啟動的es會自動發現對方&#xff0c;組成集群。 三、配置淺涉 elasticsearch的config文件夾里面有兩個配置文件&#…

MongoDB修改器使用

歡迎關注我的新微信公眾號 ipgame,有什么問題可以提供交流的平臺,歡迎大家討論。 對于文檔的更新除替換外,針對某個或多個文檔只需要部分更新可使用原子的更新修改器,能夠高效的進行文檔更新。更新修改器是中特殊的鍵, 用來指定復雜的操作,比如增加、刪除或者調整鍵,還…

Git(12)-stash, reflog

git stash1. git stash2. reflog命令概覽git stash save "WIP:xxxxx" # save后可以跟筆記&#xff0c;WIP:work in process git stash list # 查看存儲狀態棧的條目 git stash pop # 當前工作目錄和索引還原至最近一次save操作的內容…

cmake生成Win64位工程

使用cmake編譯64的dll 一開始使用cmake --build .來生成了dll&#xff0c;在導入到java項目中使用的時候&#xff0c;才發現是32位的。導致程序不能正常運行&#xff0c;報錯如下&#xff1a; Exception in thread "main" java.lang.UnsatisfiedLinkError Cant load…

leetcode 106. 從中序與后序遍歷序列構造二叉樹

根據一棵樹的中序遍歷與后序遍歷構造二叉樹。 注意: 你可以假設樹中沒有重復的元素。 例如&#xff0c;給出 中序遍歷 inorder [9,3,15,20,7] 后序遍歷 postorder [9,15,7,20,3] 返回如下的二叉樹&#xff1a; 3 / \ 9 20 / \ 15 7 思路&#xff1a;和前…

Mat矩陣(圖像容器)的創建及CV_8UC1,CV_8UC2等參數詳解

一&#xff09;Mat矩陣(圖像容器)創建時CV_8UC1,CV_8UC2等參數詳解 1--Mat不但是一個非常有用的圖像容器類,同時也是一個通用的矩陣類 2--創建一個Mat對象的方法很多 3--使用Mat圖像容器類創建Mat類的對象 //! default constructor Mat(); //! constructs …

TensorFlow(1)-模型相關基礎概念

TensorFlow-11.Graph對象2.Session對象3.Variabels變量4. placeholders與feed_dict5. tf.train.Saver() 模型參數保存、加載Tensorflow 中文官網教程–2.0版本的官方教程 TensorFlow教程&#xff1a;TensorFlow快速入門教程&#xff08;非常詳細&#xff09; pytorch Vs tensor…

memcache的使用入門C++代碼

下載源碼編譯&#xff0c;memcached就是生成的主程序&#xff0c;啟動可指定端口&#xff0c;memcached作為server端&#xff0c;依然是我們熟悉的cs模式&#xff0c;使用兩個client一個setkey&#xff0c;一個getkey一百萬個做測試。 ./memcached -d -m 300 -p 11211 -u root…

leetcode78 子集

給定一組不含重復元素的整數數組 nums&#xff0c;返回該數組所有可能的子集&#xff08;冪集&#xff09;。 說明&#xff1a;解集不能包含重復的子集。 示例: 輸入: nums [1,2,3] 輸出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ] 思路&…

Fiddler抓包工具使用

先下載Fiddler 歡迎關注我的新微信公眾號 ipgame&#xff0c;有什么問題可以提供交流的平臺&#xff0c;歡迎大家討論。 電腦最好是筆記本&#xff0c;這樣能和手機保持統一局域網內&#xff1b;其他不多說&#xff0c;直接說步驟了。 一.對PC&#xff08;筆記本&#xff0…

Tensorboard--模型可視化工具

Tensorboard1.tensorboard in tensorflow1.1 tensorboard的啟動過程1.2 tf.summary 可視化類型1.3 tf.summary 使用demo2.tensorboard in pytorch2.1 SummaryWriter 使用demo12.2 tSummaryWriter 使用demo22.3 tensorboard 數據再讀取tensorboard in tensorflow &#xff1a;te…

opencv findContours 報錯_acrt_first_block == header

報錯_acrt_first_block header 之前一直使用OpenCV3.3VS2015 void AOIAlgorithm::findUnits(Mat& blkGray, vector<vector<cv::Point>> & blkContours) {Mat blkOBW;blur(blkGray, blkGray, cv::Size(5, 5));threshold(blkGray, blkOBW, 0, 255, CV_THR…

TensorFlow(2)-訓練數據載入

tensorflow 訓練數據載入1. tf.data.Dataset2. dataset 創建數據集的方式2.1 tf.data.Dataset.from_tensor_slices()2.2 tf.data.TextLineDataset()2.3 tf.data.FixedLengthRecordDataset()2.4 tf.data.TFRecordDataset()3. dateset 迭代操作iterator3.1 make_one_shot_iterato…

leetcode66. 加一

給定一個由整數組成的非空數組所表示的非負整數&#xff0c;在該數的基礎上加一。 最高位數字存放在數組的首位&#xff0c; 數組中每個元素只存儲單個數字。 你可以假設除了整數 0 之外&#xff0c;這個整數不會以零開頭。 示例 1: 輸入: [1,2,3] 輸出: [1,2,4] 解釋: 輸入…