C++调用python模块重要方法
前言
最近想在WIndows C++下使用mediapipe,用windows版本配置配了好久没成功,
采用https://stubbornhuang.blog.youkuaiyun.com/article/details/119675282?spm=1001.2014.3001.5506
的方法应该是最好的,但是我不会配置。
Python版本跑起来最快,看了Mediapipe的封装调用,成功在VS里使用上了mediapipe,但是打包时又出现了问题,把python环境复制过来时打包不成功。
最后想了两种在C++下调用python的方式:
1.用socket通讯,一个做client端,一个做server端。
2.把python程序封装成可执行文件,在c++里用系统调用。
一、写好python程序
调用meidiapipe模块,输入图片,输出facemesh点位置。
from cv2 import cvtColor
from cv2 import COLOR_BGR2RGB
from cv2 import imread
from mediapipe import solutions
from re import split
from csv import writer
from sys import argv
mp_face_mesh = solutions.face_mesh
def detectImage(image):
with mp_face_mesh.FaceMesh(
static_image_mode=True,
max_num_faces=1,
min_detection_confidence=0.5) as face_mesh:
results = face_mesh.process(cvtColor(image, COLOR_BGR2RGB))
newList2 = []
for item in results.multi_face_landmarks:
pattern = r'x: |y: |z: |\n|landmark|{|}'
result = split(pattern, str(item)) # 以pattern的值 分割字符串
newList2 = [eval(x) for x in list(result) if len(x) > 2]
#for idx in range(0, len(newList2), 3):
# cv2.circle(image, (int(newList2[idx] * image.shape[1]), int(newList2[idx + 1] * image.shape[0])), 1, (0, 0, 255))
#cv2.imshow("",image)
#cv2.waitKey(0)
return newList2
def main():
if(len(argv)<2):
return
image = imread(str(argv[1])+'.jpg')
(height, width, channels) = image.shape
# print(height,width)
a = detectImage(image)
# for i in range(0, 468, 1):
# # 写入csv文件内容
# print([a[i * 3]*width, a[i * 3 + 1]*height])
# cv2.circle(image, (int(a[i * 3]*width), int(a[i * 3 + 1]*height)) ,1, (255,0,0), 1)
# cv2.imshow("image",image)
# while(True):
# if cv2.waitKey(5) & 0xFF == 27:
# break
with open(str(argv[1])+'.csv', 'w+', newline='') as file:
mywriter = writer(file)
for i in range(0, 468, 1):
# 写入csv文件内容
mywriter.writerow([int(a[i * 3]*width), int(a[i * 3 + 1]*height)])
if __name__ == '__main__':
main()
二、打包python程序
采用pyinstaller进行打包,
正常py程序打包如下,w表示不弹出命令行,F表示打包成一个文件:
pyinstaller xxx.py -w -F
命令行如下
pyinstaller IO_detectImage.py --add-data=“C:\Users\xujianjun\AppData\Roaming\Python\Python39\site-packages\mediapipe\modules;mediapipe/modules” -w
然后将输出的dist里的IO_detectImage的整个文件包拷贝到vs文件目录下
————————————————————————————————————————————
参考pyinstaller打包Mediapipe遇到的Failed to execute script
三、C++程序里调用
我用的visual studio写的,用_getcwd获取目录地址,再用system函数调用python程序,以下方法C++都可以用
#include "stdafx.h"
#include "IOMediaPipe.h"
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <Windows.h>
#include <direct.h>
IOMediaPipe::IOMediaPipe()
{
}
IOMediaPipe::~IOMediaPipe()
{
}
bool IOMediaPipe::savePic(cv::Mat pic)
{
return cv::imwrite("IO_detect/Temp_Facemesh.jpg", pic);
}
bool IOMediaPipe::predict(std::vector<cv::Point>& res)
{
/*TCHAR Buffer[100] = TEXT("IO_detect");
bool flag = SetCurrentDirectory(Buffer);
if (!flag) return false;*/
//system("pwd");
char m_szInitDir[_MAX_PATH];
std::string pathname = _getcwd(m_szInitDir, _MAX_PATH);
std::string exename = pathname+ std::string("\\IO_detect\\IO_detect.exe ")+ pathname+ std::string("\\IO_detect\\Temp_Facemesh");
std::cout << exename << " " << std::endl;
system(exename.c_str());
res.clear();
std::ifstream infile;//定义读取文件流,相对于程序来说是in
/*TCHAR Buffer[100] = TEXT("..");
bool flag = SetCurrentDirectory(Buffer);*/
infile.open("IO_detect/Temp_Facemesh.csv");//打开文件
std::string line;
cv::Point p;
if (infile.eof())
{
infile.close();
std::cout << "error open" << std::endl;
return false;
}
for (int val = 0; val<468; val++) {
if (std::getline(infile, line)) {
std::istringstream sin(line);
std::vector<std::string> fields;
std::string field;
while (std::getline(sin, field, ',')) {
std::cout << field << " ";
fields.push_back(field);
}
p.x = atoi(fields.at(0).data());
p.y = atoi(fields.at(1).data());
res.push_back(p);
}
else {
std::cout << "error push in point data!" << std::endl;
infile.close();
return false;
}
}
infile.close();
return true;
}
bool IOMediaPipe::predict(cv::Mat pic, std::vector<cv::Point>& res)
{
if (!savePic(pic)) return false;
if (!predict(res)) return false;
return true;
}
总结
用这个方法能解决C++调用python模块的几乎所有问题,不过执行效率可能有点点慢,后续考虑用RPC通讯模式,能提高执行效率。
感谢阅读,如果对您有帮助,欢迎点赞,关注,收藏!