定期从mexFile发送数据到MATLAB [英] Periodically send data to MATLAB from mexFile

查看:279
本文介绍了定期从mexFile发送数据到MATLAB的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在正在使用数据采集工具在MATLAB中完全编写
。这是我的同事的希望,我写这个东西在MATLAB
,以便他们可以扩展和修改它。

I'm working right now on a Data Acquisition Tool completely written in MATLAB. It was the wish of my colleagues that i write this thing in MATLAB so that they can expand and modify it.

软件需要从两个连接的USB相机抓取图片。
这些摄像机的API是用C ++编写的,并记录在文档中 - > 这里

The Software needs to grab a picture from two connected USB cameras. The API for these cameras is written in C++ and is documented -> Here.

这里是问题:
当我写一个mex文件抓住一个图片,它包括
初始化和配置加载
需要很长时间的摄像机。当我想抓住图片
这种方式需要MATLAB超过1秒执行任务。
一旦初始化,摄像机能够记录和发送100 fps。
我需要的最小帧速率是10 fps。
我需要能够发送每个记录的图片回
到MATLAB。因为需要
采集工具的记录会话需要约12小时,而我们
需要一个带有一些轻微的后处理的实况屏幕。

Here is the Problem: When i write a mex file which grabs a picture it includes the initialization and configuration-loading of the cameras which takes a long time. When i want to grab the pictures this way it takes MATLAB over 1 second to perform the task. The cameras are able, once initialized, to record and send 100 fps. The minimum frame rate i need is 10 fps. I need to be able to send every recorded picture back to MATLAB. Because the recording session for which the Acquisition Tool is needed takes approx 12 hours and we need a Live Screen with some slight PostProcessing.

它可能在mex文件中生成一个循环,
将数据发送到MATLAB,然后等待来自MATLAB
的返回信号并继续?
这种方式我可以初始化摄像机,并定期发送
的图像到MATLAB。

Is it possible to generate a loop within the mex File which sends data to MATLAB, then waits for a return signal from MATLAB and continues ? This way i could initialize the cameras and send periodically the images to MATLAB.

我是一个初学者在C + +,我不知道一个基本的概念为什么这
是不可能的。

I'am a Beginner in C++ and it is quite possible that i don't understand a fundamental concept why this is not possible.

感谢您的任何建议或来源,我可以看看。

Thank you for any advice or sources where i could look.

请在下面找到使用Basler提供的Pylon API初始化摄像机
的代码。

Please find below the Code which initializes the Cameras using the Pylon API provided by Basler.

// Based on the Grab_MultipleCameras.cpp Routine from Basler
/*
This routine grabs one frame from 2 cameras connected
via two USB3 ports. It directs the Output to MATLAB.
*/

// Include files to use the PYLON API.
#include <pylon/PylonIncludes.h>
#include <pylon/usb/PylonUsbIncludes.h>
#include <pylon/usb/BaslerUsbInstantCamera.h>
#include <pylon/PylonUtilityIncludes.h>
// Include Files for MEX Generation
#include <matrix.h>
#include <mex.h>   

// Namespace for using pylon objects.
using namespace Pylon;

// We are lazy and use Basler USB namespace
using namespace Basler_UsbCameraParams;

// Standard namespace
using namespace std;

// Define Variables Globally to be remembered between each call
// Filenames for CamConfig
const String_t filenames[] = { "NodeMapCam1.pfs","NodeMapCam2.pfs" };

// Limits the amount of cameras used for grabbing.
static const size_t camerasToUse = 2;

// Create an array of instant cameras for the found devices and 
// avoid exceeding a maximum number of devices.
CBaslerUsbInstantCameraArray cameras(camerasToUse);

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  // Automagically call PylonInitialize and PylonTerminate to ensure the pylon runtime system.
  // is initialized during the lifetime of this object
  PylonAutoInitTerm autoInitTerm;

  try
  {
    // Get the transport layer factory
    CTlFactory& tlFactory = CTlFactory::GetInstance();

    // Get all attached devices and exit application if no device or USB Port is found.
    DeviceInfoList_t devices;
    ITransportLayer *pTL = dynamic_cast<ITransportLayer*>(tlFactory.CreateTl(BaslerUsbDeviceClass));
    if (pTL == NULL)
    {
      throw RUNTIME_EXCEPTION("No USB transport layer available.");
    }

    if (pTL->EnumerateDevices(devices) == 0)
    {
      throw RUNTIME_EXCEPTION("No camera present.");
    }

    // Create and attach all Pylon Devices. Load Configuration
    for (size_t i = 0; i < cameras.GetSize(); ++i)
    {
      cameras[i].Attach(tlFactory.CreateDevice(devices[i]));
    }

    // Open all cameras.
    cameras.Open();

    // Load Configuration and execute Trigger
    for (size_t i = 0; i < cameras.GetSize(); ++i)
    {
      CFeaturePersistence::Load(filenames[i], &cameras[i].GetNodeMap());
    }
    if (cameras[0].IsOpen() && cameras[1].IsOpen())
    {
      mexPrintf("\nCameras are fired up and configuration is applied\n");
      // HERE I WOULD LIKE TO GRAB PICTURES AND SEND THEM
      // PERIODICALLY TO MATLAB.
    }
  }
  catch (GenICam::GenericException &e)
  {
    // Error handling
    mexPrintf("\nAn exception occured:\n");
    mexPrintf(e.GetDescription());
  }

  return;
}


推荐答案

定期回到MATLAB,但你想如何在工作空间(多个2D图像,巨大的3D / 4D阵列,单元格等)?我认为你正在寻找的解决方案是一个有状态的MEX文件,可以启动与'init' 'new'命令,然后用已经初始化的摄像机的'capture'命令重复调用。

You could loop and send images back to MATLAB periodically, but how do you want it to be in the workspace (multiple 2D images, a huge 3D/4D array, cell, etc.)? I think the solution you are looking for is a stateful MEX file, which can be launched with an 'init' or 'new' command, and then called again repeatedly with 'capture' commands for an already initialized camera.

有一个如何在我的GitHub中执行此操作的示例。从 class_wrapper_template.cpp 开始,并针对您的命令( new capture delete 等)。这里有一个粗略且未经测试的示例,说明其核心的外观(也可参见镜像on Gist.GitHub ):

There is an example of how to do this in my GitHub. Start with class_wrapper_template.cpp and modify it for your commands (new, capture, delete, etc.). Here is a rough and untested example of how the core of it might look (also mirrored on Gist.GitHub):

// pylon_mex_camera_interface.cpp
#include "mex.h"
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include <string>
#include <sstream>

////////////////////////  BEGIN Step 1: Configuration  ////////////////////////
// Include your class declarations (and PYLON API).
#include <pylon/PylonIncludes.h>
#include <pylon/usb/PylonUsbIncludes.h>
#include <pylon/usb/BaslerUsbInstantCamera.h>
#include <pylon/PylonUtilityIncludes.h>

// Define class_type for your class
typedef CBaslerUsbInstantCameraArray class_type;

// List actions
enum class Action
{
    // create/destroy instance - REQUIRED
    New,
    Delete,
    // user-specified class functionality
    Capture
};

// Map string (first input argument to mexFunction) to an Action
const std::map<std::string, Action> actionTypeMap =
{
    { "new",        Action::New },
    { "delete",     Action::Delete },
    { "capture",    Action::Capture }
}; // if no initializer list available, put declaration and inserts into mexFunction

using namespace Pylon;
using namespace Basler_UsbCameraParams;

const String_t filenames[] = { "NodeMapCam1.pfs","NodeMapCam2.pfs" };
static const size_t camerasToUse = 2;
/////////////////////////  END Step 1: Configuration  /////////////////////////

// boilerplate until Step 2 below
typedef unsigned int handle_type;
typedef std::pair<handle_type, std::shared_ptr<class_type>> indPtrPair_type; // or boost::shared_ptr
typedef std::map<indPtrPair_type::first_type, indPtrPair_type::second_type> instanceMap_type;
typedef indPtrPair_type::second_type instPtr_t;

// getHandle pulls the integer handle out of prhs[1]
handle_type getHandle(int nrhs, const mxArray *prhs[]);
// checkHandle gets the position in the instance table
instanceMap_type::const_iterator checkHandle(const instanceMap_type&, handle_type);

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {

    // static storage duration object for table mapping handles to instances
    static instanceMap_type instanceTab;

    if (nrhs < 1 || !mxIsChar(prhs[0]))
        mexErrMsgTxt("First input must be an action string ('new', 'delete', or a method name).");

    char *actionCstr = mxArrayToString(prhs[0]); // convert char16_t to char
    std::string actionStr(actionCstr); mxFree(actionCstr);

    for (auto & c : actionStr) c = ::tolower(c); // remove this for case sensitivity

    if (actionTypeMap.count(actionStr) == 0)
        mexErrMsgTxt(("Unrecognized action (not in actionTypeMap): " + actionStr).c_str());

    // If action is not 'new' or 'delete' try to locate an existing instance based on input handle
    instPtr_t instance;
    if (actionTypeMap.at(actionStr) != Action::New && actionTypeMap.at(actionStr) != Action::Delete) {
        handle_type h = getHandle(nrhs, prhs);
        instanceMap_type::const_iterator instIt = checkHandle(instanceTab, h);
        instance = instIt->second;
    }

    //////// Step 2: customize each action in the switch in mexFuction ////////
    switch (actionTypeMap.at(actionStr))
    {
    case Action::New:
    {
        if (nrhs > 1 && mxGetNumberOfElements(prhs[1]) != 1)
            mexErrMsgTxt("Second argument (optional) must be a scalar, N.");

        handle_type newHandle = instanceTab.size() ? (instanceTab.rbegin())->first + 1 : 1;

        // Store a new CBaslerUsbInstantCameraArray in the instance map
        std::pair<instanceMap_type::iterator, bool> insResult = 
            instanceTab.insert(indPtrPair_type(newHandle, std::make_shared<class_type>(camerasToUse)));

        if (!insResult.second) // sanity check
            mexPrintf("Oh, bad news.  Tried to add an existing handle."); // shouldn't ever happen
        else
            mexLock(); // add to the lock count

        // return the handle
        plhs[0] = mxCreateDoubleScalar(insResult.first->first); // == newHandle

        // Get all attached devices and exit application if no device or USB Port is found.
        CTlFactory& tlFactory = CTlFactory::GetInstance();
        // Check if cameras are attached
        ITransportLayer *pTL = dynamic_cast<ITransportLayer*>(tlFactory.CreateTl(BaslerUsbDeviceClass));
        // todo: some checking here... (pTL == NULL || pTL->EnumerateDevices(devices) == 0)

        // Create and attach all Pylon Devices. Load Configuration
        CBaslerUsbInstantCameraArray &cameras = *instance;
        DeviceInfoList_t devices;
        for (size_t i = 0; i < cameras.GetSize(); ++i) {
          cameras[i].Attach(tlFactory.CreateDevice(devices[i]));
        }

        // Open all cameras.
        cameras.Open();

        // Load Configuration and execute Trigger
        for (size_t i = 0; i < cameras.GetSize(); ++i) {
          CFeaturePersistence::Load(filenames[i], &cameras[i].GetNodeMap());
        }

        if (cameras[0].IsOpen() && cameras[1].IsOpen()) {
            mexPrintf("\nCameras are fired up and configuration is applied\n");

        break;
    }
    case Action::Delete:
    {
        instanceMap_type::const_iterator instIt = checkHandle(instanceTab, getHandle(nrhs, prhs));
        (instIt->second).close(); // may be unnecessary if d'tor does it
        instanceTab.erase(instIt);
        mexUnlock();
        plhs[0] = mxCreateLogicalScalar(instanceTab.empty()); // just info
        break;
    }
    case Action::Capture:
    {
        CBaslerUsbInstantCameraArray &cameras = *instance; // alias for the instance

        // TODO: create output array and capture a frame(s) into it
        plhs[0] = mxCreateNumericArray(...);
        pixel_type* data = (pixel_type*) mxGetData(plhs[0]);
        cameras[0].GrabOne(...,data,...);
        // also for cameras[1]?
        }
    }
    default:
        mexErrMsgTxt(("Unhandled action: " + actionStr).c_str());
        break;
    }
    ////////////////////////////////  DONE!  ////////////////////////////////
}

// See github for getHandle and checkHandle

这个想法是你会调用一次到init:

The idea is that you would call it once to init:

>> h = pylon_mex_camera_interface('new');

然后你可以在MATLAB循环中调用它来获取框架:

Then you would call it in a MATLAB loop to get frames:

>> newFrame{i} = pylon_mex_camera_interface('capture', h);

完成后:

>> pylon_mex_camera_interface('delete', h)

cppclass.m 导出即可轻松完成此操作。有关派生类示例,请参见 pqheap.m

You should wrap this with a MATLAB class. Derive from cppclass.m to do this easily. For a derived class example see pqheap.m.

这篇关于定期从mexFile发送数据到MATLAB的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆