mirror of
https://github.com/opencv/opencv.git
synced 2025-01-18 22:44:02 +08:00
Added multidimensional RMat::View steps
This commit is contained in:
parent
e12adcdf08
commit
724001aa0f
@ -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…
Reference in New Issue
Block a user