преобразование xtensor xarray в мат opencv вперед и назад в cpp - PullRequest
0 голосов
/ 10 июля 2020

Ищу быстрый и надежный способ конвертировать xt::xarray <-> cv::mat / cv::mat1f. В моем конкретном случае c я выбираю 2 измерения и тип данных float, но, конечно, приветствуются более общие ответы!

На данный момент у меня есть поэлементное решение. Это довольно надежно, но я был бы признателен за более быстрый. У меня проблемы с реализацией этого одного.

Поэлементные решения

#include <iostream>

#include <opencv2/opencv.hpp>

#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xadapt.hpp"


cv::Mat xarray_to_mat_elementwise(xt::xarray<float> xarr)
{
    int ndims = xarr.dimension();
    assert(ndims == 2  && "can only convert 2d xarrays");
    int nrows = xarr.shape()[0];
    int ncols = xarr.shape()[1];
    cv::Mat mat(nrows, ncols, CV_32FC1);
    for (int rr=0; rr<nrows; rr++)
    {
        for (int cc=0; cc<ncols; cc++)
        {
            mat.at<float>(rr, cc) = xarr(rr, cc);
        }
    }
    return mat;
}

xt::xarray<float> mat_to_xarray_elementwise(cv::Mat mat)
{
    int ndims = mat.dims;
    assert(ndims == 2  && "can only convert 2d xarrays");
    int nrows = mat.rows;
    int ncols = mat.cols;
    xt::xarray<float> xarr = xt::empty<float>({nrows, ncols});
    for (int rr=0; rr<nrows; rr++)
    {
        for (int cc=0; cc<ncols; cc++)
        {
            xarr(rr, cc) = mat.at<float>(rr, cc);
        }
    }
    return xarr;
}



int main()
{
    int nrows = 2, ncols = 3;
    float data[150];
    for (int i=0; i<nrows * ncols; i++)
    {
        data[i] = .1 * i;
    }

    cv::Mat mat (nrows, ncols, CV_32FC1, data, 0);
    std::cout << "mat:\n" << mat << std::endl;

    xt::xarray<float> xarr = xt::adapt(
        (float*) data, nrows * ncols, xt::no_ownership(), std::vector<std::size_t> {nrows, ncols});
    std::cout << "xarr:\n" << xarr << std::endl;

    cv::Mat mat2_ew = xarray_to_mat_elementwise(xarr);
    std::cout << "mat2_ew (from xt::xarray):\n" << mat2_ew << std::endl;

    xt::xarray<float> xarr2_ew = mat_to_xarray_elementwise(mat);
    std::cout << "xarr2_ew (from cv::mat):\n" << xarr2_ew << std::endl;

    return 0;
}

вывод

mat:
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
xarr:
{{ 0.      ,  0.1     ,  0.2     },
 { 0.3     ,  0.4     ,  0.5     }}
mat2_ew (from xt::xarray):
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
xarr2_ew (from cv::mat):
{{ 0.      ,  0.1     ,  0.2     },
 { 0.3     ,  0.4     ,  0.5     }}

1 Ответ

0 голосов
/ 10 июля 2020

Решение на основе указателей

Преобразует xt::xarray <-> cv::mat(1f) на основе указателей на базовые данные исходного объекта. Копирование данных не требуется, но может потребоваться освобождение памяти.

Указатель на данные пользователя. Конструкторы матриц, которые принимают данные и параметры шага, не выделяют матричные данные. Вместо этого они просто инициализируют заголовок матрицы, который указывает на указанные данные, что означает, что данные не копируются. Эта операция очень эффективна и может использоваться для обработки внешних данных с помощью функций OpenCV. Внешние данные не удаляются автоматически, поэтому вам следует позаботиться об этом.

Сравните документацию .

Не используйте вызов по значению , при преобразовании: Если конвертировать преобразователь в функцию (сравните xarray_to_mat), будьте осторожны, чтобы использовать вызов по ссылке. В противном случае указатель xarr.data() указывает на невыделенную позицию памяти.

xt :: layout_type :: column_major: Конечно, column_major layout в xtensor создает некоторые проблемы. Возможно, в этом случае имеет смысл вернуться к elementwise assignment.

#include <iostream>
#include <opencv2/opencv.hpp>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xadapt.hpp"

/**
 * Converts xt::xarray to cv::mat.
 *
 * First few elements are wrong (not reproducible).
 * This can be repaired by using call by reference: xt::xarray<float> xarr -> xt::xarray<float> & xarr
 */
cv::Mat xarray_to_mat(xt::xarray<float> xarr)
{
    cv::Mat mat (xarr.shape()[0], xarr.shape()[1], CV_32FC1, xarr.data(), 0);
    return mat;
}

xt::xarray<float> mat_to_xarray(cv::Mat mat)
{
    xt::xarray<float> res = xt::adapt(
        (float*) mat.data, mat.cols * mat.rows, xt::no_ownership(), std::vector<std::size_t> {mat.rows, mat.cols});
    return res;
}

cv::Mat xarray_to_mat_elementwise(xt::xarray<float> xarr)
{
    int ndims = xarr.dimension();
    assert(ndims == 2  && "can only convert 2d xarrays");
    int nrows = xarr.shape()[0];
    int ncols = xarr.shape()[1];
    cv::Mat mat(nrows, ncols, CV_32FC1);
    for (int rr=0; rr<nrows; rr++)
    {
        for (int cc=0; cc<ncols; cc++)
        {
            mat.at<float>(rr, cc) = xarr(rr, cc);
        }
    }
    return mat;
}

xt::xarray<float> mat_to_xarray_elementwise(cv::Mat mat)
{
    int ndims = mat.dims;
    assert(ndims == 2  && "can only convert 2d xarrays");
    int nrows = mat.rows;
    int ncols = mat.cols;
    xt::xarray<float> xarr = xt::empty<float>({nrows, ncols});
    for (int rr=0; rr<nrows; rr++)
    {
        for (int cc=0; cc<ncols; cc++)
        {
            xarr(rr, cc) = mat.at<float>(rr, cc);
        }
    }
    return xarr;
}

int main()
{
    int nrows = 2, ncols = 3;
    float data[150];
    for (int i=0; i<nrows * ncols; i++)
    {
        data[i] = .1 * i;
    }

    cv::Mat mat (nrows, ncols, CV_32FC1, data, 0);
    std::cout << "mat:\n" << mat << std::endl;

    xt::xarray<float> xarr = xt::adapt(
        (float*) data, nrows * ncols, xt::no_ownership(), std::vector<std::size_t> {nrows, ncols});
    std::cout << "xarr:\n" << xarr << std::endl;

    cv::Mat mat2 (nrows, ncols, CV_32FC1, xarr.data(), 0);
    std::cout << "mat2 (from xt::xarray, works):\n" << mat2 << std::endl;

    cv::Mat mat3 = xarray_to_mat(xarr);
    std::cout << "mat3 (from xt::xarray, call by value -> fails):\n" << mat3 << std::endl;

    xt::xarray<float> xarr2 = mat_to_xarray(mat);
    std::cout << "xarr2 (from cv::mat):\n" << xarr2 << std::endl;

    std::cout << "\n=========== works for cv::mat1f analoguous ===========\n" << std::endl;

    cv::Mat1f mat_1f (nrows, ncols, data, 0);
    std::cout << "mat_1f:\n" << mat_1f << std::endl;

    cv::Mat1f mat_1f_2 (nrows, ncols, (float*) xarr.data(), 0);
    std::cout << "mat_1f_2 (from xt::xarray, works):\n" << mat_1f_2 << std::endl;

    std::cout << "\n=========== column_major layout in xtensor ===========\n" << std::endl;

    xt::xarray<float, xt::layout_type::column_major> xarr_cm = xt::adapt(
        (float*) data, nrows * ncols, xt::no_ownership(), std::vector<std::size_t> {nrows, ncols});
    std::cout << "xarr_cm:\n" << xarr_cm << std::endl;

    cv::Mat mat_cm (nrows, ncols, CV_32FC1, xarr_cm.data(), 0);
    std::cout << "mat_cm (from xt::xarray, pointer based -> fails):\n" << mat_cm << std::endl;

    cv::Mat mat_cm_ew = xarray_to_mat_elementwise(xarr_cm);
    std::cout << "mat_cm_ew (from xt::xarray, elementwise -> works):\n" << mat_cm_ew << std::endl;

    return 0;
}

** вывод **

mat:
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
xarr:
{{ 0.      ,  0.1     ,  0.2     },
 { 0.3     ,  0.4     ,  0.5     }}
mat2 (from xt::xarray, works):
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
mat3 (from xt::xarray, call by value -> fails):
[4.3654774e-38, 0, 0.2;
 0.30000001, 0.40000001, 0.5]
xarr2 (from cv::mat):
{{ 0.      ,  0.1     ,  0.2     },
 { 0.3     ,  0.4     ,  0.5     }}

=========== works for cv::mat1f analoguous ===========

mat_1f:
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
mat_1f_2 (from xt::xarray, works):
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]

=========== column_major layout in xtensor ===========

xarr_cm:
{{ 0.      ,  0.1     ,  0.2     },
 { 0.3     ,  0.4     ,  0.5     }}
mat_cm (from xt::xarray, pointer based -> fails):
[0, 0.30000001, 0.1;
 0.40000001, 0.2, 0.5]
mat_cm_ew (from xt::xarray, elementwise -> works):
[0, 0.1, 0.2;
 0.30000001, 0.40000001, 0.5]
...