Path: blob/master/modules/gapi/src/backends/fluid/gfluidbuffer.cpp
16354 views
// This file is part of OpenCV project.1// It is subject to the license terms in the LICENSE file found in the top-level directory2// of this distribution and at http://opencv.org/license.html.3//4// Copyright (C) 2018 Intel Corporation567#include "precomp.hpp"89#include <iomanip> // hex, dec (debug)1011#include "opencv2/gapi/own/convert.hpp"12#include "opencv2/gapi/own/types.hpp"1314#include "opencv2/gapi/fluid/gfluidbuffer.hpp"15#include "backends/fluid/gfluidbuffer_priv.hpp"16#include "opencv2/gapi/opencv_includes.hpp"1718#include "backends/fluid/gfluidutils.hpp" // saturate1920namespace cv {21namespace gapi {22namespace fluid {23bool operator == (const fluid::Border& b1, const fluid::Border& b2)24{25return b1.type == b2.type && b1.value == b2.value;26}27} // namespace fluid2829// Fluid BorderHandler implementation /////////////////////////////////////////////////3031namespace {32template<typename T>33// Expected inputs:34// row - row buffer allocated with border in mind (have memory for both image and border pixels)35// length - size of the buffer with left and right borders included36void fillBorderReplicateRow(uint8_t* row, int length, int chan, int borderSize)37{38auto leftBorder = reinterpret_cast<T*>(row);39auto rightBorder = leftBorder + (length - borderSize) * chan;40for (int b = 0; b < borderSize; b++)41{42for (int c = 0; c < chan; c++)43{44leftBorder [b*chan + c] = leftBorder [borderSize*chan + c];45rightBorder[b*chan + c] = rightBorder[-chan + c];46}47}48}4950template<typename T>51void fillBorderReflectRow(uint8_t* row, int length, int chan, int borderSize)52{53auto leftBorder = reinterpret_cast<T*>(row);54auto rightBorder = leftBorder + (length - borderSize) * chan;55for (int b = 0; b < borderSize; b++)56{57for (int c = 0; c < chan; c++)58{59leftBorder [b*chan + c] = leftBorder [(2*borderSize - b)*chan + c];60rightBorder[b*chan + c] = rightBorder[(-b - 2)*chan + c];61}62}63}6465template<typename T>66void fillConstBorderRow(uint8_t* row, int length, int chan, int borderSize, cv::gapi::own::Scalar borderValue)67{68GAPI_DbgAssert(chan > 0 && chan <= 4);6970auto leftBorder = reinterpret_cast<T*>(row);71auto rightBorder = leftBorder + (length - borderSize) * chan;72for (int b = 0; b < borderSize; b++)73{74for (int c = 0; c < chan; c++)75{76leftBorder [b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);77rightBorder[b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);78}79}80}8182// Fills const border pixels in the whole mat83void fillBorderConstant(int borderSize, cv::gapi::own::Scalar borderValue, cv::gapi::own::Mat& mat)84{85// cv::Scalar can contain maximum 4 chan86GAPI_Assert(mat.channels() > 0 && mat.channels() <= 4);8788auto getFillBorderRowFunc = [&](int type) {89switch(type)90{91case CV_8U: return &fillConstBorderRow< uint8_t>; break;92case CV_16S: return &fillConstBorderRow< int16_t>; break;93case CV_16U: return &fillConstBorderRow<uint16_t>; break;94case CV_32F: return &fillConstBorderRow< float >; break;95default: GAPI_Assert(false); return &fillConstBorderRow<uint8_t>;96}97};9899auto fillBorderRow = getFillBorderRowFunc(mat.depth());100for (int y = 0; y < mat.rows; y++)101{102fillBorderRow(mat.ptr(y), mat.cols, mat.channels(), borderSize, borderValue);103}104}105} // anonymous namespace106107fluid::BorderHandler::BorderHandler(int border_size)108{109GAPI_Assert(border_size > 0);110m_border_size = border_size;111}112113template <int BorderType>114fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)115: BorderHandler(border_size)116{117auto getFillBorderRowFunc = [&](int border, int dataType) {118if (border == cv::BORDER_REPLICATE)119{120switch(dataType)121{122case CV_8U: return &fillBorderReplicateRow< uint8_t>; break;123case CV_16S: return &fillBorderReplicateRow< int16_t>; break;124case CV_16U: return &fillBorderReplicateRow<uint16_t>; break;125case CV_32F: return &fillBorderReplicateRow< float >; break;126default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>;127}128}129else if (border == cv::BORDER_REFLECT_101)130{131switch(dataType)132{133case CV_8U: return &fillBorderReflectRow< uint8_t>; break;134case CV_16S: return &fillBorderReflectRow< int16_t>; break;135case CV_16U: return &fillBorderReflectRow<uint16_t>; break;136case CV_32F: return &fillBorderReflectRow< float >; break;137default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>;138}139}140else141{142GAPI_Assert(!"Unsupported border type");143return &fillBorderReflectRow<uint8_t>;144}145};146147m_fill_border_row = getFillBorderRowFunc(BorderType, data_type);148}149150namespace {151template <int BorderType> int getBorderIdx(int log_idx, int desc_height);152153template<> int getBorderIdx<cv::BORDER_REPLICATE>(int log_idx, int desc_height)154{155return log_idx < 0 ? 0 : desc_height - 1;156}157158template<> int getBorderIdx<cv::BORDER_REFLECT_101>(int log_idx, int desc_height)159{160return log_idx < 0 ? -log_idx : 2*(desc_height - 1) - log_idx;161}162} // namespace163164template <int BorderType>165const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const BufferStorageWithBorder& data, int desc_height) const166{167auto idx = getBorderIdx<BorderType>(log_idx, desc_height);168return data.ptr(idx);169}170171fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value, int data_type, int desc_width)172: BorderHandler(border_size), m_border_value(border_value)173{174m_const_border.create(1, desc_width + 2*m_border_size, data_type);175m_const_border = border_value;176}177178const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const179{180return m_const_border.ptr(0, m_border_size);181}182183void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data) const184{185cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());186}187188template <int BorderType>189void fluid::BorderHandlerT<BorderType>::updateBorderPixels(BufferStorageWithBorder &data, int startLine, int nLines) const190{191auto& mat = data.data();192auto length = mat.cols;193auto chan = mat.channels();194195for (int l = startLine; l < startLine + nLines; l++)196{197auto row = mat.ptr(data.physIdx(l));198m_fill_border_row(row, length, chan, m_border_size);199}200}201202std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const203{204return m_const_border.total() * m_const_border.elemSize();205}206207// Fluid BufferStorage implementation //////////////////////////////////////////208void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype, int border_size, Border border)209{210auto width = (desc_width + 2*border_size);211m_data.create(capacity, width, dtype);212213switch(border.type)214{215case cv::BORDER_CONSTANT:216m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value, dtype, desc_width)); break;217case cv::BORDER_REPLICATE:218m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;219case cv::BORDER_REFLECT_101:220m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REFLECT_101>(border_size, dtype)); break;221default:222GAPI_Assert(false);223}224225m_borderHandler->fillCompileTimeBorder(*this);226}227228void fluid::BufferStorageWithoutBorder::create(int capacity, int desc_width, int dtype)229{230auto width = desc_width;231m_data.create(capacity, width, dtype);232233m_is_virtual = true;234}235236const uint8_t* fluid::BufferStorageWithBorder::inLineB(int log_idx, int desc_height) const237{238if (log_idx < 0 || log_idx >= desc_height)239{240return m_borderHandler->inLineB(log_idx, *this, desc_height);241}242else243{244return ptr(log_idx);245}246}247248const uint8_t* fluid::BufferStorageWithoutBorder::inLineB(int log_idx, int /*desc_height*/) const249{250return ptr(log_idx);251}252253static void copyWithoutBorder(const cv::gapi::own::Mat& src, int src_border_size, cv::gapi::own::Mat& dst, int dst_border_size, int startSrcLine, int startDstLine, int lpi)254{255auto subSrc = src(cv::gapi::own::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi});256auto subDst = dst(cv::gapi::own::Rect{dst_border_size, startDstLine, dst.cols - 2*dst_border_size, lpi});257258subSrc.copyTo(subDst);259}260261void fluid::BufferStorageWithoutBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const262{263for (int l = startLine; l < startLine + nLines; l++)264{265copyWithoutBorder(m_data, 0, dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);266}267}268269void fluid::BufferStorageWithBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const270{271// Copy required lpi lines line by line (to avoid wrap if invoked for multiple lines)272for (int l = startLine; l < startLine + nLines; l++)273{274copyWithoutBorder(m_data, borderSize(), dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);275}276}277278// FIXME? remember parent and remove src parameter?279void fluid::BufferStorageWithBorder::updateBeforeRead(int startLine, int nLines, const BufferStorage& src)280{281// TODO:282// Cover with tests!!283// (Ensure that there are no redundant copies done284// and only required (not fetched before) lines are copied)285286GAPI_DbgAssert(startLine >= 0);287288src.copyTo(*this, startLine, nLines);289m_borderHandler->updateBorderPixels(*this, startLine, nLines);290}291292void fluid::BufferStorageWithoutBorder::updateBeforeRead(int /*startLine*/, int /*lpi*/, const BufferStorage& /*src*/)293{294/* nothing */295}296297void fluid::BufferStorageWithBorder::updateAfterWrite(int startLine, int nLines)298{299// FIXME?300// Actually startLine + nLines can be > logical height so301// redundant end lines which will never be read302// can be filled in the ring buffer303m_borderHandler->updateBorderPixels(*this, startLine, nLines);304}305306void fluid::BufferStorageWithoutBorder::updateAfterWrite(int /*startLine*/, int /*lpi*/)307{308/* nothing */309}310311size_t fluid::BufferStorageWithBorder::size() const312{313return m_data.total()*m_data.elemSize() + m_borderHandler->size();314}315316size_t fluid::BufferStorageWithoutBorder::size() const317{318return m_data.total()*m_data.elemSize();319}320321namespace fluid {322namespace {323std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,324int border_size, fluid::BorderOpt border);325std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,326int border_size, fluid::BorderOpt border)327{328if (border)329{330std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);331storage->create(capacity, desc_width, type, border_size, border.value());332return std::move(storage);333}334335std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);336storage->create(capacity, desc_width, type);337return std::move(storage);338}339340std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi);341std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi)342{343std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);344storage->attach(data, roi);345return std::move(storage);346}347} // namespace348} // namespace fluid349350// Fluid View implementation ///////////////////////////////////////////////////351352void fluid::View::Priv::reset(int linesForFirstIteration)353{354GAPI_DbgAssert(m_p);355356m_lines_next_iter = linesForFirstIteration;357m_read_caret = m_p->priv().readStart();358}359360void fluid::View::Priv::readDone(int linesRead, int linesForNextIteration)361{362GAPI_DbgAssert(m_p);363m_read_caret += linesRead;364m_read_caret %= m_p->meta().size.height;365m_lines_next_iter = linesForNextIteration;366}367368bool fluid::View::Priv::ready() const369{370auto lastWrittenLine = m_p->priv().writeStart() + m_p->linesReady();371// + bottom border372if (lastWrittenLine == m_p->meta().size.height) lastWrittenLine += m_border_size;373// + top border374lastWrittenLine += m_border_size;375376auto lastRequiredLine = m_read_caret + m_lines_next_iter;377378return lastWrittenLine >= lastRequiredLine;379}380381fluid::ViewPrivWithoutOwnBorder::ViewPrivWithoutOwnBorder(const Buffer *parent, int borderSize)382{383GAPI_Assert(parent);384m_p = parent;385m_border_size = borderSize;386}387388const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const389{390GAPI_DbgAssert(m_p);391392const auto &p_priv = m_p->priv();393394GAPI_DbgAssert(index >= -m_border_size395&& index < -m_border_size + m_lines_next_iter);396397const int log_idx = m_read_caret + index;398399return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);400}401402fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int lineConsumption, int borderSize, Border border)403{404GAPI_Assert(parent);405m_p = parent;406m_border_size = borderSize;407408auto desc = m_p->meta();409int type = CV_MAKETYPE(desc.depth, desc.chan);410m_own_storage.create(lineConsumption, desc.size.width, type, borderSize, border);411}412413void fluid::ViewPrivWithOwnBorder::prepareToRead()414{415int startLine = 0;416int nLines = 0;417418if (m_read_caret == m_p->priv().readStart())419{420// Need to fetch full window on the first iteration421startLine = (m_read_caret > m_border_size) ? m_read_caret - m_border_size : 0;422nLines = m_lines_next_iter;423}424else425{426startLine = m_read_caret + m_border_size;427nLines = m_lines_next_iter - 2*m_border_size;428}429430m_own_storage.updateBeforeRead(startLine, nLines, m_p->priv().storage());431}432433std::size_t fluid::ViewPrivWithOwnBorder::size() const434{435GAPI_DbgAssert(m_p);436return m_own_storage.size();437}438439const uint8_t* fluid::ViewPrivWithOwnBorder::InLineB(int index) const440{441GAPI_DbgAssert(m_p);442GAPI_DbgAssert(index >= -m_border_size443&& index < -m_border_size + m_lines_next_iter);444445const int log_idx = m_read_caret + index;446447return m_own_storage.inLineB(log_idx, m_p->meta().size.height);448}449450const uint8_t* fluid::View::InLineB(int index) const451{452return m_priv->InLineB(index);453}454455fluid::View::operator bool() const456{457return m_priv != nullptr && m_priv->m_p != nullptr;458}459460int fluid::View::length() const461{462return m_priv->m_p->length();463}464465bool fluid::View::ready() const466{467return m_priv->ready();468}469470int fluid::View::y() const471{472return m_priv->m_read_caret - m_priv->m_border_size;473}474475const GMatDesc& fluid::View::meta() const476{477// FIXME: cover with test!478return m_priv->m_p->meta();479}480481fluid::View::Priv& fluid::View::priv()482{483return *m_priv;484}485486const fluid::View::Priv& fluid::View::priv() const487{488return *m_priv;489}490491// Fluid Buffer implementation /////////////////////////////////////////////////492493fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)494: m_readStart(read_start)495, m_roi(roi)496{}497498void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,499int wlpi,500int readStartPos,501cv::gapi::own::Rect roi)502{503m_writer_lpi = wlpi;504m_desc = desc;505m_readStart = readStartPos;506m_roi = roi == cv::Rect{} ? cv::Rect{0, 0, desc.size.width, desc.size.height}507: roi;508}509510void fluid::Buffer::Priv::allocate(BorderOpt border,511int border_size,512int line_consumption,513int skew)514{515GAPI_Assert(!m_storage);516GAPI_Assert(line_consumption > 0);517518// Init physical buffer519520// FIXME? combine line_consumption with skew?521auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1;522523m_storage = createStorage(data_height,524m_desc.size.width,525CV_MAKETYPE(m_desc.depth, m_desc.chan),526border_size,527border);528529// Finally, initialize carets530m_write_caret = 0;531}532533void fluid::Buffer::Priv::bindTo(const cv::gapi::own::Mat &data, bool is_input)534{535// FIXME: move all these fields into a separate structure536GAPI_Assert(m_desc == descr_of(data));537538// Currently m_writer_lpi is obtained from metadata which is shared between islands539// and this assert can trigger for slot which connects two fluid islands.540// m_writer_lpi is used only in write-related functions and doesn't affect541// buffer which is island's input so it's safe to skip this check.542// FIXME:543// Bring back this check when we move to 1 buffer <-> 1 metadata model544// if (is_input) GAPI_Assert(m_writer_lpi == 1);545546m_storage = createStorage(data, m_roi);547548m_is_input = is_input;549m_write_caret = is_input ? writeEnd(): writeStart();550// NB: views remain the same!551}552553bool fluid::Buffer::Priv::full() const554{555int slowest_y = writeEnd();556if (!m_views.empty())557{558// reset with maximum possible value and then find minimum559slowest_y = m_desc.size.height;560for (const auto &v : m_views) slowest_y = std::min(slowest_y, v.y());561}562563return m_write_caret + lpi() - slowest_y > m_storage->rows();564}565566void fluid::Buffer::Priv::writeDone()567{568// There are possible optimizations which can be done to fill a border values569// in compile time of the graph (for example border is const),570// so there is no need to update border values after each write.571// If such optimizations weren't applied, fill border for lines572// which have been just written573m_storage->updateAfterWrite(m_write_caret, m_writer_lpi);574575// Final write may produce less LPI, so576// write caret may exceed logical buffer size577m_write_caret += m_writer_lpi;578// FIXME: add consistency check!579}580581void fluid::Buffer::Priv::reset()582{583m_write_caret = m_is_input ? writeEnd() : writeStart();584}585586int fluid::Buffer::Priv::size() const587{588std::size_t view_sz = 0;589for (const auto &v : m_views) view_sz += v.priv().size();590591auto total = view_sz;592if (m_storage) total += m_storage->size();593594// FIXME: Change API to return size_t!!!595return static_cast<int>(total);596}597598int fluid::Buffer::Priv::linesReady() const599{600if (m_is_input)601{602return m_storage->rows();603}604else605{606const int writes = std::min(m_write_caret - writeStart(), outputLines());607return writes;608}609}610611uint8_t* fluid::Buffer::Priv::OutLineB(int index)612{613GAPI_DbgAssert(index >= 0 && index < m_writer_lpi);614615return m_storage->ptr(m_write_caret + index);616}617618int fluid::Buffer::Priv::lpi() const619{620// FIXME:621// m_write_caret can be greater than m_writeRoi.y + m_writeRoi.height, so return value can be negative !!!622return std::min(writeEnd() - m_write_caret, m_writer_lpi);623}624625fluid::Buffer::Buffer()626: m_priv(new Priv())627{628}629630fluid::Buffer::Buffer(const cv::GMatDesc &desc)631: m_priv(new Priv())632{633int lineConsumption = 1;634int border = 0, skew = 0, wlpi = 1, readStart = 0;635cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};636m_priv->init(desc, wlpi, readStart, roi);637m_priv->allocate({}, border, lineConsumption, skew);638}639640fluid::Buffer::Buffer(const cv::GMatDesc &desc,641int max_line_consumption,642int border_size,643int skew,644int wlpi,645BorderOpt border)646: m_priv(new Priv())647{648int readStart = 0;649cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};650m_priv->init(desc, wlpi, readStart, roi);651m_priv->allocate(border, border_size, max_line_consumption, skew);652}653654fluid::Buffer::Buffer(const cv::gapi::own::Mat &data, bool is_input)655: m_priv(new Priv())656{657int wlpi = 1, readStart = 0;658cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};659m_priv->init(descr_of(data), wlpi, readStart, roi);660m_priv->bindTo(data, is_input);661}662663uint8_t* fluid::Buffer::Buffer::OutLineB(int index)664{665return m_priv->OutLineB(index);666}667668int fluid::Buffer::linesReady() const669{670return m_priv->linesReady();671}672673int fluid::Buffer::length() const674{675return meta().size.width;676}677678int fluid::Buffer::lpi() const679{680return m_priv->lpi();681}682683const GMatDesc& fluid::Buffer::meta() const684{685return m_priv->meta();686}687688fluid::View::View(Priv* p)689: m_priv(p)690{ /* nothing */ }691692fluid::View fluid::Buffer::mkView(int lineConsumption, int borderSize, BorderOpt border, bool ownStorage)693{694// FIXME: logic outside of Priv (because View takes pointer to Buffer)695auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, lineConsumption, borderSize, border.value()))696: View(new ViewPrivWithoutOwnBorder(this, borderSize));697m_priv->addView(view);698return view;699}700701void fluid::debugBufferPriv(const fluid::Buffer& buffer, std::ostream &os)702{703// FIXME Use cv::gapi::own Size and Rect with operator<<, when merged ADE-285704const auto& p = buffer.priv();705os << "Fluid buffer " << std::hex << &buffer << std::dec706<< " " << p.m_desc.size.width << " x " << p.m_desc.size.height << "]"707<< " readStart:" << p.m_readStart708<< " roi:" << "[" << p.m_roi.width << " x " << p.m_roi.height << " from (" << p.m_roi.x << ", " << p.m_roi.y << ")]"709<<" (phys " << "[" << p.storage().cols() << " x " << p.storage().rows() << "]" << ") :"710<< " w: " << p.m_write_caret711<< ", r: [";712for (const auto &v : p.m_views) { os << &v.priv() << ":" << v.y() << " "; }713os << "], avail: " << buffer.linesReady()714<< std::endl;715}716717void fluid::Buffer::debug(std::ostream &os) const718{719debugBufferPriv(*this, os);720}721722fluid::Buffer::Priv& fluid::Buffer::priv()723{724return *m_priv;725}726727const fluid::Buffer::Priv& fluid::Buffer::priv() const728{729return *m_priv;730}731732int fluid::Buffer::y() const733{734return m_priv->y();735}736737} // namespace cv::gapi738} // namespace cv739740741