Added multidimensional RMat::View steps

pull/18720/head
Ruslan Garnov 4 years ago
parent e12adcdf08
commit 724001aa0f
  1. 27
      modules/gapi/include/opencv2/gapi/rmat.hpp
  2. 70
      modules/gapi/src/api/rmat.cpp
  3. 16
      modules/gapi/src/backends/common/gbackend.hpp
  4. 16
      modules/gapi/test/rmat/rmat_test_common.hpp
  5. 130
      modules/gapi/test/rmat/rmat_view_tests.cpp
  6. 9
      modules/gapi/test/s11n/gapi_s11n_tests.cpp

@ -54,11 +54,11 @@ public:
{
public:
using DestroyCallback = std::function<void()>;
using stepsT = std::vector<size_t>;
View() = default;
View(const GMatDesc& desc, uchar* data, size_t step = 0u, DestroyCallback&& cb = nullptr)
: m_desc(desc), m_data(data), m_step(step == 0u ? elemSize()*cols() : step), m_cb(std::move(cb))
{}
View(const GMatDesc& desc, uchar* data, const stepsT& steps = {}, DestroyCallback&& cb = nullptr);
View(const GMatDesc& desc, uchar* data, size_t step, DestroyCallback&& cb = nullptr);
View(const View&) = delete;
View& operator=(const View&) = delete;
@ -70,23 +70,30 @@ public:
const std::vector<int>& dims() const { return m_desc.dims; }
int cols() const { return m_desc.size.width; }
int rows() const { return m_desc.size.height; }
int type() const { return CV_MAKE_TYPE(depth(), chan()); }
int type() const;
int depth() const { return m_desc.depth; }
int chan() const { return m_desc.chan; }
size_t elemSize() const { return CV_ELEM_SIZE(type()); }
template<typename T = uchar> T* ptr(int y = 0, int x = 0) {
return reinterpret_cast<T*>(m_data + m_step*y + x*CV_ELEM_SIZE(type()));
template<typename T = uchar> T* ptr(int y = 0) {
return reinterpret_cast<T*>(m_data + step()*y);
}
template<typename T = uchar> const T* ptr(int y = 0, int x = 0) const {
return reinterpret_cast<const T*>(m_data + m_step*y + x*CV_ELEM_SIZE(type()));
template<typename T = uchar> const T* ptr(int y = 0) const {
return reinterpret_cast<T*>(m_data + step()*y);
}
size_t step() const { return m_step; }
template<typename T = uchar> T* ptr(int y, int x) {
return reinterpret_cast<T*>(m_data + step()*y + step(1)*x);
}
template<typename T = uchar> const T* ptr(int y, int x) const {
return reinterpret_cast<const T*>(m_data + step()*y + step(1)*x);
}
size_t step(size_t i = 0) const { GAPI_DbgAssert(i<m_steps.size()); return m_steps[i]; }
const stepsT& steps() const { return m_steps; }
private:
GMatDesc m_desc;
uchar* m_data = nullptr;
size_t m_step = 0u;
stepsT m_steps = {0u};
DestroyCallback m_cb = nullptr;
};

@ -8,16 +8,68 @@
using View = cv::RMat::View;
namespace {
cv::GMatDesc checkDesc(const cv::GMatDesc& desc) {
if (!desc.dims.empty() && desc.chan != -1) {
cv::util::throw_error(
std::logic_error("Multidimesional RMat::Views with chan different from -1 are not supported!"));
}
return desc;
}
int typeFromDesc(const cv::GMatDesc& desc) {
// In multidimensional case GMatDesc::chan is -1,
// change it to 1 when calling CV_MAKE_TYPE
return CV_MAKE_TYPE(desc.depth, desc.chan == -1 ? 1 : desc.chan);
}
static View::stepsT defaultSteps(const cv::GMatDesc& desc) {
const auto& dims = desc.dims.empty()
? std::vector<int>{desc.size.height, desc.size.width}
: desc.dims;
View::stepsT steps(dims.size(), 0u);
auto type = typeFromDesc(desc);
steps.back() = CV_ELEM_SIZE(type);
for (int i = static_cast<int>(dims.size())-2; i >= 0; i--) {
steps[i] = steps[i+1]*dims[i];
}
return steps;
}
} // anonymous namespace
View::View(const cv::GMatDesc& desc, uchar* data, size_t step, DestroyCallback&& cb)
: m_desc(checkDesc(desc))
, m_data(data)
, m_steps([this, step](){
GAPI_Assert(m_desc.dims.empty());
auto steps = defaultSteps(m_desc);
if (step != 0u) {
steps[0] = step;
}
return steps;
}())
, m_cb(std::move(cb)) {
}
View::View(const cv::GMatDesc& desc, uchar* data, const stepsT &steps, DestroyCallback&& cb)
: m_desc(checkDesc(desc))
, m_data(data)
, m_steps(steps == stepsT{} ? defaultSteps(m_desc): steps)
, m_cb(std::move(cb)) {
}
int View::type() const { return typeFromDesc(m_desc); }
// There is an issue with default generated operator=(View&&) on Mac:
// it doesn't nullify m_cb of a moved object
// it doesn't nullify m_cb of the moved object
View& View::operator=(View&& v) {
m_desc = v.m_desc;
m_data = v.m_data;
m_step = v.m_step;
m_cb = v.m_cb;
v.m_desc = {};
v.m_data = nullptr;
v.m_step = 0u;
v.m_cb = nullptr;
m_desc = v.m_desc;
m_data = v.m_data;
m_steps = v.m_steps;
m_cb = v.m_cb;
v.m_desc = {};
v.m_data = nullptr;
v.m_steps = {0u};
v.m_cb = nullptr;
return *this;
}

@ -23,12 +23,26 @@ namespace cv {
namespace gimpl {
inline cv::Mat asMat(RMat::View& v) {
#if !defined(GAPI_STANDALONE)
return v.dims().empty() ? cv::Mat(v.rows(), v.cols(), v.type(), v.ptr(), v.step())
: cv::Mat(v.dims(), v.type(), v.ptr(), v.steps().data());
#else
// FIXME: add a check that steps are default
return v.dims().empty() ? cv::Mat(v.rows(), v.cols(), v.type(), v.ptr(), v.step())
: cv::Mat(v.dims(), v.type(), v.ptr());
#endif
}
inline RMat::View asView(const Mat& m, RMat::View::DestroyCallback&& cb = nullptr) {
// FIXME: View doesn't support multidimensional cv::Mat's
#if !defined(GAPI_STANDALONE)
RMat::View::stepsT steps(m.dims);
for (int i = 0; i < m.dims; i++) {
steps[i] = m.step[i];
}
return RMat::View(cv::descr_of(m), m.data, steps, std::move(cb));
#else
return RMat::View(cv::descr_of(m), m.data, m.step, std::move(cb));
#endif
}
class RMatAdapter : public RMat::Adapter {

@ -19,14 +19,18 @@ public:
: m_mat(m), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) override {
RMat::View::stepsT steps(m_mat.dims);
for (int i = 0; i < m_mat.dims; i++) {
steps[i] = m_mat.step[i];
}
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step,
return RMat::View(cv::descr_of(m_mat), m_mat.data, steps,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
});
} else {
return RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step);
return RMat::View(cv::descr_of(m_mat), m_mat.data, steps);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); }
@ -42,8 +46,12 @@ public:
: m_deviceMat(m), m_hostMat(m.clone()), m_callbackCalled(callbackCalled)
{}
virtual RMat::View access(RMat::Access access) override {
RMat::View::stepsT steps(m_hostMat.dims);
for (int i = 0; i < m_hostMat.dims; i++) {
steps[i] = m_hostMat.step[i];
}
if (access == RMat::Access::W) {
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step,
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, steps,
[this](){
EXPECT_FALSE(m_callbackCalled);
m_callbackCalled = true;
@ -51,7 +59,7 @@ public:
});
} else {
m_deviceMat.copyTo(m_hostMat);
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, m_hostMat.step);
return RMat::View(cv::descr_of(m_hostMat), m_hostMat.data, steps);
}
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_hostMat); }

@ -15,6 +15,8 @@ namespace opencv_test
using cv::GMatDesc;
using View = cv::RMat::View;
using cv::Mat;
using cv::gimpl::asMat;
using cv::gimpl::asView;
using namespace ::testing;
static void expect_eq_desc(const GMatDesc& desc, const View& view) {
@ -22,7 +24,8 @@ static void expect_eq_desc(const GMatDesc& desc, const View& view) {
EXPECT_EQ(desc.dims, view.dims());
EXPECT_EQ(desc.size.width, view.cols());
EXPECT_EQ(desc.size.height, view.rows());
EXPECT_EQ(CV_MAKE_TYPE(desc.depth,desc.chan), view.type());
EXPECT_EQ(desc.depth, view.depth());
EXPECT_EQ(desc.chan, view.chan());
EXPECT_EQ(desc.depth, view.depth());
EXPECT_EQ(desc.chan, view.chan());
}
@ -40,10 +43,10 @@ TEST_P(RMatViewTest, ConstructionFromMat) {
auto type = GetParam();
Mat mat(8,8,type);
const auto desc = cv::descr_of(mat);
View view(cv::descr_of(mat), mat.ptr(), mat.step1());
View view = asView(mat);
expect_eq_desc(desc, view);
EXPECT_EQ(mat.ptr(), view.ptr());
EXPECT_EQ(mat.step1(), view.step());
EXPECT_EQ(mat.step, view.step());
}
TEST(RMatView, TestConstructionFromMatND) {
@ -66,16 +69,98 @@ TEST_P(RMatViewTest, DefaultStep) {
EXPECT_EQ(static_cast<size_t>(desc.size.width)*CV_ELEM_SIZE(type), view.step());
}
static Mat asMat(View& view) {
return Mat(view.size(), view.type(), view.ptr(), view.step());
struct RMatViewNDTest : public TestWithParam<
std::tuple<int /*depth*/, int /*ndims*/>>{};
TEST_P(RMatViewNDTest, DefaultStep) {
int depth = 0, ndims = 0;
std::tie(depth, ndims) = GetParam();
std::vector<int> dims(ndims, 12);
GMatDesc desc;
desc.dims = dims;
desc.depth = depth;
GAPI_Assert(desc.chan == -1);
auto elemSize = CV_ELEM_SIZE(depth);
auto total = std::accumulate(dims.begin(), dims.end(), elemSize, std::multiplies<int>());
std::vector<unsigned char> data(total);
View view(desc, data.data());
auto step = static_cast<size_t>(total/dims[0]);
EXPECT_EQ(step, view.step(0));
for (int i = 1; i < ndims; i++) {
step /= dims[i];
EXPECT_EQ(step, view.step(i));
}
}
TEST_P(RMatViewNDTest, StepFromMat) {
int depth = 0, ndims = 0;
std::tie(depth, ndims) = GetParam();
std::vector<int> dims(ndims, 12);
cv::Mat mat(dims, depth);
auto view = asView(mat);
EXPECT_EQ(mat.ptr(), view.ptr());
for (int i = 0; i < ndims; i++) {
EXPECT_EQ(mat.step[i], view.step(i));
}
}
TEST_P(RMatViewNDTest, StepFromView) {
int depth = 0, ndims = 0;
std::tie(depth, ndims) = GetParam();
std::vector<int> dims(ndims, 12);
std::vector<int> aligned(ndims, 16);
GMatDesc desc;
desc.dims = dims;
desc.depth = depth;
GAPI_Assert(desc.chan == -1);
auto elemSize = CV_ELEM_SIZE(depth);
auto total = std::accumulate(aligned.begin(), aligned.end(), elemSize, std::multiplies<int>());
std::vector<unsigned char> data(total);
View::stepsT steps(ndims);
auto step = static_cast<size_t>(total/aligned[0]);
steps[0] = step;
for (int i = 1; i < ndims; i++) {
step /= aligned[i];
steps[i] = step;
}
View view(desc, data.data(), steps);
auto mat = asMat(view);
EXPECT_EQ(mat.ptr(), view.ptr());
for (int i = 0; i < ndims; i++) {
EXPECT_EQ(mat.step[i], view.step(i));
}
}
INSTANTIATE_TEST_CASE_P(Test, RMatViewNDTest,
Combine(Values(CV_8U, CV_32F), // depth
Values(1,2,3,4,7))); // ndims
struct RMatViewNDTestNegative : public TestWithParam<
std::tuple<int /*depth*/, int /*chan*/, int /*ndims*/>>{};
TEST_P(RMatViewNDTestNegative, DefaultStep) {
int depth = 0, chan = 0, ndims = 0;
std::tie(depth, chan, ndims) = GetParam();
std::vector<int> dims(ndims, 12);
GMatDesc desc;
desc.dims = dims;
desc.depth = depth;
desc.chan = chan;
auto elemSize = CV_ELEM_SIZE(depth);
auto total = std::accumulate(dims.begin(), dims.end(), elemSize, std::multiplies<int>());
std::vector<unsigned char> data(total);
EXPECT_ANY_THROW(View view(desc, data.data()));
}
INSTANTIATE_TEST_CASE_P(Test, RMatViewNDTestNegative,
Combine(Values(CV_8U, CV_32F), // depth
Values(1,2,3,4), // chan
Values(2,4,7))); // ndims
TEST_P(RMatViewTest, NonDefaultStepInput) {
auto type = GetParam();
Mat bigMat(16,16,type);
cv::randn(bigMat, cv::Scalar::all(127), cv::Scalar::all(40));
Mat mat = bigMat(cv::Rect{4,4,8,8});
View view(cv::descr_of(mat), mat.data, mat.step);
View view = asView(mat);
const auto viewMat = asMat(view);
Mat ref, out;
cv::Size ksize{1,1};
@ -90,7 +175,36 @@ TEST_P(RMatViewTest, NonDefaultStepOutput) {
cv::randn(mat, cv::Scalar::all(127), cv::Scalar::all(40));
Mat bigMat = Mat::zeros(16,16,type);
Mat out = bigMat(cv::Rect{4,4,8,8});
View view(cv::descr_of(out), out.ptr(), out.step);
View view = asView(out);
auto viewMat = asMat(view);
Mat ref;
cv::Size ksize{1,1};
cv::blur(mat, viewMat, ksize);
cv::blur(mat, ref, ksize);
EXPECT_EQ(0, cvtest::norm(ref, out, NORM_INF));
}
TEST_P(RMatViewTest, NonDefaultStep2DInput) {
auto type = GetParam();
Mat bigMat(16,16,type);
cv::randn(bigMat, cv::Scalar::all(127), cv::Scalar::all(40));
Mat mat = bigMat(cv::Rect{4,4,8,8});
View view(cv::descr_of(mat), mat.data, mat.step);
const auto viewMat = asMat(view);
Mat ref, out;
cv::Size ksize{1,1};
cv::blur(viewMat, out, ksize);
cv::blur( mat, ref, ksize);
EXPECT_EQ(0, cvtest::norm(ref, out, NORM_INF));
}
TEST_P(RMatViewTest, NonDefaultStep2DOutput) {
auto type = GetParam();
Mat mat(8,8,type);
cv::randn(mat, cv::Scalar::all(127), cv::Scalar::all(40));
Mat bigMat = Mat::zeros(16,16,type);
Mat out = bigMat(cv::Rect{4,4,8,8});
View view(cv::descr_of(out), out.data, out.step);
auto viewMat = asMat(view);
Mat ref;
cv::Size ksize{1,1};
@ -107,7 +221,7 @@ struct RMatViewCallbackTest : public ::testing::Test {
: mat(8,8,CV_8UC1) {
cv::randn(mat, cv::Scalar::all(127), cv::Scalar::all(40));
}
View getView() { return {cv::descr_of(mat), mat.ptr(), mat.step1(), [this](){ callbackCalls++; }}; }
View getView() { return asView(mat, [this](){ callbackCalls++; }); }
int callbackCalls = 0;
Mat mat;
};

@ -2,6 +2,7 @@
#include "backends/common/serialization.hpp"
#include <opencv2/gapi/rmat.hpp>
#include <../src/backends/common/gbackend.hpp> // asView
namespace {
struct EmptyCustomType { };
@ -134,12 +135,8 @@ public:
MyRMatAdapter(cv::Mat m, int value, const std::string& str)
: m_mat(m), m_value(value), m_str(str)
{}
virtual cv::RMat::View access(cv::RMat::Access access) override {
if (access == cv::RMat::Access::W) {
return cv::RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step);
} else {
return cv::RMat::View(cv::descr_of(m_mat), m_mat.data, m_mat.step);
}
virtual cv::RMat::View access(cv::RMat::Access) override {
return cv::gimpl::asView(m_mat);
}
virtual cv::GMatDesc desc() const override { return cv::descr_of(m_mat); }
virtual void serialize(cv::gapi::s11n::IOStream& os) override {

Loading…
Cancel
Save