图像拼接(四):双摄像头实时视频拼接(平移模型)
2018-04-03 21:38
2026 查看
原文地址:https://blog.csdn.net/czl389/article/details/54604677
假设两个摄像头平行固定,所拍摄的图像视差很小,可以通过“柱面投影+模板匹配+渐入渐出融合”的解决方案实现视频拼接。
关于这种方法的静态图像拼接,参考图像拼接(一):柱面投影+模板匹配+渐入渐出融合
OpenCV双摄像头捕获视频并实时显示的代码,参见:图像拼接(三):OpenCV同时打开两个摄像头捕获视频
将代码整合,实现双摄像头实时视频拼接:
假设两个摄像头平行固定,所拍摄的图像视差很小,可以通过“柱面投影+模板匹配+渐入渐出融合”的解决方案实现视频拼接。
关于这种方法的静态图像拼接,参考图像拼接(一):柱面投影+模板匹配+渐入渐出融合
OpenCV双摄像头捕获视频并实时显示的代码,参见:图像拼接(三):OpenCV同时打开两个摄像头捕获视频
将代码整合,实现双摄像头实时视频拼接:
#include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include<iostream> using namespace cv; using namespace std; //以下3个函数,与系列文章:图像拼接(一)...中的函数,相同 //柱面投影 Mat cylinder(Mat imgIn, int f); //计算两幅图像之间的平移量 Point2i getOffset(Mat img, Mat img1); //线性融合 Mat linearFusion(Mat img, Mat img1, Point2i a); int main() { VideoCapture cap1(0); VideoCapture cap2(1); double rate = 60; int delay = 1000 / rate; bool stop(false); Mat frame1; Mat frame2; Mat frame; Point2i a;//存储偏移量 int k = 0; namedWindow("cam1", CV_WINDOW_AUTOSIZE); namedWindow("cam2", CV_WINDOW_AUTOSIZE); namedWindow("stitch", CV_WINDOW_AUTOSIZE); if (cap1.isOpened()&&cap2.isOpened()) { cout << "*** ***" << endl; cout << "摄像头已启动!"<<endl; } else { cout << "*** ***" << endl; cout << "警告:摄像头打开不成功或者未检测到有两个摄像头!" << endl; cout << "程序结束!" <<endl<< "*** ***" << endl; return -1; } //cap1.set(CV_CAP_PROP_FRAME_WIDTH, 640); //cap1.set(CV_CAP_PROP_FRAME_HEIGHT, 480); //cap2.set(CV_CAP_PROP_FRAME_WIDTH, 640); //cap2.set(CV_CAP_PROP_FRAME_HEIGHT, 480); cap1.set(CV_CAP_PROP_FOCUS,0); cap2.set(CV_CAP_PROP_FOCUS, 0); while (!stop) { if (cap1.read(frame1) && cap2.read(frame2)) { imshow("cam1", frame1); imshow("cam2", frame2); //彩色帧转灰度 cvtColor(frame1, frame1, CV_RGB2GRAY); cvtColor(frame2, frame2, CV_RGB2GRAY); //柱面投影变换 //frame1 = cylinder(frame1, 1005); //frame2 = cylinder(frame2, 1005); //匹配和拼接 /*视频拼接通过while循环实现,下面这个判断的意思是,有两 *种情形才计算平移参数,一是程序启动时,前3个循环内;二 *是按下回车键时。这样在场景和摄像头相对固定时,避免了平 *移量的重复计算,提高了拼接的实时性 */ if (k < 3 || waitKey(delay) == 13)//按回车键 { cout << "正在匹配..."<<endl; a = getOffset(frame1, frame2); } frame = linearFusion(frame1, frame2, a); imshow("stitch", frame); k++; } else { cout << "----------------------" << endl; cout << "waitting..." << endl; } //按下ESC键,退出循环,程序结束 if (waitKey(1) == 27) { stop = true; cout << "程序结束!" << endl; cout << "*** ***" << endl; } } return 0; } //计算平移参数 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; } //线性(渐入渐出)融合 Mat linearFusion(Mat img, Mat img1, Point2i a) { 4000 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; } //柱面投影校正 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; }
相关文章推荐
- 图像拼接(九):双摄像头实时视频拼接(单应变换模型)
- 图像拼接(九):双摄像头实时视频拼接(单应变换模型)
- 图像拼接(十一):双摄像头实时拼接+stitching_detailed
- 图像拼接(三):OpenCV同时打开两个摄像头捕获视频
- OpenCV打开摄像头并获取实时视频图像代码
- OpenCV学习笔记(9)利用MFC的Picture控件显示图像+播放视频和捕获摄像头画面
- opecv学习(三)视频读取及摄像头采集图像/边缘检测/模糊去燥
- 摄像头视频二值化(基于图像)
- 【嵌入式开发】树莓派+官方摄像头模块+VLC串流实时输出网络视频流
- 采集音频和摄像头视频并实时H264编码及AAC编码
- luvcview,使用mplayer查看摄像头和luvcview保存YUV图像视频的播放(转)
- JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头
- javacpp-opencv图像处理3:使用opencv原生方法遍历摄像头设备及调用(增加实时帧率计算方法)
- MFC编个对话框,能够实时显示摄像头捕捉的镜头, 点击确定,保存当前图像。
- Android开发:实时处理摄像头预览帧视频------浅析PreviewCallback,onPreviewFrame,AsyncTask的综合应用
- iOS RTMP 视频直播开发笔记(1) – 采集摄像头图像
- Android开发:实时处理摄像头预览帧视频------浅析PreviewCallback,onPreviewFrame,AsyncTask的综合应用
- vs2010+opencv249人脸正侧脸摄像头实时检测并调用对应不同小视频
- Qt5.3里使用OpenCV库采集摄像头图像保存为图片和视频
- 视频与图像开发——摄像头访问