图像拼接——基于homography的特征匹配算法


目录

  • 1. 任务要求
  • 2. 数据集
  • 3. 基于homography的特征匹配算法
  • 4. 拼接流程展示
    • 4.1 图片实例
    • 4.2 特征点位图
    • 4.3 特征点匹配结果
    • 4.4 相机校准结果
    • 4.5 拼接结果
  • 5. 部分图像拼接结果展示


1. 任务要求

  • 输入:同一个场景的两张待拼接图像(有部分场景重合)。
  • 任务:从输入的两张图像中提取特征点和描述子,可以使用现有的图像处理库来执行此任务。自己实现特征匹配算法,将来自两张图像的特征点进行匹配。最后根据匹配的特征点估计单应性变换,从而通过映射拼接成一张全景图。
  • 输出
    • 显示两张图像上提取的特征点的位置;
    • 显示特征点匹配对应的结果;
    • 显示经过几何变换后的图像叠加的结果;
    • 显示最终拼接的结果。

2. 数据集

  • 其中两组图像“cat”和“bridge”拍摄于杭州。
  • 其他图像分别来自测试数据链接和其他来源。

3. 基于homography的特征匹配算法

基于homography的特征匹配算法在图像拼接中起着关键作用,它能够定位和匹配两张待拼接图像中的特征点,从而实现图像的对齐和融合。该算法主要包括以下实现步骤:

  • 特征点提取和描述:使用ORB和SIFT等特征检测器对待拼接图像进行特征点提取。这些特征点具有在不同尺度和旋转下的不变性。对每个特征点计算其对应的特征描述子,用于后续的特征匹配。
  • 特征匹配:对两幅待拼接图像中的特征点进行匹配。我们使用基于最近邻的匹配,其中对于每个特征点,找到其在另一幅图像中的最佳匹配点。通过计算特征描述子之间的距离或相似度,确定最佳匹配点。
  • 计算homography矩阵:使用筛选后的特征点匹配对应的坐标,计算homography矩阵。homography矩阵可以将一个图像上的点映射到另一个图像上,从而实现图像的对齐。
  • 图像校准和拼接:使用计算得到的homography矩阵对多张图像进行透视变换,使其对齐。将校准后的图像进行融合,生成拼接结果图像。
import mathimport cv2 as cv
import numpy as npclass FeatureMatcher:def __init__(self, matcher_type="homography", range_width=-1, **kwargs):if matcher_type == "homography":if range_width == -1:self.matcher = cv.detail_BestOf2NearestMatcher(**kwargs)else:self.matcher = cv.detail_BestOf2NearestRangeMatcher(range_width, **kwargs)else:raise ValueError("Unknown matcher type")def match_features(self, features, *args, **kwargs):pairwise_matches = self.matcher.apply2(features, *args, **kwargs)self.matcher.collectGarbage()return pairwise_matches@staticmethoddef draw_matches_matrix(imgs, features, matches, conf_thresh=1, inliers=False, **kwargs):matches_matrix = FeatureMatcher.get_matches_matrix(matches)for idx1, idx2 in FeatureMatcher.get_all_img_combinations(len(imgs)):match = matches_matrix[idx1, idx2]if match.confidence < conf_thresh:continueif inliers:kwargs["matchesMask"] = match.getInliers()yield idx1, idx2, FeatureMatcher.draw_matches(imgs[idx1], features[idx1], imgs[idx2], features[idx2], match, **kwargs)@staticmethoddef get_confidence_matrix(pairwise_matches):matches_matrix = FeatureMatcher.get_matches_matrix(pairwise_matches)match_confs = [[m.confidence for m in row] for row in matches_matrix]match_conf_matrix = np.array(match_confs)return match_conf_matrix

4. 拼接流程展示

4.1 图片实例

为了演示图像拼接的整体实现流程,这里我们选择一组我本人拍摄的玉泉校内的两只猫和周边环境图——“cat”,两幅有重叠画面的原图如下图所示。其中下面那只白猫几乎是静止不动的,上面的带橘色斑点的白猫在两幅图中的位置有相对移动。

from stitching.images import Images# 1. load
images, low_imgs, medium_imgs, final_imgs = load_images(img_path)
images_to_match = medium_imgs# 2. plot original images
plot_images(images_to_match, (20, 20), save=f'{save_path}/1-original.png')# 3. print image size
print(f'Original image size: {images_to_match[0].shape}')################ Load images ####################
def load_images(img_path):images = Images.of(img_path)medium_imgs = list(images.resize(Images.Resolution.MEDIUM))low_imgs = list(images.resize(Images.Resolution.LOW))final_imgs = list(images.resize(Images.Resolution.FINAL))return images, low_imgs, medium_imgs, final_imgs################ Plot function####################
def plot_image(img, figsize_in_inches=(10, 10), save=None):
"""N_image = 1"""def plot_images(imgs, figsize_in_inches=(10, 10), save=None):
"""N_images > 1"""

在这里插入图片描述

4.2 特征点位图

根据特征检测器提取的特征点,生成特征点位置图。这里我们以ORB特征检测器为例,下图中的绿色小圈展示了待拼接图像中检测到的特征点的分布情况。

from stitching.feature_detector import FeatureDetector# 4. Feature detection: ORB, SIFT
finder = FeatureDetector(detector=detector)
features = [finder.detect_features(img) for img in images_to_match]key_points_img = []
for i in range(len(images_to_match)):key_points_img.append(finder.draw_keypoints(images_to_match[i], features[i]))plot_images(key_points_img, (20, 20), save=f'{save_path}/2-key_points.png')

在这里插入图片描述

4.3 特征点匹配结果

通过homography特征匹配算法(具体代码见第3节),将两张待拼接图像中匹配的特征点进行连接,生成特征点匹配结果图。下图中的绿色线段展示了特征点之间的对应关系。

from Feature_matcher import *# 5. Feature matching: homography
matcher = FeatureMatcher()
matches = matcher.match_features(features)print(matcher.get_confidence_matrix(matches))# 6. plot matching
all_relevant_matches = matcher.draw_matches_matrix(images_to_match, features, matches, conf_thresh=1,inliers=True, matchColor=(0, 255, 0))for idx1, idx2, img in all_relevant_matches:print(f"Matches Image {idx1 + 1} to Image {idx2 + 1}")plot_image(img, (20, 10), save=f'{save_path}/3-matching.png')

4.4 相机校准结果

根据homography矩阵,对两张图像进行透视变换,使其对齐,生成校准结果图。下图的子图a为校准过程得到的mask图,子图b展示了经过校准后的待拼接图像,最终拼接图的大小与待拼接图像的大小一致。

from stitching.camera_estimator import CameraEstimator
from stitching.camera_adjuster import CameraAdjuster
from stitching.camera_wave_corrector import WaveCorrector
from stitching.warper import Warper
from stitching.timelapser import Timelapser# 7. Camera Estimation, Adjustion and Correction
cameras = camera_correction(features, matches)# 8. Warp images
(warped_low_imgs, warped_low_masks, low_corners, low_sizes,warped_final_imgs, warped_final_masks, final_corners, final_sizes, frame) \= warp_image(images, cameras, low_imgs, final_imgs)plot_images(warped_low_imgs, (10, 10), save=f'{save_path}/4-warped_low_imgs.png')
plot_images(warped_low_masks, (10, 10), save=f'{save_path}/4-warped_low_masks.png')
plot_images(frame, (20, 10), save=f'{save_path}/4-warped_final_imgs.png')################ Camera Estimation ##################
def camera_correction(features, matches):camera_estimator = CameraEstimator()camera_adjuster = CameraAdjuster()wave_corrector = WaveCorrector()cameras = camera_estimator.estimate(features, matches)cameras = camera_adjuster.adjust(features, matches, cameras)cameras = wave_corrector.correct(cameras)return cameras
################ Warp images ####################
def warp_image(images, cameras, low_imgs, final_imgs):warper = Warper()warper.set_scale(cameras)low_sizes = images.get_scaled_img_sizes(Images.Resolution.LOW)camera_aspect = images.get_ratio(Images.Resolution.MEDIUM,Images.Resolution.LOW)  # since cameras were obtained on medium imgswarped_low_imgs = list(warper.warp_images(low_imgs, cameras, camera_aspect))warped_low_masks = list(warper.create_and_warp_masks(low_sizes, cameras, camera_aspect))low_corners, low_sizes = warper.warp_rois(low_sizes, cameras, camera_aspect)final_sizes = images.get_scaled_img_sizes(Images.Resolution.FINAL)camera_aspect = images.get_ratio(Images.Resolution.MEDIUM, Images.Resolution.FINAL)warped_final_imgs = list(warper.warp_images(final_imgs, cameras, camera_aspect))warped_final_masks = list(warper.create_and_warp_masks(final_sizes, cameras, camera_aspect))final_corners, final_sizes = warper.warp_rois(final_sizes, cameras, camera_aspect)# Timelapsertimelapser = Timelapser('as_is')timelapser.initialize(final_corners, final_sizes)frame = []for img, corner in zip(warped_final_imgs, final_corners):timelapser.process_frame(img, corner)frame.append(timelapser.get_frame())return (warped_low_imgs, warped_low_masks, low_corners, low_sizes,warped_final_imgs, warped_final_masks, final_corners, final_sizes, frame)

在这里插入图片描述

4.5 拼接结果

将经过校准的两张图像进行融合,生成拼接结果图。根据用户的选择,可以提供剪裁相机校准结果的选项(stitching(crop = True),默认为False)。图1分别展示了未剪裁和剪裁后的校准图(5a&c)和拼接图时的接缝(5b&d)。最后拼接图结果见图2,上面三幅图不包括剪裁步骤,下面三幅存在剪裁步骤。可以看到,在拼接之前剪裁至规则的四边形对拼接时的seam line的选取有较大的影响,有一定概率导致最终的拼接图像不符合预期。

from stitching.cropper import Cropper
from stitching.seam_finder import SeamFinder# 9. Crop images
if crop:(cropped_low_imgs, cropped_low_masks, cropped_final_imgs,cropped_final_masks, final_corners, final_sizes, frame) = (crop_image(images, warped_low_imgs, warped_low_masks, low_corners, low_sizes,warped_final_imgs, warped_final_masks, final_corners, final_sizes))plot_images(frame, (20, 10), save=f'{save_path}/5-cropped_final_imgs.png')
else:cropped_low_imgs = warped_low_imgscropped_low_masks = warped_low_maskscropped_final_imgs = warped_final_imgscropped_final_masks = warped_final_masks# 10. Seam Masks
seam_finder, seam_masks_plots, compensated_imgs, seam_masks = (seam(cropped_low_imgs, low_corners, cropped_low_masks,cropped_final_masks, cropped_final_imgs, final_corners))
plot_images(seam_masks_plots, (15, 10), save=f'{save_path}/6-seam_masks.png')# 11. Matching result
blender = Blender()
blender.prepare(final_corners, final_sizes)
for img, mask, corner in zip(compensated_imgs, seam_masks, final_corners):blender.feed(img, mask, corner)
panorama, _ = blender.blend()
blended_seam_masks = seam_finder.blend_seam_masks(seam_masks, final_corners, final_sizes)plot_image(panorama, (20, 20), save=f'{save_path}/7-matched_result.png')
plot_image(seam_finder.draw_seam_lines(panorama, blended_seam_masks, linesize=3), (15, 10),save=f'{save_path}/8-seam_lines.png')
plot_image(seam_finder.draw_seam_polygons(panorama, blended_seam_masks), (15, 10),save=f'{save_path}/9-seam_polygons.png')# 12. Done
print('Done!')################ Crop images ####################
def crop_image(images, warped_low_imgs, warped_low_masks, low_corners, low_sizes,warped_final_imgs, warped_final_masks, final_corners, final_sizes):cropper = Cropper()mask = cropper.estimate_panorama_mask(warped_low_imgs, warped_low_masks, low_corners, low_sizes)lir = cropper.estimate_largest_interior_rectangle(mask)low_corners = cropper.get_zero_center_corners(low_corners)rectangles = cropper.get_rectangles(low_corners, low_sizes)overlap = cropper.get_overlap(rectangles[1], lir)intersection = cropper.get_intersection(rectangles[1], overlap)cropper.prepare(warped_low_imgs, warped_low_masks, low_corners, low_sizes)cropped_low_masks = list(cropper.crop_images(warped_low_masks))cropped_low_imgs = list(cropper.crop_images(warped_low_imgs))low_corners, low_sizes = cropper.crop_rois(low_corners, low_sizes)lir_aspect = images.get_ratio(Images.Resolution.LOW, Images.Resolution.FINAL)  # since lir was obtained on low imgscropped_final_masks = list(cropper.crop_images(warped_final_masks, lir_aspect))cropped_final_imgs = list(cropper.crop_images(warped_final_imgs, lir_aspect))final_corners, final_sizes = cropper.crop_rois(final_corners, final_sizes, lir_aspect)# Redo the timelapse with cropped Images:timelapser = Timelapser('as_is')timelapser.initialize(final_corners, final_sizes)frame = []for img, corner in zip(cropped_final_imgs, final_corners):timelapser.process_frame(img, corner)frame.append(timelapser.get_frame())return (cropped_low_imgs, cropped_low_masks, cropped_final_imgs,cropped_final_masks, final_corners, final_sizes, frame)

在这里插入图片描述

图1. Crop images. a, Warpped. b, Wrapped to seam. c, Warp and crop. b, Cropped to seam.

在这里插入图片描述

图2. Stitching results. a, Original result. b, Result with seam line. c, Result with seam ploygons. d-f, Cropped results.

5. 部分图像拼接结果展示

在这里插入图片描述

Fig. S1. Examples using ORB detector and without crop&mask. a, bridge. b, building. c, sportfield. d, door. e, barcode. f, exposure_error. Left, Original. Middle, Stitching results. Right, Result with seam ploygons.

创作不易,麻烦点点赞和关注咯!

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

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

相关文章

2023年03月18日_微软office365 copilot相关介绍

文章目录 Copilot In WordCopilot In PowerpointCopilot In ExcelCopilot In OutlookCopilot In TeamsBusiness Chat1 - copilot in word2 - copilot in excel3 - copilot in powerpoint4 - copilot in outlook5 - copilot in teams6 - business chat word 1、起草草稿 2、自动…

命令模式-举例

开关和电灯之间并不存在直接耦合关系&#xff0c;在命令模式中&#xff0c;发送者与接收者之间引入了新的命令对象&#xff0c;将发送者的请求封装在命令对象中&#xff0c;再通过命令对象来调用接收者的方法。 命令模式的主要缺点如下&#xff1a; 使用命令模式可能会导致某…

自然语言处理2——轻松入门情感分析 - Python实战指南

目录 写在开头1.了解情感分析的概念及其在实际应用中的重要性1.1 情感分析的核心概念1.1.1 情感极性1.1.2 词汇和上下文1.1.3 情感强度1.2 实际应用中的重要性 2. 使用情感分析库进行简单的情感分析2.1 TextBlob库的基本使用和优势2.1.1 安装TextBlob库2.1.2 文本情感分析示例2…

二、RK3588-安装Opencv-4.8.1(C++版本)

1.前言 OpenCV是一个跨平台的计算机视觉和机器学习软件库&#xff0c;基于Apache2.0许可&#xff08;开源&#xff09;发行。它可以在Linux、Windows、Android和Mac OS操作系统上运行。OpenCV由一系列C函数和少量C类构成&#xff0c;同时提供了Python、Ruby、MATLAB等语言的接口…

Stimulsoft BI Designer 2024.1.2 Crack

Stimulsoft BI Designer Do you want to create reports and dashboards, but you do not need to create your application? We want to offer you a great solution – Stimulsoft Designer! All you need is to connect your data, drag it onto the template page, config…

Java日期和时间(一)

传统的日期和时间 Date 代表的是日期和时间 构造器说明public Date&#xff08;&#xff09;创建一个Date对象&#xff0c;代表的是系统当前此刻日期时间public Date&#xff08;long time&#xff09;把时间毫秒值转换成Date日期对象 import java.util.Date;public class …

weston 源码下载及编译

文章目录 前言一、安装 meson1. ubuntu 上安装pip32. 使用pip3安装meson3. 安装 ninja二、获取weston 源码三、编译 weston 源码1. meson build2. ninja -C build3. 安装编译生成的weston四、执行编译生成的weston总结参考资料前言 本文主要介绍 weston 9.0源码的下载和编译,…

DolphinScheduler 介绍及系统架构

目录 一、DolphinScheduler 介绍 1.1 关于 DolphinScheduler 1.2 特性 简单易用 丰富的使用场景 High Reliability High Scalability 1.3 名词解释 1.3.1 名词解释 1.3.2 模块介绍 二、DolphinScheduler 系统架构 2.1 系统架构图 2.2 架构说明 MasterServer 该服…

Mybatis插件入门

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…

Java:IO流——字节流和字符流

目录 IO流的基本概念 IO流体系结构 FileOutputStream字节输出流 构造方法 成员方法 细节 关流 FileInputStream字节输入流 构造方法及成员方法 read不带参数代码示例 read带参数代码示例​编辑 将字节数组或字符数组转成字符串 FileReader 字符输入流 构造方法和…

远程桌面连接问题:出现身份验证错误。要求的函数不受支持错误【已解决】

问题描述&#xff1a; 在使用远程桌面时&#xff0c;出现的错误&#xff0c;原因在于CredSSP加密数据库修正问题&#xff0c;可以通过设置组策略解决问题 远程桌面连接 出现身份验证错误。 要求的函数不受支持错误注意&#xff1a; 如果使用注册表编辑器或其他方法修改注册表不…

方案:智能分析网关V4区域人数超员AI算法模型的应用场景介绍

视频AI智能分析技术已经深入到人类生活的各个角落&#xff0c;与社会发展的方方面面紧密相连。从日常生活中的各种场景&#xff0c;如人脸识别、车牌识别&#xff0c;到工业生产中的安全监控&#xff0c;如工厂园区的翻越围栏识别、入侵识别、工地的安全帽识别、车间流水线产品…

GPT-5、开源、更强的ChatGPT!OpenAI公布2024年计划

年终岁尾&#xff0c;正值圣诞节热闹气氛的OpenAI写下了2024年的发展清单。 OpenAI联合创始人兼首席执行官Sam Altman在社交平台公布&#xff0c;AGI&#xff08;稍晚一些&#xff09;、GPT-5、更好的语音模型、更高的费率限制&#xff1b; 更好的GPTs&#xff1b;更好的推理…

C语言课程设计|学生成绩管理系统(含完整代码)

目录 前言 简介 学生信息录入功能 学生单个信息查询 查询全部学生信息 修改学生信息 删除学生信息 退出 完整代码 前言 在临近期末之际&#xff0c;相信好多初学C语言的同学都开始为写C语言课程设计这件事开始焦虑了吧&#xff1f;或许会不知所措&#xff0c;或许会…

这些浏览器中的AI扩展太香了

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 众所周知&#xff0c;浏览器只有添加了扩展才能让其火力全开&#xff0c;效率翻倍&#xff0c;而当有了AI的加持后&#xff0c;直接起飞 本期将整理一些目前肥肠火的基于AI语言模型的扩展程序&#x…

java企业网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web企业网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

【Linux驱动】设备树简介 | 内核对设备树的处理

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux驱动》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f9f2;设备树简介&#x1f3f9;设备树语法&#x1f3f9;常见节点和属性&#x1f3f9…

基于ssm的二手商品交易平台+vue论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

基于ssm的航空票务推荐系统的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;航班信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

Vue.js学习笔记(1)——Visual Studio Code搭建Vue.js框架

1 安装Node.js 1、下载安装包&#xff1a;进入官网&#xff08;https://nodejs.org/en&#xff09;&#xff0c;下载左侧的稳定版。 2、选择安装位置&#xff0c;不用勾选自动安装必要工具。 其他都默认Next。 配置环境&#xff0c;具体参考本文章&#xff1a; https://blo…