OpenCV入门10——特征点检测与匹配

news2024/9/28 23:35:36

文章目录

  • 特征检测的基本概念
  • Harris角点检测
  • Shi-Tomasi角点检测
  • SIFT关键点检测
  • SIFT计算描述子
  • SURF特征检测
  • OBR特征检测
  • 暴力特征匹配
  • FLANN特征匹配
  • 实战flann特征匹配
  • 图像查找
  • 图像拼接基础知识
  • 图像拼接实战

特征点检测与匹配是计算机视觉中非常重要的内容。不是所有图像操作都是对每个像素进行处理,有些只需使用4个顶点即可,如图像的拼接、二维码定位等

特征检测的基本概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Harris角点检测

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
详情见官方参考文档
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

blockSize = 2
ksize = 3
k = 0.04

img = cv2.imread('./chess.png')

# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Harris角点检测
dst = cv2.cornerHarris(gray, blockSize, ksize, k)

# Harris角点展示
img[dst > 0.01 * dst.max()] = [0, 255, 0]

cv2.imshow('harris', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

Shi-Tomasi角点检测

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
距离越大检测到的角数越少,距离越小检测到的角数越多

# -*- coding: utf-8 -*-
import cv2
import numpy as np

# Harris
# blockSize = 2
# ksize = 3
# k = 0.04

# Shi-Tomasi
maxCorners = 1000
ql = 0.01
minDistance = 10

img = cv2.imread('./chess.png')

# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Harris角点检测
# dst = cv2.cornerHarris(gray, blockSize, ksize, k)

# Harris角点展示
# img[dst > 0.01 * dst.max()] = [0, 255, 0]

corners = cv2.goodFeaturesToTrack(gray, maxCorners, ql, minDistance)

corners = np.int0(corners)

for i in corners:
    x, y = i.ravel()
    cv2.circle(img, (x, y), 3, (0, 255, 0), -1)

cv2.imshow('Tomasi', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

SIFT关键点检测

详情见官方文档
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

The distinguishing qualities of an image that make it stand out are referred to as key points in an image. The key points of a particular image let us recognize objects and compare images. Detecting critical spots in a picture may be done using a variety of techniques and algorithms. We utilize the drawKeypoints() method in OpenCV to be able to draw the identified key points on a given picture. The input picture, keypoints, color, and flag are sent to the drawKeypoints() method. key points are the most important aspects of the detection. Even after the image is modified the key points remain the same. As of now, we can only use the SIRF_create() function as the surf function is patented.

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img = cv2.imread('./chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()
# 进行检测
kp = sift.detect(gray, None)

# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)

cv2.imshow('img', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

  1. sift = cv2.xfeatures2d.SIFT_create() 实例化
    参数说明:sift为实例化的sift函数

  2. kp = sift.detect(gray, None) 找出图像中的关键点
    参数说明: kp表示生成的关键点,gray表示输入的灰度图,

  3. ret = cv2.drawKeypoints(gray, kp, img) 在图中画出关键点
    参数说明:gray表示输入图片, kp表示关键点,img表示输出的图片

  4. kp, dst = sift.compute(kp) 计算关键点对应的sift特征向量
    参数说明:kp表示输入的关键点,dst表示输出的sift特征向量,通常是128维的

SIFT计算描述子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img = cv2.imread('./chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()
# 进行检测
kp, des = sift.detectAndCompute(gray, None)

print(des)

# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)

cv2.imshow('img', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

SURF特征检测

在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img = cv2.imread('./chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建SIFT对象
# sift = cv2.xfeatures2d.SIFT_create()

# 创建SURF对象
surf = cv2.xfeatures2d.SURF.create()

# 进行检测
# kp, des = sift.detectAndCompute(gray, None)
kp, des = surf.detectAndCompute(gray, None)

# print(des[0])

# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)

cv2.imshow('img', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述
好消息,SURF付费了,不是开源的接口了,需要大家自己造轮子,写新的好算法!

OBR特征检测

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img = cv2.imread('./chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建SIFT对象
# sift = cv2.xfeatures2d.SIFT_create()

# 创建SURF对象
# surf = cv2.xfeatures2d.SURF.create()

# 创建ORB对象
orb = cv2.ORB_create()

# 进行检测
# kp, des = sift.detectAndCompute(gray, None)
# kp, des = surf.detectAndCompute(gray, None)
kp, des = orb.detectAndCompute(gray, None)

# print(des[0])

# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)

cv2.imshow('img', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

暴力特征匹配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

# img = cv2.imread('./chess.png')
img1 = cv2.imread('./opencv_search.png')
img2 = cv2.imread('./opencv_orig.png')

# 灰度化
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# create SIFT feature extractor
# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()

# 创建SURF对象
# surf = cv2.xfeatures2d.SURF.create()

# 创建ORB对象
orb = cv2.ORB_create()


# detect features from the image
# 进行检测
# kp, des = sift.detectAndCompute(gray, None)
# kp, des = surf.detectAndCompute(gray, None)
# kp, des = orb.detectAndCompute(gray, None)


# draw the detected key points
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)

# print(des[0])

# 绘制keypoints
# cv2.drawKeypoints(gray, kp, img)

# 创建匹配器
bf = cv2.BFMatcher(cv2.NORM_L1)
match = bf.match(des1, des2)

img3 = cv2.drawMatches(img1, kp1, img2, kp2, match, None)

cv2.imshow('img', img3)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

FLANN特征匹配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实战flann特征匹配

参考的官网手册

# -*- coding: utf-8 -*-
import cv2
import numpy as np

# queryImage
img1 = cv2.imread('./opencv_search.png')
# trainImage
img2 = cv2.imread('./opencv_orig.png')

# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建SIFT特征检测器
# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()

# 计算描述子与特征点
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)

# 创建匹配器
# FLANN parameters
index_params = dict(algorithm = 1, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

# 对描述子进行匹配计算
matchs = flann.knnMatch(des1, des2, k = 2)

good = []
# ratio test as per Lowe's paper
for i, (m, n) in enumerate(matchs):
    if m.distance < 0.7 * n.distance:
        good.append(m)

ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)

cv2.imshow('res', ret)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

图像查找

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img1 = cv2.imread('./opencv_search.png')
img2 = cv2.imread('./opencv_orig.png')

MIN_MATCH_COUNT = 4

# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建SIFT特征检测器
sift = cv2.xfeatures2d.SIFT_create()

# 计算描述子与特征点
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)

# 创建匹配器
index_params = dict(algorithm = 1, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

# 对描述子进行匹配计算
matchs = flann.knnMatch(des1, des2, k = 2)

good = []
for i, (m, n) in enumerate(matchs):
    if m.distance < 0.7 * n.distance:
        good.append(m)

if len(good) >= MIN_MATCH_COUNT:
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)

    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

    h,w = img1.shape[:2]
    pts = np.float32([ [0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1,1,2)
    dst = cv2.perspectiveTransform(pts, M)
    cv2.polylines(img2, [np.int32(dst)], True, (0, 255, 0))
else:
    print('the number of good is less than 4.')
    exit()

ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)

cv2.imshow('res', ret)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

图像拼接基础知识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(0,0)是第二张图的左上角
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

图像拼接实战

# -*- coding: utf-8 -*-
import cv2
import numpy as np

#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))
cv2.imshow('input_img', inputs)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

MIN_MATCH_COUNT = 8

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    img1_dims = np.float32([[0,0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0,0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)

    img1_transform = cv2.perspectiveTransform(img1_dims, H)
    print(img1_dims)
    print(img2_dims)
    print(img1_transform)


def get_homo(img1, img2):
    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    # 创建SIFT特征检测器
    sift = cv2.xfeatures2d.SIFT_create()
    # 计算描述子与特征点
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)

    # 创建特征匹配器
    bf = cv2.BFMatcher()
    # 对描述子进行匹配计算
    matchs = bf.knnMatch(des1, des2, k = 2)

    verify_matches = []
    for i, (m, n) in enumerate(matchs):
        if m.distance < 0.8 * n.distance:
            verify_matches.append(m)

    if len(verify_matches) > MIN_MATCH_COUNT:
        img1_pts = []
        img2_pts = []
        for m in verify_matches:
            img1_pts.append(kp1[m.queryIdx].pt)
            img2_pts.append(kp2[m.trainIdx].pt)

        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]    
        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        # 获取单应性矩阵
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    else:
        print('err: Not enough matches!')
        exit()

img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))

#获得单应性矩阵
H = get_homo(img1, img2)

#进行图像拼接
result_image = stitch_image(img1, img2, H)

cv2.imshow('input_img', inputs)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

MIN_MATCH_COUNT = 8

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    img1_dims = np.float32([[0,0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0,0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)

    img1_transform = cv2.perspectiveTransform(img1_dims, H)
    # print(img1_dims)
    # print(img2_dims)
    # print(img1_transform)
    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    # print(result_dims)
    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max ] = np.int32(result_dims.max(axis=0).ravel()+0.5)

    #平移的距离
    transform_dist = [-x_min, -y_min]

    

    result_img = cv2.warpPerspective(img1, H, (x_max-x_min, y_max-y_min))

    

    return result_img


def get_homo(img1, img2):
    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    # 创建SIFT特征检测器
    sift = cv2.xfeatures2d.SIFT_create()
    # 计算描述子与特征点
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)

    # 创建特征匹配器
    bf = cv2.BFMatcher()
    # 对描述子进行匹配计算
    matchs = bf.knnMatch(des1, des2, k = 2)

    verify_matches = []
    for i, (m, n) in enumerate(matchs):
        if m.distance < 0.8 * n.distance:
            verify_matches.append(m)

    if len(verify_matches) > MIN_MATCH_COUNT:
        img1_pts = []
        img2_pts = []
        for m in verify_matches:
            img1_pts.append(kp1[m.queryIdx].pt)
            img2_pts.append(kp2[m.trainIdx].pt)

        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]    
        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        # 获取单应性矩阵
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    else:
        print('err: Not enough matches!')
        exit()

img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))

#获得单应性矩阵
H = get_homo(img1, img2)

#进行图像拼接
result_image = stitch_image(img1, img2, H)

cv2.imshow('input_img', result_image)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

MIN_MATCH_COUNT = 8

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    img1_dims = np.float32([[0,0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0,0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)

    img1_transform = cv2.perspectiveTransform(img1_dims, H)
    # print(img1_dims)
    # print(img2_dims)
    # print(img1_transform)
    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    # print(result_dims)
    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max ] = np.int32(result_dims.max(axis=0).ravel()+0.5)

    #平移的距离
    transform_dist = [-x_min, -y_min]

    #[1, 0, dx]
    #[0, 1, dy]         
    #[0, 0, 1 ]
    transform_array = np.array([[1, 0, transform_dist[0]],
                                [0, 1, transform_dist[1]],
                                [0, 0, 1]])

    result_img = cv2.warpPerspective(img1, transform_array.dot(H), (x_max-x_min, y_max-y_min))

    

    return result_img


def get_homo(img1, img2):
    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    # 创建SIFT特征检测器
    sift = cv2.xfeatures2d.SIFT_create()
    # 计算描述子与特征点
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)

    # 创建特征匹配器
    bf = cv2.BFMatcher()
    # 对描述子进行匹配计算
    matchs = bf.knnMatch(des1, des2, k = 2)

    verify_matches = []
    for i, (m, n) in enumerate(matchs):
        if m.distance < 0.8 * n.distance:
            verify_matches.append(m)

    if len(verify_matches) > MIN_MATCH_COUNT:
        img1_pts = []
        img2_pts = []
        for m in verify_matches:
            img1_pts.append(kp1[m.queryIdx].pt)
            img2_pts.append(kp2[m.trainIdx].pt)

        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]    
        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        # 获取单应性矩阵
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    else:
        print('err: Not enough matches!')
        exit()

img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))

#获得单应性矩阵
H = get_homo(img1, img2)

#进行图像拼接
result_image = stitch_image(img1, img2, H)

cv2.imshow('input_img', result_image)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

MIN_MATCH_COUNT = 8

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    img1_dims = np.float32([[0,0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0,0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)

    img1_transform = cv2.perspectiveTransform(img1_dims, H)
    # print(img1_dims)
    # print(img2_dims)
    # print(img1_transform)
    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    # print(result_dims)
    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max ] = np.int32(result_dims.max(axis=0).ravel()+0.5)

    #平移的距离
    transform_dist = [-x_min, -y_min]
	# 齐次坐标
    #[1, 0, dx]
    #[0, 1, dy]         
    #[0, 0, 1 ]
    transform_array = np.array([[1, 0, transform_dist[0]],
                                [0, 1, transform_dist[1]],
                                [0, 0, 1]])

    result_img = cv2.warpPerspective(img1, transform_array.dot(H), (x_max-x_min, y_max-y_min))

    result_img[transform_dist[1]:transform_dist[1]+h2, 
                transform_dist[0]:transform_dist[0]+w2] = img2

    return result_img


def get_homo(img1, img2):
    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    # 创建SIFT特征检测器
    sift = cv2.xfeatures2d.SIFT_create()
    # 计算描述子与特征点
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)

    # 创建特征匹配器
    bf = cv2.BFMatcher()
    # 对描述子进行匹配计算
    matchs = bf.knnMatch(des1, des2, k = 2)

    verify_matches = []
    for i, (m, n) in enumerate(matchs):
        if m.distance < 0.8 * n.distance:
            verify_matches.append(m)

    if len(verify_matches) > MIN_MATCH_COUNT:
        img1_pts = []
        img2_pts = []
        for m in verify_matches:
            img1_pts.append(kp1[m.queryIdx].pt)
            img2_pts.append(kp2[m.trainIdx].pt)

        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]    
        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        # 获取单应性矩阵
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    else:
        print('err: Not enough matches!')
        exit()

img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))

#获得单应性矩阵
H = get_homo(img1, img2)

#进行图像拼接
result_image = stitch_image(img1, img2, H)

cv2.imshow('input_img', result_image)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1242687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯又出王炸产品!使用混元大模型进行数据报表测试

最近腾讯出了自己的大模型&#xff0c;命名混元。 现在已经开始内测&#xff0c;感谢腾讯小伙伴卢晓明同学帮我们提前申请到了内测机会&#xff0c;接下来我们用腾讯混元大模型与实际工作结合&#xff0c;开始我的报表测试之旅。 腾讯混元大模型官方入口:https://hunyuan.ten…

深入了解前馈网络、CNN、RNN 和 Hugging Face 的 Transformer 技术!

一、说明 本篇在此对自然语言模型做一个简短总结&#xff0c;从CNN\RNN\变形金刚&#xff0c;和抱脸的变形金刚库说起。 二、基本前馈神经网络&#xff1a; 让我们分解一个基本的前馈神经网络&#xff0c;也称为多层感知器&#xff08;MLP&#xff09;。此代码示例将&#xff1…

玻色量子“揭秘”之集合划分问题与QUBO建模

摘要&#xff1a;集合划分问题&#xff08;Set Partitioning Problem&#xff09;是一种组合优化问题&#xff0c;其中给定一个集合S和其若干个不同的子集S1&#xff0c;S2&#xff0c;...&#xff0c;Sn后&#xff0c;需要找到子集的有效组合&#xff0c;使得集合S的每个元素正…

高通Camera HAL3: CamX、Chi-CDK要点

目录 一、概述 二、目录 三、CamX组件之前的关系 一、概述 高通CamX架构是高通实现的相机HAL3架构&#xff0c;被各OEM厂商广泛采用。 二、目录 代码位于vendor/qcom/proprietary下&#xff1a; camx&#xff1a;通用功能性接口的代码实现集合chi-cdk&#xff1a;可定制化…

shell循环语句 for while until

目录 什么是循环语句 概念 for循环 格式 while循环 格式 until 循环 格式 实验 for &#xff08;1&#xff09;计算1到100的和 ​编辑 &#xff08;2&#xff09;100以内的偶数 &#xff08;从0开始到100结束&#xff0c;每次加2步 打印的都是偶数&#xff09; &…

ELK架构

经典的ELK 经典的ELK主要是由Filebeat Logstash Elasticsearch Kibana组成&#xff0c;如下图&#xff1a;&#xff08;早期的ELK只有Logstash Elasticsearch Kibana&#xff09; 此架构主要适用于数据量小的开发环境&#xff0c;存在数据丢失的危险。 整合消息队列Ngin…

Spring框架学习 -- 读取和存储Bean对象

目录 &#x1f680;&#x1f680; 回顾 getBean()方法的使用 根据name来获取对象 再谈getBean() (1) 配置扫描路径 (2) 添加注解 ① spring注解简介 ② 对类注解的使用 ③ 注解Bean对象的命名问题 ④ 方法加Bean注解 (3) Bean 注解的重命名 (4) 获取Bean对象 -- …

投标文件的注意事项

一、检查标书 1.1有时候标书需要从别的地方复制黏贴文件&#xff0c;记住复制内容可以&#xff0c;但是不要复制“落款和时间”的格式&#xff0c;落款和时间的格式借鉴你的招标文件中给响应文件格式的落款和时间&#xff0c;切记&#xff01; 1.2检查标书是否有空页&#xf…

数据中心运维管理:从人工到智能需要走几步?

一切的变化来自于数据中心规模、复杂度、设备多样性的挑战&#xff0c;将运维平台的重要性推向历史高点。 此外&#xff0c;基于业务连续性方面的考虑&#xff0c;分布式数据中心成为越来越多客户的选择。 一、数据中心面临的挑战 运维管理分散&#xff0c;缺乏统一的管理 I…

Linux:设置Ubuntu的root用户密码

执行以下命令&#xff1a; 给root用户设置密码 sudo passwd 输入两次密码 切换root su root 退出root用户 exit

地埋式积水监测仪厂家直销推荐,致力于积水监测

地埋式积水监测仪是一种高科技设备&#xff0c;能够实时监测地面积水深度&#xff0c;并及时发出预警信息&#xff0c;有效避免因积水而产生的安全隐患。这种智能监测仪可以安装在城市道路、立交桥、地下车库等易积水地势较低的地方&#xff0c;以确保及时监测特殊地段的积水&a…

【Sorted Set】Redis常用数据类型: ZSet [使用手册]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 目录 ⑤Redis Zset 操作命令汇总1. zadd 添加或…

APM工具skywalking部署

一 整体架构 整个架构&#xff0c;分成上、下、左、右四部分&#xff1a; 上部分 Agent &#xff1a;负责从应用中&#xff0c;收集链路信息&#xff0c;发送给 SkyWalking OAP 服务器。目前支持 SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是&…

css渐变详解(重复性线性渐变、径向渐变、重复性径向渐变的使用)

目录 线性渐变 重复性线性渐变 径向渐变 重复性径向渐变的使用 线性渐变 线性渐变是向下、向上、向左、向右、对角方向的颜色渐变。 其语法格式为&#xff1a; background-image: linear-gradient(side-or-corner|angle, linear-color-stop); 参数说明如下&#xff1a; …

mongo DB -- aggregate分组查询后字段展示

一、分组查询 在mongoDB中可以使用aggregate中的$group操作对集合中的文档进行分组,但是查询后的数据不显示其他字段,只显示分组字段 aggregate进行分组示例 db.collection.aggregate([{$group: {_id: "$field"}},]) 查询后显示 展开只显示两个字段 二、显示所有字段…

10.分组循环练习题

分组循环 https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/solutions/2528771/jiao-ni-yi-ci-xing-ba-dai-ma-xie-dui-on-zuspx/?envTypedaily-question&envId2023-11-16 分组循环 适用场景&#xff1a; 按照题目要求&#xff0c;数组会被分割成若…

微信运营神器:从群发到批量添加,让你的微信营销更轻松

在这个数字化时代&#xff0c;微信已经成为了我们生活中不可或缺的一部分。对于许多企业和个人来说&#xff0c;微信营销也是非常重要的一部分。但是&#xff0c;微信营销并不是一件容易的事情&#xff0c;需要花费大量的时间和精力。为了解决这个问题&#xff0c;今天我们将向…

邻趣连接力:如何无代码集成CRM、电商平台和营销系统,提升广告推广效率

连接即服务&#xff1a;邻趣无代码集成方法 传统的电商系统集成过程需要大量的时间和资源进行API开发&#xff0c;这不仅耗时耗力&#xff0c;还需要专业的技术团队支持。然而&#xff0c;邻趣通过提供一种无需API开发的连接方法&#xff0c;极大地简化了整个集成过程。商家只…

3D人脸扫描设备助力企业家数字人复刻,打破商业边界

京都薇薇推出数字人VN&#xff0c;以京都薇薇董事长为原型制作&#xff0c;赋能品牌直播、短片宣传、线上面诊等活动&#xff0c;进一步增强消费者对品牌的交互体验&#xff0c;把元宇宙与品牌相融合&#xff0c;推动品牌线上服务与线下服务实现数字一体化&#xff0c;打造一个…

【C/C++】排序算法代码实现

这里&#xff0c;汇总了常见的排序算法具体代码实现。使用C语言编写。 排序算法实现 插入排序冒泡排序选择排序快速排序希尔排序归并排序 插入排序 #include <stdio.h> #include <stdlib.h>void InsertSort(int arr[],int n){int i,j,temp;for(i 1;i < n;i){ …