根据变换矩阵进行图像拼接
描述:在多相机组合拍摄时,相机的位置相对固定。通常进行图像拼接时,需要寻找两副图像间的特征点,计算出转化矩阵,再进行图像拼接。在寻找特征点和生成转化矩阵这一过程中,会消耗较多的计算量,导致速度较慢。对于固定的相机组合来说,其转化矩阵计算一次即可,后续直接读取矩阵内容就行。
一、获取转化矩阵
直接贴代码main.py
from stitcher import stitcher
import cv2
import time
# 读取拼接图片
t0 = time.time()
imageA = cv2.imread("left_01.png")
imageB = cv2.imread("right_01.png")
# 把图片拼接成全景图
stitcher = stitcher()
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)
print('Use time is : ',time.time()-t0)
# 显示所有图片
cv2.imshow("Image A", imageA)
cv2.imshow("Image B", imageB)
cv2.imwrite('vis.jpg',vis)
cv2.imshow("Keypoint Matches", vis)
cv2.imwrite('result.jpg',result)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
stitcher.py的代码
import numpy as np
import cv2
class stitcher:
def stitch(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False):
(imageA, imageB) = images
# 找特征点
(kpsA, npkpsA, feasA) = self.find_kps_feas(imageA)
(kpsB, npkpsB, feasB) = self.find_kps_feas(imageB)
M = self.matchKeypoints(npkpsA, npkpsB, feasA, feasB, ratio, reprojThresh)
(good, H, status) = M
np.savetxt("data.txt", H)
result = cv2.warpPerspective(imageB, H, (imageA.shape[1] + imageB.shape[1], imageB.shape[0]))
#self.cv_show('result1', result)
result[0:imageA.shape[0], 0:imageA.shape[1]] = imageA
#self.cv_show('result2', result)
img = cv2.drawMatchesKnn(imageB, kpsB, imageA, kpsA, good, None, flags=2)
#self.cv_show('result', img)
return result, img
def find_kps_feas(self, image):
# 建立SIFT生成器
descriptor = cv2.xfeatures2d.SIFT_create()
# 检测SIFT特征点
(kps, features) = descriptor.detectAndCompute(image, None)
# 将结果转换成NumPy数组
npkps = np.float32([kp.pt for kp in kps])
# 返回特征点集,及对应的描述特征
return (kps, npkps, features)
def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
bf = cv2.BFMatcher()
allMatches = bf.knnMatch(featuresB, featuresA, k=2)
matches = []
good = []
for m, n in allMatches:
if m.distance < ratio * n.distance:
matches.append((m.trainIdx, m.queryIdx))
good.append([m])
if len(matches) > 4:
ptsA = np.float32([kpsA[i] for (i, _) in matches])
ptsB = np.float32([kpsB[i] for (_, i) in matches])
(H, status) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, reprojThresh)
return (good, H, status)
def cv_show(self, name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
在上述代码中,主要将特征矩阵保存为data.txt。方便后续的数据读取。经过测试,上述代码用时大概70ms。
二、直接读取转化矩阵进行图像拼接
1.python版本
代码如下:
import cv2
import time
import numpy as np
# 读取拼接图片
t0 = time.time()
imageA = cv2.imread("left_01.png")
imageB = cv2.imread("right_01.png")
A = np.zeros((3, 3), dtype=float)
f = open('/home/cj/work/python/proc_image/concatimg/2/data.txt') # 打开数据文件文件
lines = f.readlines() # 把全部数据文件读到一个列表lines中
A_row = 0 # 表示矩阵的行,从0行开始
for line in lines: # 把lines中的数据逐行读取出来
list = line.strip('\n').split(' ') # 处理逐行数据:strip表示把头尾的'\n'去掉,split表示以空格来分割行数据,然后把处理后的行数据返回到list列表中
A[A_row:] = list[0:3] # 把处理后的数据放到方阵A中。list[0:4]表示列表的0,1,2,3列数据放到矩阵A中的A_row行
A_row += 1 # 然后方阵A的下一行接着读
result = cv2.warpPerspective(imageB, A, (imageA.shape[1] + imageB.shape[1], imageB.shape[0]))
result[0:imageA.shape[0], 0:imageA.shape[1]] = imageA
print("Use calculated matrix operation me is : ",time.time()-t0)
cv2.imwrite('myresult.jpg',result)
直接读取矩阵进行图像拼接,大概用时11ms,相对来说快了很多了!
2.C++版本
代码如下
#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include<time.h>
#include <chrono>
#include <ratio>
#include <thread>
using namespace cv;
using namespace std;
int main()
{
Mat imageA=imread("/home/cj/work/code/C++/concatimg/2/untitled/left_01.png");
Mat imageB=imread("/home/cj/work/code/C++/concatimg/2/untitled/right_01.png");
int W_imageA = imageA.cols;
int H_imageA = imageA.rows;
int W_imageB = imageB.cols;
int H_imageB = imageB.rows;
int new_w = W_imageA+W_imageB;
auto t1 = std::chrono::high_resolution_clock::now();
//读取变换矩阵
float A[3][3];
//Mat A;
FILE* fpread;
fpread = fopen("/home/cj/work/code/C++/concatimg/2/untitled/data.txt", "r");
if (fpread == NULL)
{
printf("file is error.");
return -1;
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
fscanf(fpread, "%e", &A[i][j]);
}
}
fclose(fpread);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%e\t",A[i][j]);
}
printf("\n");
}
//imshow("img",imageA);
Mat H(3,3,CV_32FC1,A);//转化为Mat矩阵
cout<<"H is :"<<H;
//dst = cv::warpPerspective(imageB, A, dsize[, dst[, flags[, borderMode[, borderValue]]]])
Mat dst;
//cv::warpPerspective(imageB,dst,H,imageB.size());
cv::warpPerspective(imageB,dst,H,cv::Size(new_w,H_imageB));//透视变换
//cout<<"Size of imageB is :"<<imageB.size();
//cv::imwrite("dst.jpg",dst);
cv::Rect roi_rect = cv::Rect(0, 0, imageA.cols, imageA.rows);
imageA.copyTo(dst(roi_rect));
auto t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> fp_ms = t2 - t1;
std::cout << "f() took " << fp_ms.count() << " ms"<<std::endl;
cv::imwrite("dst_result.jpg",dst);
waitKey(2000);
destroyAllWindows();
}
C++运行终端显示内容为:
时间只需要1.45ms!
效果