Python 调用 C++ 动态库做图像处理

这次就讲一些关于python 调用c++动态库的话题(毕竟图像算法,我就用opencv做些事情啦)。

首先推荐一下Clion

以前在windows 上使用vs20..系列的IDE,被IDE蒙蔽了双眼,其实很多c/c++基本功底都没有掌握,现在使用macbook进行开发,没有vs可以用,所以我安利一下Clion,对于我们用惯pycharm的人会很爽,对学生也比较友好,我们上了班的程序员就尽量支持下正版啦,少使用点破解注册码。下面我们就进入正题啦,整篇都是代码。

1,C/C++编写Cmake的要点

我这个是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 教程)

二,开始写C/C++代码

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文件,

Python 调用 C++ 动态库做图像处理_第1张图片

不是clion 的话

mkdir build && cd build

cmake ..   #这里可以使用cmake gui去配置哦,mac下如果要用-fopenmp可能要使用 llvm 的clang

make

Python 调用 C++ 动态库做图像处理_第2张图片

3,编写python调用libImgSegmentation.dylib的代码

建立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)

4,大功告成!

python run.py

代码里的5.jpg

Python 调用 C++ 动态库做图像处理_第3张图片

生成结果

Python 调用 C++ 动态库做图像处理_第4张图片

最后一句就是:有问题的地方欢迎大家的指正。毕竟我也是分享一下自己的菜鸟经验。

你可能感兴趣的:(c/c++,python,c++,动态库,dll)