/** Implementation of MxArray.
* @file MxArray.cpp
* @author Kota Yamaguchi
* @date 2012
*/
#include "MxArray.hpp"
namespace {
/// Field names for cv::Moments.
const char *cv_moments_fields[24] = {
"m00", "m10", "m01", "m20", "m11", "m02","m30", "m21", "m12", "m03",
"mu20", "mu11", "mu02", "mu30", "mu21", "mu12", "mu03",
"nu20", "nu11", "nu02", "nu30", "nu21", "nu12", "nu03"};
/// Field names for cv::RotatedRect.
const char *cv_rotated_rect_fields[3] = {"center", "size", "angle"};
/// Field names for cv::TermCriteria.
const char *cv_term_criteria_fields[3] = {"type", "maxCount", "epsilon"};
/// Field names for cv::KeyPoint.
const char *cv_keypoint_fields[6] = {"pt", "size", "angle", "response",
"octave", "class_id"};
/// Field names for cv::DMatch.
const char *cv_dmatch_fields[4] = {"queryIdx", "trainIdx", "imgIdx",
"distance"};
/** Translates data type definition used in MATLAB to that of OpenCV.
* @param classid data type of MATLAB's mxArray. e.g., \c mxDOUBLE_CLASS.
* @return OpenCV's data type. e.g., \c CV_8U.
*/
const ConstMap<mxClassID, int> DepthOf = ConstMap<mxClassID, int>
(mxDOUBLE_CLASS, CV_64F)
(mxSINGLE_CLASS, CV_32F)
(mxINT8_CLASS, CV_8S)
(mxUINT8_CLASS, CV_8U)
(mxINT16_CLASS, CV_16S)
(mxUINT16_CLASS, CV_16U)
(mxINT32_CLASS, CV_32S)
(mxUINT32_CLASS, CV_32S)
(mxLOGICAL_CLASS, CV_8U);
/** Translates data type definition used in OpenCV to that of MATLAB.
* @param depth data depth of OpenCV's Mat class. e.g., \c CV_32F.
* @return data type of MATLAB's mxArray. e.g., \c mxDOUBLE_CLASS.
*/
const ConstMap<int,mxClassID> ClassIDOf = ConstMap<int,mxClassID>
(CV_64F, mxDOUBLE_CLASS)
(CV_32F, mxSINGLE_CLASS)
(CV_8S, mxINT8_CLASS)
(CV_8U, mxUINT8_CLASS)
(CV_16S, mxINT16_CLASS)
(CV_16U, mxUINT16_CLASS)
(CV_32S, mxINT32_CLASS);
/** Comparison operator for sparse matrix elements.
* This functor sorts SparseMat nodes in column-major order.
* Only meant to be used on arrays with 2 dimensions.
*/
struct CompareSparseMatNode {
/// Comparison functor
bool operator () (const cv::SparseMat::Node* rhs,
const cv::SparseMat::Node* lhs) const
{
// sort by column, then by row
if (rhs->idx[1] < lhs->idx[1])
return true;
if (rhs->idx[1] == lhs->idx[1] && rhs->idx[0] < lhs->idx[0])
return true;
return false;
}
};
/// Inverse TermCriteria type map for option processing.
const ConstMap<int, std::string> InvTermCritType = ConstMap<int, std::string>
(cv::TermCriteria::COUNT, "Count")
(cv::TermCriteria::EPS, "EPS")
(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, "Count+EPS");
/// TermCriteria type map for option processing.
const ConstMap<std::string, int> TermCritType = ConstMap<std::string, int>
("Count", cv::TermCriteria::COUNT)
("EPS", cv::TermCriteria::EPS)
("Count+EPS", cv::TermCriteria::COUNT+cv::TermCriteria::EPS);
} // anonymous namespace
int MexErrorHandler(int status, const char *func_name, const char *err_msg,
const char *file_name, int line, void * /*userdata*/)
{
mexErrMsgIdAndTxt("mexopencv:error",
"OpenCV Error:\n"
" Status : %s (%d)\n"
" Message : %s\n"
" Function: %s\n"
" File : <a href=\"matlab:opentoline('%s',%d)\">%s</a>\n"
" Line : %d\n",
cvErrorStr(status), status, err_msg,
(func_name ? func_name : "(unknown)"),
file_name, line, file_name, line);
return 0;
}
MxArray& MxArray::operator=(const MxArray& rhs)
{
if (this != &rhs)
this->p_ = rhs.p_;
return *this;
}
MxArray::MxArray(const int i)
: p_(mxCreateDoubleScalar(static_cast<double>(i)))
{
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
}
MxArray::MxArray(const double d)
: p_(mxCreateDoubleScalar(d))
{
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
}
MxArray::MxArray(const bool b)
: p_(mxCreateLogicalScalar(b))
{
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
}
MxArray::MxArray(const std::string& s)
: p_(mxCreateString(s.c_str()))
{
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
}
#if 0
// - works for multi-channel arrays, but doesnt work for ND-arrays because
// the order of dimensions is not right (row to column major order)
// (the std::swap below only gets it right for 2D arrays).
// - There's another bug regarding multi-channel arrays where mat.channels()
// is limited because of cv::transpose, which is only implemented for a number
// of cases, and asserts that mat.elementSize() <= 32
// (elementSize = sizeof(depth)*nchannels), so for mat.depth()==CV_8U we can
// go up to 32 channels, but for mat.depth()==CV_64F we can only go up to a
// maximum of 4 channels (8*4 == 32)
MxArray::MxArray(const cv::Mat& mat, mxClassID classid, bool transpose)
{
// handle special case of empty input Mat by creating an empty array
classid = (classid == mxUNKNOWN_CLASS) ? ClassIDOf[mat.depth()] : classid;
if (mat.empty()) {
p_ = mxCreateNumericMatrix(0, 0, classid, mxREAL);
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
return;
}
// transpose cv::Mat if needed
cv::Mat input(mat);
if (input.dims == 2 && transpose)
input = input.t();
// Create a new mxArray (of the specified classID) equivalent to cv::Mat
const mwSize nchannels = input.channels();
const int* dims_ = input.size;
std::vector<mwSize> d(dims_, dims_ + input.dims);
d.push_back(nchannels); // mxCreate* ignores trailing singleton dimensions
std::swap(d[0], d[1]);
if (classid == mxLOGICAL_CLASS) {
// OpenCV's logical true is any nonzero, while MATLAB's true is 1.
cv::compare(input, 0, input, cv::CMP_NE);
input.setTo(1, input);
p_ = mxCreateLogicalArray(d.size(), &d[0]);
}
else
p_ = mxCreateNumericArray(d.size(), &d[0], classid, mxREAL);
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
// split input cv::Mat into several single-channel arrays
std::vector<cv::Mat> channels;
channels.reserve(nchannels);
cv::split(input, channels);
// Copy each channel from Mat to mxArray (converting to specified classid),
// as in: p_(:,:,i) <- cast_to_classid_type(channels[i])
std::vector<mwSize> si(d.size(), 0); // subscript index
const int type = CV_MAKETYPE(DepthOf[classid], 1); // destination type
for (mwIndex i = 0; i < nchannels; ++i) {
si[si.size() - 1] = i; // last dim is a channel index
void *ptr = reinterpret_cast<void*>(
reinterpret_cast<size_t>(mxGetData(p_)) +
mxGetElementSize(p_) * subs(si)); // ptr to i-th channel data
cv::Mat m(input.dims, dims_, type, ptr); // only creates Mat header
channels[i].convertTo(m, type); // Write to mxArray through m
}
}
#else
// works for any cv::Mat/cv::MatND (any combination of channels and dimensions)
MxArray::MxArray(const cv::Mat& mat, mxClassID classid, bool)
{
// determine classID of output array
classid = (classid == mxUNKNOWN_CLASS) ? ClassIDOf[mat.depth()] : classid;
// handle special case of empty input Mat by returning 0x0 array
if (mat.empty()) {
// TODO: maybe return empty array of same dimensions 0x1, 1x0x2, ...
p_ = mxCreateNumericMatrix(0, 0, classid, mxREAL);
if (!p_)
mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
return;
}
// Create output mxArray (of specified type), equivalent to the input Mat
const mwSize cn = mat.channels();
const mwSize len = mat.