高分系列遥感影像的一个问题是难以进行精准的几何校正,通常是在专业软件(如ENVI)中处理,需要借助标准影像手动选择地面控制点来进行校正,操作比较复杂。本文的思路是借助Landsat8影像来进行关键点匹配,这样就能自动得到控制点。难点有:
①两种不同卫星的影像分辨率和细节方面差异较大,难以进行准确的匹配。为解决此问题,将分辨率较高的Landsat影像进行了均值滤波。
②遥感影像的存储格式不能直接用opencv处理,需要转换成8位整型灰度图,这里只用了一个全色波段。
# -*- coding: utf-8 -*-
# @File : siftMatch.py
# @Author: Freezinghot
# @Date : 2021/2/7
# @Desc : SIFT图像配准
import cv2
import numpy as np
import gdal
from PIL import Image
from matplotlib import pyplot as plt
# SIFT特征检测
def sift_kp(image):
#gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 灰度图转换,如果是普通RGB图像需要这一行
sift = cv2.xfeatures2d.SIFT_create() # 实例化sift
kp, des = sift.detectAndCompute(image, None) # 找出关键点并计算关键点对应的sift特征向量。
# kp 是一个关键点列表。des是一个numpy数组,其大小是关键的数目乘以128.image表示输入的灰度图
# kp_image = cv2.drawKeypoints(gray_image, kp, None) # 在图中画出关键点(输入,关键点,输出)
kp_image = cv2.drawKeypoints(image, kp, None)
# print(kp, des)
return kp_image, kp, des
def get_good_match(des1, des2):
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2) # des1为模板图,des2为匹配图
#matches = sorted(matches, key=lambda x: x[0].distance / x[1].distance)
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append(m)
return good
def siftImageAlignment(img1, img2):
_, kp1, des1 = sift_kp(img1)
_, kp2, des2 = sift_kp(img2)
goodMatch = get_good_match(des1, des2)
if len(goodMatch) > 10:
ptsA = np.float32([kp1[m.queryIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
ptsB = np.float32([kp2[m.trainIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
ransacReprojThreshold = 5
H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, ransacReprojThreshold)
imgOut = cv2.warpPerspective(img2, H, (img1.shape[1], img1.shape[0]),
flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)
else:
print("goodMatches less than 10")
return imgOut, H, status
def tiftoint8(min, max, imdata):
scale = max-min
out_img = ((imdata/scale)*256).astype(np.uint8)
return out_img
inds2 = "E:\\RSDATA\\GFdata\\LC08_L1TP_120035_20200424_01_T1\\LC08_L1TP_120035_20200424_20200508_01_T1_B8_resized50.TIF"
inds1 = "E:\\RSDATA\\GFdata\\tempout\\GF4_PMI_E121.6_N37.1_20200424_L1A0000296007\\GF4_PMS_E121.6_N37.1_20200424_L1A0000296007_clip.tiff"
img1 = gdal.Open(inds1)
img2 = gdal.Open(inds2)
im1_width = img1.RasterXSize # 栅格矩阵的列数
im1_height = img1.RasterYSize # 栅格矩阵的行数
im1_bands = img1.RasterCount # 波段数
im1_geotrans = img1.GetGeoTransform() # 获取仿射矩阵信息
im1_proj = img1.GetProjection() # 获取投影信息
im2_width = img2.RasterXSize # 栅格矩阵的列数
im2_height = img2.RasterYSize # 栅格矩阵的行数
im2_bands = img2.RasterCount # 波段数
im_data1 = img1.ReadAsArray(0,0,im1_width,im1_height)#获取数据
max1 = im_data1.max()
min1 = im_data1.min()
in_img1uint8 = tiftoint8(min1, max1, im_data1)
im_data2 = img2.ReadAsArray(0,0,im2_width,im2_height)#获取数据
max2 = im_data2.max()
min2 = im_data2.min()
in_img2uint8 = tiftoint8(min2, max2, im_data2)
'''
# 平滑滤波
in_img1uint8 = cv2.blur(in_img1uint8[4],(3,5))#模板大小3*5
in_img2uint8 = cv2.blur(in_img2uint8,(3,5))
plt.subplot(1,2,1),plt.imshow(in_img1uint8,'gray')
plt.subplot(1,2,2),plt.imshow(in_img2uint8,'gray')
plt.show()
'''
in_img2uint8 = cv2.blur(in_img2uint8,(3,5))
# 执行sift
_, kp1, des1 = sift_kp(in_img1uint8[0])
_, kp2, des2 = sift_kp(in_img2uint8)
goodMatch = get_good_match(des1, des2)
print(goodMatch)
# 提取关键点中的坐标
src_pts = np.float32([kp1[m.queryIdx].pt for m in goodMatch]).reshape(-1,1,2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in goodMatch]).reshape(-1,1,2)
print(src_pts,dst_pts)
#H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 2.0)
#wrapimg = cv2.warpPerspective(in_img1uint8[0], H, (im1_width, im1_height))
wrapimg, H, status = siftImageAlignment(in_img1uint8[0], in_img2uint8)
# 显示匹配点和匹配结果
matchesMask = status.ravel().tolist()
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
singlePointColor = None,
matchesMask = matchesMask, # draw only inliers
flags = 2)
img3 = cv2.drawMatches(in_img1uint8[0],kp1,in_img2uint8,kp2,goodMatch,None,**draw_params)
plt.imshow(img3,'gray')
plt.show()
# 写入新的tiff文件
datatype = gdal.GDT_UInt16
wrapimg = ((wrapimg*32767)//255).astype(np.uint16)
path = "E:\\RSDATA\\GFdata\\tempout\\LC8wrapimg.tif"
driver = gdal.GetDriverByName("GTiff")
dataset = driver.Create(path, im1_width, im1_height, 1, datatype,options=["INTERLEAVE=BAND"])
print("创建文件成功")
dataset.SetGeoTransform(im1_geotrans) # 写入仿射变换参数
dataset.SetProjection(im1_proj) # 写入投影
print("写入投影")
dataset.GetRasterBand(1).WriteArray(wrapimg)
del dataset
本来是计划用匹配点生成的转换矩阵直接对高分影像做一个变换,结果不理想,后面考虑将提取到的匹配点作为控制点,用gdal对原影像进行控制点校正。程序不完善,只是一个预研实验,在这里做个记录,供大家参考。