Steffan
Steffan

Reputation: 185

Passing a High dimensional matrix from C++ to R

I want to pass a M \times N \times K matrix from C++ to R. Basically I have a cv::Mat object and I want to pass it efficiently in R. It is not very difficult to convert a M \times N cv::Mat to arma::Mat or Rcpp::NumericMatrix. But it will be great if I can get a wrapper for higher dimensional matrices as well.

Upvotes: 1

Views: 237

Answers (1)

Artem Klevtsov
Artem Klevtsov

Reputation: 9433

Now you can use the xtensor package from CRAN.

// [[Rcpp::plugins(cpp14)]]
// [[Rcpp::plugins(opencv)]]
// [[Rcpp::depends(xtensor)]]

#include <xtensor/xadapt.hpp>
#include <xtensor-r/rtensor.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <Rcpp.h>

using xtensor3d = xt::xtensor<double, 3>;
using rtensor3d = xt::rtensor<double, 3>;

template <typename T, int NCH, typename XT=xt::xtensor<T,3,xt::layout_type::column_major>>
XT to_xt(const cv::Mat_<cv::Vec<T, NCH>>& src) {
    std::vector<int> shape = {src.rows, src.cols, NCH};
    size_t size = src.total() * NCH;
    XT res = xt::adapt((T*) src.data, size, xt::no_ownership(), shape);
    return res;
}

// [[Rcpp::export]]
rtensor3d read_image(std::string file) {
    cv::Mat img = cv::imread(file);
    img.convertTo(img, CV_32FC3);
    xtensor3d res = img.channels() == 3 ? to_xt<double, 3>(img) : to_xt<double, 1>(img);
    return res;
}

opencv Rcpp plugin should be exported as:

Rcpp::registerPlugin("opencv", function() {
    pkg_config_name <- "opencv4"
    pkg_config_bin <- Sys.which("pkg-config")
    checkmate::assert_file_exists(pkg_config_bin, access = "x")
    list(env = list(
        PKG_CXXFLAGS = system(paste(pkg_config_bin, "--cflags", pkg_config_name), intern = TRUE),
        PKG_LIBS = system(paste(pkg_config_bin, "--libs", pkg_config_name), intern = TRUE)
    ))
})

Upvotes: 1

Related Questions