GitHub-demo:Image-Stitching

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):
	#特征提取
	# 初始化SIFT
	sift=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: stackoverflow
	verified_matches=[]
	for m1,m2 in matches:
		# 只加入理想匹配
		if m1.distance<0.8*m2.distance:
			verified_matches.append(m1)

	# 最少匹配数
	min_matches=8
	if 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 M
	else: # 如果不够最小匹配数
		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 result
	return 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 dimesions
	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)

	# Get relative perspective of second image
	img2_dims=cv2.perspectiveTransform(img2_dims_temp,M)

	# Resulting dimensions
	result_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 image
	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 result
	return result_img

# Find SIFT and return Homography Matrix
def get_sift_homography(img1, img2):

	# Initialize SIFT 
	sift=cv2.xfeatures2d.SIFT_create() #修改点 版本原因

	# Extract keypoints and descriptors
	k1,d1=sift.detectAndCompute(img1,None)
	k2,d2=sift.detectAndCompute(img2,None)

	# Bruteforce matcher on the descriptors
	bf=cv2.BFMatcher()
	matches=bf.knnMatch(d1,d2, k=2)

	# Make sure that the matches are good
	verify_ratio=0.8 # Source: stackoverflow
	verified_matches=[]
	for m1,m2 in matches:
		# Add to array only if it's a good match
		if m1.distance<0.8*m2.distance:
			verified_matches.append(m1)

	# Mimnum number of matches
	min_matches=8
	if len(verified_matches)>min_matches:
		
		# Array to store matching points
		img1_pts=[]
		img2_pts=[]

		# Add matching points to array
		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)
		
		# Compute homography matrix
		M,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0)
		return M
	else:
		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 images
	img1=cv2.imread("/Applications/code/test/01_suburbA.jpg") #修改点
	img2=cv2.imread("/Applications/code/test/01_suburbB.jpg")

	# Equalize histogram
	img1=equalize_histogram_color(img1)
	img2=equalize_histogram_color(img2)

	# Show input images
	input_images=np.hstack((img1,img2))
	cv2.imshow('Input Images',input_images)

	# Use SIFT to find keypoints and return homography matrix
	M=get_sift_homography(img1, img2)

	# Stitch the images together using homography matrix
	result_image=get_stitched_image(img2,img1,M)

	# Write the result to the same directory
	result_image_name='result.jpg'           #需要带后缀写入
	cv2.imwrite(result_image_name,result_image)

	# Show the resulting image
	cv2.imshow('Result',result_image)
	cv2.waitKey()

# Call main function
if __name__=='__main__':
	main()
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值