将Python函数打包成.so库的基本流程
-
安装依赖:
- 安装Cython:
pip install cython
- 安装OpenCV的Python包和开发库:
pip install opencv-python # Ubuntu系统安装OpenCV开发库 sudo apt-get install libopencv-dev
- 安装Cython:
-
编写Cython代码(
.pyx
文件):- 使用
cdef
和cpdef
定义C/C++接口。 - 处理Python对象与C/C++类型的转换。
- 使用
-
配置
setup.py
:- 使用
Cython.Build.cythonize
编译代码。 - 指定头文件路径和链接库(如OpenCV)。
- 使用
-
编译生成.so文件:
- 运行
python setup.py build_ext --inplace
。
- 运行
-
测试生成的库:
- 在Python中导入.so文件并调用函数。
示例:图像亮度调整库(使用OpenCV)
目录结构
project/
├── imgproc.pyx # Cython代码
├── setup.py # 编译配置
└── test.py # 测试脚本
1. Cython代码(imgproc.pyx
)
# distutils: language=c++
# cython: language_level=3
# 导入C++标准库和OpenCV
from libcpp.string cimport string
from libc.math cimport abs
cimport numpy as np
import numpy as np
# 导入OpenCV的C++接口
cdef extern from "opencv2/opencv.hpp" namespace "cv":
cdef cppclass Mat:
Mat()
int rows, cols, channels()
uchar* data
Mat imread(const string& filename, int flags)
b imwrite(const string& filename, const Mat& img)
void cvtColor(Mat& src, Mat& dst, int code)
# Python接口函数
cpdef adjust_brightness(str input_path, str output_path, float alpha=1.0, int beta=0):
cdef string c_input = input_path.encode('utf-8')
cdef string c_output = output_path.encode('utf-8')
# 读取图像
cdef Mat image = imread(c_input, 1) # 1表示彩色图
if image.rows == 0:
raise ValueError("无法读取图像文件: " + input_path)
# 调整亮度/对比度:image = alpha * image + beta
cdef Mat adjusted_image
image.convertTo(adjusted_image, -1, alpha, beta)
# 保存图像
if not imwrite(c_output, adjusted_image):
raise RuntimeError("保存图像失败: " + output_path)
2. 编译配置(setup.py
)
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy as np
# 配置OpenCV的头文件和库路径(根据系统实际路径调整)
opencv_inc = '/usr/local/include/opencv4' # 常见路径
opencv_libdir = '/usr/local/lib'
extensions = [
Extension(
name="imgproc",
sources=["imgproc.pyx"],
language="c++",
include_dirs=[np.get_include(), opencv_inc],
libraries=["opencv_core", "opencv_highgui", "opencv_imgcodecs", "opencv_imgproc"],
library_dirs=[opencv_libdir],
extra_compile_args=["-std=c++11"], # 启用C++11标准
)
]
setup(ext_modules=cythonize(extensions, compiler_directives={'language_level' : "3"}))
3. 编译并测试
-
编译:
python setup.py build_ext --inplace
生成
imgproc.cpython-xxx-linux-gnu.so
。 -
测试脚本(
test.py
):import imgproc input_path = "input.jpg" output_path = "output.jpg" # 增加亮度(alpha=1.2,亮度增量beta=50) imgproc.adjust_brightness(input_path, output_path, alpha=1.2, beta=50) print("处理完成!")
常见问题与解决方案
1. 头文件找不到(fatal error: opencv2/opencv.hpp: No such file or directory
)
- 原因:OpenCV开发头文件未安装或路径配置错误。
- 解决:
# 安装OpenCV开发包 sudo apt-get install libopencv-dev # 或手动指定头文件路径(修改setup.py中的opencv_inc)
2. 链接错误(undefined reference to cv::imread()
)
- 原因:未正确链接OpenCV库(如缺少
opencv_imgcodecs
)。 - 解决:检查
setup.py
的libraries
是否包含所有依赖的OpenCV组件。
3. Python字符串到C++ string的转换错误
- 现象:传入非字符串类型或编码错误。
- 解决:在Cython中使用
.encode('utf-8')
显式转换字符串。
4. 图像保存失败(RuntimeError: 保存图像失败
)
- 原因:输出路径无写入权限或格式不支持。
- 解决:检查路径权限并使用OpenCV支持的格式(如.jpg/.png)。
5. 生成的.so文件无法导入
- 原因:依赖的OpenCV动态库未找到。
- 解决:
# 添加OpenCV库路径到LD_LIBRARY_PATH export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
高级优化:直接处理NumPy数组
如果需要直接传递NumPy数组(避免文件IO),需处理数据指针转换:
# 在imgproc.pyx中添加
cpdef adjust_brightness_np(np.ndarray img, float alpha=1.0, int beta=0):
cdef Mat image
cdef np.ndarray[np.uint8_t, ndim=3] img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # 确保通道顺序
image = Mat(img_bgr.shape[0], img_bgr.shape[1], CV_8UC3, <uchar*>img_bgr.data)
# ...处理并返回numpy数组
注意:需处理OpenCV(BGR)与NumPy(RGB)的通道顺序差异。
通过上述步骤,你可以将Python图像处理函数高效地编译为.so库,并在其他Python项目中直接调用。