根据变换矩阵进行图像拼接

根据变换矩阵进行图像拼接

描述:在多相机组合拍摄时,相机的位置相对固定。通常进行图像拼接时,需要寻找两副图像间的特征点,计算出转化矩阵,再进行图像拼接。在寻找特征点和生成转化矩阵这一过程中,会消耗较多的计算量,导致速度较慢。对于固定的相机组合来说,其转化矩阵计算一次即可,后续直接读取矩阵内容就行。

一、获取转化矩阵

直接贴代码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!
效果
在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小俊俊的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值