Image-Stitching

学习源码来自Github-pavanpn/Image-Stitching


主要函数:

1. cv2.cvtColor()  颜色空间转换函数
img_yuv=cv2.cvtColor(img,k) # img是需要转换对图片,k是转换成何种格式
# cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式
# cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片

2. cv2.equalizeHist()   直方图均衡化函数
cv2.equalizeHist(img) # 将要均衡化的原图像[要求是灰度图像]作为参数传入,则返回值即为均衡化后的图像
3. bf=cv2.BFMatcher()、matches=bf.knnMatch()   K-最邻近匹配
# 暴力匹配BFMatcher,遍历描述符,确定描述符是否匹配,然后计算匹配距离并排序
# BFMatcher函数参数:
# normType:NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2。
# NORM_L1和NORM_L2是SIFT和SURF描述符的优先选择
# NORM_HAMMING和NORM_HAMMING2是用于ORB算法
bf = cv2.BFMatcher(normType=cv2.NORM_HAMMING, crossCheck=True)
matches = bf.knnMatch(d1, d2, k=1) #获得knn检测器,d1,d2为描述子
# knn匹配可以返回k个最佳的匹配项,bf返回所有的匹配项
4. cv2.findHomography()   单应矩阵发现函数
M,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0)
# img1_pts 目标图像的特征点集合
# 第三个参数 配准方法的选择
# 第四个参数 每次抽取样本的个数,仅RANSAC和RHO有
5. cv2.perspectiveTransform()   透视变换函数
cv2.perspectiveTransform(src, m[, dst]) → dst
# src:输入的2通道或者3通道的图片
# m:变换矩阵
6. np.concatenate()
result_dims=np.concatenate((img1_dims,img2_dims),axis=0)
# 前两个参数为要拼接对数组
# axis参数为指定按照哪个维度进行拼接,axis=0则代表着按照第一维度进行拼接


上述两个数组不能按行拼接(axis不能为0),因为第二维度尺寸不同。

7. cv2.warpPerspective()
cv2.warpPespective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))  # 获得根据单应性矩阵变化后的图像
# image表示输入图像,H表示单应性的矩阵,(imageA.shape[1] + imageB.shape[1], imageA.shape[0])表示矩阵变化后的维度
8. sift.detectAndComputer()
sift.detectAndComputer(gray, None) # 计算出图像的关键点和sift特征向量,gray表示输入的图片
9. np.reshape()
numpy.reshape(a, newshape, order='C')
# a 需要reshape的array,可以看成是对数组的扩展,但是不影响原始数组
# 关于newshape参数
np.reshape(m,n) # 返回一个m行n列的数组
np.reshape(-1) # 返回一个一维数组
np.reshape(-1,n) # 返回一个n列的数组,行数自动给定
np.reshape(k,n,m) # 创建一个三维 (k个n*m)的数组
np.reshape(p,k,n,m) # 四维 p个(k,n,m)# order: 可选为(C, F, A)
C: 按照行来填充
F: 按照列的顺序来填充
A: 按任意方向,默认,这里相当于行
arr=np.array([1,2,3,4,5,6,7,8,9,10,11,12])
m=arr.reshape(3,4)
print(m)>>
[[ 1  2  3  4][ 5  6  7  8][ 9 10 11 12]]m=arr.reshape(3,2,2)>>
[[[ 1  2][ 3  4]][[ 5  6][ 7  8]][[ 9 10][11 12]]]m=arr.reshape(2,3,1,2)>>
[[[[ 1  2]][[ 3  4]][[ 5  6]]][[[ 7  8]][[ 9 10]][[11 12]]]]

当newshape参数大于2个的时候,pylint就开始报参数过多的错 ?

官方文档


步骤:

  1. 读入图像,
  2. 用SIFT/SURF/FAST/ORB提取每张图的特征点 和 每个特征点对应的描述子
  3. 通过匹配特征点描述子,找到两张图中匹配的特征点对(这里可能存在错误匹配)
  4. 使用RANSAC算法剔除错误匹配
  5. 求解方程组,计算Homograph单应性变换矩阵
  6. 通过Homograph单应性变换矩阵对其中一张图片进行仿射变换
  7. 拼接图片

1.彩色图像的直方图均衡化

直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。

中心思想:原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内均匀分布
def equalize_histogram_color(img):img_yuv=cv2.cvtColor(img,cv2.COLOR_BGR2YUV)img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])img = cv2.cvtColor(img_yuv,cv2.COLOR_YUV2BGR)return img

为什么要做直方图均衡化?

一副高质量的图像的像素值分布应该很广泛,所以应该把它的直方图做一个横向拉伸,这就是直方图均衡化要做的事情。通常情况下这种操作会改善图像的对比度

2.特征点匹配和计算单应矩阵

def get_sift_homography(img1, img2):#特征提取# 初始化SIFTsift=cv2.xfeatures2d.SIFT_create() # 修改点 版本原因#提取关键点和描述子k1,d1=sift.detectAndCompute(img1,None) # k1存储提取的特征点,d1存储对应的描述子k2,d2=sift.detectAndCompute(img2,None)#图片匹配# 描述子计算:k-最邻近匹配bf=cv2.BFMatcher()matches=bf.knnMatch(d1,d2, k=2)# 获得理想匹配verify_ratio=0.8 # Source: stackoverflowverified_matches=[]for m1,m2 in matches:# 只加入理想匹配if m1.distance<0.8*m2.distance:verified_matches.append(m1)# 最少匹配数min_matches=8if len(verified_matches)>min_matches:# 存储匹配点img1_pts=[]img2_pts=[]# 把匹配点加进去for match in verified_matches:img1_pts.append(k1[match.queryIdx].pt)img2_pts.append(k2[match.trainIdx].pt)img1_pts=np.float32(img1_pts).reshape(-1,1,2)img2_pts=np.float32(img2_pts).reshape(-1,1,2)# 计算单应矩阵M,掩模 mark,使用了RANSAC算法剔初错误匹配M,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0) return Melse: # 如果不够最小匹配数print('Error: Not enough matches')exit()

3.拼接图片

# 使用关键点来拼接图片
def get_stitched_image(img1,img2,M):# 获得输入图片的宽、高w1,h1=img1.shape[:2]w2,h2=img2.shape[:2]# 获得图像维度img1_dims=np.float32([[0,0],[0,w1],[h1,w1],[h1,0]]).reshape(-1,1,2)img2_dims_temp=np.float32([[0,0],[0,w2],[h2,w2],[h2,0]]).reshape(-1,1,2)# 获取第二幅图像的相对透视图img2_dims=cv2.perspectiveTransform(img2_dims_temp,M) # 执行矢量的透视矩阵变换# 得到结果图片的维度result_dims=np.concatenate((img1_dims,img2_dims),axis=0)# 拼接图像# 计算匹配点的维度[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]transform_array=np.array([[1, 0, transform_dist[0]], [0, 1, transform_dist[1]], [0,0,1]]) # 扭曲图像以用于拼接result_img=cv2.warpPerspective(img2,transform_array.dot(M), (x_max-x_min,y_max-y_min))result_img[transform_dist[1]:w1+transform_dist[1], transform_dist[0]:h1+transform_dist[0]]=img1# Return the resultreturn result_img

参考链接:Python+OpenCV实现图像的全景拼接


几个修改点

1. >83 print后需要带括号输出 版本原因
2. >45 module ‘cv2’ has no attribute ‘SIFT’ 版本原因

3.4.2版本中,openCV将SIFT等算法整合到xfeatures2d集合里面了

sift=cv2.SIFT()变成sift=cv2.xfeatures2d.SIFT_create()

3. >97 98 关于读入图像cv2.imread()函数的使用
# 一直越界报错 (可能是我环境下的命令行参数没有配置好)
img1 = cv2.imread(sys.argv[1])
img2 = cv2.imread(sys.argv[2])

需要修改成路径形式

注意:

1.路径形式不支持 单右斜线形式,可以支持 双右斜线、双左斜线、单左斜线形式、混合形式

#include<opencv2\opencv.hpp>
using namespace cv;
int main(int argc,char* argv[])
{Mat img;//-- 1 --双右斜线法//string imgpath = "C:\\Users\\bingbuyu\\Pictures\\photo\\miao1.jpg";//-- 2 --双左斜线法//string imgpath = "C://Users//bingbuyu//Pictures//photo//miao1.jpg";//-- 3 --单左斜线法//string imgpath = "C:/Users/bingbuyu/Pictures/photo/miao1.jpg";//-- 4 --以上三种混合法//string imgpath = "C:/Users//bingbuyu\\Pictures//photo//miao1.jpg";//-- 5 --相对路径法//string imgpath = "miao.jpg";//-- 6 --命令行参数法string imgpath = argv[1];img = imread(imgpath, 1);imshow("img", img);waitKey(0);return 0;
}

参考链接:使用imread()函数读取图片的六种正确姿势

2.命令行参数法
Python中 sys.argv[]的用法简明解释

4. >115 写入时,同样是由sys.argv[]引起的越界错误,写入时需要带后缀名写入


不能为imwrite()找到特定扩展名的writer,因为result_image_name必须是.png.jpg.bmg,改成带后缀的即可。


虽然能跑出来,但是有个报错没整明白 :是因为多于了2个newshape参数,变三维、四维数组的时候就会报这个,但还是可以用


方法调用的位置参数过多?reshape()


完整代码:

import sys
import cv2
import numpy as np
# Use the keypoints to stitch the images
def get_stitched_image(img1,img2,M):# Get width and height of input images  w1,h1=img1.shape[:2]w2,h2=img2.shape[:2]# Get the canvas dimesionsimg1_dims=np.float32([[0,0],[0,w1],[h1,w1],[h1,0]]).reshape(-1,1,2)img2_dims_temp=np.float32([[0,0],[0,w2],[h2,w2],[h2,0]]).reshape(-1,1,2)# Get relative perspective of second imageimg2_dims=cv2.perspectiveTransform(img2_dims_temp,M)# Resulting dimensionsresult_dims=np.concatenate((img1_dims,img2_dims),axis=0)# Getting images together# Calculate dimensions of match points[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)# Create output array after affine transformation transform_dist=[-x_min,-y_min]transform_array=np.array([[1, 0, transform_dist[0]], [0, 1, transform_dist[1]], [0,0,1]]) # Warp images to get the resulting imageresult_img=cv2.warpPerspective(img2,transform_array.dot(M), (x_max-x_min,y_max-y_min))result_img[transform_dist[1]:w1+transform_dist[1], transform_dist[0]:h1+transform_dist[0]]=img1# Return the resultreturn result_img# Find SIFT and return Homography Matrix
def get_sift_homography(img1, img2):# Initialize SIFT sift=cv2.xfeatures2d.SIFT_create() #修改点 版本原因# Extract keypoints and descriptorsk1,d1=sift.detectAndCompute(img1,None)k2,d2=sift.detectAndCompute(img2,None)# Bruteforce matcher on the descriptorsbf=cv2.BFMatcher()matches=bf.knnMatch(d1,d2, k=2)# Make sure that the matches are goodverify_ratio=0.8 # Source: stackoverflowverified_matches=[]for m1,m2 in matches:# Add to array only if it's a good matchif m1.distance<0.8*m2.distance:verified_matches.append(m1)# Mimnum number of matchesmin_matches=8if len(verified_matches)>min_matches:# Array to store matching pointsimg1_pts=[]img2_pts=[]# Add matching points to arrayfor match in verified_matches:img1_pts.append(k1[match.queryIdx].pt)img2_pts.append(k2[match.trainIdx].pt)img1_pts=np.float32(img1_pts).reshape(-1,1,2)img2_pts=np.float32(img2_pts).reshape(-1,1,2)# Compute homography matrixM,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0)return Melse:print('Error: Not enough matches')exit()# Equalize Histogram of Color Images
def equalize_histogram_color(img):img_yuv=cv2.cvtColor(img,cv2.COLOR_BGR2YUV)img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])img = cv2.cvtColor(img_yuv,cv2.COLOR_YUV2BGR)return img# Main function definition
def main():# Get input set of imagesimg1=cv2.imread("/Applications/code/test/01_suburbA.jpg") #修改点img2=cv2.imread("/Applications/code/test/01_suburbB.jpg")# Equalize histogramimg1=equalize_histogram_color(img1)img2=equalize_histogram_color(img2)# Show input imagesinput_images=np.hstack((img1,img2))cv2.imshow('Input Images',input_images)# Use SIFT to find keypoints and return homography matrixM=get_sift_homography(img1, img2)# Stitch the images together using homography matrixresult_image=get_stitched_image(img2,img1,M)# Write the result to the same directoryresult_image_name='result.jpg'           #需要带后缀写入cv2.imwrite(result_image_name,result_image)# Show the resulting imagecv2.imshow('Result',result_image)cv2.waitKey()# Call main function
if __name__=='__main__':main()

GitHub-demo:Image-Stitching相关推荐

  1. Github Actions:再次改变软件开发

    本文转自 FEPulse 公众号(微信搜索 FEPulse,精选国内外最新前端资讯,为你把握前端脉搏). Github Actions 是 GitHub Universe 大会上发布的,被 Githu ...

  2. 小程序-demo:知乎日报

    ylbtech-小程序-demo:知乎日报 1.返回顶部 0.         1.app.js //app.js App({onLaunch: function () {//调用API从本地缓存中获 ...

  3. 小程序-demo:小熊の日记

    ylbtech-小程序-demo:小熊の日记 1.CHANGELOG.md # 2016-10-12* 更新开发者工具至`v0.10.101100` * 修改`new`页的数据绑定方式 & 修 ...

  4. Github 开源:升讯威 Winform 开源控件库( Sheng.Winform.Controls)

    Github 地址:https://github.com/iccb1013/Sheng.Winform.Controls 本控件库中的代码大约写于10年前(2007年左右),难免有不成熟与欠考虑之处, ...

  5. Android Demo : 悬浮窗

    Android Demo : 悬浮窗 搬砖自:简书 链接:https://www.jianshu.com/p/ac63c57d2555 设计思路分析 本Demo的设计思路如下: 一个MainActiv ...

  6. 隐身术?登顶 GitHub Top1:200 行 JS 代码让画面人物瞬间消失!

    整理 | 夕颜 出品 | CSDN(ID:CSDNnews) 今天,一个名为 Real-Time-Person-Removal(实时人物去除)项目在GitHub上火了,登上近日GitHub Trend ...

  7. GitHub开源:100美元自制激光雷达

    近期俄罗斯一位 Github 主开源了一款DIY激光雷达项目,从 PCB 到上位机软件全部开源,激光雷达实物效果图如下所示: 该激光雷达性能指标如下所示: 扫描速度:每秒 15 次 分辨率:大约检测距 ...

  8. GitHub开源:17M超轻量级中文OCR模型、支持NCNN推理

    目录 1.项目简介 2.项目配置 3.问题解决 1.项目简介 近期GitHub上一位大神开源了一个叫做chineseocr_lite的OCR的项目,这是一个超轻量级中文OCR,支持竖排文字识别.NCN ...

  9. 一个丧心病狂的Github项目:东北话编程,大写的服!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:开源最前线(ID:OpenSourceTop) 还记得12月 ...

  10. github工具:Octotree安装和使用教程

    文章目录 介绍 安装过程 测试 介绍 Octotree是专门为github设计的,可以做到将github项目结构在侧边展示(在线的,无需下载到本地),有点类似于IDEA或者pycharm左边的那个项目 ...

最新文章

  1. 【ES6】数组的拓展
  2. php实现qq相册功能,使用javascript如何实现QQ空间相册展示
  3. 数据库更新记录,但程序查不到新记录问题
  4. .net core中不支持GB2312编码的问题
  5. Java中Date日期以及日期格式化
  6. vscode配置js环境_VS Code配置Python开发环境
  7. Direct3D顶点结构使用总结
  8. Qt4项目迁移到Qt5问题:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets .
  9. Mybatis中是否需要依赖配置文件的名称要和mapper接口的名称一致 params错误
  10. php 判断是否为360浏览器,怎么判断浏览器是否是360浏览器
  11. c# NPOI 导出Excel 冻结窗格
  12. Moya、RxMoya基本使用
  13. 微型计算机2019年年度盘点,「2019 IT产业市场回顾」性能堆砌、轴之战再起、多声道普及!2019年游戏外设市场年终盘点...
  14. macos最新版本是什么_macOS的最新版本是什么?
  15. 鱼眼图像校正(球面等距投影模型)_||
  16. 人尽皆知的云原生,到底是大势所趋还是过度炒作?
  17. css美化浏览器滚动条
  18. 离散傅里叶变换(DFT/IDFT、FFT/IFFT)运算量的讨论
  19. MM,这是我第一次给你写的Blog,用一首《那一夜》开始吧
  20. 英语敢死队 第三周学习总结感受

热门文章

  1. Bootstrap5 弹出框
  2. linux启动zabbix服务,zabbix监控Linux系统服务的流程
  3. Java jcmd内存远大于top_Java堆外内存排查小结
  4. 视频直播制作软件:MimoLive Mac v5.2b2
  5. 银行贷款客户拉新活动分析——数据分析项目实战
  6. 如何激活预装的office
  7. D2. Balance
  8. dumpsys查看应用信息
  9. markdown 编辑器实现双屏同步滚动
  10. 【找不到与请求 URI匹配的 HTTP 资源】(转)