最近在使用python调用c语言写的dll,遇到不少问题记录一下。
就比如说你生成的dll通过在linux ldd 看到有其他的依赖
但是如果直接调用你的dll文件
cdll.LoadLibrary(param.fcdll)
可能会有如下的错误
OSError: /home/wpr/bin/anaconda4.3.0/lib/python3.6/lib-dynload/../../libgomp.so: version
GOMP_4.0’ not found (required by`
或者是类似
ctypes loading a c shared library that has dependencies 这里面的错误
这估计是因为使用python调用的时候会搜索python环境里面自以为有的库,总之应该要强制指定动态库的位置这样才可以
比如改成
CDLL("/usr/lib/x86_64-linux-gnu/libgomp.so.1", mode=RTLD_GLOBAL)
self.cdll = cdll.LoadLibrary(param.fcdll)
这样就没问题了。 另外上面的问题在windows下面是没有的
这个在linux下面好像很直接,在windows下面就需要个dllexport,具体看代码
#include
#include
#ifdef _WIN32
#define DLL_API _declspec(dllexport)
#else
#define DLL_API
#endif // _WIN32
DLL_API void test(){
...
}
首先在.h文件中写上必要的结构体,函数,设为A。A这个函数是在.cpp文件中一个函数中调用的,它实际上是在.c中。
.cpp 引用.h的时候要加上
extern "C" {
#include "xxx.h"
}
void test(){
A();
}
这样才行。
另外上传我这次写程序需要的cmake文件,感觉还算是比较通用
cmake_minimum_required(VERSION 2.8)
project(python_use)
# target_compile_options(myLib PRIVATE -fPIC)
#
#add_compile_options(-fpic)
#
#set(CMAKE_C_FLAGS“$ {CMAKE_C_FLAGS} -fpic”)
#set(CMAKE_CXX_FLAGS“$ {CMAKE_CXX_FLAGS} -fpic”)
IF (WIN32)
MESSAGE(STATUS "Now is windows")
ELSEIF (APPLE)
MESSAGE(STATUS "Now is Apple systens.")
ELSEIF (UNIX)
add_compile_options(-fPIC)
ENDIF ()
find_package(OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
#set(EXCUTABLE_OUPT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(SRC_DIR ${PROJECT_SOURCE_DIR}/)
include_directories(${SRC_DIR}/src)
include_directories(${CMAKE_CURRENT_LIST_DIR}/src/liblinear/)
include(${CMAKE_CURRENT_LIST_DIR}/src/liblinear/liblinear.cmake)
#include_directories()
#LINK_DIRECTORIES()
file(GLOB_RECURSE CURRENT_HEADERS ${SRC_DIR}/src/*.h)
file(GLOB CURRENT_SOURCES ${SRC_DIR}/src/*.cpp ${SRC_DIR}/src/*.c)
add_library(python_use SHARED ${CURRENT_SOURCES} )
target_link_libraries(python_use liblinear)
第二次在后面改了些
file(GLOB_RECURSE CURRENT_HEADERS ${SRC_DIR}/src/*.h ${SRC_DIR}/lbf/*.h)
file(GLOB CURRENT_SOURCES ${SRC_DIR}/src/*.cpp ${SRC_DIR}/src/*.c )
file(GLOB LBF_SRCS ${SRC_DIR}/src/lbf_src/*.cpp )
source_group(lbf FILES ${LBF_SRCS})
# 这个 lbf srcs 也需要加入add library中
add_library(3000fps_python_use SHARED ${CURRENT_SOURCES} ${LBF_SRCS})
target_link_libraries(3000fps_python_use liblinear ${OpenCV_LIBS} )
其实基本框架配好之后数据交换就是中投戏了。不过关于ctypes的介绍比较多,这里就介绍一些难搜到的东西。
比如你的C中是这样的结构体(其实我这个是传图片用的)
struct global_param {
int img_n;
int * img_wh; // 如果地址是连续的即使python中是数组最好也用一个指针
unsigned char ** imgs; // 接受list,地址不连续
char * str;
};
# 首先定义一个一样的结构体
class set_global_param_param(Structure):
_fields_ = [
("img_n", c_int),
("img_wh", POINTER(c_int)),
("imgs", POINTER(POINTER(c_uint8))),
("str", c_char_p),
# 下面这个粗略写一下,大家应该能明白
imgs_wh=np.zeros( (self.n_imgs,2),np.int32 )
imgs_p= ( POINTER(c_uint8)*self.n_imgs )() # 这个用法找了很久
...
imgs_p[i]=img.ctypes.data_as(POINTER(c_uint8))
...
# 然后如下使用
pm=set_global_param_param()
pm.img_n=self.n_imgs
pm.img_wh=imgs_wh.ctypes.data_as(POINTER(c_int))
pm.imgs= imgs_p
pm.str=(c_char * 100)().value = bytes("xxxxx",encoding="ascii")
# 其实就是用b"xxxxxx"
这样就可以实现把python中加载的图片地址传给C语言了,然后用opencv操作,就很方便了。
给个处理的示例
void handle_img_mat(uint8_t ** pimgs,int img_n, int * img_wh, vector & imgs) {
imgs.resize(img_n);
for (int i = 0; i < img_n; i++) {
Mat img = cv::Mat(img_wh[i * 2], img_wh[i * 2 + 1], CV_8UC1, pimgs[i]);
imgs[i] = img;
//cv::imshow("pic", img);
//cv::waitKey(3000);
//break;
}
}
其中字符串那个弄了不少时间,参考的博客有
https://stackoverflow.com/questions/2365411/python-convert-unicode-to-ascii-without-errors
https://stackoverflow.com/questions/7237133/create-string-buffer-throwing-error-typeerror-str-bytes-expected-instead-of-str
目前还不会二者同时调试,只能通过vs运行 python程序,然后到了加载dll那个地方就会进入要调试的c++程序。
过程是:
调试->xxxx 属性 ->
这样直接运行就可以了
弄了很久,不断搜资料并尝试研究才成功,希望对大家有帮助