大疆精灵4多光谱无人机P4M影像辐射定标方法

大疆精灵4多光谱无人机P4M影像辐射定标方法

目前,市场上主流的多光谱相机有进口的红衫多光谱相机(Sequoia)、Readeage多光谱相机、Altum多光谱相机(readeage升级版增加了热红外波段)以及国产的MS600、精灵P4M等。P4M作为后起之秀,不仅价格亲民,还实现了多光谱相机与无人机深度集成。
在数据处理方面, 以往的多光谱相机均配有校准板,可利用PIX4D中进行正射影像拼接和校准处理。而P4M若直接用第三放软件处理,并不能得到反射率影像。因此,本文参考《P4 Multispectral图像处理指南》编写了DN值向反射率转换的代码,以供其他朋友参考。

#!/usr/bin/env python3.7
# encoding: utf-8
"""
  @author: ISR
  @contact: [email protected]
  @file:
  @time:
  @desc:
  mtx,相机内参;dist,畸变系数;
"""

import cv2
import numpy as np
from pyexiv2 import Image
import piexif
from scipy import misc

def image_info(imagepath):

    """获取xmp、xeif信息"""

    img = Image(imagepath)
    exif = img.read_exif()  # 读取 EXIF 元数据,这会返回一个字典
    #exifdata = piexif.load(imagepath)
    img.read_iptc()
    xmp = img.read_xmp()
    #b_name = xmp['Xmp.drone-dji.BandName']
    img.close()  # 操作完之后,记得关闭图片

    return xmp, exif

def image_mat(xmp):

    """根据xmp获取影像内方位元素和畸变系数"""

    # 内方位元素和畸变校正参数 calibrate_date,fx,fy,cx,cy,k1,k2,p1,p2,k3
    clibrate_data = xmp['Xmp.drone-dji.DewarpData']
    clibrate_data = clibrate_data.split(";")[1].split(",")

    # 内参
    fx = float(clibrate_data[0])
    fy = float(clibrate_data[1])
    cX = float(clibrate_data[2])
    cY = float(clibrate_data[3])

    # 内参矩阵
    cam_mat = np.zeros((3, 3))
    cam_mat[0, 0] = fx
    cam_mat[1, 1] = fy
    cam_mat[2, 2] = 1.0
    cam_mat[0, 2] = cX
    cam_mat[1, 2] = cY
    # print(cam_mat)

    # 畸变系数
    k1 = float(clibrate_data[4])
    k2 = float(clibrate_data[5])
    p1 = float(clibrate_data[6])
    p2 = float(clibrate_data[7])
    k3 = float(clibrate_data[8])

    # 畸变系数
    # dist_coeffs = np.array([k1, k2, p1, p2, k3]).reshape((1,5))
    dist_coeffs = (k1, k2, p1, p2)

    return cam_mat, dist_coeffs

def raw2ref(xmp,img):

    """
    DN值转反射率
    img为数组
    """

    # 黑电平
    blacklevel = exif['Exif.Image.BlackLevel']
    blacklevel = int(blacklevel)

    # sens0rgain 增益
    sensorgain = float(xmp['Xmp.drone-dji.SensorGain'])

    # ExposureTime 曝光时间
    ExposureTime = float(xmp['Xmp.drone-dji.ExposureTime'])

    # 暗角补偿图像中心
    CalibratedOpticalCenterX = float(xmp['Xmp.drone-dji.CalibratedOpticalCenterX'])
    CalibratedOpticalCenterY = float(xmp['Xmp.drone-dji.CalibratedOpticalCenterY'])

    # 暗角补偿多项式系数
    VignettingData = xmp[
        'Xmp.drone-dji.VignettingData']  # '0.000218235, 1.20722e-6, -2.8676e-9, 5.1742e-12, -4.16853e-15, 1.36962e-18'
    VignettingData = VignettingData.split(",")
    VignettingData = [float(i) for i in VignettingData]

    # pcamera_band
    pcamera_band = float(xmp['Xmp.drone-dji.SensorGainAdjustment'])

    # nirls*plsnir
    Camera_Irradiance = float(xmp['Xmp.Camera.Irradiance'])

    Band_canmera = np.zeros((h, w))

    # 数据归一化
    img = (img - blacklevel) / 65535

    # 暗角补偿
    for i in range(h):  # i对应y
        for j in range(w):  # j对应x

            r = ((j - CalibratedOpticalCenterX) ** 2 + (i - CalibratedOpticalCenterY) ** 2) ** 0.5

            correction = ((VignettingData[5]) * (r ** 6) + (VignettingData[4]) * (r ** 5) + (VignettingData[3]) * (
                        r ** 4) + (VignettingData[2]) * (r ** 3) + (VignettingData[1]) * (r ** 2)
                          + (VignettingData[0]) * r) + 1.0

            Band_canmera[i, j] = (img[i, j] * correction) / (sensorgain * ExposureTime / 1000000.0)

    # 计算反射率
    Band_ref = (Band_canmera * pcamera_band) / Camera_Irradiance

    return Band_ref

def dis_correction(cam_mat,dist_coeffs,Band_ref):

    '''
    优化相机内参(camera matrix),这一步可选。
    参数1表示保留所有像素点,同时可能引入黑色像素,
    设为0表示尽可能裁剪不想要的像素,这是个scale,0-1都可以取。
    '''

    (h,w) = Band_ref.shape

    # 优化内方位元素
    newcameramtx, roi = cv2.getOptimalNewCameraMatrix(cam_mat, dist_coeffs, (h, w), 0, (h, w))

    # 畸变校正
    dst = cv2.undistort(Band_ref, cam_mat, dist_coeffs, None, newcameramtx)

    print("newcameramtx:", newcameramtx)

    # 输出纠正畸变以后的图片
    x, y, w, h = roi

    dst = dst[y:y + h, x:x + w]


    return dst

if __name__ == '__main__':

    imagepath = r"C:\Users\79420\Desktop\image\DJI_0015.TIF"
    outimage = r"C:\Users\79420\Desktop\image\DJI_001GGG.TIF"


    xmp, exif = image_info(imagepath)

    img = cv2.imread(imagepath, 2)

    h, w = img.shape

    cam_mat, dist_coeffs = image_mat(xmp)

    Band_ref = raw2ref(xmp, img)

    new_image = dis_correction(cam_mat, dist_coeffs, Band_ref)

    cv2.imwrite(outimage, new_image*1000)

#计算误差
# tot_error = 0
# for i in range(len(objpoints)):
#     imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
#     error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
#     tot_error += error
# print ("total error: ", tot_error/len(objpoints))

20210406日更新:
经过本人不断努力,目前已实现大疆P4M多光谱影像反射率校正(处理后影像可用第三方软件做拼接处理)、波段配准(像素级)、光谱指数提取(反射率和60个植被指数)和影像的相对辐射校正,欢迎感兴趣的朋友留言或提供数据测试。
大疆精灵4多光谱无人机P4M影像辐射定标方法_第1张图片

你可能感兴趣的:(大疆精灵4多光谱无人机P4M影像辐射定标方法)