当前位置:   article > 正文

[深度学习]C++调用Python-YOLO模型进行目标检测_c++调用yolov5

c++调用yolov5

文章目录:

  • 前言
  • C++调用Python的步骤
  • 修改YOLOv5源码
  • C++读取Python返回值

     ​​​​​

前言 

        目前深度学习算法大多数是基于Python实现,但一些项目的框架是用C++搭建,所以就出现了在C++中调用模型的问题。本文主要记录C++调用Python-YOLOv5模型的步骤,实现C++中读取图片然后传入YOLO模型中进行检测,最后将类名、坐标、置信度返回到C++中。开发环境为QT5、Python3.8、Opencv3.414以及运行YOLOv5源码的虚拟环境。


一、C++调用Python步骤

1.配置环境

        Python中自带有C++接口。首先需要导入Python目录下的include文件夹和库文件。

        我的Python是用Anaconda安装,所以这里我加入的是Anaconda虚拟环境里的路径,同时也导入了Opencv3.4。

        在.pro文件中加入以下内容,根据自己的路径更改:

  1. INCLUDEPATH+=D:/Opencv_Source/build_x64/install/include \
  2. D:/Anaconda/envs/yolov5/include \
  3. D:/Anaconda/envs/yolov5/Lib/site-packages/numpy/core/include/numpy
  4. Debug:
  5. {
  6. LIBS+=D:/Opencv_Source/build_x64/install/x64/vc14/lib/opencv_world3414d.lib \
  7. D:/Anaconda/envs/yolov5/libs/python38_d.lib
  8. }
  9. Release:
  10. {
  11. LIBS+=D:/Opencv_Source/build_x64/install/x64/vc14/lib/opencv_world3414.lib \
  12. D:/Anaconda/envs/yolov5/libs/python38.lib
  13. }

2.调用Python步骤

         大致步骤为:

        (1)初始化

        (2)设置文件所在路径

        (3)调用文件

        (4)获得函数列

        (5)调用函数

        下面直接贴代码,包含前四部,可根据注释理解:

  1. PyObject* pModule; //.py文件
  2. PyObject* pFunc; //py文件中的函数
  3. PyObject* pClass; //类
  4. PyObject* pInstance; //实例
  5. PyObject* args;//参数
  6. Py_SetPythonHome(L"D:/Anaconda/envs/yolov5");//指定python.exe位置
  7. Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化
  8. PyRun_SimpleString("import sys");
  9. PyRun_SimpleString("sys.path.append('./')");//设置.py文件所在位置
  10. //file为不包含扩展名的文件名
  11. pModule = PyImport_ImportModule(file); //调用上述路径下的.py文件
  12. if (pModule == NULL)
  13. {
  14. cout << "Can't find the python file!" << endl;
  15. return 0;
  16. }
  17. cout << "find file succed" << endl;
  18. // 模块的字典列表
  19. PyObject* pDict = PyModule_GetDict(pModule); //获得Python模块中的函数列
  20. if (pDict == NULL)
  21. {
  22. cout << "Can't find the dictionary!" << endl;
  23. return 0;
  24. }
  25. cout << "find dictionary succed" << endl;

        

3.调用函数并传入参数

        调用Python模块中的函数只需要两句代码,但C++中并没有直接将Mat类转换为Python数据类型的函数。

        这里借鉴了别的博文的代码,将C++中Mat类型里的数据转换为Python里的元组作为参数传入Python模块.附链接:https://blog.csdn.net/qq_38109843/article/details/87969732

         以下为C++中的代码

  1. import_array();
  2. int m, n;
  3. n = img.cols*3;
  4. m = img.rows;
  5. unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char) * m * n);
  6. int p = 0;
  7. for (int i = 0; i < m; i++)
  8. {
  9. for (int j = 0; j < n; j++)
  10. {
  11. data[p] = img.at<unsigned char>(i, j);
  12. p++;
  13. }
  14. }
  15. npy_intp Dims[2] = { m, n }; //给定维度信息
  16. PyObject*PyArray = PyArray_SimpleNewFromData(2, Dims, NPY_UBYTE, data);
  17. PyObject *ArgArray = PyTuple_New(2);
  18. PyObject *arg = PyLong_FromLong(30);
  19. PyTuple_SetItem(ArgArray, 0, PyArray);
  20. PyTuple_SetItem(ArgArray, 1, arg);
  21. //pDict是Python模块中的函数列,function是函数名
  22. PyObject*pFunc = PyDict_GetItemString(pDict, function); //获取函数
  23. //ArgArray是传入的参数,pRet是返回值
  24. PyObject* pRet= PyObject_CallObject(pFunc, ArgArray); //调用函数

        Python中的代码:

  1. def arrayreset(array):
  2. a = array[:, 0:len(array[0] - 2):3]
  3. b = array[:, 1:len(array[0] - 2):3]
  4. c = array[:, 2:len(array[0] - 2):3]
  5. a = a[:, :, None]
  6. b = b[:, :, None]
  7. c = c[:, :, None]
  8. m = np.concatenate((a, b, c), axis=2)
  9. return m

         注意传入的图像必须是RGB图像。


二、修改YOLOv5源码

1.YOLOv5环境配置

        YOLO的环境配置我也是照着别人的博文配置,作为一个知识尚匮乏的大学生就不再写一篇误人子弟了。

        直接上链接:https://blog.csdn.net/kasaiki/article/details/108651751

        训练自己模型的方法:https://blog.csdn.net/weixin_44936889/article/details/110661862

2.修改YOLOv5代码

        YOLOv5源码中就已经有detect.py文件用作目标检测,其功能也非常丰富,可以改变参数来实现不同的输入输出方式,这里我就直接做减法,只实现传入一张图片进行检测,返回目标的类名、坐标和置信度。

        

  1. def detect(image,a=1,
  2. weights='best.pt', # model.pt path(s)
  3. imgsz=640, # inference size (pixels)
  4. conf_thres=0.25, # confidence threshold
  5. iou_thres=0.45, # NMS IOU threshold
  6. device='cpu',
  7. max_det=1000, # maximum detections per image
  8. classes=None, # filter by class: --class 0, or --class 0 2 3
  9. agnostic_nms=False, # class-agnostic NMS
  10. augment=False, # augmented inference
  11. line_thickness=3, # bounding box thickness (pixels)
  12. hide_labels=False, # hide labels
  13. hide_conf=False, # hide confidences
  14. half=False, # use FP16 half-precision inference
  15. ):
  16. #Initialize
  17. set_logging()
  18. device = select_device(device)
  19. half &= device.type != 'cpu' # half precision only supported on CUDA
  20. #加载图像
  21. im0s = arrayreset(image)
  22. img = letterbox(im0s)[0]
  23. img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
  24. img = np.ascontiguousarray(img)
  25. # Load model
  26. model = attempt_load(weights, map_location=device) # load FP32 model
  27. stride = int(model.stride.max()) # model stride
  28. imgsz = check_img_size(imgsz, s=stride) # check image size
  29. names = model.module.names if hasattr(model, 'module') else model.names # get class names
  30. if half:
  31. model.half() # to FP16
  32. # Second-stage classifier
  33. classify = False
  34. if classify:
  35. modelc = load_classifier(name='resnet101', n=2) # initialize
  36. modelc.load_state_dict(torch.load('weights/resnet101.pt', map_location=device)['model']).to(device).eval()
  37. # Run inference
  38. if device.type != 'cpu':
  39. model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
  40. t0 = time.time()
  41. img = torch.from_numpy(img).to(device)
  42. img = img.half() if half else img.float() # uint8 to fp16/32
  43. img /= 255.0 # 0 - 255 to 0.0 - 1.0
  44. if img.ndimension() == 3:
  45. img = img.unsqueeze(0)
  46. # Inference
  47. t1 = time_synchronized()
  48. pred = model(img, augment=augment)[0]
  49. # Apply NMS
  50. pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
  51. t2 = time_synchronized()
  52. # Apply Classifier
  53. if classify:
  54. pred = apply_classifier(pred, modelc, img, im0s)
  55. info = []
  56. for i, det in enumerate(pred): # detections per image
  57. if len(det):
  58. # Rescale boxes from img_size to im0 size
  59. det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0s.shape).round()
  60. for *xyxy, conf, cls in reversed(det):# Add bbox to image
  61. c = int(cls) # integer class
  62. label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
  63. plot_one_box(xyxy, im0s, label=label, color=colors(c, True), line_thickness=line_thickness)
  64. x1,y1,x2,y2=xyxy[0].item(), xyxy[1].item(), xyxy[2].item(), xyxy[3].item()
  65. info.append(names[c])
  66. info.append((x1, y1, x2, y2,conf.item()))
  67. cv2.imshow('show', im0s)
  68. print(f'Done. ({time.time() - t0:.3f}s)')
  69. cv2.waitKey(0) # 1 millisecond
  70. return info

        返回的info是一个列表,其格式是[name,(x1,y1,x2,y2,value)......],name是类名,以字符串表示,元组里是坐标和置信度。将识别到的目标依次存储到列表里,再返回到C++中解析即可。 


三、C++读取返回值

        C++中有函数能转换Python的数据类型,这里我们返回值里有列表、字符串和元组,都有对应的函数进行转换。

        我是看这篇博文学习的:https://blog.csdn.net/stu_csdn/article/details/69488385

        下面附上代码:

  1. char * buffer1; //储存Python文件返回值
  2. PyObject *ListItem;
  3. //定义坐标,置信度
  4. float x1 = 0;
  5. float y1=0;
  6. float x2 = 0;
  7. float y2=0;
  8. float value=0;
  9. if(PyList_Check(pRet)) //检查返回值是不是列表
  10. {
  11. for(int i=0;i<PyList_Size(pRet);i+=2)
  12. {
  13. ListItem=PyList_GetItem(pRet,i); //读取列表里的第i个元素
  14. PyArg_Parse(ListItem,"s",&buffer1); //转换为字符串类型
  15. buffer2=buffer1;
  16. cout<<buffer2<<endl;
  17. ListItem=PyList_GetItem(pRet,i+1);
  18. PyArg_ParseTuple(ListItem, "f|f|f|f|f", &x1,&y1,&x2,&y2,&value);//转换为浮点型
  19. cout<<"x1: "<<x1<<"y1: "<<y1<<"x2: "<<x2<<"y2: "<<y2<<" "<<value<<endl;
  20. }
  21. }

        整个代码运行得到的结果:

        

        


总结

        想说一下自己短短涉猎深度学习半年多时间的感想。在学习深度学习之前我只是学习了Opencv来做一些比赛,后来看有图像处理的基础就跟着老师做项目才进阶深度学习。但我确实也像网上经常被嘲笑的调包侠一样,做深度学习也就会调个包,改改参数,工程能力确实有比较大的锻炼,但没有理论基础。所以接下来也会补一下理论知识来丰富自己,也想不只是学习深度学习的算法,多学一些Opencv的优秀算法。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/136719?site
推荐阅读
相关标签
  

闽ICP备14008679号