Commit 443fed79 authored by Ruslan Garnov's avatar Ruslan Garnov Committed by Alexander Alekhin

Merge pull request #12990 from rgarnov:gapi_fluid_reshape_support

G-API: Introduce new `reshape()` API (#12990)

* Moved initFluidUnits, initLineConsumption, calcLatency, calcSkew to separate functions

* Added Fluid::View::allocate method (moved allocation logic from constructor)

* Changed util::zip to util::indexed, utilized collectInputMeta in GFluidExecutable constructor

* Added makeReshape method to FluidExecutable

* Removed m_outputRoi from GFluidExecutable

* Added reshape feature

* Added switch of resize mapper if agent ratio was changed

* Added more TODOs and renamed a function

* G-API reshape(): add missing `override` specifiers

Fix warnings on all platforms
parent 08536943
......@@ -105,7 +105,7 @@ public:
const GMatDesc& meta() const;
View mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage);
View mkView(int borderSize, bool ownStorage);
class GAPI_EXPORTS Priv; // internal use only
Priv& priv(); // internal use only
......
......@@ -50,6 +50,9 @@ public:
const GMetaArgs& metas() const; // Meta passed to compile()
const GMetaArgs& outMetas() const; // Inferred output metadata
bool canReshape() const; // is reshape mechanism supported by GCompiled
void reshape(const GMetaArgs& inMetas, const GCompileArgs& args); // run reshape procedure
protected:
std::shared_ptr<Priv> m_priv;
};
......
......@@ -76,15 +76,46 @@ cv::GCompiled cv::GComputation::compile(GMetaArgs &&metas, GCompileArgs &&args)
return comp.compile();
}
// FIXME: Introduce similar query/test method for GMetaArgs as a building block
// for functions like this?
static bool formats_are_same(const cv::GMetaArgs& metas1, const cv::GMetaArgs& metas2)
{
return std::equal(metas1.cbegin(), metas1.cend(), metas2.cbegin(),
[](const cv::GMetaArg& meta1, const cv::GMetaArg& meta2) {
if (meta1.index() == meta2.index() && meta1.index() == cv::GMetaArg::index_of<cv::GMatDesc>())
{
const auto& desc1 = cv::util::get<cv::GMatDesc>(meta1);
const auto& desc2 = cv::util::get<cv::GMatDesc>(meta2);
// comparison by size is omitted
return (desc1.chan == desc2.chan &&
desc1.depth == desc2.depth);
}
else
{
return meta1 == meta2;
}
});
}
void cv::GComputation::apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args)
{
const auto in_metas = descr_of(ins);
// FIXME Graph should be recompiled when GCompileArgs have changed
if (m_priv->m_lastMetas != in_metas)
{
if (m_priv->m_lastCompiled &&
m_priv->m_lastCompiled.canReshape() &&
formats_are_same(m_priv->m_lastMetas, in_metas))
{
m_priv->m_lastCompiled.reshape(in_metas, args);
}
else
{
// FIXME: Had to construct temporary object as compile() takes && (r-value)
m_priv->m_lastCompiled = compile(GMetaArgs(in_metas), std::move(args));
m_priv->m_lastMetas = in_metas; // Update only here, if compile() was ok
}
m_priv->m_lastMetas = in_metas;
}
m_priv->m_lastCompiled(std::move(ins), std::move(outs));
}
......
......@@ -54,6 +54,15 @@ public:
GCPUExecutable(const ade::Graph &graph,
const std::vector<ade::NodeHandle> &nodes);
virtual inline bool canReshape() const override { return false; }
virtual inline void reshape(ade::Graph&, const GCompileArgs&) override
{
// FIXME: CPU plugin is in fact reshapeable (as it was initially,
// even before outMeta() has been introduced), so this limitation
// should be dropped.
util::throw_error(std::logic_error("GCPUExecutable::reshape() should never be called"));
}
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override;
};
......
......@@ -49,6 +49,19 @@ struct FluidData
gapi::fluid::BorderOpt border;
};
struct FluidMapper
{
FluidMapper(double ratio, int lpi) : m_ratio(ratio), m_lpi(lpi) {}
virtual ~FluidMapper() = default;
virtual int firstWindow(int outCoord, int lpi) const = 0;
virtual int nextWindow(int outCoord, int lpi) const = 0;
virtual int linesRead(int outCoord) const = 0;
protected:
double m_ratio = 0.0;
int m_lpi = 0;
};
struct FluidAgent
{
public:
......@@ -72,8 +85,6 @@ public:
int m_outputLines = 0;
int m_producedLines = 0;
double m_ratio = 0.0f;
// Execution methods
void reset();
bool canWork() const;
......@@ -83,10 +94,12 @@ public:
bool done() const;
void debug(std::ostream& os);
// FIXME:
// refactor (implement a more solid replacement or
// drop this method completely)
virtual void setInHeight(int h) = 0;
virtual void setRatio(double ratio) = 0;
private:
// FIXME!!!
// move to another class
......@@ -99,7 +112,6 @@ class GFluidExecutable final: public GIslandExecutable
{
const ade::Graph &m_g;
GModel::ConstGraph m_gm;
const std::vector<ade::NodeHandle> m_nodes;
std::vector<std::unique_ptr<FluidAgent>> m_agents;
std::vector<cv::gapi::fluid::Buffer> m_buffers;
......@@ -109,23 +121,25 @@ class GFluidExecutable final: public GIslandExecutable
std::size_t m_num_int_buffers; // internal buffers counter (m_buffers - num_scratch)
std::vector<std::size_t> m_scratch_users;
std::vector<cv::gapi::fluid::View> m_views;
std::vector<cv::gapi::own::Rect> m_outputRois;
std::unordered_map<int, std::size_t> m_id_map; // GMat id -> buffer idx map
std::map<std::size_t, ade::NodeHandle> m_all_gmat_ids;
void bindInArg (const RcDesc &rc, const GRunArg &arg);
void bindOutArg(const RcDesc &rc, const GRunArgP &arg);
void packArg (GArg &in_arg, const GArg &op_arg);
void initBufferRois(std::vector<int>& readStarts, std::vector<cv::gapi::own::Rect>& rois);
void initBufferRois(std::vector<int>& readStarts, std::vector<cv::gapi::own::Rect>& rois, const std::vector<gapi::own::Rect> &out_rois);
void makeReshape(const std::vector<cv::gapi::own::Rect>& out_rois);
public:
GFluidExecutable(const ade::Graph &g,
const std::vector<ade::NodeHandle> &nodes,
const std::vector<cv::gapi::own::Rect> &outputRois);
virtual inline bool canReshape() const override { return true; }
virtual void reshape(ade::Graph& g, const GCompileArgs& args) override;
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override;
};
......
......@@ -115,10 +115,10 @@ template <int BorderType>
fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)
: BorderHandler(border_size)
{
auto getFillBorderRowFunc = [&](int border, int dataType) {
auto getFillBorderRowFunc = [&](int border, int depth) {
if (border == cv::BORDER_REPLICATE)
{
switch(dataType)
switch(depth)
{
case CV_8U: return &fillBorderReplicateRow< uint8_t>; break;
case CV_16S: return &fillBorderReplicateRow< int16_t>; break;
......@@ -129,7 +129,7 @@ fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type
}
else if (border == cv::BORDER_REFLECT_101)
{
switch(dataType)
switch(depth)
{
case CV_8U: return &fillBorderReflectRow< uint8_t>; break;
case CV_16S: return &fillBorderReflectRow< int16_t>; break;
......@@ -145,7 +145,7 @@ fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type
}
};
m_fill_border_row = getFillBorderRowFunc(BorderType, data_type);
m_fill_border_row = getFillBorderRowFunc(BorderType, CV_MAT_DEPTH(data_type));
}
namespace {
......@@ -169,20 +169,20 @@ const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const Buf
return data.ptr(idx);
}
fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width)
fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value)
: BorderHandler(border_size), m_border_value(border_value)
{
m_const_border.create(1, desc_width + 2*m_border_size, data_type);
m_const_border = border_value;
}
{ /* nothing */ }
const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const
{
return m_const_border.ptr(0, m_border_size);
}
void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data) const
void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data)
{
m_const_border.create(1, data.cols(), data.data().type());
m_const_border = m_border_value;
cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());
}
......@@ -206,15 +206,12 @@ std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const
}
// Fluid BufferStorage implementation //////////////////////////////////////////
void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype, int border_size, Border border)
void fluid::BufferStorageWithBorder::init(int dtype, int border_size, Border border)
{
auto width = (desc_width + 2*border_size);
m_data.create(capacity, width, dtype);
switch(border.type)
{
case cv::BORDER_CONSTANT:
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value, dtype, desc_width)); break;
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value)); break;
case cv::BORDER_REPLICATE:
m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;
case cv::BORDER_REFLECT_101:
......@@ -222,6 +219,13 @@ void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dt
default:
GAPI_Assert(false);
}
}
void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype)
{
auto borderSize = m_borderHandler->borderSize();
auto width = (desc_width + 2*borderSize);
m_data.create(capacity, width, dtype);
m_borderHandler->fillCompileTimeBorder(*this);
}
......@@ -329,7 +333,8 @@ std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width
if (border)
{
std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);
storage->create(capacity, desc_width, type, border_size, border.value());
storage->init(type, border_size, border.value());
storage->create(capacity, desc_width, type);
return std::move(storage);
}
......@@ -400,15 +405,19 @@ const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const
return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);
}
fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int lineConsumption, int borderSize, Border border)
fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int borderSize)
{
GAPI_Assert(parent);
m_p = parent;
m_border_size = borderSize;
}
void fluid::ViewPrivWithOwnBorder::allocate(int lineConsumption, BorderOpt border)
{
auto desc = m_p->meta();
int type = CV_MAKETYPE(desc.depth, desc.chan);
m_own_storage.create(lineConsumption, desc.size.width, type, borderSize, border);
m_own_storage.init(type, m_border_size, border.value());
m_own_storage.create(lineConsumption, desc.size.width, type);
}
void fluid::ViewPrivWithOwnBorder::prepareToRead()
......@@ -497,14 +506,14 @@ fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
{}
void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
int wlpi,
int writer_lpi,
int readStartPos,
cv::gapi::own::Rect roi)
{
m_writer_lpi = wlpi;
m_writer_lpi = writer_lpi;
m_desc = desc;
m_readStart = readStartPos;
m_roi = roi == cv::Rect{} ? cv::Rect{0, 0, desc.size.width, desc.size.height}
m_roi = roi == own::Rect{} ? own::Rect{ 0, 0, desc.size.width, desc.size.height }
: roi;
}
......@@ -513,7 +522,6 @@ void fluid::Buffer::Priv::allocate(BorderOpt border,
int line_consumption,
int skew)
{
GAPI_Assert(!m_storage);
GAPI_Assert(line_consumption > 0);
// Init physical buffer
......@@ -690,10 +698,10 @@ fluid::View::View(Priv* p)
: m_priv(p)
{ /* nothing */ }
fluid::View fluid::Buffer::mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage)
fluid::View fluid::Buffer::mkView(int borderSize, bool ownStorage)
{
// FIXME: logic outside of Priv (because View takes pointer to Buffer)
auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, lineConsumption, borderSize, border.value()))
auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, borderSize))
: View(new ViewPrivWithoutOwnBorder(this, borderSize));
m_priv->addView(view);
return view;
......
......@@ -30,7 +30,7 @@ public:
virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const = 0;
// Fills border pixels after buffer allocation (if possible (for const border))
inline virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const { /* nothing */ }
inline virtual void fillCompileTimeBorder(BufferStorageWithBorder &) { /* nothing */ }
// Fills required border lines
inline virtual void updateBorderPixels(BufferStorageWithBorder& /*data*/, int /*startLine*/, int /*lpi*/) const { /* nothing */ }
......@@ -56,9 +56,9 @@ class BorderHandlerT<cv::BORDER_CONSTANT> : public BorderHandler
cv::gapi::own::Mat m_const_border;
public:
BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width);
BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value);
virtual const uint8_t* inLineB(int log_idx, const BufferStorageWithBorder &data, int desc_height) const override;
virtual void fillCompileTimeBorder(BufferStorageWithBorder &) const override;
virtual void fillCompileTimeBorder(BufferStorageWithBorder &) override;
virtual std::size_t size() const override;
};
......@@ -151,7 +151,8 @@ public:
return m_data.ptr(physIdx(idx), borderSize());
}
void create(int capacity, int desc_width, int type, int border_size, Border border);
void init(int depth, int border_size, Border border);
void create(int capacity, int desc_width, int dtype);
virtual const uint8_t* inLineB(int log_idx, int desc_height) const override;
......@@ -178,6 +179,7 @@ public:
virtual ~Priv() = default;
// API used by actors/backend
virtual void allocate(int lineConsumption, BorderOpt border) = 0;
virtual void prepareToRead() = 0;
void readDone(int linesRead, int linesForNextIteration);
......@@ -198,6 +200,7 @@ public:
// API used by actors/backend
ViewPrivWithoutOwnBorder(const Buffer *p, int borderSize);
inline virtual void allocate(int, BorderOpt) override { /* nothing */ }
inline virtual void prepareToRead() override { /* nothing */ }
inline virtual std::size_t size() const override { return 0; }
......@@ -212,8 +215,9 @@ class ViewPrivWithOwnBorder final : public View::Priv
public:
// API used by actors/backend
ViewPrivWithOwnBorder(const Buffer *p, int lineCapacity, int borderSize, Border border);
ViewPrivWithOwnBorder(const Buffer *p, int borderSize);
inline virtual void allocate(int lineConsumption, BorderOpt border) override;
virtual void prepareToRead() override;
virtual std::size_t size() const override;
......@@ -253,7 +257,7 @@ public:
// API used by actors/backend
void init(const cv::GMatDesc &desc,
int wlpi,
int writer_lpi,
int readStart,
cv::gapi::own::Rect roi);
......
......@@ -59,6 +59,19 @@ void cv::GCompiled::Priv::checkArgs(const cv::gimpl::GRuntimeArgs &args) const
}
}
bool cv::GCompiled::Priv::canReshape() const
{
GAPI_Assert(m_exec);
return m_exec->canReshape();
}
void cv::GCompiled::Priv::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
{
GAPI_Assert(m_exec);
m_exec->reshape(inMetas, args);
m_metas = inMetas;
}
const cv::gimpl::GModel::Graph& cv::GCompiled::Priv::model() const
{
GAPI_Assert(nullptr != m_exec);
......@@ -118,7 +131,6 @@ void cv::GCompiled::operator ()(const std::vector<cv::Mat> &ins,
}
#endif // !defined(GAPI_STANDALONE)
const cv::GMetaArgs& cv::GCompiled::metas() const
{
return m_priv->metas();
......@@ -129,8 +141,17 @@ const cv::GMetaArgs& cv::GCompiled::outMetas() const
return m_priv->outMetas();
}
cv::GCompiled::Priv& cv::GCompiled::priv()
{
return *m_priv;
}
bool cv::GCompiled::canReshape() const
{
return m_priv->canReshape();
}
void cv::GCompiled::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
{
m_priv->reshape(inMetas, args);
}
......@@ -46,6 +46,9 @@ public:
std::unique_ptr<cv::gimpl::GExecutor> &&pE);
bool isEmpty() const;
bool canReshape() const;
void reshape(const GMetaArgs& inMetas, const GCompileArgs &args);
void run(cv::gimpl::GRuntimeArgs &&args);
const GMetaArgs& metas() const;
const GMetaArgs& outMetas() const;
......
......@@ -111,7 +111,7 @@ cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c,
m_e.addPass("init", "check_islands_content", passes::checkIslandsContent);
m_e.addPassStage("meta");
m_e.addPass("meta", "initialize", std::bind(passes::initMeta, _1, std::ref(m_metas)));
m_e.addPass("meta", "propagate", passes::inferMeta);
m_e.addPass("meta", "propagate", std::bind(passes::inferMeta, _1, false));
m_e.addPass("meta", "finalize", passes::storeResultingMeta);
// moved to another stage, FIXME: two dumps?
// m_e.addPass("meta", "dump_dot", passes::dumpDotStdout);
......
......@@ -109,6 +109,9 @@ public:
virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) = 0;
virtual bool canReshape() const = 0;
virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0;
virtual ~GIslandExecutable() = default;
};
......
......@@ -33,7 +33,7 @@ void cv::gimpl::passes::initMeta(ade::passes::PassContext &ctx, const GMetaArgs
// Iterate over all operations in the topological order, trigger kernels
// validate() function, update output objects metadata.
void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx)
void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx, bool meta_is_initialized)
{
// FIXME: ADE pass dependency on topo_sort?
// FIXME: ADE pass dependency on initMeta?
......@@ -75,7 +75,7 @@ void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx)
// Now ask kernel for it's output meta.
// Resulting out_args may have a larger size than op.outs, since some
// outputs could stay unused (unconnected)
const GMetaArgs out_metas = op.k.outMeta(input_meta_args, op.args);
const auto& out_metas = op.k.outMeta(input_meta_args, op.args);
// Walk through operation's outputs, update meta of output objects
// appropriately
......@@ -87,7 +87,7 @@ void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx)
GAPI_Assert(gr.metadata(output_nh).get<NodeType>().t == NodeType::DATA);
auto &output_meta = gr.metadata(output_nh).get<Data>().meta;
if (!util::holds_alternative<util::monostate>(output_meta))
if (!meta_is_initialized && !util::holds_alternative<util::monostate>(output_meta))
{
GAPI_LOG_INFO(NULL,
"!!! Output object has an initialized meta - "
......
......@@ -38,7 +38,7 @@ void checkIslands(ade::passes::PassContext &ctx);
void checkIslandsContent(ade::passes::PassContext &ctx);
void initMeta(ade::passes::PassContext &ctx, const GMetaArgs &metas);
void inferMeta(ade::passes::PassContext &ctx);
void inferMeta(ade::passes::PassContext &ctx, bool meta_is_initialized);
void storeResultingMeta(ade::passes::PassContext &ctx);
void expandKernels(ade::passes::PassContext &ctx,
......
......@@ -13,6 +13,7 @@
#include "opencv2/gapi/opencv_includes.hpp"
#include "executor/gexecutor.hpp"
#include "compiler/passes/passes.hpp"
cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
: m_orig_graph(std::move(g_model))
......@@ -34,7 +35,6 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
// 6. Run GIslandExecutable
// 7. writeBack
m_ops.reserve(m_gim.nodes().size());
auto sorted = m_gim.metadata().get<ade::passes::TopologicalSortData>();
for (auto nh : sorted.nodes())
{
......@@ -59,6 +59,7 @@ cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
// (3)
for (auto in_slot_nh : nh->inNodes()) xtract(in_slot_nh, input_rcs);
for (auto out_slot_nh : nh->outNodes()) xtract(out_slot_nh, output_rcs);
m_ops.emplace_back(OpDesc{ std::move(input_rcs)
, std::move(output_rcs)
, m_gim.metadata(nh).get<IslandExec>().object});
......@@ -224,3 +225,20 @@ const cv::gimpl::GModel::Graph& cv::gimpl::GExecutor::model() const
{
return m_gm;
}
bool cv::gimpl::GExecutor::canReshape() const
{
// FIXME: Introduce proper reshaping support on GExecutor level
// for all cases!
return (m_ops.size() == 1) && m_ops[0].isl_exec->canReshape();
}
void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
{
GAPI_Assert(canReshape());
auto& g = *m_orig_graph.get();
ade::passes::PassContext ctx{g};
passes::initMeta(ctx, inMetas);
passes::inferMeta(ctx, true);
m_ops[0].isl_exec->reshape(g, args);
}
......@@ -85,6 +85,9 @@ public:
explicit GExecutor(std::unique_ptr<ade::Graph> &&g_model);
void run(cv::gimpl::GRuntimeArgs &&args);
bool canReshape() const;
void reshape(const GMetaArgs& inMetas, const GCompileArgs& args);
const GModel::Graph& model() const; // FIXME: make it ConstGraph?
};
......
......@@ -51,7 +51,7 @@ TEST(FluidBuffer, InputTest)
cv::Mat in_mat = cv::Mat::eye(buffer_size, CV_8U);
cv::gapi::fluid::Buffer buffer(to_own(in_mat), true);
cv::gapi::fluid::View view = buffer.mkView(1, 0, {}, false);
cv::gapi::fluid::View view = buffer.mkView(0, {});
view.priv().reset(1);
int this_y = 0;
......@@ -74,7 +74,7 @@ TEST(FluidBuffer, CircularTest)
cv::gapi::fluid::Buffer buffer(cv::GMatDesc{CV_8U,1,buffer_size}, 3, 1, 0, 1,
util::make_optional(cv::gapi::fluid::Border{cv::BORDER_CONSTANT, cv::gapi::own::Scalar(255)}));
cv::gapi::fluid::View view = buffer.mkView(3, 1, {}, false);
cv::gapi::fluid::View view = buffer.mkView(1, {});
view.priv().reset(3);
buffer.debug(std::cout);
......
......@@ -8,6 +8,9 @@
#include "test_precomp.hpp"
#include "api/gcomputation_priv.hpp"
#include <backends/fluid/gfluidcore.hpp>
#include <backends/fluid/gfluidimgproc.hpp>
namespace opencv_test
{
......@@ -68,4 +71,160 @@ TEST(GComputationCompile, RecompileWithDifferentMeta)
EXPECT_NE(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, FluidReshapeWithDifferentDims)
{
cv::GMat in;
cv::GComputation cc(in, in+in);
cv::Mat in_mat1 = cv::Mat::eye (32, 32, CV_8UC1);
cv::Mat in_mat2 = cv::Mat::zeros(64, 64, CV_8UC1);
cv::Mat out_mat;
cc.apply(in_mat1, out_mat, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, FluidReshapeResizeDownScale)
{
cv::Size szOut(4, 4);
cv::GMat in;
cv::GComputation cc(in, cv::gapi::resize(in, szOut));
cv::Mat in_mat1( 8, 8, CV_8UC3);
cv::Mat in_mat2(16, 16, CV_8UC3);
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat out_mat1, out_mat2;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
cv::Mat cv_out_mat1, cv_out_mat2;
cv::resize(in_mat1, cv_out_mat1, szOut);
cv::resize(in_mat2, cv_out_mat2, szOut);
EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1));
EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2));
}
TEST(GComputationCompile, FluidReshapeSwitchToUpscaleFromDownscale)
{
cv::Size szOut(4, 4);
cv::GMat in;
cv::GComputation cc(in, cv::gapi::resize(in, szOut));
cv::Mat in_mat1( 8, 8, CV_8UC3);
cv::Mat in_mat2( 2, 2, CV_8UC3);
cv::Mat in_mat3(16, 16, CV_8UC3);
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat3, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat out_mat1, out_mat2, out_mat3;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
cc.apply(in_mat3, out_mat3);
auto comp3 = cc.priv().m_lastCompiled;
EXPECT_EQ(&comp1.priv(), &comp2.priv());
EXPECT_EQ(&comp1.priv(), &comp3.priv());
cv::Mat cv_out_mat1, cv_out_mat2, cv_out_mat3;
cv::resize(in_mat1, cv_out_mat1, szOut);
cv::resize(in_mat2, cv_out_mat2, szOut);
cv::resize(in_mat3, cv_out_mat3, szOut);
EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1));
EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2));
EXPECT_EQ(0, cv::countNonZero(out_mat3 != cv_out_mat3));
}
TEST(GComputationCompile, ReshapeBlur)
{
cv::Size kernelSize{3, 3};
cv::GMat in;
cv::GComputation cc(in, cv::gapi::blur(in, kernelSize));
cv::Mat in_mat1( 8, 8, CV_8UC1);
cv::Mat in_mat2(16, 16, CV_8UC1);
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat out_mat1, out_mat2;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::imgproc::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
cv::Mat cv_out_mat1, cv_out_mat2;
cv::blur(in_mat1, cv_out_mat1, kernelSize);
cv::blur(in_mat2, cv_out_mat2, kernelSize);
EXPECT_EQ(0, cv::countNonZero(out_mat1 != cv_out_mat1));
EXPECT_EQ(0, cv::countNonZero(out_mat2 != cv_out_mat2));
}
TEST(GComputationCompile, ReshapeRois)
{
cv::Size kernelSize{3, 3};
cv::Size szOut(8, 8);
cv::GMat in;
auto blurred = cv::gapi::blur(in, kernelSize);
cv::GComputation cc(in, cv::gapi::resize(blurred, szOut));
cv::Mat first_in_mat(8, 8, CV_8UC3);
cv::Mat first_out_mat;
auto fluidKernels = cv::gapi::combine(gapi::imgproc::fluid::kernels(),
gapi::core::fluid::kernels(),
cv::unite_policy::REPLACE);
cc.apply(first_in_mat, first_out_mat, cv::compile_args(fluidKernels));
auto first_comp = cc.priv().m_lastCompiled;
constexpr int niter = 4;
for (int i = 0; i < niter; i++)
{
int width = 4 + 2*i;
int height = width;
cv::Mat in_mat(width, height, CV_8UC3);
cv::Mat out_mat = cv::Mat::zeros(szOut, CV_8UC3);
int x = 0;
int y = szOut.height * i / niter;
int roiW = szOut.width;
int roiH = szOut.height / niter;
cv::Rect roi{x, y, roiW, roiH};
cc.apply(in_mat, out_mat, cv::compile_args(cv::GFluidOutputRois{{to_own(roi)}}));
auto comp = cc.priv().m_lastCompiled;
EXPECT_EQ(&first_comp.priv(), &comp.priv());
cv::Mat blur_mat, cv_out_mat;
cv::blur(in_mat, blur_mat, kernelSize);
cv::resize(blur_mat, cv_out_mat, szOut);
EXPECT_EQ(0, cv::countNonZero(out_mat(roi) != cv_out_mat(roi)));
}
}
} // opencv_test
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