SWIG妙用: MATLAB程序转python和Java库

news2024/11/18 2:33:30

1.引言

Python或Java调用MATLAB程序需要安装MATLAB官方提供的支持库(MATLAB Runtime),而且适配的python或JDK版本有限,不方便移植。本文的思路是用MATLAB Coder将MATLAB源程序转为C/C++代码,然后用swig将其打包成python 动态库pyd或java dll,从而避免程序移植时需要安装MATLAB支持库。

2.swig安装

首先在swig官网下载swig:https://www.swig.org/download.html.

选择最新版本4.1.1。

如果是windows记得选择下载swigwin-4.1.1。下载完成后解压得到以下目录:

将该目录中的 swig.exe 所在路径添加至 系统变量Path 中:

这里将swigwin-4.1.1目录放在了D盘下。打开windows cmd,运行:

swig -version

出现以下内容则表示swig已经安装成功了:

SWIG Version 4.1.1

Compiled with i686-w64-mingw32-g++ [i686-w64-mingw32]

Configured options: +pcre

Please see https://www.swig.org for reporting bugs and further information

3.MATLAB 源程序

设需要封装的程序为静止圆球下,地心地固坐标系(ECEF)位置转经纬高(LLA)的函数ECEF2LLA.m:

function LLA=ECEF2LLA(ECEFPos)
% 地球固连坐标系到经纬高坐标系位置
x=ECEFPos.x;
y=ECEFPos.y;
z=ECEFPos.z;
E2=6.69438e-3;
Ra=6378137.0;

LLA=struct;
L=atan(y/x);
LLA.longtitude=180+rad2deg(L);
B=0; % 给纬度初始值,然后循环迭代求解,很快收敛
for m=1:20
    N=Ra/sqrt(1-E2*sin(B)^2);
    
    H=sqrt(x^2+y^2)/cos(B)-N;
    B=atan(z*(N+H)/(sqrt(x^2+y^2)*(N+H-N*E2)));
    LLA.latitude=rad2deg(B);
    LLA.height=H;
end
end

该函数的调用示例程序main.m为:

ECEFPos=struct("x",0,"y",3e5,"z",200);
disp(ECEF2LLA(ECEFPos));

4.使用MATLAB coder生成C源码

4.1选择入口函数

在matlab命令行输入:

coder

打开代码生成器:

选择要入口函数(Entry-point Function)为ECEF2LLA:

:点击Add Entry-Point Function 可以继续添加多个入口函数

4.2 定义函数参数和返回值类型

点击下一步,对函数参数和返回值进行定义。MATLAB可以根据入口函数的调用示例自动推导函数的参数和返回值类型。选择main,m脚本作为函数的调用示例,点击Autodefine Input Types自动推导类型:

点击 Add global可以添加为生成的C/C++代码添加全局变量,点击加号可以添加参数字段(如果有缺失),点击表格可以编辑参数字段。返回值类型则由MATLAB自动确定。

4.3 MATLAB源程序运行正确性检查

为了避免MATLAB源码存在问题导致生成的代码存在bug,MATLAB会先为要生成代码的MATLAB源程序创建MEX函数然后运行,以检查源程序中可能存在的问题。点击check for Issues检查代码:

4.4 代码生成

点击下一步,打开生成的C/C++程序设置界面:

选择构建类型为Source Code。注意:

生成的语言最好选择为C,因为CPython的解释器是用C实现的,选择C++可能会因为版本问题导致不兼容,使得生成的pyd不能被CPython的解释器加载。

点击Generate生成代码。

点击Next可以查看代码生成总结:

从Generate Output一栏可以看到生成的C代码在MATLAB源码目录的codegen\lib\ECEF2LLA子文件夹下;运行示例在codegen\lib\ECEF2LLA\examples内。

5.使用swig封装C源码为python动态库

5.1 编写swig接口文件

生成的C代码会缺少一个tmwtypes.h头文件,该文件在MATLAB安装目录的extern\include子文件内。例如MATLAB安装目录为:

D:\Matlab\extern\include

则该文件位置为:

D:\Matlab\extern\include

将该文件复制到生成的C代码目录内,这里即codegen\lib\ECEF2LLA。然后编写如下的swig接口文件

ECEF2LLA.i:

%module ECEF2LLA
%include<carrays.i>
%include<typemaps.i>
%{
// 引入源码中的头文件,以定义python接口文件中的数据类型和方法
#include "ECEF2LLA.h"
#include "ECEF2LLA_types.h"
%};
// 生成的python接口文件中的数据类型定义
typedef struct {
  double x;
  double y;
  double z;
} struct0_T;
typedef struct {
  double longtitude;
  double latitude;
  double height;
} struct1_T;
//接口文件提供给外部调用的函数声明
extern void ECEF2LLA(const struct0_T *ECEFPos, struct1_T *LLA);

注意:

(1)上述swig接口文件中的struct0_Tstruct1_TECEF2LLA是要暴露给pyd调用者的数据类型和方法,其中struct0_Tstruct1_TECEF2LLA_types.h中定义ECEF2LLAECEF2LLA.h中定义(2)swig接口文件中的%{...%}块表示引入源码中的内容。

5.2 新建CMake工程

使用ClionMATLAB生成的C源码目录codegen\lib\ECEF2LLA中新建一个项目,添加如下一个CMakeLists.txt文件,内容如下:

#[[ CMakeLists.txt ]]

#指定CMake的最小版本
cmake_minimum_required(VERSION 3.17)
#CMake项目名称,要和swig module名称一致
project(ECEF2LLA)

#C++标准,这里设置为C++20
set(CMAKE_CXX_STANDARD 20)

#指定你的.cxx等文件的目录
include_directories(${PROJECT_SOURCE_DIR})


#寻找安装好的swig,其实就是去电脑中找你安装好的Swig环境,所以我们需要提前安装环境。
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})

#寻找python解释器和库
find_package(Python3 COMPONENTS Interpreter Development)
include_directories(${Python3_INCLUDE_DIRS})
link_libraries(${Python3_LIBRARIES})
#swig生成的接口文件存放位置
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/python)

#查找.cpp, .c和.h文件并存到source_files变量中
file(GLOB source_files *.cpp *.c *.h)
#打印找到的文件到控制台
message("find fies: ${source_files}")

#设定生成的语言:java python等
set(LANGUAGE "python")
#swig 接口文件路径
set(SWIG_INTERFACE "ECEF2LLA.i")

#设置swig为C++模式
#SET_SOURCE_FILES_PROPERTIES(${SWIG_INTERFACE} PROPERTIES CPLUSPLUS ON)
#自动展开头文件
SET_SOURCE_FILES_PROPERTIES(${SWIG_INTERFACE} PROPERTIES SWIG_FLAGS "-includeall")
#swig生成指令,ECEF2LL即为ECEF2LLA.i中的swig module名称
swig_add_library(ECEF2LLA TYPE SHARED LANGUAGE ${LANGUAGE} SOURCES
        ${SWIG_INTERFACE}
        ${source_files}
        )

然后点击Reload changes刷新项目:

5.3 生成pyd

点击Clion的Build按钮(右上角工具栏的绿色小锤子)构建项目,构建完成后打开生成的cmake-build-debug目录:

目录中有三个文件需要特别关注:

  • python/ECEF2LLAPYTHON_wrap.c: 这个就是swig生成的python程序C接口文件,它的位置就是在CMakeLists.txt中SWIG_OUTFILE_DIR(第24行)变量定义的路径。

  • _ECEF2LLA.pyd:这就是我们需要的Python动态库文件。注意这个文件不要重命名!!

  • ECEF2LLA.py:这是给python调用的接口文件。

打开ECEF2LLA.py可以看到包含如下内容:

class struct0_T(object):
    ...
class struct1_T(object):
    ...
def ECEF2LLA(ECEFPos, LLA):
    return _ECEF2LLA.ECEF2LLA(ECEFPos, LLA)

这些就是在swig接口文件ECEF2LLA.i中定义的提供给外部程序调用的数据类型和函数。

5.4 运行pyd

复制_ECEF2LLA.pydECEF2LLA.py到同一文件夹下,

新建main.py:

from ECEF2LLA import struct0_T, struct1_T, ECEF2LLA

if __name__ == '__main__':
    ECEFPos = struct0_T()
    ECEFPos.x = 0
    ECEFPos.y = 3e5
    ECEFPos.z = 200
    LLA = struct1_T()
    ECEF2LLA(ECEFPos, LLA)
    print(LLA.longtitude, LLA.latitude, LLA.height)

运行main.py得到以下输出:

270.0 0.04453575351433897 -6078136.922270441

和MATLAB示例程序的输出一致:

说明MATLAB源程序已经正确封装为python库了。

5.5 更改pyd路径

如果需要更改pyd的位置,例如存到./dll子文件夹下:

此时运行main.py会报错:

此时只需要修改接口文件ECEF2LLA.py中的pyd导入路径即可。例如原来的pyd导入路径为:

# Import the low-level C/C++ module
if __package__ or "." in __name__:
    from . import _ECEF2LLA
else:
    import _ECEF2LLA

现在修改为:

# Import the low-level C/C++ module
if __package__ or "." in __name__:
    from .dll import _ECEF2LLA
else:
    import dll._ECEF2LLA as _ECEF2LLA

6.使用swig封装C源码为Java dll

6.1 使用swig生成Java dll 的CMakeLists.txt

使用swig封装C源码为Java dll的swig接口文件和之前是一样的,只用更改CMakeLists.txt文件的内容为生成java dll:

#[[ CMakeLists.txt ]]

#指定CMake的最小版本
cmake_minimum_required(VERSION 3.17)
#CMake项目名称,要和swig module名称一致
project(ECEF2LLA)

#C++标准,这里设置为C++20
set(CMAKE_CXX_STANDARD 20)

#指定你的.cxx等文件的目录
include_directories(${PROJECT_SOURCE_DIR})


#寻找安装好的swig,其实就是去电脑中找你安装好的Swig环境,所以我们需要提前安装环境。
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})

#寻找jdk的库
include_directories($ENV{JAVA_HOME}/include)
include_directories($ENV{JAVA_HOME}/include/win32)

#swig输出文件存放位置
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/java)

#查找.cpp, .c和.h文件并存到source_files变量中
file(GLOB source_files *.cpp *.c *.h)
#打印找到的文件到控制台
message("find fies: ${source_files}")

#设定生成的语言:java python等
set(LANGUAGE "java")
#swig 接口文件路径
set(SWIG_INTERFACE "ECEF2LLA.i")

#设置生成的dll java包名
set(JAVA_GEN_PACKAGE "com.demo.ecef2lla")
set(CMAKE_SWIG_FLAGS  -package ${JAVA_GEN_PACKAGE} )

#设置swig为C++模式
#SET_SOURCE_FILES_PROPERTIES(${SWIG_INTERFACE} PROPERTIES CPLUSPLUS ON)
#自动展开头文件
SET_SOURCE_FILES_PROPERTIES(${SWIG_INTERFACE} PROPERTIES SWIG_FLAGS "-includeall")
#swig生成指令,ECEF2LL即为ECEF2LLA.i中的swig module名称
swig_add_library(ECEF2LLA TYPE SHARED LANGUAGE ${LANGUAGE} SOURCES
        ${SWIG_INTERFACE}
        ${source_files}
        )

上述的CMakeLists.txt文件中有几处需要特别注意的地方

(1)JDK库路径

上述CMakeLists.txt第20,21行中:

#寻找jdk的库
include_directories($ENV{JAVA_HOME}/include)
include_directories($ENV{JAVA_HOME}/include/win32)

JAVA_HOME是JDK安装时配置的环境变量:

(2)生成的dll中java包名

第37,38行中:

#设置生成的dll java包名
set(JAVA_GEN_PACKAGE "com.demo.ecef2lla")
set(CMAKE_SWIG_FLAGS  -package ${JAVA_GEN_PACKAGE} )

设置生成的dll中java包名为com.demo.ecef2lla,这是因为dll中封装的JNI接口类权限为protected,所以必须指定包名,而且接口文件必须放在这个包名下才能被JDK访问。

6.2 使用Java调用dll

新建一个Java工程,在src下新建一个包com.demo.ecef2lla(必须和CMake中设置的一致),将cmake-build-debug目录下的.java和dll文件都复制到包内:

这些文件都不能重命名!!调用dll的Main.java为:

import com.demo.ecef2lla.ECEF2LLA;
import com.demo.ecef2lla.struct0_T;
import com.demo.ecef2lla.struct1_T;

public class Main {
    public static void main(String[] args) {
        System.loadLibrary("src/com/demo/ecef2lla/ECEF2LLA");
        System.out.println("load lib success");
        struct0_T ecefPos = new struct0_T();
        ecefPos.setX(0);
        ecefPos.setY(3e5);
        ecefPos.setZ(200);
        struct1_T LLA = new struct1_T();
        ECEF2LLA.ECEF2LLA(ecefPos, LLA);
        System.out.printf("%f,%f,%f%n", LLA.getLongtitude(), LLA.getLatitude(), LLA.getHeight());
    }
}

运行结果为:

load lib success
270.000000,0.044536,-6078136.922270

和MATLAB示例程序的输出一致,说明Java dll已正确封装。

6.3 更改dll路径

如果需要更改dll的位置,需要将com.demo.ecef2lla包文件夹整个复制过去,例如放到src/dll内:

此时只需要更改System.loadLibrary中的路径为相应路径即可:

import com.demo.ecef2lla.ECEF2LLA;
import com.demo.ecef2lla.struct0_T;
import com.demo.ecef2lla.struct1_T;

public class Main {
    public static void main(String[] args) {
        System.loadLibrary("src/dll/com/demo/ecef2lla/ECEF2LLA");
        ...
    }
}

7.常见问题

Q:MATLAB程序中调用了不支持生成C代码的MATLAB内置函数(如标准大气参数计算函数atmoscoesa)怎么办?

A: 自己用matlab将该函数实现一遍,替换内置的MATLAB函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/338761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud Alibaba Sentinel 动态规则扩展

前言 到目前为止&#xff0c;我们的规则定义是这样的&#xff1a; PostConstructpublic void initRole(){List<FlowRule> rules new ArrayList<>(1);FlowRule rule new FlowRule();// 设置规则匹配的资源名称rule.setResource("myFlowResource");// 规…

基于云原生分布式存储ceph实现k8s数据持久化

文章目录1、初始化集群1.1 集群机器配置1.2 配置主机名1.3 配置hosts文件1.4、配置互信1.5、关闭防火墙1.6、关闭selinux1.7、配置Ceph安装源1.8、配置时间同步1.9、安装基础软件包2、安装ceph集群2.1 安装ceph-deploy2.2 创建monitor节点2.3 安装ceph-monitor2.4 部署osd服务2…

Springboot毕业生生活用品出售网站系统-java ssm

(a) 管理员&#xff1b;管理员进入系统主要功能包括个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;用品分类管理&#xff0c;用品信息管理&#xff0c;系统管理&#xff0c;订单管理等功能并进行操作。 (b) 商家&#xff1b;商家进入系统主要功能包括个人中心&…

2004-2019年285个地级市实际GDP与名义GDP

2004-2019年285个地级市实际GDP和名义GDP 1、时间&#xff1a;2004-2019年 2、范围&#xff1a;285个地级市 3、说明&#xff1a;GDP平减指数采用地级市所在省份当年平减指数 4、代码&#xff1a; "gen rgdp gdp if year 2003 gen rgdp gdp if year 2003" re…

Java集合面试题:HashMap源码分析

文章目录一、HashMap源码二、HashMap数据结构模型图三、HashMap中如何确定元素位置四、关于equals与hashCode函数的重写五、阅读源码基本属性参考文章&#xff1a;史上最详细的 JDK 1.8 HashMap 源码解析参考文章&#xff1a;Hash详解参考文章&#xff1a;hashCode源码分析参考…

elasticsearch映射及字段类型

查询映射关系类型上对字段的类型进行映射&#xff0c;我们前面知道可以通过get方法请求_mapping查询指定类型的映射关系&#xff1a;此语句可以查询get-together索引下的group类型的映射关系更新映射关系使用put方法可以更新类型的映射这里指定了new-events类型的字段映射关系&…

【C语言】qsort——回调函数

目录 1.回调函数 2.qsort函数 //整形数组排序 //结构体排序 3.模拟实现qsort //整型数组排序 //结构体排序 1.回调函数 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来…

基础篇——如何在HTML元素中设置CSS样式

CSS Id 和 Class选择器 如果你要在HTML元素中设置CSS样式,你需要在元素中设置"id" 和 "class"选择器。 id 选择器 id 选择器可以为标有特定 id 的 HTML 元素指定特定的样式。 HTML元素以id属性来设置id选择器,CSS 中 id 选择器以 "#" 来定义…

四、Java框架之SpringMVC2_SSM整合

黑马课程 文章目录1. SSM整合案例1.1 创建web项目&#xff0c;并导入坐标1.2 完整的配置类SpringConfigJdbcConfigMyBatisConfigSpringMvcConfigSpringMvcSupportServletConfig1.3 创建数据库及表1.4 编写功能模块domain包dao包service包controller包1.5 接口测试2. 表现层与前…

XSS跨站脚本攻击剖析与防御:初识XSS

目录 跨站脚本介绍 1. 什么是XSS跨站脚本 2. XSS跨站脚本实例 3. XSS漏洞的危害 XSS的分类 1. 反射型XSS 2. 持久性XSS XSS构造 1. 利用< >标记注射Html /Javascript 2. 利用HTML标签属性值执行XSS 3. 空格回车Tab 4. 对标签属性值转码 5. 产生自己的事件…

PorterDuffXfermode与圆角图片

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 圆角图片 在项目开发中&#xff0c;我们常用到这样的功能&#xff1a;显示圆角图片。 这个是咋做的呢&#xff1f;我们来瞅瞅其中一种实现方式 /*** param bitmap 原图* p…

ChatGPT的注册与使用方法

ChatGPT无疑是最近最火的一个大模型AI应用,当艺术家还在跟AI争论AI绘画的问题时,NLP领域中的ChatGPT大模型应用,仿佛一下子替代了AI绘画应用,成为了最新争论的话题。最近也看到很多人在讨论ChatGPT,当然,很多人就算不是人工智能领域的也开始讨论ChatGPT,可见ChatGPT的风…

代替swagger的api接口神器

自动化API文档-APIFOX 文章作者&#xff1a;老杨 一&#xff1a;概述 大家在后端开发开发过程中&#xff0c;最痛恨的两天事情&#xff1a;1.写文档&#xff0c;2.别人不写文档。而我们后端开发&#xff0c;必定经历的事情就是要和前端&测试对接&#xff0c;我们需要把我…

vue104-123

影院组件 更改滚动条范围&#xff1a; 动态结算高度 //动态结算高度 this.height document.documentElement.clientHeight-选项卡高度this.height document.documentElement.clientHeight- document.querySelector(footer).offsetHeight px组件库elementUI 网址elementUI…

Docker Compose编排

一、概念1、Docker Compose是什么Docker Compose的前身是Fig&#xff0c;它是一个定义及运行多个Docker容器的工具通过 Compose&#xff0c;不需要使用shell脚本来启动容器&#xff0c;而使用 YAML 文件来配置应用程序需要的所有服务然后使用一个命令&#xff0c;根据 YAML 的文…

Spring Boot课程评价管理系统

文章目录主要功能截图&#xff1a;主要代码展示数据库设计设计总结项目地址&#x1f345; 作者主页&#xff1a;Java韩立 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、【java韩立】公号作者✌ 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 …

handler解析(2) -Handler源码解析

目录 基础了解&#xff1a; 相关概念解释 整体流程图&#xff1a; 源码解析 Looper 总结&#xff1a; sendMessage 总结&#xff1a; ThreadLocal 基础了解&#xff1a; Handler是一套 Android 消息传递机制,主要用于线程间通信。实际上handler其实就是主线程在起了一…

聊聊并发与锁

持续坚持原创输出&#xff0c;点击蓝字关注我吧1.并发与并行并发可以充分地利用 CPU 资源&#xff0c;一般都会使用多线程实现。多线程的作用是提高任务的平均执行速度&#xff0c;但是会导致程序可理解性变差&#xff0c;编程难度加大。关于对并发与并行的概念&#xff0c;大家…

共享模型之无锁(三)

1.原子累加器 示例代码: public class TestAtomicAdder {public static void main(String[] args) {for (int i 0; i < 5; i) {demo(() -> new AtomicLong(0),(adder) -> adder.getAndIncrement());}for (int i 0; i < 5; i) {demo(() -> new LongAdder(),(…

C++复习笔记6

1.String类的实现 注意深浅拷贝&#xff0c; C语言字符串拼接函数strcat() #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vld.h> #include<assert.h> using namespace std;class String {friend ostream& operator<<(ostream &am…