欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

全景图片拼接(python、C++)

程序员文章站 2022-04-27 10:43:32
终于有空余时间来整理之前图片拼接的代码,要实现图片拼接的原因是课题需要。需要实现对高分辨率(每个的图片大小大概为5500*4000)的图片拼接。尝试了许多方法,由于图片本身的特殊性,始终无法达到理想目标,但也有了还算不错的拼接结果。这篇博客就主要整理......

终于有空余时间来整理之前图片拼接的代码,要实现图片拼接的原因是课题需要。

需要实现对高分辨率(每个的图片大小大概为5500*4000)的图片拼接。尝试了许多方法,由于图片本身的特殊性,始终无法达到理想目标,但也有了还算不错的拼接结果。

这篇博客主要整理尝试的不同方法,如果能帮助到有需要的人就更好了。

一、opencv官方API&c++

关于环境配置的过程,可见下面的链接博客:

https://blog.csdn.net/packdge_black/article/details/107843346

样例和代码如下:


#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/stitching.hpp"
#include <opencv2/core.hpp>
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;

bool try_use_gpu = false;                  //默认不使用GPU
vector<Mat> imgs;
string result_name = "result.jpg";

int main(int argc, char* argv[])
{
	/**/
	Mat img1 = imread("7.jpg");
	Mat img2 = imread("8.jpg");

	Mat img1_1;
	Mat img2_2;

	/**/
	resize(img1, img1_1, Size(400, 300), 0, 0, CV_INTER_LINEAR);
	resize(img2, img2_2, Size(400, 300), 0, 0, CV_INTER_LINEAR);

	//resize(img3, img3_3, Size(400, 300), 0, 0, CV_INTER_LINEAR);
	//resize(img4, img4_4, Size(400, 300), 0, 0, CV_INTER_LINEAR);
	//resize(img5, img5_5, Size(400, 300), 0, 0, CV_INTER_LINEAR);

	/**/
	imgs.push_back(img1_1);
	imgs.push_back(img2_2);


	Stitcher stitcher = Stitcher::createDefault(try_use_gpu);

	Mat pano;
	Stitcher::Status status = stitcher.stitch(imgs, pano);


	if (status != Stitcher::OK)
	{
		cout << "Can't stitch images, error code = " << status << endl;
		return -1;
	}

	namedWindow(result_name);
	imshow(result_name, pano);
	//imwrite(result_name, pano);

	waitKey();
	//getchar();
	return 0;
}

样例图片:

 

全景图片拼接(python、C++)全景图片拼接(python、C++)

全景图片拼接(python、C++)

全景图片拼接(python、C++)

拼接结果:

全景图片拼接(python、C++)

具体的官方文档链接:

https://docs.opencv.org/master/d8/d19/tutorial_stitcher.html

说明:

最重要的代码部分是:

第二行和第三行是最重要的两行,大多数的内容都封装进去了:

Mat pano;

Ptr<Stitcher> stitcher = Stitcher::create(mode);

Stitcher::Status status = stitcher->stitch(imgs, pano);

if (status != Stitcher::OK)

{

cout << "Can't stitch images, error code = " << int(status) << endl;

return EXIT_FAILURE;

}

 

 

 

主要的管线图(如果想了解更多的细节,可以使用C ++或python中可用的stitching_detailed源代码)

全景图片拼接(python、C++)

 

二、RANSAC算法+SIFT&Python

代码如下:

import cv2
import numpy as np
import sys

class Image_Stitching():
    def __init__(self) :
        self.ratio=0.85
        self.min_match=10
        self.sift=cv2.xfeatures2d.SIFT_create()
        self.smoothing_window_size=800

    def registration(self,img1,img2):
        kp1, des1 = self.sift.detectAndCompute(img1, None)
        kp2, des2 = self.sift.detectAndCompute(img2, None)
        matcher = cv2.BFMatcher()
        raw_matches = matcher.knnMatch(des1, des2, k=2)
        good_points = []
        good_matches=[]
        for m1, m2 in raw_matches:
            if m1.distance < self.ratio * m2.distance:
                good_points.append((m1.trainIdx, m1.queryIdx))
                good_matches.append([m1])
        img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good_matches, None, flags=2)
        cv2.imwrite('matching.jpg', img3)
        if len(good_points) > self.min_match:
            image1_kp = np.float32(
                [kp1[i].pt for (_, i) in good_points])
            image2_kp = np.float32(
                [kp2[i].pt for (i, _) in good_points])
            H, status = cv2.findHomography(image2_kp, image1_kp, cv2.RANSAC,5.0)
        return H

    def create_mask(self,img1,img2,version):
        height_img1 = img1.shape[0]
        width_img1 = img1.shape[1]
        width_img2 = img2.shape[1]
        height_panorama = height_img1
        width_panorama = width_img1 +width_img2
        offset = int(self.smoothing_window_size / 2)
        barrier = img1.shape[1] - int(self.smoothing_window_size / 2)
        mask = np.zeros((height_panorama, width_panorama))
        if version== 'left_image':
            mask[:, barrier - offset:barrier + offset ] = np.tile(np.linspace(1, 0, 2 * offset ).T, (height_panorama, 1))
            mask[:, :barrier - offset] = 1
        else:
            mask[:, barrier - offset :barrier + offset ] = np.tile(np.linspace(0, 1, 2 * offset ).T, (height_panorama, 1))
            mask[:, barrier + offset:] = 1
        return cv2.merge([mask, mask, mask])

    def blending(self,img1,img2):
        H = self.registration(img1,img2)
        height_img1 = img1.shape[0]
        width_img1 = img1.shape[1]
        width_img2 = img2.shape[1]
        height_panorama = height_img1
        width_panorama = width_img1 +width_img2

        panorama1 = np.zeros((height_panorama, width_panorama, 3))
        mask1 = self.create_mask(img1,img2,version='left_image')
        panorama1[0:img1.shape[0], 0:img1.shape[1], :] = img1
        panorama1 *= mask1
        mask2 = self.create_mask(img1,img2,version='right_image')
        panorama2 = cv2.warpPerspective(img2, H, (width_panorama, height_panorama))*mask2
        result=panorama1+panorama2

        rows, cols = np.where(result[:, :, 0] != 0)
        min_row, max_row = min(rows), max(rows) + 1
        min_col, max_col = min(cols), max(cols) + 1
        final_result = result[min_row:max_row, min_col:max_col, :]
        return final_result
def main(argv1,argv2):
    img1 = cv2.imread(argv1)
    img2 = cv2.imread(argv2)
    final=Image_Stitching().blending(img1,img2)
    cv2.imwrite('example.jpg', final)
if __name__ == '__main__':
    try: 
        main("C:/Users/10937/Desktop/Image-Stitching-OpenCV-master//1.jpg","C:/Users/10937/Desktop/Image-Stitching-OpenCV-master//2.jpg")
    except IndexError:
        print ("Please input two source images: ")
        print ("For example: python Image_Stitching.py '/Users/linrl3/Desktop/picture/p1.jpg' '/Users/linrl3/Desktop/picture/p2.jpg'")
    

样例图片:

全景图片拼接(python、C++)全景图片拼接(python、C++)

过程:

先使用Ransac等鲁棒技术来提取特征,然后将它们进行匹配。

 

有许多不同的特征检测方法:


哈里斯角落探测器
缩放不变特征变换(SIFT),加速鲁棒特征(SURF),定向快速旋转BRIM(ORB)等。

matching image:

全景图片拼接(python、C++)

Final result:

全景图片拼接(python、C++)

额外尝试的代码:

import timeit

import cv2
import numpy as np


class Matcher:
    def __init__(self):
        self.surf = cv2.xfeatures2d.SURF_create()
        index_params = dict(algorithm=0, trees=5)
        search_params = dict(checks=50)
        self.flann = cv2.FlannBasedMatcher(index_params, search_params)

    def match(self, i1, i2):
        image_set_1 = self.get_SURF_features(i1)
        image_set_2 = self.get_SURF_features(i2)
        matches = self.flann.knnMatch(image_set_2["des"], image_set_1["des"], k=2)
        good = []
        for i, (m, n) in enumerate(matches):
            if m.distance < 0.7 * n.distance:
                good.append((m.trainIdx, m.queryIdx))

        if len(good) > 4:
            points_current = image_set_2["kp"]
            points_previous = image_set_1["kp"]

            matched_points_current = np.float32(
                [points_current[i].pt for (__, i) in good]
            )
            matched_points_prev = np.float32(
                [points_previous[i].pt for (i, __) in good]
            )

            H, _ = cv2.findHomography(
                matched_points_current, matched_points_prev, cv2.RANSAC, 4
            )
            return H
        return None

    def get_SURF_features(self, im):
        gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
        kp, des = self.surf.detectAndCompute(gray, None)
        return {"kp": kp, "des": des}


class Stitcher:
    def __init__(
        self,
        number_of_images,
        crop_x_min=None,
        crop_x_max=None,
        crop_y_min=None,
        crop_y_max=None,
    ):

        self.matcher_obj = Matcher()
        self.homography_cache = {}
        self.overlay_cache = {}

        self.count = number_of_images

        self.crop_x_min = crop_x_min
        self.crop_x_max = crop_x_max
        self.crop_y_min = crop_y_min
        self.crop_y_max = crop_y_max

    def stitch(self, images=[]):
        """
        stitches the images into a panorama
        """
        self.images = images

        self.prepare_lists()

        # left stitching
        start = timeit.default_timer()
        self.left_shift()
        self.right_shift()
        stop = timeit.default_timer()
        duration = stop - start
        print("stitching took %.2f seconds." % duration)

        if self.crop_x_min and self.crop_x_max and self.crop_y_min and self.crop_y_max:
            return self.result[
                self.crop_y_min : self.crop_y_max, self.crop_x_min : self.crop_x_max
            ]
        else:
            return self.result

    def prepare_lists(self):

        # reset lists
        self.left_list = []
        self.right_list = []

        self.center_index = int(self.count / 2)

        self.result = self.images[self.center_index]

        for i in range(self.count):
            if i <= self.center_index:
                self.left_list.append(self.images[i])
            else:
                self.right_list.append(self.images[i])

    def get_homography(self, image_1, image_1_key, image_2, image_2_key, direction):
        # TODO: use image indexes from the input array
        """
        Calculate the homography matrix between two images.
        Return from cache if possible.

        Args:
            image_1 (np.array) - first image
            image_1_key (str) - identifier for cache
            image_2 (np.array) - second image
            image_2_key (str) - identifier for cache
            direction (str) - "left" or "right"
        Returns:
            homography (np.array) - Homograpy Matrix
        """

        cache_key = "_".join([image_1_key, image_2_key, direction])
        homography = self.homography_cache.get(cache_key, None)
        if homography is None:
            # TODO: is the homography the same regardless of order??
            homography = self.matcher_obj.match(image_1, image_2)
            # put in cache
            self.homography_cache[cache_key] = homography
        return homography

    def left_shift(self):
        """
        stitch images center to left
        """
        # start off with center image
        a = self.left_list[0]

        for i, image in enumerate(self.left_list[1:]):
            H = self.get_homography(a, str(i), image, str(i + 1), "left")

            # inverse homography
            XH = np.linalg.inv(H)

            ds = np.dot(XH, np.array([a.shape[1], a.shape[0], 1]))
            ds = ds / ds[-1]

            f1 = np.dot(XH, np.array([0, 0, 1]))
            f1 = f1 / f1[-1]

            XH[0][-1] += abs(f1[0])
            XH[1][-1] += abs(f1[1])

            ds = np.dot(XH, np.array([a.shape[1], a.shape[0], 1]))
            offsety = abs(int(f1[1]))
            offsetx = abs(int(f1[0]))

            # dimension of warped image
            dsize = (int(ds[0]) + offsetx, int(ds[1]) + offsety)

            tmp = cv2.warpPerspective(a, XH, dsize, borderMode=cv2.BORDER_TRANSPARENT)

            # punch the image in there
            tmp[
                offsety : image.shape[0] + offsety, offsetx : image.shape[1] + offsetx
            ] = image

            a = tmp

        self.result = tmp

    def right_shift(self):
        """
        stitch images center to right
        """
        for i, imageRight in enumerate(self.right_list):
            imageLeft = self.result

            H = self.get_homography(imageLeft, str(i), imageRight, str(i + 1), "right")

            # args: original_image, matrix, output shape (width, height)
            result = cv2.warpPerspective(
                imageRight,
                H,
                (imageLeft.shape[1] + imageRight.shape[1], imageLeft.shape[0]),
                borderMode=cv2.BORDER_TRANSPARENT,
            )

            mask = np.zeros((result.shape[0], result.shape[1], 3), dtype="uint8")
            mask[0 : imageLeft.shape[0], 0 : imageLeft.shape[1]] = imageLeft
            self.result = self.blend_images(mask, result, str(i))

    def blend_images(self, background, foreground, i):
        """
        inspired by this answer:

        https://*.com/a/54129424/1909378
        """

        only_right = self.overlay_cache.get(i, None)
        if only_right is None:
            only_right = np.nonzero(
                (np.sum(foreground, 2) != 0) * (np.sum(background, 2) == 0)
            )
            self.overlay_cache[i] = only_right

        background[only_right] = foreground[only_right]
        return background


if __name__ == "__main__":
    FRAME_WIDTH = 768
    FRAME_HEIGHT = 432

    shanghai_files = [
        "images2/1.jpg",
        "images2/2.jpg",
        "images2/3.jpg",
        "images2/4.jpg",
       # "images2/shanghai-05.png",
    ]

    shanghai = [
        cv2.resize(cv2.imread(f), (FRAME_WIDTH, FRAME_HEIGHT)) for f in shanghai_files
    ]

    crop_x_min = 30
    crop_x_max = 1764
    crop_y_min = 37
    crop_y_max = 471

    s = Stitcher(
        len(shanghai_files),
        crop_x_min=crop_x_min,
        crop_x_max=crop_x_max,
        crop_y_min=crop_y_min,
        crop_y_max=crop_y_max,
    )

    panorama = s.stitch(shanghai)

    cv2.imwrite("panorama.png", panorama)

 

 

 

本文地址:https://blog.csdn.net/packdge_black/article/details/109545597

相关标签: 图片拼接