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

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

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


У меня есть Brisk исходный код (скачать), который я хотел бы создать и использовать в моем приложении 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 + vС++ express 2010 следующим образом:

    • cmake gui: источник: c:/brisk, build: c:/brisk/build
    • cmake gui: настроить для Visual Studio 10
    • cmake gui: используйте параметры по умолчанию и сгенерируйте (CMAKE_BACKWARDS_COMPATIBILITY, CMAKE_INSTALL_PREFIX, EXECUTABLE_OUTPUT_PATH, LIBRARY_OUTPUT_PATH)
    • VС++ Express 10: Измените Release и создайте решение, сгенерированное 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

Ответ 1

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

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

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

EDIT: Поскольку вы, похоже, приложили немало усилий для решения проблемы самостоятельно и опубликовали хороший вопрос, я решил дать более подробный ответ о том, как это сделать.

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

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

Установка boost

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

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

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

Далее вы хотите настроить визуальный проект студии для brisk.pyd. Если вы откроете визуальную студию, перейдите в New → Project, затем найдите вариант для Win32 Project. Настройте свое местоположение и т.д. И нажмите "ОК". В появившемся окне выберите тип проекта DLL, а затем отметьте флажок пустой проект.

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

В Visual Studio Solution Explorer щелкните правой кнопкой мыши на своем проекте и выберите свойства из появившегося меню. Это должно открыть страницы свойств для вашего проекта. Перейдите в раздел Linker → General и найдите раздел дополнительных библиотек. Вам нужно будет заполнить это с помощью путей к файлам .lib для boost, python и вашего brisk_static.lib. Обычно их можно найти в подкаталогах lib (или libs) где бы вы ни устанавливали библиотеки. Пути разделены точкой с запятой. Я добавил скриншот моих настроек ниже:

Additional library directories settings

Затем вам нужно будет получить визуальную студию для ссылки на .lib файлы. Эти разделы можно найти в поле Дополнительные зависимости раздела Linker → Input свойств. Опять же, это список с разделителями с запятой. Вам нужно добавить библиотеки для python (в моем случае это python27.lib, но это будет зависеть от версии) и brisk_static.lib. Они не требуют полного пути, как вы добавили, что на предыдущем этапе. Снова, вот скриншот:

additional dependencies settings

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

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

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

enter image description here

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

Обтекание существующих классов С++ с помощью boost:: python.

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

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

#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 С++, и выставлять его как BriskPatternPoint в python.

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

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

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

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

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

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

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

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

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

import brisk

patternPoint = brisk.BriskPatternPoint()
....    

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

Ответ 2

В случае, если кому-то это нужно, это то, что у меня есть до сих пор. В основном BriskFeatureDetector, который можно создать на Python, а затем вызвать detect. Большая часть этого только подтверждает/копирует то, что obmarg показал мне, но я добавил детали, которые полностью соответствуют библиотеке 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
  • next → Тип приложения DLL; пустой проект → завершено
  • наверху, измените Отладка Win32 на Отпустите Win32

Создать main.cpp в исходных файлах

Сделайте это до настроек проекта, чтобы опции С++ стали доступны в настройках проекта

#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/С++ → Общие сведения
    • добавьте в Дополнительные каталоги Include (настройте свои собственные пути python/brisk/и т.д.):

      C:\opencv2.2\include;

      C:\boost\boost_1_47;

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

      C:\python27\include;

  • Библиотеки (компоновщик)

    • Свойства конфигурации → Linker → Общие сведения
    • добавить в Дополнительные каталоги библиотек (настроить на собственные базовые пути python/brisk/etc.):

      C:\opencv2.2\lib;

      C:\boost\boost_1_47\lib;

      C:\brisk\win32\lib;

      C:\python27\Libs;

    • Свойства конфигурации → Linker → Input

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

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

      agast_static.lib; brisk_static.lib;

      python27.lib;

  • .pyd output вместо .dll

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

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

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

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

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

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