JAVA-通过大疆TSDK的API直接获取红外图片温度信息

一、前言

看过很多关于大疆红外图片用TSDK取温的方式,但是网上能搜到的大部分教程都是通过官方下载文件smple编译出来的程序来取温,如果这样做,虽然确实也能够实现目的,但不得不说,不但会降低运行速度,而且代码调用起来也麻烦。所以不如研究一下怎么直接调用他们的C++ API。

先说下大疆TSDK(Thermal Sdk),目前更新版本为1.6,这个版本目前已经适配的机型和镜头包括御2、御3无人机、M30T无人机、H20T/N镜头、H30T镜头,理论上这个SDK能够在任何基于X86架构的操作系统中使用,包含Linux和windows,但对于ARM架构的操作系统比如Android、移动计算设备等都是不支持的,这个SDK定位就只是桌面端应用。

Thermal Sdk下载地址:https://www.dji.com/cn/downloads/softwares/dji-thermal-sdk,下载之后可以看到以下目录:
sample目录是一个集成所有功能的实例程序,可以直接运行里面的build就可编译出来,然后参考readme.md来执行命名行,相应的在JAVA可以通过调用命令行来实现功能。但本文并不讲这个。

调用api所需要的所有库都存放在tsdk-coare,打开后是这样的:
api中的头文件可以看到官方暴露出来的api,lib则是对应的so/dll库文件,需要将所有库文件放在同一目录。

二、环境依赖

既然TSDK的库是so或dll,那么理所当然的可以使用JNA或者JNI调用,我使用的是JNA。在pom.xml中引入JNA依赖:
java 复制代码
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.13.0</version>
</dependency>
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform</artifactId>
    <version>5.13.0</version>
</dependency>

三、调用

1、调用准备

新建一个TsdkLibrary类,并继承jna的library接口:
去api目录中的dirp_api.h找需要的函数,比如说下面几个是红外取温需要的:
java 复制代码
// 存放图片分辨率的结构体
typedef struct { 
    int32_t width;                              /**< Horizontal size */
    int32_t height;                             /**< Vertical size */
} dirp_resolution_t;
// 通过图片创建指针DIRP_HANDLE
dllexport int32_t dirp_create_from_rjpeg(const uint8_t *data, int32_t size, DIRP_HANDLE *ph);
// 获取图片分辨率
dllexport int32_t dirp_get_rjpeg_resolution(DIRP_HANDLE h, dirp_resolution_t *resolution);
// 销毁DIRP_HANDLE
dllexport int32_t dirp_destroy(DIRP_HANDLE h);
// 获取红外图片温度,返回值为整数(真实温度=返回值/10)
dllexport int32_t dirp_measure(DIRP_HANDLE h, int16_t *temp_image, int32_t size);
// 获取红外图片温度,返回值为小数(为真实温度)
dllexport int32_t dirp_measure_ex(DIRP_HANDLE h, float *temp_image, int32_t size);
因为有结构体,所以还有创建一个jna对应的图片分辨率结构体类,然后将属性填入(注意要初始化):
java 复制代码
package org.fly.library.struct;

import com.sun.jna.Structure;

public class dirp_resolution_t extends Structure {
    public int width = 0;
    public int height = 0;
  
  
    @Override
    protected java.util.List<String> getFieldOrder() {
        return java.util.Arrays.asList("width", "height");
    }
}
比如要重写getFieldOrder方法!!!

然后再将需要的函数填写到TsdkLibrary接口(要注意名字一定要对应上!!):
java 复制代码
package org.fly.library;

import com.sun.jna.Library;
import com.sun.jna.Pointer;
import org.fly.library.struct.dirp_resolution_t;

public interface TsdkLibrary extends Library {

    // 通过图片创建指针DIRP_HANDLE
    int dirp_create_from_rjpeg(byte[] data, int size, Pointer ph);
    // 获取图片分辨率
    int dirp_get_rjpeg_resolution(Pointer h, dirp_resolution_t resolution);
    // 销毁DIRP_HANDLE
    int dirp_destroy(Pointer h);
    // 获取红外图片温度,返回值为整数(真实温度=返回值/10)
    int dirp_measure(Pointer h, byte[] temp_image, int size);
    // 获取红外图片温度,返回值为小数(为真实温度)
    int dirp_measure_ex(Pointer h, byte[] temp_image, int size);
}
因为c++中很多数据类型是java中没有的,所以需要更换对应,比如说c++中的int32_t对应java中的int,int8对应byte,typedef void对应Pointer等等,可自行网上查找,这里不列举了。

2、使用

新建一个工具类或者在你想要调用api的类中通过静态代码块加载TsdkLibrary:
java 复制代码
    // tsdk库
    public static TsdkLibrary tsdkLibrary;

    static {
        tsdkLibrary = Native.load("E:\\SDK\\dji_thermal_sdk_v1.6_20240927\\tsdk-core\\lib\\windows\\release_x64\\libdirp.dll", TsdkLibrary.class);
    }
然后就可以进行使用了,获取图片温度如下:
java 复制代码
package org.fly;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import org.fly.library.TsdkLibrary;
import org.fly.library.struct.dirp_resolution_t;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;


public class Main {

    // tsdk库
    public static TsdkLibrary tsdkLibrary;

    static {
        tsdkLibrary = Native.load("E:\\SDK\\dji_thermal_sdk_v1.6_20240927\\tsdk-core\\lib\\windows\\release_x64\\libdirp.dll", TsdkLibrary.class);
    }
    // 图像处理指针
    public static Pointer dirpHandle;

    /**
     * 创建指针句柄
     */
    public static void createHandle(InputStream is,long length) throws IOException {
        // 创建空句柄指针
        dirpHandle = new Memory(length);
        byte[] buffer = new byte[(int)length];
        is.read(buffer,0,(int)length);
        // 指针分配
        tsdkLibrary.dirp_create_from_rjpeg(buffer, buffer.length, dirpHandle);
    }

    /**
     * 关闭指针
     */
    public static void closeHandle(Pointer handle) {
        tsdkLibrary.dirp_destroy(handle.getPointer(0));
    }

    /**
     * 设置红外图片
     */
    public static void setImageFile(String fileName) throws IOException {
        FileInputStream fis = new FileInputStream(fileName);
        if(dirpHandle !=null){
            closeHandle(dirpHandle);
        }
        createHandle(fis,fis.available());
        fis.close();
    }
    /**
     * 获取红外图片温度
     */
    public static float[][] getImageTem(String fileName) throws IOException {
        setImageFile(fileName);
        // 获取分辨率
        dirp_resolution_t resolution = new dirp_resolution_t();

        tsdkLibrary.dirp_get_rjpeg_resolution(dirpHandle.getPointer(0),resolution);
        int imageSize = resolution.width * resolution.height*4;
        byte[] imageBytes = new byte[imageSize];
        tsdkLibrary.dirp_measure_ex(dirpHandle.getPointer(0),imageBytes,imageSize);
        int k = 0;
        float[][] imageTem = new float[resolution.height][resolution.width];

        byte[] data = new byte[4];
        int col = 0;
        int row = 0;
        // 循环获取float温度
        while (k < imageSize) {
            data[0] = imageBytes[k];
            data[1] = imageBytes[k+1];
            data[2] = imageBytes[k+2];
            data[3] = imageBytes[k+3];
            imageTem[row][col] = bytes2Float(data);
            k += 4;
            col++;
            if(col == resolution.width){
                row++;
                col = 0;
            }
        }
        return imageTem;
    }
    /**
     * 字节数组转float
     */
    public static float bytes2Float(byte[] bytes) {
        int accum = 0;
        accum = accum | ( bytes[0] & 0xFF);
        accum = accum | ( bytes[1] & 0xFF) << 8;
        accum = accum | ( bytes[2] & 0xFF) << 16;
        accum = accum | ( bytes[3] & 0xFF) << 24;
        return Float.intBitsToFloat(accum);
    }

    public static void main(String[] args) {
        float[][] imageTem = null;
        try {
            imageTem = getImageTem("E:\\SDK\\dji_thermal_sdk_v1.6_20240927\\dataset\\H20T\\DJI_0001_R.JPG");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}