1. 程式人生 > >liunx python呼叫c++庫(類、函式),傳入傳出引數

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)

$(/usr/lib/x86_64-linux-gnu/libboost_filesystem.so;/usr/lib/x86_64-linux-gnu/libboost_system.so)-I /usr/include/boost -L /usr/lib/x86_64-linux-gnu/ -I boost_system –l boost_filesystem

(在使用上面命令程式碼編譯時,先將程式碼放到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)



附件