liunx python呼叫c++庫(類、函式),傳入傳出引數
一、使用c++封裝動態庫
1.新建test類
1.1新建 test.cpp 檔案
程式碼如下:
#include<iostream>
extern "C"
int myadd(int a, int b)
{
return a + b;
}
備註:extern"C" 必須有,不然會報undefined symbol: myadd.
1.2新建test.h檔案
程式碼如下:
#ifndef _TESTSO_H
#define_TESTSO_H
extern"C"
{
intmyadd(int a, int b);
typedefint myadd_t(int, int); // myadd function type
}
#endif// _TESTSO_H
備註:test.h檔案可以沒有,因為test.cpp也沒有#include <test.h>,再說myadd()函式也不是類的成員函式。
2.編譯
g++ -shared -fPIC -o test.so test.cpp
備註1:
-fPIC:生成位置無關目的碼,適用於動態連線;
-L path:表示在path目錄中搜索庫檔案,如-L.表示在當前目錄;
-I path:表示在path目錄中搜索標頭檔案;
-o file:制定輸出檔案為file;
-shared:生成一個共享庫檔案;
備註2:
編譯多個cpp檔案:
g++ -shared-fPIC -o demo.so demo.cpp CCNF_patch_expert.cpp LandmarkDetectorFunc.cppLandmarkDetectorModel.cpp LandmarkDetectorUtils.cppLandmarkDetectorParameters.cpp LandmarkDetectionValidator.cpp Patch_experts.cppPAW.cpp PDM.cpp SVR_patch_expert.cpp stdafx.cpp$(pkg-config opencv --cflags --libs)
(在使用上面命令程式碼編譯時,先將程式碼放到txt中,寫成一行,最後在賦值到命令列進行編譯,不然可能會出錯)
,最好是寫個makefile檔案來編譯。
3. C++方式呼叫庫
3.1新建main.cpp
程式碼如下:
#include <stdio.h>
#include <dlfcn.h> //必須有
#include <iostream>
#include"test.h" // for dynamic library函式
int main(int argc, char*argv[])
{
if (2 != argc)
{
return-1;
}
const char *soname =argv[1];
void *so_handle = dlopen(soname,RTLD_LAZY); // 載入.so檔案
if (!so_handle)
{
fprintf(stderr,"Error: load so `%s' failed./n", soname);
return-1;
}
dlerror(); // 清空錯誤資訊
myadd_t *fn =(myadd_t*)dlsym(so_handle,"myadd"); // 載入函式
char *err =dlerror();
if (NULL != err)
{
fprintf(stderr,"%s/n", err);
return-1;
}
printf("myadd57 + 3 = %d/n", fn(57, 3)); // 呼叫函式
dlclose(so_handle);// 關閉so控制代碼
return 0;
}
3.2 編譯
g++ main.cpp -o main -ldl
備註:如果沒有-ldl引數,會報undefinedreference to `dlopen'錯誤解決。
3.3執行
./main test.so
4. Python方式呼叫庫
4.1新建main.py
from ctypesimport *
#loaddll and get the function object
dll= cdll.LoadLibrary(’/landmark/LandmarkDetector/test.so');
t= dll.myadd(1,2)
print(t)
4.2執行
python main.py
二、python調c++庫,並傳入傳出引數
2.1 c++庫函式
例如:
c++庫函式:
inttest(const cv::Mat img, conststd::vector<cv::Rect> vRect, const std::string sModelPath, cv::Vec3f&pose)
輸入引數:
const cv::Mat img:圖片資料
conststd::vector<cv::Rect> vRect:人臉區域
const std::stringsModelPath:模型路徑
輸出引數:
cv::Vec3f& pose:人臉角度(x,y,z)
由於python、c++是兩種不同的程式語言,在相互呼叫時,需要將資料型別轉換成對方認可的。
2.2 輸入引數
2.2.1 const cv::Mat img引數修改
C++庫部分
1. const cv::Mat img 引數改成(int*img, int rows, int cols, int channels)
2. 將int* img影象資料解析成cv::Mat img。
程式碼如下:
cv::Mat show_matrix(int *matrix, int rows, int cols, int channels)
{
int i, j, c;
if (3==channels)
{
cv::Mat img(rows,cols,CV_8UC3,cv::Scalar(0,0,0));
std::cout<<img.rows<<","<<img.cols<<std::endl;
uchar* pxvec =img.ptr<uchar>(0);
for (i=0; i<rows*cols*2*3; i++)
{
if (0==i%2)
{
intii = i/cols/2/3;
pxvec =img.ptr<uchar>(ii);
j = i%(cols*2*3)/2;
c = j%3;
j = j/3;
pxvec[j*3 + c] =(unsigned char)matrix[i];
//printf("pxvec[%d][%d][%d]= %d\n", ii,j,c, matrix[i]);
}
}
return img;
}
else
{
cv::Mat img(rows,cols,CV_8UC1,cv::Scalar(0,0,0));
std::cout<<img.rows<<","<<img.cols<<std::endl;
uchar* pxvec =img.ptr<uchar>(0);
for (i=0; i<rows*cols*2; i++)
{
if (0==i%2)
{
int ii = i/cols/2;
pxvec =img.ptr<uchar>(ii);
j = i%(cols*2)/2;
pxvec[j] =(unsigned char)matrix[i];
//printf("pxvec[%d][%d]= %d\n", ii, j, matrix[i]);
}
}
return img;
}
}
Python呼叫部分
程式碼如下:
src= cv2.imread("/OpenFace-master/test/face_0.jpg") #0 - gray
cols= src.shape[1]
rows =src.shape[0]
channels= 0
if 3==len(src.shape):
channels= 3
src= np.asarray(src, dtype=np.int)#需要與定義的int* img型別一致
src1 = src.ctypes.data_as(ctypes.c_char_p)#將一個多維陣列轉成char*
t =dll.test(src1, rows, cols, channels)
備註:
src = np.array([[11,22],[3,4]])
src1 = src.ctypes.data_as(ctypes.c_char_p)
t = dll.test(src1, rows, cols, channels)
c++中src1的數值如下:
matrix[0] = 11
matrix[1] = 0
matrix[2]= 22
matrix[3]= 0
matrix[4]= 3
matrix[5]= 0
matrix[6]= 4
matrix[7] = 0
2.2.2 const cv::vector<cv::Rect> vRect引數修改
c++庫部分
1. const cv::vector<cv::Rect>vRect引數改成(int* rect, int num)
2. 將int* rect人臉區域資料解析成cv::vector<cv::Rect> vRect;
程式碼如下:
vector<cv::Rect> show_rect(int* rect, introws)
{
vector<cv::Rec > vRect;
int cols = 4; //(x,y,w,h)
int i,j;
int x=0,y=0,w=0,h=0;
cv::Rect roi;
for (i=0; i<rows*cols*2; i++)
{
if (0==i%2)
{
int ii = i/cols/2;
j = i%(cols*2)/2;
if (0==j)
{
x = rect [i];
}
else if (1==j)
{
y = rect [i];
}
else if (2==j)
{
w = rect [i];
}
else if (3==j)
{
h = rect [i];
//printf("rect[%d][%d] = %d,%d,%d,%d\n", ii, j, x,y,w,h);
roi = cv::Rect (x,y,w,h);//(17,42,76,75);
vRect.push_back(roi );
}
}
}
return vRect;
}
Python呼叫部分
程式碼如下:
num = 1 #表示人臉區域的個數
rect = np.zeros((num,4),dtype=np.int) #4表示人臉區域的(x,y,w,h)
rect[0][0] = 0
rect[0][1] = 0
rect[0][2] = cols-1
rect[0][3] = rows-1
#print (src.dtype) #uint8
rect = np.asarray(rect, dtype=np.int)
#print (src.dtype)
#int64
rect1 = rect.ctypes.data_as(ctypes.c_char_p)
t = dll.test(src1, rows, cols, channels, rect1, num)
備註:如果是float型別,需要使用 c_float(cx)轉換後,再傳入就ok,不然會報型別不匹配。
2.3 輸出引數
參考文獻:http://blog.csdn.net/uniqsa/article/details/78603082
2.3.1 cv::Vec3f& pose引數修改
c++庫部分
1. 將cv::Vec3f& pose引數改成struHeadPose& pose;
2. 在test.cpp中定義struHeadPose結構體:
structstruHeadPose
{
float angleX;
float angleY;
float angleZ;
};
3. struHeadPose pose賦值並返回:
pose.angleX = angleX;
pose.angleY = angleY;
pose.angleZ= angleZ;
python呼叫部分
程式碼如下:
class struHeadPose(ctypes.Structure): _fields_ =[("angleX", ctypes.c_float),("angleY",ctypes.c_float),("angleZ", ctypes.c_float)] pose =struHeadPose(0.0, 0.0, 0.0) sModelPath ="/LandmarkDetector/model/main_clnf_general.txt" t = dll.test(src1,rows, cols, channels, rect1, num, sModelPath, ctypes.byref(pose)) print(pose.angleX) print(pose.angleY) print(pose.angleZ)
三、c++封裝類的動態庫、python呼叫
3.1 c++封裝類的動態庫
3.1.1 demo.h檔案
#ifndef _DEMOSO_H
#define _DEMOSO_H
// OpenCV includes
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
struct struHeadPose
{
float angleX;
float angleY;
float angleZ;
};
class CCaculateFaceAngle
{
public:
int getPose(int* imgData, int h, int w, int channels, float cx, float cy, float fx, float fy,struHeadPose& pose);
int LoadModel(char* sModelPath);
private:
LandmarkDetector::FaceModelParameters m_det_parameters;
LandmarkDetector::CLNF m_clnf_model;
};
#endif // _DEMOSO_H
3.1.2 demo.cpp檔案
#include "demo.h"
int CCaculateFaceAngle::getPose(int* imgData, int h, int w, int channels, float cx, float cy, float fx, float fy, struHeadPose& pose)
{
內容省略
}
int CCaculateFaceAngle::LoadModel(char* sModelPath)
{
內容省略
}
//以下是重點,不然不會匯出c++的類。
extern "C"
{
CCaculateFaceAngle obj;
int getPose(int* imgData, int h, int w, int channels, float cx, float cy, float fx, float fy,struHeadPose& pose)
{
return obj.getPose(imgData, h, w, channels, cx, cy, fx, fy,pose);
}
int LoadModel(char* sModelPath)
{
return obj.LoadModel(sModelPath);
}
}
3.2 python呼叫c++類
from ctypes import *
import cv2
import ctypes
import numpy as np
dll = cdll.LoadLibrary('/LandmarkDetector/class_so/demo.so');
# model path
sModelPath = "/LandmarkDetector/model/main_clnf_general.txt"
#image data
src = cv2.imread("/OpenFace-master/test/face_0.jpg") #0-gray
cols = src.shape[1]
rows = src.shape[0]
#print('img shape:{}'.format(src.shape))
channels = 0
if 3==len(src.shape):
channels = 3
src = np.asarray(src, dtype=np.int)
src1 = src.ctypes.data_as(ctypes.c_char_p)
#fx fy cx cy
cx = cols / 2.0
cy = rows / 2.0
fx = 500 * (cols / 640.0)
fy = 500 * (rows / 480.0)
fx = (fx + fy) / 2.0
fy = fx
cx1 = c_float(cx)
cy1 = c_float(cy)
fx1 = c_float(fx)
fy1 = c_float(fy)
#head pose
class struHeadPose(ctypes.Structure):
_fields_ = [("angleX", ctypes.c_float),("angleY", ctypes.c_float),("angleZ", ctypes.c_float)]
pose = struHeadPose(0.0, 0.0, 0.0)
if dll.LoadModel(sModelPath) > -1:
t = dll.getPose(src1, rows, cols, channels, cx1, cy1, fx1, fy1, ctypes.byref(pose))
print (pose.angleX)
print (pose.angleY)
print (pose.angleZ)
附件