这次就讲一些关于python 调用c++动态库的话题(毕竟图像算法,我就用opencv做些事情啦)。
首先推荐一下Clion
以前在windows 上使用vs20..系列的IDE,被IDE蒙蔽了双眼,其实很多c/c++基本功底都没有掌握,现在使用macbook进行开发,没有vs可以用,所以我安利一下Clion,对于我们用惯pycharm的人会很爽,对学生也比较友好,我们上了班的程序员就尽量支持下正版啦,少使用点破解注册码。下面我们就进入正题啦,整篇都是代码。
我这个是clion生成了一部分,另一部分是我自己填写的,cmake就是加入一些关于代码编译的配置,
然后生成makefile,makefile使用make指令就能编译了
cmake_minimum_required(VERSION 3.14)
project(load_dylib_test) #工程名
set(CMAKE_CXX_STANDARD 11)
set(OpenCV_DIR /Users/aidaihanati/Library_c++/opencv-4.1.1/opencv-4.1.1_lib/lib/cmake/opencv4)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OpenCV_DIR})
#设置cmake的目录,找到opencv的cmake,从而找到opencv的include,OpenCV_DIR这个目录下有这些cmake
#文件,cmake就把这个当作module去用
find_package(OpenCV REQUIRED)
#这个为什么这么设置,我们从OpenCVConfig.cmake文件里看,主要看看下图的红箭头,剩下的自己悟
message(${CMAKE_MODULE_PATH}) # 自己使用这句验证一下路径之类,帮助自己理解
message(${OpenCV_INCLUDE_DIRS})
include_directories(${OpenCV_INCLUDE_DIRS})
# 填加头文件路径,OpenCV_INCLUDE_DIRS就是在OpenCVConfig.cmake里定义的
#add_executable(load_dylib_test main.cpp)
#这个可以写写验证程序, load_dylib_test这个就是生成的可执行文件的名字
add_definitions(-DENABLE_DEBUG -lstdc++ -o3)
#定义一些编译选项,-fopenmp可以在我上个博客看看怎么安装。
add_library(ImgSegmentation SHARED ImgSegmentation.hpp ImgSegmentation.cpp)
# 第一个是生成的库的名字,实际是libImgSegmentation.dylib,第二个是生成可调用库的类型,SHARED代# 表动态库,STATIC代表静态库。后面把编译要用到代码文件添加进来。
set(Opencv_lib /Users/aidaihanati/Library_c++/opencv-4.1.1/opencv-4.1.1_lib/lib)
# 这里我就自己设置一下opencv库的路径,其实可以使用OpenCVConfig.cmake里的OpenCV_LIBS
target_link_libraries(ImgSegmentation
/usr/local/opt/libomp/lib/libomp.dylib
${Opencv_lib}/libopencv_core.4.1.1.dylib
${Opencv_lib}/libopencv_core.4.1.dylib
${Opencv_lib}/libopencv_core.dylib
${Opencv_lib}/libopencv_imgproc.4.1.1.dylib
${Opencv_lib}/libopencv_imgproc.4.1.dylib
${Opencv_lib}/libopencv_imgproc.dylib
${Opencv_lib}/libopencv_highgui.4.1.1.dylib
${Opencv_lib}/libopencv_highgui.4.1.dylib
${Opencv_lib}/libopencv_highgui.dylib
)#这里添加一下我们会链接到的动态库文件,如果不知道添加哪些,可以先编译代码试试,笑哭脸~
想了解更多关于cmake的内容可以看这篇(CMakeLists.txt 语法介绍与实例演练_阿飞的博客-CSDN博客_cmakelists.txt 教程)
1,创建ImgSegmentation.hpp
//
// Created by BboyHanat on 2019-09-25.
//
#ifndef LOAD_DYLIB_TEST_IMGSEGMENTATION_HPP
#define LOAD_DYLIB_TEST_IMGSEGMENTATION_HPP
#endif //LOAD_DYLIB_TEST_IMGSEGMENTATION_HPP
extern "C" { //因为python一般只支持c的接口(从网上查的,具体为什么,大家自己研究下哦)
typedef struct ImageBase {
int w; //图像的宽
int h; //图像的高
int c; //通道数
unsigned char *data; //我们要写python和c++交互的数据结构,
};
typedef ImageBase ImageMeta;
void SegmentImage(ImageMeta *data, ImageMeta *level_mask);
};
2,创建ImgSegmentation.cpp
//
// Created by BboyHanat on 2019-09-25.
//
#include
#include "ImgSegmentation.hpp"
using namespace cv;
void SegmentImage(ImageMeta *data, ImageMeta *level_mask) {
Mat img = Mat::zeros(Size(data->w,data->h),CV_8UC3);
Mat img_cp;
img.data=data->data;
cv::resize(img, img_cp,Size(320,320));
int sp = 15; //空间窗口大小
int sc = 30; //色彩窗口大小
int PyrLevel = 0; //金字塔层数
boxFilter(img_cp,img_cp, -1,Size(3,3));
pyrMeanShiftFiltering( img_cp, img_cp, sp, sc, PyrLevel);
Mat mask( img_cp.rows+2, img_cp.cols+2, CV_8UC1, Scalar::all(0) );
for( int y = 0; y < img_cp.rows; y++ )
{
for( int x = 0; x < img_cp.cols; x++ )
{
if( mask.data[(y+1) * img_cp.cols + (x+1)] == 0 )//只填充没有填充过的区域
{
Vec3b rgb = img_cp.at(y,x);
Scalar newVal(rgb[0], rgb[1], rgb[2]);
floodFill( img_cp, mask, Point(x,y), newVal, 0, Scalar::all(30), Scalar::all(30)); //漫水填充
}
}
}
Mat gray;
cvtColor(img_cp, gray,COLOR_BGR2GRAY);
resize(gray, gray, Size(data->w, data->h),0,0,INTER_NEAREST);
memcpy(level_mask->data, gray.data, data->w*data->h);
return;
}
在这里是clion,就用小锤子build一下,会生成libImgSegmentation.dylib文件,
不是clion 的话
mkdir build && cd build
cmake .. #这里可以使用cmake gui去配置哦,mac下如果要用-fopenmp可能要使用 llvm 的clang
make
建立run.py, 并把libImgSegmentation.dylib拷贝到同级目录
from ctypes import *
from io import BytesIO
import numpy as np
import cv2
# 这个写法是看到yolo的darknet.py里看到的,学以致用一下
def c_array(ctype, values): # 把图像的数据转化为内存连续的列表使c++能使用这块内存
arr = (ctype * len(values))()
arr[:] = values
return arr
def array_to_image(arr):
c = arr.shape[2]
h = arr.shape[0]
w = arr.shape[1]
arr = arr.flatten()
data = c_array(c_uint8, arr)
im = IMAGE(w, h, c, data)
return im
class IMAGE(Structure): # 这里和ImgSegmentation.hpp里面的结构体保持一致。
_fields_ = [("w", c_int),
("h", c_int),
("c", c_int),
("data", POINTER(c_uint8))]
img = cv2.imread('5.jpg')
h, w, c = img.shape[0], img.shape[1], img.shape[2]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)*0
gray = np.reshape(gray, (h, w, 1)) # 一定要使用(h, w, 1),最后的1别忘。
im = array_to_image(img)
gray_img = array_to_image(gray)
lib = CDLL('./libImgSegmentation.dylib') # 读取动态库文件
segment = lib.SegmentImage # 这个就是我们的函数名
segment.argtypes = [POINTER(IMAGE), POINTER(IMAGE)] # 设置函数参数
segment(im, gray_img) # 执行函数,这里直接修改gray_img的内存数据
y = gray_img.data # 获取data
array_length = h*w
“”“转化为numpy的ndarray”“”
buffer_from_memory = pythonapi.PyMemoryView_FromMemory # 这个是python 3的使用方法
buffer_from_memory.restype = py_object
buffer = buffer_from_memory(y, array_length)
img = np.frombuffer(buffer, dtype=np.uint8)
img = np.reshape(img, (h, w))
cv2.imshow('test', img)
cv2.waitKey(0)
python run.py
代码里的5.jpg
生成结果
最后一句就是:有问题的地方欢迎大家的指正。毕竟我也是分享一下自己的菜鸟经验。