Last active
November 18, 2021 20:24
-
-
Save chappjc/7385fa9cd5d8d8da7db7 to your computer and use it in GitHub Desktop.
Add comments/documentation. Append checkHandle() and getHandle() definitions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// pylon_mex_camera_interface.cpp | |
// Code for StackOverflow Q&A: http://stackoverflow.com/a/32529850/2778484 | |
// by Jon Chappelow (chappjc) | |
// For example MATLAB-side implementation (class wrapper), see https://github.com/chappjc/MATLAB/tree/master/cppClass | |
// Use: | |
// 1. Enumerate the different actions (e.g. New, Delete, Insert, etc.) in the | |
// Actions enum. For each enumerated action, specify a string (e.g. | |
// "new", "delete", "insert", etc.) to be passed as the first argument to | |
// the MEX function in MATLAB. | |
// 2. Customize the handling for each action in the switch statement in the | |
// body of mexFunction (e.g. call the relevant C++ class method). | |
// | |
// Implementation: | |
// | |
// For your C++ class, class_type, mexFunction uses static data storage to hold | |
// a persistent (between calls to mexFunction) table of integer handles and | |
// smart pointers to dynamically allocated class instances. A std::map is used | |
// for this purpose, which facilitates locating known handles, for which only | |
// valid instances of your class are guaranteed to exist: | |
// | |
// typedef unsigned int handle_type; | |
// std::map<handle_type, std::shared_ptr<class_type>> | |
// | |
// A std::shared_ptr takes care of deallocation when either (1) a table element | |
// is erased via the "delete" action or (2) the MEX-file is unloaded. | |
// | |
// To prevent the MEX-file from unloading while a MATLAB class instances exist, | |
// mexLock is called each time a new C++ class instance is created, adding to | |
// the MEX-file's lock count. Each time a C++ instance is deleted mexUnlock is | |
// called, removing one lock from the lock count. | |
// | |
// Requirements: | |
// | |
// A modern compiler with the following C++11 features: | |
// - shared_ptr | |
// - auto | |
// - enum class | |
// - initializer_list (for const map initialization) | |
// (VS2013, recent GCC possibly with -std=c++11, Clang since 3.1) | |
#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! //////////////////////////////// | |
} | |
handle_type getHandle(int nrhs, const mxArray *prhs[]) | |
{ | |
if (nrhs < 2 || mxGetNumberOfElements(prhs[1]) != 1) // mxIsScalar in R2015a+ | |
mexErrMsgTxt("Specify an instance with an integer handle."); | |
return static_cast<handle_type>(mxGetScalar(prhs[1])); | |
} | |
instanceMap_type::const_iterator checkHandle(const instanceMap_type& m, handle_type h) | |
{ | |
auto it = m.find(h); | |
if (it == m.end()) { | |
std::stringstream ss; ss << "No instance corresponding to handle " << h << " found."; | |
mexErrMsgTxt(ss.str().c_str()); | |
} | |
return it; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment