Как получить 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
Куда я иду не так?
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:/ оживленный, сборка: 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
Дальше хлопаем.
Соответствующая структура исходного файла 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)
;
}
Настройки проекта (щелкните правой кнопкой мыши на проекте -> Свойства):
Включает / Заголовки
- Свойства конфигурации -> 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 / 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;
вывод.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`