Как получить Python .pyd для Windows из исходного кода c/ C++? (обновление: оживленный теперь в Python на тот случай, если вы этого хотите)

Как получить исходный код расширения C/C++ в pyd-файл для Windows (или другой элемент, который я могу импортировать в Python)?

редактировать: конкретная библиотека, которую я хотел использовать (BRISK), была включена в OpenCV 2.4.3, поэтому моя потребность в этом умении на время исчезла. Если вы пришли сюда в поисках BRISK, вот простая демонстрация BRISK в Python, которую я разместил.


У меня есть исходный код Brisk ( скачать), который я хотел бы построить и использовать в своем приложении на Python. Я дошел до генерации файла brisk.pyd... но это было 0 байтов. Если есть лучший / альтернативный способ нацеливания на файл brisk.pyd, то, конечно, я открыт для этого.

редактировать: пожалуйста, проигнорируйте все попытки в моем оригинальном вопросе ниже и посмотрите мой ответ, который стал возможным благодаря подробному прохождению obmarg

Куда я иду не так?

  1. 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
    
  2. 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
    
  3. Неконтролируемое колебание: я подумал, что, возможно, библиотеки нужно было собрать, поэтому я прошел ускоренный курс (много аварий) с помощью cmake + mingw - mingw + vC++ express 2010 следующим образом:

    • cmake gui: источник: c:/ оживленный, сборка: c:/ оживленный / сборка
    • cmake gui: настройка для Visual Studio 10
    • cmake gui: использовать параметры по умолчанию и генерировать (CMAKE_BACKWARDS_COMPATIBILITY, CMAKE_INSTALL_PREFIX, EXECUTABLE_OUTPUT_PATH, LIBRARY_OUTPUT_PATH)
    • VC++ Express 10. Перейдите на Release и создайте решение, созданное cmake, и получите около 20 страниц, которые выглядят как некритические предупреждения, за которыми следуют все успешные. Примечание - это не генерирует dll. Он генерирует следующие библиотеки схожего размера с библиотеками, включенными в загрузку:

      win32/lib/Release/
          agast_static.lib
          brisk_static.lib
      
  4. Дальше хлопаем.

Соответствующая структура исходного файла 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

2 ответа

Решение

Вы уверены, что эта оживленная библиотека экспортирует даже привязки Python? Я не вижу ссылки на него в исходном коде - он даже не импортирует заголовочные файлы python. Это, безусловно, объясняет, почему вы до сих пор не добились большого успеха - вы не можете просто скомпилировать простой код C++ и ожидать, что Python будет взаимодействовать с ним.

Я думаю, что ваш второй пример distutils наиболее близок к правильному: он явно компилирует вещи и попадает на стадию компоновщика, но затем вы сталкиваетесь с этой ошибкой. Эта ошибка просто означает, что она не может найти функцию с именем initbrisk, которая, я полагаю, была бы функцией инициализации верхнего уровня для модуля. Опять же, это говорит о том, что вы пытаетесь скомпилировать модуль Python из кода, который не предназначен для этого.

Если вы хотите обернуть код C++ в оболочку Python самостоятельно, вы можете взглянуть на официальную документацию по написанию расширений c/ C++. В качестве альтернативы вы можете взглянуть на boost::python, SIP или shiboken, которые пытаются несколько (или полностью) автоматизировать процесс создания расширений python из кода C++.

РЕДАКТИРОВАТЬ: Поскольку вы, кажется, приложили немало усилий, чтобы решить проблему самостоятельно и опубликовали хороший вопрос, я решил дать более подробный ответ о том, как это сделать.

Краткое руководство по переносу библиотек C++ с использованием boost::python

Лично я когда-либо использовал boost::python для подобных вещей, поэтому я постараюсь дать вам краткое изложение того, как это сделать. Я собираюсь предположить, что вы используете Visual C++ 2010. Я также собираюсь предположить, что у вас установлена ​​32-битная версия python, так как я считаю, что библиотеки boost pro предоставляют только 32-битные двоичные файлы.

Установка буста

Сначала вам нужно взять копию библиотеки наддува. Самый простой способ сделать это - загрузить установщик с веб-сайта boost pro. Они должны установить все заголовочные и двоичные файлы, необходимые для использования библиотеки boost C++ в windows. Обратите внимание на то, куда вы устанавливаете эти файлы, так как они понадобятся вам позже - может быть, лучше установить по пути без пробела. Для простоты я собираюсь предположить, что вы поместили эти файлы в C:\boost, но вы можете заменить его на путь, который вы фактически использовали.

В качестве альтернативы, вы можете следовать этим инструкциям для создания надстройки из источника. Я не уверен на 100%, но, возможно, вам нужно сделать это, чтобы получить версию boost::python, совместимую с версией python, которую вы установили.

Настройка визуального студийного проекта

Далее вы захотите настроить визуальный студийный проект для brisk.pyd. Если вы открываете Visual Studio, перейдите в New -> Project, а затем найдите вариант для Win32 Project. Укажите свое местоположение и т. Д. И нажмите ОК. В появившемся мастере выберите тип проекта DLL и установите флажок пустого проекта.

Теперь, когда вы создали свой проект, вам нужно настроить пути include & library, чтобы позволить вам использовать python, boost::python и файл brisk.lib.

В обозревателе решений Visual Studios щелкните правой кнопкой мыши свой проект и выберите свойства в появившемся меню. Это должно открыть страницы свойств для вашего проекта. Перейдите в раздел Linker -> General и найдите раздел "Дополнительные каталоги библиотек". Вам нужно будет заполнить это путями к .lib файлы для повышения, Python и ваш brisk_static.lib, Обычно их можно найти в lib (или же libs) подкаталоги, где вы установили библиотеки. Пути разделяются точками с запятой. Я прикрепил скриншот моих настроек ниже:

Дополнительные настройки каталогов библиотеки

Далее вам нужно получить Visual Studio для ссылки на файлы.lib. Эти разделы можно найти в поле Дополнительные зависимости в разделе Свойства компоновщика -> Вход. Опять же это список, разделенный точкой с запятой. Вам нужно добавить в библиотеки для Python (в моем случае это python27.lib но это будет зависеть от версии) и brisk_static.lib, Они не требуют полного пути, как вы добавили его на предыдущем этапе. Опять же, вот скриншот:

настройки дополнительных зависимостей

Вам также может понадобиться добавить файл библиотеки boost_python, но я думаю, что boost использует некоторую магию файла заголовка, чтобы избавить вас от проблем. Если я ошибаюсь, взгляните на ваш путь к библиотеке для файла с именем, аналогичным boost_python-vc100-mt.lib и добавить это в.

Наконец, вам нужно настроить пути включения, чтобы ваш проект мог включать соответствующие заголовочные файлы C++. Чтобы соответствующие параметры отображались в свойствах проекта, вам нужно добавить файл.cpp в ваш проект. Щелкните правой кнопкой мыши папку с исходными файлами в обозревателе решений и перейдите к добавлению нового элемента. Выберите файл C++ (.cpp) и назовите его main.cpp (или как хотите).

Затем вернитесь к свойствам вашего проекта и перейдите на C/C++ -> General. Под каталогом дополнительных библиотек вам нужно добавить пути включения для brisk, python и boost. Снова точки с запятой для разделителей, и снова вот скриншот:

Я подозреваю, что вам, возможно, потребуется обновить эти настройки, чтобы включить в них также библиотеки opencv2 и agast, но я оставлю это как задачу для вас, чтобы выяснить - это должен быть почти такой же процесс.

Обертывание существующих классов C++ с boost::python.

Теперь немного сложнее - на самом деле писать C++, чтобы обернуть вашу оживленную библиотеку в Boost Python. Вы можете найти учебник для этого здесь, но я попытаюсь пройтись по нему немного.

Это будет происходить в main.cpp файл, который вы создали ранее. Сначала добавьте соответствующие операторы включения в верхней части файла:

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

Далее вам нужно объявить свой модуль Python. Я предполагаю, что вы хотите, чтобы это называлось оживленным, поэтому вы делаете что-то вроде этого:

BOOST_PYTHON_MODULE(brisk)
{
}

Это должно сказать boost::python для создания модуля python с именем brisk,

Далее это просто пример прохождения всех классов и структур, которые вы хотите обернуть, и объявление с ними классов повышения 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_< cv::BriskPatternPoint >( "BriskPatternPoint" ) говорит boost::python объявить класс, используя cv::BriskPatternPoint Класс C++, и выставить его как BriskPatternPoint в питоне.

.def_readwrite("y", &cv::BriskPatternPoint::y) добавляет читаемое и записываемое свойство к BriskPatternPoint учебный класс. Свойство называется y и будет отображаться на BriskPatternPoint::y поле с ++.

class< cv::BriskScaleSpace >( "BriskScaleSpace", init< uint8_t >() ) объявляет другой класс, на этот раз BriskScaleSpace но также предоставляет конструктор, который принимает uint8_t (беззнаковый байт - который должен просто отображаться в целое число в python, но я бы не стал передавать один больше 255 байтов - я не знаю, что произойдет в этом ситуация)

Следующие .def line просто объявляет функцию - boost::python должен (я думаю) иметь возможность автоматически определять типы аргументов функций, поэтому вам не нужно их предоставлять.

Вероятно, стоит отметить, что я на самом деле не скомпилировал ни один из этих примеров - они вполне могут не работать вообще.

В любом случае, чтобы это полностью работало в python, нужно просто выполнить аналогичные действия для каждой структуры, класса, свойства и функции, которые вы хотите получить доступ из python - что потенциально может занять довольно много времени!

Если вы хотите увидеть другой пример этого в действии, я сделал это здесь, чтобы завершить этот класс

Сборка и использование расширения

Visual Studio должна позаботиться о создании расширения - тогда использовать его - это просто случай взять.DLL и переименовать его в.pyd (вы можете заставить VS сделать это за вас, но я оставлю это на ваше усмотрение).

Тогда вам просто нужно скопировать ваш файл Python куда-нибудь на вашем пути Python (site-packages например), импортируйте и используйте!

import brisk

patternPoint = brisk.BriskPatternPoint()
....    

Во всяком случае, я провел хороший час или около того, записывая это - так что я собираюсь остановиться здесь. Извиняюсь, если я что-то пропустил или что-то не понятно, но я делаю это в основном по памяти. Надеюсь, это помогло вам. Если вам нужно что-то прояснить, просто оставьте комментарий или задайте другой вопрос.

На случай, если это кому-то нужно, это то, что я имею до сих пор. По сути, это BriskFeatureDetector, который можно создать в Python, а затем определить вызываемый. Большая часть этого просто подтверждает / копирует то, что показал мне Обмарг, но я добавил детали, которые попадают в библиотеку pyd.

Метод обнаружения все еще неполон для меня, так как он не конвертирует типы данных. Любой, кто знает хороший способ улучшить это, пожалуйста, сделайте! Я нашел, например, эту библиотеку, которая, кажется, конвертирует numpy ndarray в cv::Mat, но у меня нет времени, чтобы выяснить, как интегрировать его сейчас. Есть и другие типы данных, которые необходимо преобразовать.

Установите OpenCV 2.2

  • для настройки ниже, я установил C:\opencv2.2
  • Что-то в API или реализации изменилось в версии 2.4, что вызвало у меня проблемы (может быть, новый объект Algorithm?), Поэтому я остановился на версии 2.2, с которой был разработан BRISK.

Установить Boost с Boost Python

  • для настройки ниже, я установил C:\boost\boost_1_47

Создайте проект Visual Studio 10:

  • новый проект -> win32
  • для настройки ниже, я назвал его brisk
  • далее -> тип приложения 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)
    ;
}

Настройки проекта (щелкните правой кнопкой мыши на проекте -> Свойства):

  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;

  2. Библиотеки (линкер)

    • Свойства конфигурации -> Линкер -> Общие
    • добавить в каталог дополнительных библиотек (скорректировать свои собственные базовые пути python / brisk / etc):

      C:\opencv2.2\lib;

      C:\boost\boost_1_47\lib;

      C:\brisk\win32\lib;

      C:\python27\Libs;

    • Свойства конфигурации -> Линкер -> Ввод

    • добавьте к дополнительным зависимостям (настройте свои собственные базовые пути python / brisk / etc):

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

      agast_static.lib; brisk_static.lib;

      python27.lib;

  3. вывод.pyd вместо.dll

    • Свойства конфигурации -> Общие
    • изменить расширение цели на.pyd

Сборка и переименование в случае необходимости

  • Щелкните правой кнопкой мыши на решении и соберите / перестройте
  • вам может понадобиться переименовать вывод из "Brisk.pyd" в "brisk.pyd", иначе python выдаст вам ошибки из-за невозможности загрузить DLL
  • Сделайте brisk.pyd доступным для python, поместив его в пакеты сайта или поместив файл.pth, который ссылается на его путь

Обновить переменную среды Path

  • В настройках Windows убедитесь, что в ваш путь включены следующие данные (опять же, подгоните их под свои пути):

    `C:\boost\boost_1_47\lib;C:\brisk\win32\bin`
    
Другие вопросы по тегам