Swig实现C++的VTK对象到Python的传递

2023-12-22 11:22:29

案例需求:

在某些场景下需要使用python脚本的形式将一些符号(markers、glyphs、...)加到图形应用程序中已存在的图元之上,以起到标记的作用。

之所以使用脚本来添加符号而非在原程序代码中开发,一方面是考虑此功能非程序需求定义的功能,另一方面是考虑在原程序代码中开发的时间成本和改动的影响范围。

针对此需求,有两种实现方式:

(1)在C++的应用中创建VTKRenderWindow对象,经过包装之后给到Python脚本,脚本中向VTKRenderWindow增加Renderer和Renderer下的Actor;

(2)在Python的脚本中创建VTKRenderWindow对象并增加Renderer和Renderer下的Actor,经过包装之后传给C++。

下面的案例实现虽然采用的第一种方式,但如果需要使用第二种方式,只需要在mod.i中增加VTK对象作为输入的接口,然后将VTKRenderWindow对象的创建换到Python脚本中。

案例实现:

1.安装Python。用于从VTK源码编译生成Python下的VTK包

我安装的是Python3.10.8

https://www.python.org/ftp/python/3.10.8/python-3.10.8-amd64.exe

2.从GitLab克隆VTK9.2.2

git clone -b v9.2.2 https://gitlab.kitware.com/vtk/vtk.git

3.编译VTK源码

以下是编译的脚本文件configure_and_build_VTK_9_2_2.bat

Windows:


setlocal EnableDelayedExpansion

set BUILD_DIR=D:\code\VTK2023\vtk

set PYTHONHOME=C:\Python\Python310

set PYTHONPATH=C:\Python\Python310\Lib;C:\Python\Python310\DLLs;C:\Python\Python310\libs

:: Create build and install directories
mkdir "D:\code\VTK2023\vtk9.2.2\vtk_build"
mkdir "D:\code\VTK2023\vtk9.2.2\vtk_install"

:: Execute cmake from VS build tools to build VTK
cmake -S"D:\code\VTK2023\vtk" ^
-G"Visual Studio 17 2022" ^
-B"D:\code\VTK2023\vtk9.2.2\vtk_build" ^
-DVTK_VERSIONED_INSTALL:BOOL=ON ^
-DVTK_CUSTOM_LIBRARY_SUFFIX:STRING=9.2 ^
-DVTK_WRAP_PYTHON=ON ^
-DVTK_PYTHON_VERSION=3 ^
-DVTK_WHEEL_BUILD=ON ^
-DVTK_INSTALL_SDK:BOOL=ON ^
-DCMAKE_BUILD_TYPE:STRING=Debug ^
-DVTK_SMP_IMPLEMENTATION_TYPE=TBB ^
-DVTK_SMP_ENABLE_STDTHREAD=ON ^
-DVTK_SMP_ENABLE_SEQUENTIAL=ON ^
-DCMAKE_INSTALL_LIBDIR:PATH=lib ^
-DCMAKE_INSTALL_INCLUDEDIR:PATH=include ^
-DCMAKE_INSTALL_PREFIX:PATH="D:/code/VTK2023/vtk9.2.2/vtk_install"

:: Compile code using msbuild:
cd "D:\code\VTK2023\vtk9.2.2\vtk_build"
:: Create Release DLL's
"C:\Program Files\Microsoft Visual Studio\2022\Professional\Msbuild\Current\Bin\amd64\MSBuild.exe" VTK.sln /p:Configuration="Release"

"C:\Program Files\Microsoft Visual Studio\2022\Professional\Msbuild\Current\Bin\amd64\MSBuild.exe" INSTALL.vcxproj /p:Configuration="Release"

:: Build the wheel
python setup.py bdist_wheel

endlocal

出现以下错误:

D:\code\VTK2023\vtk9.2.2\vtk_build>python setup.py bdist_wheel
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

error: invalid command 'bdist_wheel'

bdist_wheel是一个用于构建Python包发布格式的命令,它可以生成.whl格式的二进制分发包。

解决方法:

pip install wheel
pip install --upgrade setuptools

最后在vtk_build目录下再来运行python setup.py bdist_wheel命令,会在dist文件夹中生成一个vtk-9.2.2.dev0-cp310-cp310-win_amd64.whl文件。

Install VTK的python包:

C:\Python\Python310\Scripts>pip install D:/code\VTK2023/vtk9.2.2/vtk_build/dist/vtk-9.2.2.dev0-cp310-cp310-win_amd64.whl

以一个C++的Demo验证编译的C++库文件是否可以正常使用:

头文件的目录为:D:/code/VTK2023/vtk9.2.2/vtk_install/vtk-9.2.2.data/headers/vtk-9.2

库文件的目录为:D:/code/VTK2023/vtk9.2.2/vtk_install/lib

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkFloatArray.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>

#include <array>


int main(int, char* [])
{
    vtkNew<vtkNamedColors> colors;

    std::array<std::array<double, 3>, 8> pts = { {{{0, 0, 0}},
                                                 {{1, 0, 0}},
                                                 {{1, 1, 0}},
                                                 {{0, 1, 0}},
                                                 {{0, 0, 1}},
                                                 {{1, 0, 1}},
                                                 {{1, 1, 1}},
                                                 {{0, 1, 1}}} };
    // The ordering of the corner points on each face.
    std::array<std::array<vtkIdType, 4>, 6> ordering = { {{{0, 3, 2, 1}},
                                                         {{4, 5, 6, 7}},
                                                         {{0, 1, 5, 4}},
                                                         {{1, 2, 6, 5}},
                                                         {{2, 3, 7, 6}},
                                                         {{3, 0, 4, 7}}} };

    // We'll create the building blocks of polydata including data attributes.
    vtkNew<vtkPolyData> cube;
    vtkNew<vtkPoints> points;
    vtkNew<vtkCellArray> polys;
    vtkNew<vtkFloatArray> scalars;

    // Load the point, cell, and data attributes.
    for (auto i = 0ul; i < pts.size(); ++i)
    {
        points->InsertPoint(i, pts[i].data());
        scalars->InsertTuple1(i, i);
    }
    for (auto&& i : ordering)
    {
        polys->InsertNextCell(vtkIdType(i.size()), i.data());
    }

    // We now assign the pieces to the vtkPolyData.
    cube->SetPoints(points);
    cube->SetPolys(polys);
    cube->GetPointData()->SetScalars(scalars);

    // Now we'll look at it.
    vtkNew<vtkPolyDataMapper> cubeMapper;
    cubeMapper->SetInputData(cube);
    cubeMapper->SetScalarRange(cube->GetScalarRange());
    vtkNew<vtkActor> cubeActor;
    cubeActor->SetMapper(cubeMapper);

    // The usual rendering stuff.
    vtkNew<vtkCamera> camera;
    camera->SetPosition(1, 1, 1);
    camera->SetFocalPoint(0, 0, 0);

    vtkNew<vtkRenderer> renderer;
    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(renderer);
    renWin->SetWindowName("Cube");

    vtkNew<vtkRenderWindowInteractor> iren;
    iren->SetRenderWindow(renWin);

    renderer->AddActor(cubeActor);
    renderer->SetActiveCamera(camera);
    renderer->ResetCamera();
    renderer->SetBackground(colors->GetColor3d("Cornsilk").GetData());

    renWin->SetSize(600, 600);

    // interact with data
    renWin->Render();
    iren->Start();

    getchar();
    return EXIT_SUCCESS;
}

以一个Python的Demo验证安装的VTK Python包是否可以正常使用:

# -*- coding: utf-8 -*-
import sys
# noinspection PyUnresolvedReferences
try:
    import vtkmodules.vtkInteractionStyle
except ImportError as e:
    sys.exit("cannot import vtk module:" + str(e))
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)


def main():
    colors = vtkNamedColors()

    # Create a sphere
    sphereSource = vtkSphereSource()
    sphereSource.SetCenter(0.0, 0.0, 0.0)
    sphereSource.SetRadius(5.0)
    # Make the surface smooth.
    sphereSource.SetPhiResolution(100)
    sphereSource.SetThetaResolution(100)

    mapper = vtkPolyDataMapper()
    mapper.SetInputConnection(sphereSource.GetOutputPort())

    actor = vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetColor(colors.GetColor3d("Cornsilk"))

    renderer = vtkRenderer()
    renderWindow = vtkRenderWindow()
    renderWindow.SetWindowName("Sphere")
    renderWindow.AddRenderer(renderer)
    renderWindowInteractor = vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)

    renderer.AddActor(actor)
    renderer.SetBackground(colors.GetColor3d("DarkGreen"))

    renderWindow.Render()
    renderWindowInteractor.Start()


if __name__ == '__main__':
    main()

会报以下错误:

D:\code\VTK2023\vtk9.2.2\swigtest\vtkwrapper>"C:/Python/Python310/python.exe" vtkPyDemo.py
cannot import vtk module:DLL load failed while importing vtkInteractionStyle: The specified module could not be found.

解决步骤:

(1)在python的根目录下新建一个文件夹ExtraDLLs,从以下链接下载tbb12.dll放到该文件夹中:

tbb12.dll : Free .DLL download. - DLLme.com

(2)在python的根目录下新建一个set_lib_path.pth文件,内容如下:

import os, sys; os.add_dll_directory(sys.prefix+'\\DLLs'); os.add_dll_directory(sys.prefix+'\\ExtraDLLs'); os.add_dll_directory(sys.prefix+'\\Lib\\site-packages\\vtkmodules\\Release')

Python使用.pth文件扩展环境路径,也就是查找vtk的库文件时,会根据这个文件里的路径查找。

再来运行就正常了。

4.下载swigwin-4.0.2。用于包装C++的vtk对象并传递给python。

SWIG download | SourceForge.net

5.Swig包装VTK对象传递给Python的Demo代码

在同一文件夹(vtkwrapper)下新建以下三个文件:mod.h, mod.cpp, mod.i

mod.h

#pragma once

#include <vtkRenderWindow.h>

vtkRenderWindow* getVTKRenderWindow();

mod.cpp

#include "mod.h"


vtkRenderWindow* getVTKRenderWindow()
{
    vtkRenderWindow* renderWindow = vtkRenderWindow::New();
    renderWindow->SetWindowName("Sphere");
	return renderWindow;
}

mod.i

%module mod

%{
#include "mod.h"
#include <vtkPythonUtil.h>
%}


%typemap(out) vtkRenderWindow* 
{
    $result = vtkPythonUtil::GetObjectFromPointer (static_cast<vtkRenderWindow*>($1));
}

%include "mod.h"

%init
%{
%}

6. 打包接口并生成相应的模块。

将swigwin-4.0.2放在与vtkwrapper文件夹同一层的目录,然后在vtkwrapper文件夹下,打开Windows PowerShell,输入以下命令将接口打包:

"..\swigwin-4.0.2\swig.exe" -c++ -python mod.i

执行完以上命令后,会生成mod.pymod_wrap.cxx文件。

把接口打包成模块:

首先新增setup.py文件:

from distutils.core import setup, Extension
 
 
mod_module = Extension(
    '_mod',
    sources=['mod_wrap.cxx', 'mod.cpp'],
             library_dirs=['D:/code/VTK2023/vtk9.2.2/vtk_install/lib'],
             libraries=['vtksys', 'vtkRenderingCore', 'vtkWrappingPythonCore3.10'],
    )

setup (name = 'mod',
       version = '0.1',
       author      = "beshar",
       description = """Simple swig example from docs""",
       include_dirs=['D:/code/VTK2023/vtk9.2.2/vtk_install/vtk-9.2.2.data/headers/vtk-9.2'],
       ext_modules = [mod_module],
       py_modules = ["mod"],
       )

然后执行以下命令:

"C:\Python\Python310\python.exe" setup.py build_ext --inplace

完成之后,会生成build文件夹,文件夹下中有一个python的库文件_mod.cp310-win_amd64.pyd

7.使用dumpbin查生成的pyd文件的依赖项。用于确定运行时的依赖。

::打开"VS2022 开发人员命令提示"
::切换目录到pyd文件所在的目录
D:

cd D:\code\VTK2023\vtk9.2.2\swigtest\vtkwrapper\build\lib.win-amd64-cpython-310

::执行dumpbin命令
dumpbin /dependents _mod.cp310-win_amd64.pyd

依赖性信息如下:

D:\code\VTK2023\vtk9.2.2\swigtest\vtkwrapper\build\lib.win-amd64-cpython-310>dumpbin /dependents _mod.cp310-win_amd64.pyd
Microsoft (R) COFF/PE Dumper Version 14.31.31107.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file _mod.cp310-win_amd64.pyd

File Type: DLL

  Image has the following dependencies:

    vtksys.dll
    vtkRenderingCore.dll
    vtkWrappingPythonCore3.10.dll
    python310.dll
    KERNEL32.dll
    VCRUNTIME140.dll
    api-ms-win-crt-heap-l1-1-0.dll
    api-ms-win-crt-stdio-l1-1-0.dll
    api-ms-win-crt-string-l1-1-0.dll
    api-ms-win-crt-runtime-l1-1-0.dll

  Summary

        1000 .data
        1000 .pdata
        2000 .rdata
        1000 .reloc
        1000 .rsrc
        3000 .text

可以看到pyd依赖VTK的C++库文件。

所以,我们将第3步(编译VTK源码)的VTK动态库文件拷贝到_mod.cp310-win_amd64.pyd同一文件夹下。

8.使用打包好的mod模块和getVTKRenderWindow接口

新建一个test.py文件来测试C++打包成python的getVTKRenderWindow接口

# -*- coding: utf-8 -*-

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)

import mod

def main():
    colors = vtkNamedColors()

    # Create a sphere
    sphereSource = vtkSphereSource()
    sphereSource.SetCenter(0.0, 0.0, 0.0)
    sphereSource.SetRadius(5.0)
    # Make the surface smooth.
    sphereSource.SetPhiResolution(100)
    sphereSource.SetThetaResolution(100)

    mapper = vtkPolyDataMapper()
    mapper.SetInputConnection(sphereSource.GetOutputPort())

    actor = vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetColor(colors.GetColor3d("Cornsilk"))

    renderer = vtkRenderer()
    renderWindow = mod.getVTKRenderWindow()
    renderWindow.AddRenderer(renderer)
    renderWindowInteractor = vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)

    renderer.AddActor(actor)
    renderer.SetBackground(colors.GetColor3d("DarkGreen"))

    renderWindow.Render()
    renderWindowInteractor.Start()


if __name__ == '__main__':
    main()
    
    

使用以下命令运行test.py文件:

D:\code\VTK2023\vtk9.2.2\swigtest\vtkwrapper\build\lib.win-amd64-cpython-310>"C:/Python/Python310/python.exe" test.py

运行正常,至此全部流程完成。

文章来源:https://blog.csdn.net/charce_you/article/details/134775057
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。