如何从 c/c++ 源代码获取适用于 Windows 的 python .pyd? (更新:如果你想要的话,现在在 Python 中很活跃)

Posted

技术标签:

【中文标题】如何从 c/c++ 源代码获取适用于 Windows 的 python .pyd? (更新:如果你想要的话,现在在 Python 中很活跃)【英文标题】:How to get a python .pyd for Windows from c/c++ source code? (update: brisk now in Python in case that's what you want) 【发布时间】:2012-05-26 17:42:44 【问题描述】:

如何从 C/C++ 扩展源代码获取 windows 的 pyd 文件(或我可以导入 Python 的其他项目)?

编辑: 我想使用的特定库 (BRISK) 包含在 OpenCV 2.4.3 中,所以我对这项技能的需求暂时消失了存在。如果您来这里寻找 BRISK,这是我发布的一个简单的BRISK in Python demo。


我有 Brisk 源代码 (download),我想在我的 python 应用程序中构建和使用它。我已经生成了一个 brisk.pyd 文件......但它是 0 字节。如果有更好/替代方法来瞄准 brisk.pyd 文件,那么我当然也愿意接受。

编辑:请忽略下面我原始问题中的所有尝试,并查看我的答案,这是通过 obmarg 的详细演练实现的

我哪里出错了?

    没有库路径的 Distutils: 首先,我尝试使用 distutils 和以下 setup.py 构建源代码(我刚刚开始学习 distutils,所以这是在黑暗中拍摄)。 BRISK源代码的结构在这个问题的底部供参考。

    from distutils.core import setup, Extension
    module1 = Extension('brisk',
        include_dirs = ['include', 'C:/opencv2.4/build/include', 'C:/brisk/thirdparty/agast/include'],
        #libraries = ['agast_static', 'brisk_static'],
        #library_dirs = ['win32/lib'],
        sources = ['src/brisk.cpp'])
    setup (name = 'BriskPackage',
        ext_modules = [module1])
    

    这立即给了我以下几行和构建文件夹中某处的 0 字节 brisk.pyd。这么近?

    running build
    running build_ext
    

    带库路径的 Distutils: 暂缓该尝试。所以我添加了上面 setup.py 中注释掉的两个库行。在我收到此链接错误之前,这似乎没问题:

    creating build\lib.win32-2.7
    C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN\link.exe /DLL /nologo /INCREMENTAL:NO /LIBPATH:win32/lib /LIB
    PATH:C:\Python27_32bit\libs /LIBPATH:C:\Python27_32bit\PCbuild agast_static.lib brisk_static.lib /EXPORT:initbrisk build
    \temp.win32-2.7\Release\src/brisk.obj /OUT:build\lib.win32-2.7\brisk.pyd /IMPLIB:build\temp.win32-2.7\Release\src\brisk.
    lib /MANIFESTFILE:build\temp.win32-2.7\Release\src\brisk.pyd.manifest
    LINK : error LNK2001: unresolved external symbol initbrisk
    build\temp.win32-2.7\Release\src\brisk.lib : fatal error LNK1120: 1 unresolved externals
    error: command '"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN\link.exe"' failed with exit status 1120
    

    不受控制的甩尾:我认为可能需要构建库,所以我用 cmake + mingw - mingw + vc++ express 2010 做了一个速成课程(很多崩溃),如下所示:

    cmake gui: 来源:c:/brisk,构建:c:/brisk/build cmake gui: 为 Visual Studio 10 配置 cmake gui:使用默认选项并生成(CMAKE_BACKWARDS_COMPATIBILITY、CMAKE_INSTALL_PREFIX、EXECUTABLE_OUTPUT_PATH、LIBRARY_OUTPUT_PATH)

    VC++ Express 10: 更改为发布并构建由 cmake 生成的解决方案,并获得大约 20 页看起来像非关键警告的内容,然后全部成功。注意 - 不会由此生成任何 dll。它确实会生成以下与下载中包含的类似大小的库:

    win32/lib/Release/
        agast_static.lib
        brisk_static.lib
    

    进一步甩尾。

相关BRISK源文件结构供参考:

build/ (empty)
include/brisk/
    brisk.h
    hammingsse.hpp
src
    brisk.cpp
    demo.cpp
thirdparty/agast/
    include/agast/
        agast5_8.h ....
        cvWrapper.h
    src/
        agast5_8.cc ...
    CMakeLists.txt
win32/
    bin/
        brisk.mexw32
        opencv_calib3d220.dll ...
    lib/
        agast_static.lib
        brisk_static.lib
CMakeLists.txt
FindOpenCV.cmake
Makefile

【问题讨论】:

Steps to get from the BRISK download to a working OpenCV (~2.4) FeatureDetector and DescriptionExtractor in a Python cv2 interface on Windows的可能重复 请勿转发:meta.stackexchange.com/questions/7046/… 最后一个没有回复或cmets,所以我将其删除。这个更具体,所以希望有人能指出我出错的地方。为了让其他看到这一点的人清楚,这个问题的第一个文本是原始帖子的链接。我不是想偷偷溜进去。 @Joe 同意,不过。我应该完全重做第一个而不是制作一个新的。感谢您提供元链接。 【参考方案1】:

您确定这个轻快的库甚至可以导出 python 绑定吗?我在源代码中看不到对它的任何引用——它甚至似乎都没有导入 python 头文件。这当然可以解释为什么到目前为止你还没有取得太大的成功——你不能只编译普通的 C++ 代码并期望 python 与之交互。

我认为您的第二个 distutils 示例最接近正确 - 它显然正在编译内容并进入链接器阶段,但随后您遇到了此错误。该错误仅意味着它找不到名为 initbrisk 的函数,我猜这将是模块的*** init 函数。这再次表明您正在尝试从不适合它的代码编译 python 模块。

如果您想自己将 C++ 代码包装在 python 包装器中,您可以查看the official documentation on writing c/c++ extensions。或者,您可以查看 boost::python、SIP 或 shiboken,它们尝试在某种程度上(或完全)自动化从 C++ 代码制作 python 扩展的过程。

编辑:由于您似乎已经为自己解决问题付出了相当大的努力并发布了一个很好的问题,因此我决定就如何进行此操作提供更详细的回复。

使用 boost::python 包装 C++ 库的快速教程

就我个人而言,我只使用过 boost::python 来处理这样的事情,所以我会试着给你一个很好的总结,告诉你如何去做。我将假设您使用的是 Visual C++ 2010。我还将假设您已安装 32 位版本的 python,因为我相信 boost pro 库仅提供 32 位二进制文​​件。

安装提升

首先,您需要获取 boost 库的副本。最简单的方法是从the boost pro website 下载安装程序。这些应该安装在 Windows 上使用 boost c++ 库所需的所有头文件和二进制文件。记下将这些文件安装到的位置,因为稍后您将需要它们 - 最好安装到其中没有空格的路径。为方便起见,我假设您将这些文件放在 C:\boost 中,但您可以将其替换为您实际使用的路径。

或者,您可以关注these instructions 从源代码构建提升。我不能 100% 确定,但可能是您需要 这样做以获得与您已安装的 python 版本兼容的 boost::python 版本。

设置 Visual Studio 项目

接下来,您需要为 brisk.pyd 设置一个 Visual Studio 项目。如果您打开 Visual Studio,请转到新建 -> 项目,然后找到 Win32 项目的选项。设置您的位置等,然后单击确定。在出现的向导中选择一个 DLL 项目类型,然后勾选空项目复选框。

现在您已经创建了项目,您需要设置包含和库路径以允许您使用 python、boost::python 和 brisk.lib 文件。

在 Visual Studios 解决方案资源管理器中,右键单击您的项目,然后从出现的菜单中选择属性。这应该会为您的项目打开属性页。转到 Linker -> General 部分并查找 Additional Library Directories 部分。您需要使用 boost、python 和您的 brisk_static.lib.lib 文件的路径填写此文件。通常这些可以在lib(或libs)的子目录中找到 无论您安装了这些库。路径用分号分隔。我在下面附上了我的设置截图:

接下来,您需要让 Visual Studio 链接到 .lib 文件。这些部分可以在属性的 Linker -> Input 部分的 Additional Dependencies 字段中找到。同样,它是一个分号分隔的列表。您应该需要为 python 添加库(在我的情况下这是python27.lib,但这会因版本而异)和brisk_static.lib。这些不需要您在上一阶段添加的完整路径。同样,这是一个屏幕截图:

可能还需要添加 boost_python 库文件,但我认为 boost 使用了一些头文件魔术来为您省去麻烦。如果我不正确,请查看您提升库路径的名称类似于boost_python-vc100-mt.lib 的文件并将其添加进去。

最后,您需要设置包含路径以允许您的项目包含相关的 C++ 头文件。要使相关设置出现在项目属性中,您需要将 .cpp 文件添加到您的项目中。右键单击解决方案资源管理器中的源文件文件夹,然后转到添加新项目。选择一个 C++ 文件 (.cpp) 并将其命名为 main.cpp(或您想要的任何其他名称)。

接下来,返回您的项目属性并转到 C/C++ -> 常规。在附加库目录下,您需要添加 brisk、python 和 boost 的包含路径。再一次,分号用于分隔符,这是一个屏幕截图:

我怀疑您可能需要更新这些设置以包括 opencv2 和 agast 库,但我将把它留给您自己弄清楚 - 它应该是相同的过程。

用 boost::python 包装现有的 c++ 类。

现在有点棘手 - 实际上是编写 C++ 来将您的轻量库包装在 boost python 中。你可以找到这个here 的教程,但我也会试着复习一下。

这将发生在您之前创建的main.cpp 文件中。首先,在文件顶部添加您需要的相关包含语句:

#include <brisk/brisk.h>
#include <Python.h>
#include <boost/python.hpp>

接下来,您需要声明您的 python 模块。我假设你希望这被称为轻快,所以你做这样的事情:

BOOST_PYTHON_MODULE(brisk)


这应该告诉 boost::python 创建一个名为 brisk 的 python 模块。

接下来,它只是遍历所有要包装的类和结构并用它们声明 boost python 类的情况。类的声明都应该包含在 brisk.h 中。您应该只包装类的公共成员,而不是任何受保护或私有成员。举个简单的例子,我在这里做了几个结构:

BOOST_PYTHON_MODULE(brisk)

    using namespace boost::python;

    class_< cv::BriskPatternPoint >( "BriskPatternPoint" )
         .def_readwrite("x", &cv::BriskPatternPoint::x)
         .def_readwrite("y", &cv::BriskPatternPoint::y)
         .def_readwrite("sigma", &cv::BriskPatternPoint::sigma);

    class< cv::BriskScaleSpace >( "BriskScaleSpace", init< uint8_t >() )
         .def( "constructPyramid", &cv::BriskScaleSpace::constructPyramid );

这里我已经包装了 cv::BriskPatternPoint 结构和 cv::BriskScaleSpace 类。一些简单的解释:

class_&lt; cv::BriskPatternPoint &gt;( "BriskPatternPoint" ) 告诉 boost::python 声明一个类,使用 cv::BriskPatternPoint C++ 类,并在 python 中将其公开为 BriskPatternPoint

.def_readwrite("y", &amp;cv::BriskPatternPoint::y)BriskPatternPoint 类添加了一个可读写的属性。该属性名为 y,并将映射到 BriskPatternPoint::y c++ 字段。

class&lt; cv::BriskScaleSpace &gt;( "BriskScaleSpace", init&lt; uint8_t &gt;() ) 声明了另一个类,这次是BriskScaleSpace,但也提供了一个接受 uint8_t 的构造函数(一个无符号字节 - 它应该只映射到 python 中的一个整数,但我会小心不要传入一个大于 255 字节 - 我不知道在那种情况下会发生什么)

下面的.def 行只是声明了一个函数 - boost::python 应该(我认为)能够自动确定函数的参数类型,所以你不需要提供它们。

值得注意的是,我实际上并没有编译任何这些示例 - 它们很可能根本不起作用。

无论如何,要让它在 python 中完全工作,它应该只是对你希望从 python 访问的每个结构、类、属性和函数做类似的事情——这可能是一项相当耗时的任务!

如果你想看到另一个例子,我做了this here 来结束this class

构建和使用扩展

Visual Studio 应该负责构建扩展 - 然后使用它只是获取 .DLL 并将其重命名为 .pyd 的情况(您可以让 VS 为您执行此操作,但我会留给你)。

然后你只需要将你的 python 文件复制到你的 python 路径上的某个地方(例如site-packages),导入它并使用它!

import brisk

patternPoint = brisk.BriskPatternPoint()
....    

无论如何,我已经花了一个小时左右的时间来写这篇文章 - 所以我要在这里停下来。如果我遗漏了任何内容或有任何不清楚的地方,我深表歉意,但我主要是凭记忆这样做的。希望它对您有所帮助。如果您需要澄清任何事情,请发表评论或提出其他问题。

【讨论】:

你的第一段让我觉得你知道你在说什么,也意识到我缺少比我想象的更多的理解 :) 实际上我已经阅读了扩展文档并且我没有无法将它与我正在尝试做的事情联系起来。我在扩展 python 本身吗?我以为没有。在 C 代码中包含 python?我以为没有。我从未见过看起来像我正在尝试做的事情,但我可能只是误解了它。看来我需要一步一步地检查文档中任何说 C++ 的内容,直到我掌握所有部分。 感谢您的指导。我有很多阅读和练习要做。 @kobejohn 没问题!我刚刚写了一个 大量 编辑,其中包含更多细节 - 希望对您有所帮助。 哇!好的,请给我时间来完成。看起来正是我所需要的。 今天必须停下来,但这必须是正确的方法(或一种正确的方法)才能快速工作。谢谢你。我终于到了可以构建项目的地步(只是您示例中的模式点),它生成了 .exp、.pyd、.lib、.pdb 文件。但是,当我导入 Brisk.pyd 时,我得到“ImportError:DLL 加载失败:找不到指定的模块。”明天我会调查它(依赖步行者?..我也是多年前最后一次使用的)。你有什么快速的建议吗?【参考方案2】:

如果有人需要,这是我目前所拥有的。基本上是一个 BriskFeatureDetector,可以在 Python 中创建,然后调用 detect。其中大部分只是确认/复制 obmarg 向我展示的内容,但我已经添加了一直到 pyd 库的详细信息。

detect 方法对我来说仍然不完整,因为它不转换数据类型。任何知道改善这一点的好方法的人,请做!例如,我确实找到了this library,它似乎将 numpy ndarray 转换为 cv::Mat,但我现在没有时间弄清楚如何集成它。还有其他数据类型需要转换。

安装OpenCV 2.2

对于下面的设置,我安装到C:\opencv2.2 2.4 版对 API 或实现进行了更改,这给我带来了问题(可能是新的算法对象?)所以我坚持使用 BRISK 开发的 2.2。

使用 Boost Python 安装 Boost

对于下面的设置,我安装到C:\boost\boost_1_47

创建一个 Visual Studio 10 项目:

新项目--> win32 对于下面的设置,我将其命名为brisk next --> DLL 应用程序类型;空项目 --> 完成 在顶部,从 Debug Win32 更改为 Release Win32

在源文件中创建 main.cpp

在项目设置之前执行此操作,以便 C++ 选项在项目设置中可用

#include <boost/python.hpp>
#include <opencv2/opencv.hpp>
#include <brisk/brisk.h>

BOOST_PYTHON_MODULE(brisk)

    using namespace boost::python;

    //this long mess is the only way I could get the overloaded signatures to be accepted
    void (cv::BriskFeatureDetector::*detect_1)(const cv::Mat&,
                std::vector<cv::KeyPoint, std::allocator<cv::KeyPoint>>&,
                const cv::Mat&) const
                = &cv::BriskFeatureDetector::detect;
    void (cv::BriskFeatureDetector::*detect_vector)(const std::vector<cv::Mat, std::allocator<cv::Mat>>&,
                std::vector< std::vector< cv::KeyPoint, std::allocator<cv::KeyPoint>>, std::allocator< std::vector<cv::KeyPoint, std::allocator<cv::KeyPoint>>>>&,
                const std::vector<cv::Mat, std::allocator<cv::Mat>>&) const
                = &cv::BriskFeatureDetector::detect;

    class_< cv::BriskFeatureDetector >( "BriskFeatureDetector", init<int, int>())
        .def( "detect", detect_1)
    ;

项目设置(右键项目-->属性):

    包含/标题

    配置属性 --> C/C++ --> 常规

    添加到其他包含目录(调整为您自己的python/brisk/等基本路径):

    C:\opencv2.2\include;

    C:\boost\boost_1_47;

    C:\brisk\include;C:\brisk\thirdparty\agast\include;

    C:\python27\include;

    库(链接器)

    配置属性 --> 链接器 --> 常规

    添加到附加库目录(调整为您自己的python/brisk/等基本路径):

    C:\opencv2.2\lib;

    C:\boost\boost_1_47\lib;

    C:\brisk\win32\lib;

    C:\python27\Libs;

    配置属性 --> 链接器 --> 输入

    添加到Additional Dependencies(调整到你自己的python/brisk/等基本路径):

    opencv_imgproc220.lib;opencv_core220.lib;opencv_features2d220.lib;

    agast_static.lib; brisk_static.lib;

    python27.lib;

    .pyd 输出而不是 .dll

    配置属性 --> 常规 将目标扩展名更改为 .pyd

必要时构建和重命名

右键单击解决方案并构建/重建 您可能需要将输出从“Brisk.pyd”重命名为“brisk.pyd”,否则 python 会提示您无法加载 DLL 的错误 通过将 brisk.pyd 放入站点包或放入链接到其路径的 .pth 文件,使 python 可以使用 brisk.pyd

更新路径环境变量

在 Windows 设置中,确保您的路径中包含以下内容(再次调整为您的路径):

`C:\boost\boost_1_47\lib;C:\brisk\win32\bin`

【讨论】:

以上是关于如何从 c/c++ 源代码获取适用于 Windows 的 python .pyd? (更新:如果你想要的话,现在在 Python 中很活跃)的主要内容,如果未能解决你的问题,请参考以下文章

3 个最新值,但仅适用于特定用户

如何从 Maven 获取适用于 Android 的 JAR

使用 sntp (windows c++) 从服务器获取时间/日期

如何使用 Swift 从适用于 ios 应用程序的 HealthKit 获取循环持续时间

window.navigate 仅适用于 Internet Explorer

适用于 iOS 的 JavaScript 解包库