Commit 60eae2ea authored by Patrik Huber's avatar Patrik Huber

Support more conversions for Matlab <=> cv::Mat

* Can now return any cv::Mat to Matlab
* Can convert 3 and 4-channel images from Matlab to cv::Mat
parent c68ffec6
...@@ -33,30 +33,33 @@ ...@@ -33,30 +33,33 @@
namespace mexplus { namespace mexplus {
// Note/Todo: Currently only works for 4-chan U8 images /**
* @brief Convert a cv::Mat to a Matlab matrix.
*
* Conversion should work for matrices of any type, with any number of channels.
* The function creates a new MxArray of the same data type and copies the data over.
*
* Note: Even non-standard step/stride sizes should work, but that is not tested.
*/
template <typename InputScalar, typename OutputScalar> template <typename InputScalar, typename OutputScalar>
void deepcopy_and_transpose(const cv::Mat& in, MxArray& out) void deepcopy_and_transpose(const cv::Mat& in, MxArray& out)
{ {
if (in.channels() != 4)
{
mexErrMsgIdAndTxt("eos:matlab", "Currently only works for 4-channel U8 images.");
}
const std::size_t num_rows = in.rows; const std::size_t num_rows = in.rows;
const std::size_t num_cols = in.cols; const std::size_t num_cols = in.cols;
const std::size_t num_chans = in.channels(); const std::size_t num_chans = in.channels();
out = MxArray::Numeric<std::uint8_t>({num_rows, num_cols, num_chans}); out = MxArray::Numeric<OutputScalar>({num_rows, num_cols, num_chans});
for (std::size_t c = 0; c < num_cols; ++c) { for (std::size_t c = 0; c < num_cols; ++c) { // outer loop over rows would be faster if OpenCV stores data row-major?
for (std::size_t r = 0; r < num_rows; ++r) { for (std::size_t r = 0; r < num_rows; ++r) {
for (std::size_t chan = 0; chan < num_chans; ++chan) { for (std::size_t chan = 0; chan < num_chans; ++chan) {
out.set(std::vector<mwIndex>{r, c, chan}, in.at<cv::Vec4b>(r, c)[chan]); out.set(std::vector<mwIndex>{r, c, chan}, in.ptr<InputScalar>(r, c)[chan]);
} }
} }
} }
}; };
// Note/Todo: Currently only works for 3-chan U8 images // Note/Todo: Currently only works for 3-chan and 4-chan U8 images
// Matlab stores matrices in col - major order in memory, OpenCV stores them in row - major.Thus, we copy & transpose...
template <typename InputScalar, typename OutputScalar> template <typename InputScalar, typename OutputScalar>
void deepcopy_and_transpose(const MxArray& in, cv::Mat& out) void deepcopy_and_transpose(const MxArray& in, cv::Mat& out)
{ {
...@@ -68,19 +71,18 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out) ...@@ -68,19 +71,18 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out)
auto s = in.size(); auto s = in.size();
auto n = in.isNumeric(); auto n = in.isNumeric();
const auto num_channels = [num_dims = in.dimensionSize()]() { const auto num_channels = [num_dims = in.dimensionSize(), dims = in.dimensions()]() {
if (num_dims == 1 || num_dims == 2) if (num_dims == 1 || num_dims == 2)
{ {
return 1; return std::size_t(1);
} }
else if (num_dims == 3) { else if (num_dims == 3) { // the dims vector has 3 entries, the 3rd entry tell us if there are 3 or 4 channels.
return 3; return dims[2];
} }
// What about 4-channel images, what dimensionSize() will they have?
}(); }();
const auto actual_cols = [&num_channels, num_cols = in.cols()]() { const auto actual_cols = [&num_channels, num_cols = in.cols()]() {
if (num_channels == 3) if (num_channels == 3 || num_channels == 4) // aargh... simplify all this... just use in.dimensions()?
{ {
return num_cols / num_channels; return num_cols / num_channels;
} }
...@@ -94,6 +96,7 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out) ...@@ -94,6 +96,7 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out)
{ {
cv::Mat outmat; cv::Mat outmat;
// Note: I think this doesn't actually copy the data, does it? // Note: I think this doesn't actually copy the data, does it?
// We construct with (cols, rows) because it'll later get transposed.
cv::Mat inmat(actual_cols, in.rows(), cv::DataType<InputScalar>::type, cv::Mat inmat(actual_cols, in.rows(), cv::DataType<InputScalar>::type,
static_cast<void*>(const_cast<InputScalar*>(in.getData<InputScalar>() + actual_cols * in.rows() * c))); static_cast<void*>(const_cast<InputScalar*>(in.getData<InputScalar>() + actual_cols * in.rows() * c)));
inmat.convertTo(outmat, cv::DataType<OutputScalar>::type); inmat.convertTo(outmat, cv::DataType<OutputScalar>::type);
...@@ -104,24 +107,37 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out) ...@@ -104,24 +107,37 @@ void deepcopy_and_transpose(const MxArray& in, cv::Mat& out)
}; };
/** /**
* @brief Convert a 4-channel uchar cv::Mat to a Matlab matrix. * @brief Convert a cv::Mat to a Matlab matrix.
* *
* Currently, only CV_8UC4 matrices are supported. Don't try to convert anything else. * Conversion should work for matrices of type CV_8U, CV_32F and CV_64F, with any number of channels.
* Todo: Add some error detection, it will silently fail or crash when the type/channels are
* not correct.
*/ */
template <> template <>
mxArray* MxArray::from(const cv::Mat& opencv_matrix) mxArray* MxArray::from(const cv::Mat& opencv_matrix)
{ {
MxArray out_array; MxArray out_array;
deepcopy_and_transpose<std::uint8_t, std::uint8_t>(opencv_matrix, out_array); if (opencv_matrix.depth() == CV_8U)
{
deepcopy_and_transpose<std::uint8_t, std::uint8_t>(opencv_matrix, out_array);
}
else if (opencv_matrix.depth() == CV_32F)
{
deepcopy_and_transpose<float, float>(opencv_matrix, out_array);
}
else if (opencv_matrix.depth() == CV_64F)
{
deepcopy_and_transpose<double, double>(opencv_matrix, out_array);
}
else
{
mexErrMsgIdAndTxt("eos:matlab", "Can only convert CV_8U, CV_32F and CV_64F matrices at this point.");
}
return out_array.release(); return out_array.release();
}; };
/** /**
* @brief Convert a 3-channel uint8 Matlab matrix to a cv::Mat. * @brief Convert a 3-channel or 4-channel uint8 Matlab matrix to a cv::Mat.
* *
* Currently only works for 3-channel uint8 Matlab matrices (i.e. images)! * Currently only works for 3-channel and 4-channel uint8 Matlab matrices (i.e. images and isomaps).
* Todo: Add some error detection, it will silently fail or crash when the type/channels are * Todo: Add some error detection, it will silently fail or crash when the type/channels are
* not correct. * not correct.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment