• Interactive Tools Recommendation System integrating QT/ROS /Pytorch


    项目背景

    任务为交互工具的推荐。需要实现的目标为:机器人进入场景中自动识别工具以及被操作物体,通过对任务进行分析,自动推荐出操作该物体的合适工具。最终将整个流程通过QT交互界面实现与机器人,深度模型的通信。

    环境搭建

    这里的环境搭建主要是针对ROS和QT的配置,有些细节可以在之前发的一片博客里找到:QT接收ROS视频信息,并将其显示在界面上

    创建ROS工作空间

    • 创建ROS的工作空间xinjianjia_qt(此时与Qt还没有关系,只是单纯的ROS空间)
    mkdir -p ~/xinjianjia_qt/src
    
    • 1
    • 进入src文件夹:
    cd ~/xinjianjia_qt/src
    
    • 1
    • 初始化ROS工作空间:
    catkin_init_workspace
    
    • 1

    这样就在src文件中创建了一个 CMakeLists.txt 的文件,目的是告诉系统,这个是ROS的工作空间。(qt_gui)是下一步生成了,这里是后截图,所以才存在的。

    在这里插入图片描述

    将ROS和QT集成起来

    • 创建完ROS的工作空间后,就能通过catkin_create_qt_pkg命令创建qt_gui包了,在src目录下创建包,本例程将ros_gui包命名为qt_gui:
      (此时与Qt与ROS已经有关系了)
     1. cd ~/xinjianjia_qt/src
     2.  catkin_create_qt_pkg qt_gui
    
    • 1
    • 2

    这时,qt_gui就出现了

    在这里插入图片描述

    进入qt_gui里,可以看到主要的文件是inclide,src,ui。

    在这里插入图片描述

    其中,src里存储cpp文件

    • main.cpp是主函数文件
    • main_window.cpp是Qt的ui管理文件
    • qnode.cpp是ROS管理节点的文件

    在这里插入图片描述

    其中,include里存储hpp文件,用于存放声明

    在这里插入图片描述

    其中,ui里存储ui文件,也就是QT的设计文件

    在这里插入图片描述

    编译整个工作空间,生成qt_gui的ROS节点文件

    进入工作空间xinjianjia_qt编译

    catkin_make
    
    • 1

    编译的目的是生成可执行的ROS节点文件(后续QT Creator需要导入该节点)

    在这里插入图片描述

    但,在编译之前需要配置该功能包的CMakeList.txt和packge.xml文件,导入需要的包(主要就是Opencv的导入)

    在这里插入图片描述

    适用于本机的CMakeList.txt文件

    ##############################################################################
    # CMake
    ##############################################################################
    
    cmake_minimum_required(VERSION 2.8.0)
    project(qt_gui)
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    
    ##############################################################################
    # Catkin
    ##############################################################################
    
    # qt_build provides the qt cmake glue, roscpp the comms for a default talker
    set(OpenCV_DIR /usr/share/OpenCV/)
    find_package(catkin REQUIRED COMPONENTS 
    	OpenCV 
    	sensor_msgs 
    	image_transport 
    	std_msgs 
    	cv_bridge
    )
    	
    find_package(Qt5 REQUIRED Core Widgets)
    set(QT_LIBRARIES Qt5::Widgets)
    include_directories(${catkin_INCLUDE_DIRS})
    # Use this to define what the package will export (e.g. libs, headers).
    # Since the default here is to produce only a binary, we don't worry about
    # exporting anything. 
    catkin_package()
    
    ##############################################################################
    # Qt Environment
    ##############################################################################
    
    # this comes from qt_build's qt-ros.cmake which is automatically 
    # included via the dependency call in package.xml
    #rosbuild_prepare_qt4(QtCore QtGui) # Add the appropriate components to the component list here
    
    ##############################################################################
    # Sections
    ##############################################################################
    
    file(GLOB QT_FORMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ui/*.ui)
    file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/*.qrc)
    file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS include/qt_gui/*.hpp *.h)
    
    QT5_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})
    QT5_WRAP_UI(QT_FORMS_HPP ${QT_FORMS})
    QT5_WRAP_CPP(QT_MOC_HPP ${QT_MOC})
    
    ##############################################################################
    # Sources
    ##############################################################################
    
    file(GLOB_RECURSE QT_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)
    
    ##############################################################################
    # Binaries
    ##############################################################################
    add_executable(qt_gui ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
    target_link_libraries(qt_gui ${QT_LIBRARIES} ${catkin_LIBRARIES} )
    install(TARGETS qt_gui RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
    
    target_link_libraries(qt_gui 
        /usr/lib/x86_64-linux-gnu/libtiff.so.5
        /usr/lib/x86_64-linux-gnu/libgdal.so.20
        /usr/lib/x86_64-linux-gnu/libsqlite3.so.0
    )
    
    
    message(STATUS "OpenCV_INCLUDE_DIRS ?" : ${OpenCV_INCLUDE_DIRS})
    message(STATUS "OpenCV_VERSION ?" : ${OpenCV_VERSION})
    message(STATUS "OpenCV_LIB ?" : ${OpenCV_LIBS})
    message(STATUS "OpenCV_CONGIG_PATH ?" : ${OpenCV_CONFIG_PATH})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    适用于本机的packge.xml文件

    注意:如果配置了CMakeList.txt,系统就会首先默认找CMakeList.txt里的配置选项,因此packge.xml无需更改。这里我使用的也是默认生成的。

    <?xml version="1.0"?>
    <package>
      <name>qt_gui</name>
      <version>0.1.0</version>
      <description>
    
         qt_gui
    
      </description>
      <maintainer email="robot@gmail.com">robot</maintainer>
      <author>robot</author>
      <license>BSD</license>
      <!-- <url type="bugtracker">https://github.com/stonier/qt_ros/issues</url> -->
      <!-- <url type="repository">https://github.com/stonier/qt_ros</url> -->
    
      <buildtool_depend>catkin</buildtool_depend>
      <build_depend>qt_build</build_depend>
      <build_depend>roscpp</build_depend>
      <build_depend>libqt4-dev</build_depend>
      <run_depend>qt_build</run_depend>
      <run_depend>roscpp</run_depend>
      <run_depend>libqt4-dev</run_depend>
    
    </package>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    打开QT Creator

    建项目的过程:QT接收ROS视频信息,并将其显示在界面上,这里也有。

    项目建完之后,比较重要的一步就是要配置刚才的ROS节点,只有配置了这里的ROS节点,ROS和QT Creator才可以正常通信

    项目->RUN->运行,配置运行配置为Custom Executable,可执行文件Executable选择编译好的节点,工作,目录Working directory就选择节点的上一级目录即可

    在这里插入图片描述

    整个QT的项目如下:

    在这里插入图片描述

    技术实现

    先预览一下最终的交互界面

    在这里插入图片描述

    左侧为图像/视频显示区域,右侧为按键控制区。

    显示区域:

    • Camera View需要实时显示相机的画面,因此需要放在单独的子线程里
    • Capture Tools Frame只需要显示相机画面中工具集的关键帧,这个操作由按钮Capture Tools KeyFrame控制
    • Capture Object Frame只需要显示相机画面中物体的关键帧,这个操作由按钮Capture Object KeyFrame控制
    • Tools Recommendation Result只需要显示最终的推荐结果,这个操作由按钮Tools Recommendation控制

    按键控制区:

    • ----- Task Selection ------是一个QComboBox类,用来选择任务类型,是一个下拉菜单(双击即可编辑内容)
      在这里插入图片描述
      任务种类如下:
    task_list = ['cutting cucumber', 'cutting banana', 'cutting bread', 'cutting cake', 'cutting pizza',
                 'containing tea', 'containing redwine',  'containing coffee',
                 'cropping paper', 'cropping tape', 'cropping cottonmaterial ',  'cropping rope ',
                 'hitting iron nail', 'hitting raw meat',
                 'storing cloth',  'storing book']
    
    • 1
    • 2
    • 3
    • 4
    • 5

    main_window.hpp
    槽函数的声明,接受的const QString &类型,因为我们要得到具体任务的信息,也就是具体字符信息

    public Q_SLOTS:
        /******************************************
        ** Task Selection/Analysis
        *******************************************/
        void on_comboBox_currentStringChanged(const QString &);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    main_window.cpp

    信号和槽的绑定:currentIndexChanged(QString)是固定的信号,不可以更改,传入的是QString,也就是我们在下拉菜单中选择的具体字符。

    槽函数实现:通过方法currentText()得到具体的字符str,供后续使用

    /******************************************
    ** Task Selection/Analysis
    *******************************************/
    QObject::connect(ui.Task_Selection_comboBox,
                     SIGNAL(currentIndexChanged(QString)),
                     this,
                     SLOT(on_comboBox_currentStringChanged(QString)));
    
    
    void MainWindow::on_comboBox_currentStringChanged(const QString &arg1)
    {
        QString str = ui.Task_Selection_comboBox->currentText();
        qDebug()<<"Task is :"<<str;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • Analysis Task为任务解析按钮,需要对任务进行解析,调用的是Python文件

    main_window.hpp

    public Q_SLOTS:
        /******************************************
        ** Task Selection/Analysis
        *******************************************/
        void Call_Task_Analysis_Python();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    main_window.cpp

    调研python文件就采用如下的格式,但这种类型命令只能调用普通python文件,不可以调用那些需要激活虚拟环境再执行的python文件。后续会给出如何先激活虚拟环境,然后再调用python文件

    QObject::connect(ui.Analysis_Task_Button, //先调python
                     SIGNAL(clicked()),
                     this,
                     SLOT(Call_Task_Analysis_Python()));
                     
    void MainWindow::Call_Task_Analysis_Python()
    {
        qDebug()<<"void MainWindow::Call_Task_Analysis_Python()";
        system("gnome-terminal --tab -- python '/home/robot/catkin_ws/devel/Task_Analysis.py'&");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • Open Camera View是开启子线程的按钮,目前不需要,开启相机画面只需要connect ROS话题即可就可以

    main_window.hpp

    public Q_SLOTS:
        void display_camera_view(cv::Mat image);
    
    • 1
    • 2

    main_window.cpp

    // 信号和槽的绑定
    QObject::connect(&qnode,
                     SIGNAL(Camera_ViewSingal(cv::Mat)),
                     this,
                     SLOT(display_camera_view(cv::Mat)),
                     Qt::BlockingQueuedConnection);
    // 槽函数的实现
    void MainWindow::display_camera_view(cv::Mat image)
    {
        qDebug()<<"void MainWindow::display_camera_view(cv::Mat image)";
        cv::imwrite("/home/robot/xinjianjia_qt/capture.jpg",image);//一直保存
        cv::imwrite("/home/robot/xinjianjia_qt/Tail.jpg",image);//一直保存
        cv::Mat rgb;
        QImage img;
    
        if(image.channels() == 3) //如果是三通道需要先转为RGB才转为QImage
        {
          cv::cvtColor(image,rgb,cv::COLOR_BGR2RGB);//Mat类型的BGR转为QImage类型的RGB
          img = QImage((const unsigned char*)(rgb.data),
                       rgb.cols,
                       rgb.rows,
                       rgb.cols*rgb.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
        else  //如果是单通道可以直接转QImage了
        {
          img = QImage((const unsigned char*)(image.data),
                       image.cols,
                       image.rows,
                       image.cols*image.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
    
        ui.Camera_View_label->setPixmap(QPixmap::fromImage(img));
        ui.Camera_View_label->setScaledContents(true);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    qnode.hpp

    public:
        /******************************************
        ** Camera View
        *******************************************/
        void Camera_Callback_img(const sensor_msgs::ImageConstPtr& msg);//回调函数
        cv::Mat Camera_View_img;
        
    Q_SIGNALS:
    	/******************************************
    	** Camera View
    	*******************************************/
    	void Camera_ViewSingal(cv::Mat); //信号
    	
    private:
        /******************************************
        ** Camera View
        *******************************************/
        image_transport::Subscriber camera_view_sub; //声明订阅者,订阅相机节点的话题
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    qnode.cpp

    需要在QNode::init() 的两个重载函数中,都初始化订阅者camera_view_sub。QNode::run()中的ros::spin()是必须加的,因为我要持续读取相机画面,需要spin接着。

    bool QNode::init() {
    	ros::init(init_argc,init_argv,"qt_gui");
    	if ( ! ros::master::check() ) {
    		return false;
    	}
    	ros::start(); // explicitly needed since our nodehandle is going out of scope.
    	ros::NodeHandle n;
    	// Add your ros communications here.
    	chatter_publisher = n.advertise<std_msgs::String>("chatter", 1000);
    
        /******************************************
        ** Camera View node init
        *******************************************/
        image_transport::ImageTransport it(n);
        camera_view_sub = it.subscribe("/camera/color/image_raw",1,&QNode::Camera_Callback_img,this);
    
    	start();
    	return true;
    
    }
    
    bool QNode::init(const std::string &master_url, const std::string &host_url) {
    	std::map<std::string,std::string> remappings;
    	remappings["__master"] = master_url;
    	remappings["__hostname"] = host_url;
    	ros::init(remappings,"qt_gui");
    	if ( ! ros::master::check() ) {
    		return false;
    	}
    	ros::start(); // explicitly needed since our nodehandle is going out of scope.
    	ros::NodeHandle n;
    	// Add your ros communications here.
    	chatter_publisher = n.advertise<std_msgs::String>("chatter", 1000);
    
        /******************************************
        ** Camera View node init
        *******************************************/
        image_transport::ImageTransport it(n);
        camera_view_sub = it.subscribe("/camera/color/image_raw",1,&QNode::Camera_Callback_img,this);
    
    
    	start();
    	return true;
    }
    
    void QNode::run() {
    
        /******************************************
        ** Camera View node init
        *******************************************/
        ros::NodeHandle n;
        image_transport::ImageTransport it(n);
        camera_view_sub = it.subscribe("/camera/color/image_raw",1,&QNode::Camera_Callback_img,this);
    
        ros::spin();
    
    	std::cout << "Ros shutdown, proceeding to close the gui." << std::endl;
    	Q_EMIT rosShutdown(); // used to signal the gui for a shutdown (useful to roslaunch)
    }
    
    // 回调函数的编写
    void QNode::Camera_Callback_img(const sensor_msgs::ImageConstPtr &msg)
    {
        cv_bridge::CvImagePtr cv_ptr;
        try
        {
           cv_ptr = cv_bridge::toCvCopy(msg,sensor_msgs::image_encodings::BGR8);
           Camera_View_img = cv_ptr->image;
           if(Camera_View_img.empty()){
               qDebug()<<"Camera img is empty";
           }
           Q_EMIT Camera_ViewSingal(Camera_View_img); //信号发出,由SLOT(display_camera_view(cv::Mat))接着
        }
        catch (cv_bridge::Exception &e) {
            ROS_ERROR("Could not convert from '%s' to 'bgr8",msg->encoding.c_str());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • Capture Tools KeyFrame为捕获当前相机拍摄工具的关键帧,捕获到之后,需要调研seg.py文件进行工具检测

    main_window.hpp
    槽函数的声明

    public Q_SLOTS:
        void display_Capture_KeyFrame_Tools();
        void Call_Detection_Python();
    
    • 1
    • 2
    • 3

    main_window.cpp
    信号和槽的绑定

    QObject::connect(ui.Capture_KeyFrame_Button_Tools,
                     SIGNAL(clicked()),
                     this,
                     SLOT(display_Capture_KeyFrame_Tools()));
    
    QObject::connect(ui.Capture_KeyFrame_Button_Tools, //先调20220827_seg.py
                     SIGNAL(clicked()),
                     this,
                     SLOT(Call_Detection_Python()));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    main_window.cpp
    槽函数的实现

    void MainWindow::display_Capture_KeyFrame_Tools()
    {
        qDebug()<<"void MainWindow::display_Capture_KeyFrame()";
        cv::Mat rgb;
        QImage img;
        cv::Mat image = cv::imread("/home/robot/xinjianjia_qt/capture.jpg");
        if(image.empty()){
            qDebug()<<"KeyFrame not be Captured";
        }else {
            qDebug()<<"KeyFrame Capture Successfully";
            cv::imwrite("/home/robot/xinjianjia_qt/code_v2/xitong_seg/rgb/capture.jpg",image);//读取之后,立马保存该image到指定路径
        }
    
        if(image.channels() == 3) //如果是三通道需要先转为RGB才转为QImage
        {
          cv::cvtColor(image,rgb,cv::COLOR_BGR2RGB);//Mat类型的BGR转为QImage类型的RGB
          img = QImage((const unsigned char*)(rgb.data),
                       rgb.cols,
                       rgb.rows,
                       rgb.cols*rgb.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
        else  //如果是单通道可以直接转QImage了
        {
          img = QImage((const unsigned char*)(image.data),
                       image.cols,
                       image.rows,
                       image.cols*image.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
    
    
        ui.Capture_Frame_label->setPixmap(QPixmap::fromImage(img));
        ui.Capture_Frame_label->setScaledContents(true);
    
    }
    
    void MainWindow::Call_Detection_Python()
    {
        qDebug()<<"void MainWindow::Call_Detection_Python()";
        system("gnome-terminal --tab  -- bash -c '/home/robot/tool_seg.sh'&"); //调用shell脚本
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    这里注意一个非常重要的点,那就是调用shell脚本,命令格式就是这样,需要额外写-- bash -c。让我们看看shell脚本里写了什么:

    #!/bin/bash
    
    source  /home/robot/anaconda3/envs/grasp/bin/activate grasp
    
    python /home/robot/xinjianjia_qt/code_v2/xitong_seg/20220827xitong_seg.py
    
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 首先是激活虚拟环境grasp,这里采用的是该教程:在shell文件中启动anaconda的虚拟环境
    • /home/robot/anaconda3/envs/grasp/bin/activate就是该虚拟环境下bin目录下的activate文件
    • 启动虚拟环境之后,直接执行python文件即可
    • 最后exit 0

    20220827xitong_seg.py的内容如下:

    # coding:utf-8
    
    import torchvision
    from Mask_model import maskrcnn as maskrcnn
    import torch
    import numpy as np
    import cv2 as cv
    from torchvision import transforms
    import warnings
    import os
    import argparse
    import json
    
    warnings.filterwarnings('ignore')
    
    # 加载权重
    mask_rcnn = maskrcnn(ckpt_path='/home/robot/xinjianjia_qt/code_v2/xitong_seg/maskrcnn.pth')
    
    mask_rcnn.eval()
    transform_mask = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
    
    train_on_gpu = torch.cuda.is_available()
    
    if train_on_gpu:
        mask_rcnn.cuda()
    
    
    def get_one_hot(labels, num_classes):
        one_hot = np.zeros((1, num_classes), dtype=np.float32)
        one_hot[np.arange(1), labels] = 1
        return one_hot
    
    def seg_mask(img_path, img_save_path):
        frame = cv.imread(img_path)
        blob = transform_mask(frame)
        c, h, w = blob.shape
        input_x = blob.view(1, c, h, w).cuda()
        output = mask_rcnn(input_x)[0]
        boxes = output['boxes'].cpu().detach().numpy()
        scores = output['scores'].cpu().detach().numpy()
        labels = output['labels'].cpu().detach().numpy()
        masks = output['masks'].cpu().detach().numpy()
        print('boxes', boxes)
        print('scores', scores)
        print('labels', labels)
        index = 0
        instances_dict = {}
        boxes_list = []
        instances_list=[]
        for x1, y1, x2, y2 in boxes:
            if scores[index] > 0.1:
                # predict_id = int(coco_class[label_txt])
                print('boxes', x1, y1, x2, y2)
                print('scores', scores[index])
                print('labels', labels[index])
                boxes_list.append([int(x1),int(y1),int(x2),int(y2)])
                # print('boxes center: ', (x1+x2)/2, (y1+y2)/2)
                frame_idx = frame[int(y1):int(y2), int(x1):int(x2)]
                cv.imwrite(img_save_path + '{}.jpg'.format(index), frame_idx)
                index = index+1
        for j in range(len(boxes_list)):
            # instances_list.append("/home/data3/xinjianjia/xitong_recommend/Head/{}.jpg+{}".format(j, boxes_list[j]))
            instances_list.append(["/home/robot/xinjianjia_qt/code_v2/xitong_recommend/Head/{}.jpg".format(j), boxes_list[j]])
    
        instances_dict["/home/robot/xinjianjia_qt/code_v2/xitong_recommend/Tail/Tail.jpg"] = instances_list
        with open("/home/robot/xinjianjia_qt/code_v2/xitong_recommend/test_realscene.json","w") as dump_f:
            json.dump([instances_dict], dump_f)
    
    if __name__ == "__main__":
        path = '/home/robot/xinjianjia_qt/code_v2/xitong_seg/rgb/'
        save_path = '/home/robot/xinjianjia_qt/code_v2/xitong_recommend/Head/'
        file = open("/home/robot/xinjianjia_qt/code_v2/xitong_seg/rgb/test.txt",'w')
        if not os.path.exists(path):
            os.mkdir(path)
        if not os.path.exists(save_path):
            os.mkdir(save_path)
        path_dir = []
        img_name = []
        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                path_dir.append(os.path.join(dirpath, filename))
                img_name.append(filename)
        for img_path, img_name in zip(path_dir, img_name):
            # folder = os.path.exists(save_path + img_name)
            # if not folder:
            #     os.makedirs(save_path + img_name)
            seg_mask(img_path, save_path)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    至此,按钮按下,显示图像,然后调用shell脚步的过程就完成了。

    • Capture Object KeyFrame为捕获当前相机拍摄物体的关键帧,捕获到之后,显示+保存图像即可

    main_window.hpp
    槽函数的声明

    public Q_SLOTS:
        void display_Capture_KeyFrame_Object();
    
    • 1
    • 2

    main_window.cpp
    信号和槽的绑定+槽函数的实现

    QObject::connect(ui.Capture_KeyFrame_Button_Object, //先将tail.jpg显示、保存
                     SIGNAL(clicked()),
                     this,
                     SLOT(display_Capture_KeyFrame_Object()));
    
    void MainWindow::display_Capture_KeyFrame_Object()
    {
        qDebug()<<"void MainWindow::display_Object_Detection_result()";
        cv::Mat rgb;
        QImage img;
        cv::Mat image = cv::imread("/home/robot/xinjianjia_qt/Tail.jpg");//读取面包的捕获结果
    
        if(image.empty()){
            qDebug()<<"Detection_result not be Complete";
        }else {
            qDebug()<<"Detection_result Successfully";
            cv::imwrite("/home/robot/xinjianjia_qt/code_v2/xitong_recommend/Tail/Tail.jpg",image);//保存该image到指定路径
        }
    
        if(image.channels() == 3) //如果是三通道需要先转为RGB才转为QImage
        {
          cv::cvtColor(image,rgb,cv::COLOR_BGR2RGB);//Mat类型的BGR转为QImage类型的RGB
          img = QImage((const unsigned char*)(rgb.data),
                       rgb.cols,
                       rgb.rows,
                       rgb.cols*rgb.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
        else  //如果是单通道可以直接转QImage了
        {
          img = QImage((const unsigned char*)(image.data),
                       image.cols,
                       image.rows,
                       image.cols*image.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
    
        ui.Detection_Result_label->setPixmap(QPixmap::fromImage(img));
        ui.Detection_Result_label->setScaledContents(true);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • Tools Recommendation为工具推荐按钮,功能是:调用recommendation.py进行工具推荐,然后将推荐结果显示在页面上

    main_window.hpp
    槽函数的声明

    public Q_SLOTS:
        /******************************************
        ** Tools Recommendation
        *******************************************/
        void Call_Recommendation_Python();
        void display_Tools_Recommendation_result();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    main_window.cpp
    信号和槽的绑定+槽函数的实现

    /******************************************
    ** Tools Recommendation
    *******************************************/
    QObject::connect(ui.Tools_Recommendation_Button, //先调python
    	               SIGNAL(clicked()),
    	               this,
    	               SLOT(Call_Recommendation_Python()));
    
    QObject::connect(ui.Tools_Recommendation_Button, //然后再显示
    	               SIGNAL(clicked()),
    	               this,
    	               SLOT(display_Tools_Recommendation_result()));
    
    /******************************************
    ** Tools Recommendation
    *******************************************/
    void MainWindow::Call_Recommendation_Python()
    {
        qDebug()<<"void MainWindow::Call_Recommendation_Python()";
        system("gnome-terminal --tab  -- bash -c '/home/robot/tool_recommend.sh'&");
    }
    
    void MainWindow::display_Tools_Recommendation_result()
    {
        qDebug()<<"void MainWindow::display_Tools_Recommendation_result()";
        cv::Mat rgb;
        QImage img;
        cv::Mat image = cv::imread("/home/robot/xinjianjia_qt/code_v2/xitong_recommend/recommend_result.jpg");//读取推荐的结果
    
        if(image.empty()){
            qDebug()<<"Tools_Recommendation_result not be Complete";
        }else {
            qDebug()<<"Tools_Recommendation_result Successfully";
        }
    
        if(image.channels() == 3) //如果是三通道需要先转为RGB才转为QImage
        {
          cv::cvtColor(image,rgb,cv::COLOR_BGR2RGB);//Mat类型的BGR转为QImage类型的RGB
          img = QImage((const unsigned char*)(rgb.data),
                       rgb.cols,
                       rgb.rows,
                       rgb.cols*rgb.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
        else  //如果是单通道可以直接转QImage了
        {
          img = QImage((const unsigned char*)(image.data),
                       image.cols,
                       image.rows,
                       image.cols*image.channels(),
                       QImage::Format_RGB888);
          qDebug()<<"Mat BGR convert QImage RGB successful";
        }
    
        ui.Tools_Recommendation_label->setPixmap(QPixmap::fromImage(img));
        ui.Tools_Recommendation_label->setScaledContents(true);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    /home/robot/tool_recommend.sh文件如下,原理跟上一个类似。

    #!/bin/bash
    
    source  /home/robot/anaconda3/envs/grasp/bin/activate grasp
    
    python /home/robot/xinjianjia_qt/code_v2/xitong_recommend/recommend_tools.py
    
    exit 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    recommend_tools.py内容如下:

    # -*- coding:utf-8 -*-
    
    import argparse
    import os
    import torch
    import torch.nn as nn
    import torch.optim as optim
    import json
    import numpy as np
    from PIL import Image, ImageDraw
    from torchvision import transforms
    
    
    f_dim = 2048
    num_class = 37
    
    BASE = '/home/robot/xinjianjia_qt/code_v2/xitong_recommend/'
    
    TRAINED_MODEL = BASE + 'epoch_8.pth'#  test_sep_pos_neg_dis.py  dis mse,只有pos和neg,没有mid,用的随机方法产生的
    val_file = BASE + 'test_realscene.json'
    
    mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]
    INPUT_SIZE = 224
    
    #os.environ["CUDA_VISIBLE_DEVICES"] = '1'#
    def load_checkpoint(filepath):
        checkpoint = torch.load(filepath)
        model = checkpoint['model']  # 提取网络结构
        model.load_state_dict(checkpoint['model_state_dict'])  # 加载网络权重参数
        for parameter in model.parameters():
            parameter.requires_grad = False
        model.eval()
        return model
    
    def get_test_transform(mean=mean, std=std, size=0):
        return transforms.Compose([
            Resize((int(size * (256 / 224)), int(size * (256 / 224)))),
            transforms.CenterCrop(size),
            transforms.ToTensor(),
            transforms.Normalize(mean=mean, std=std),
        ])
    class Resize(object):
        def __init__(self, size, interpolation=Image.BILINEAR):
            self.size = size
            self.interpolation = interpolation
    
        def __call__(self, img):
            # padding
            ratio = self.size[0] / self.size[1]
            w, h = img.size
            if w / h < ratio:
                t = int(h * ratio)
                w_padding = (t - w) // 2
                img = img.crop((-w_padding, 0, w+w_padding, h))
            else:
                t = int(w / ratio)
                h_padding = (t - h) // 2
                img = img.crop((0, -h_padding, w, h+h_padding))
    
            img = img.resize(self.size, self.interpolation)
    
            return img
    
    
    def valData(val_file):
        with open(val_file, encoding='utf-8-sig', errors='ignore') as f:
            data = json.load(f, strict=False)
        data_keys = []
        for i in range(len(data)):
            data_keys.append(list(data[i].keys()))
        return data, data_keys
    
    
    network = load_checkpoint(TRAINED_MODEL)
    def valItem(data, data_keys,i,num_classes):
        '''返回一个键对应的的所有三元组,就是一个batch;适用于计算头结点和尾节点的特征距离(cos距离和MSE距离)的方法的测试
        '''
        [tail] = data_keys[i]
        [head] = list(data[i].values())
        [img_h_list, c_h_list, img_t_list, c_t_list, c_r_list, c_h_idx_list, c_t_idx_list, c_r_idx_list] = [[],[],[],[],[],[],[],[]]
        for j in range(len(head)):
            # print(head[j])
            img_head_path = head[j][0]
            img_tail_path = tail
            img_h = Image.open(img_head_path).convert('RGB')
            img_t = Image.open(img_tail_path).convert('RGB')
            img_h = get_test_transform(size=INPUT_SIZE)(img_h).unsqueeze(0)
            img_t = get_test_transform(size=INPUT_SIZE)(img_t).unsqueeze(0)
            #######
            img_h_list.append(img_h)
            img_t_list.append(img_t)
        return img_h_list, c_h_list, img_t_list, c_t_list, c_h_idx_list, c_t_idx_list, c_r_list, c_r_idx_list
    
    if torch.cuda.is_available():
        network.cuda()
    ##########################################
    
    
    data, data_keys = valData(val_file)
    
    def sep_task():
        dict_idx = 0
        img_h_list, c_h_list, img_t_list, c_t_list, c_h_idx_list, c_t_idx_list, c_r_list, c_r_idx_list \
            = valItem(data, data_keys, dict_idx, num_class)
        score_batch = []
        for i in range(len(img_h_list)):  # 一个batch
            head = img_h_list[i].cuda()
            tail = img_t_list[i].cuda()
            score, logits_h, logits_t = network.test(head, tail)
            # print("score:",score)
            score_batch.append(score)
        s_i = torch.topk(torch.tensor(score_batch).detach(), 1, largest=False)[1].tolist()
        print(s_i, )#[9]
        recommned_tool_box = list(data[0].values())[0][s_i[0]]
        # print("recommned_tool_box", recommned_tool_box)
        recommned_box =  recommned_tool_box[1]
        img = Image.open("/home/robot/xinjianjia_qt/code_v2/xitong_seg/rgb/capture.jpg").convert('RGB')
        a = ImageDraw.ImageDraw(img)  # 用a来表示右侧这段#
        # print(int(float(recommned_box[0])), int(float(recommned_box[1])), int(float(recommned_box[2])), int(float(recommned_box[3])))
        # print(recommned_box)
        a.rectangle((recommned_box[0],recommned_box[1],recommned_box[2],recommned_box[3]), outline='red', width=10)  # 在100,150起点画长800宽200的图形,填充白色,边框黑色,边框像素为1
        img.save("/home/robot/xinjianjia_qt/code_v2/xitong_recommend/recommend_result.jpg")
        file = open("/home/robot/xinjianjia_qt/code_v2/xitong_seg/rgb/test1.txt",'w')
    
    
    sep_task()#@hits N
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    成果展示

    在这里插入图片描述

  • 相关阅读:
    MySQ漏洞修复
    计算机网络-网络层(移动IP通信过程,网络层设备路由器,路由表与路由转发)
    UnityShader-深度纹理(理解以及遇到的问题)
    云数据库 Redis 性能深度评测(阿里云、华为云、腾讯云、百度智能云)
    promise返回值多层嵌套
    信号处理中简单实用的方法——提取信号中的包络
    统计学-双变量相关分析-相关系数、相关比、克莱姆相关系数
    vue学习之基本用法
    Qwt-QwtPlot类详解
    Helm简易安装使用
  • 原文地址:https://blog.csdn.net/weixin_45452278/article/details/126617938