1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
5 // Copyright (C) 2018-2019 Intel Corporation
10 #include <iomanip> // hex, dec (debug)
12 #include "opencv2/gapi/own/convert.hpp"
13 #include "opencv2/gapi/own/types.hpp"
15 #include "opencv2/gapi/fluid/gfluidbuffer.hpp"
16 #include "backends/fluid/gfluidbuffer_priv.hpp"
17 #include "opencv2/gapi/opencv_includes.hpp"
19 #include "backends/fluid/gfluidutils.hpp" // saturate
24 bool operator == (const fluid::Border& b1, const fluid::Border& b2)
26 return b1.type == b2.type && b1.value == b2.value;
30 // Fluid BorderHandler implementation /////////////////////////////////////////////////
35 // row - row buffer allocated with border in mind (have memory for both image and border pixels)
36 // length - size of the buffer with left and right borders included
37 void fillBorderReplicateRow(uint8_t* row, int length, int chan, int borderSize)
39 auto leftBorder = reinterpret_cast<T*>(row);
40 auto rightBorder = leftBorder + (length - borderSize) * chan;
41 for (int b = 0; b < borderSize; b++)
43 for (int c = 0; c < chan; c++)
45 leftBorder [b*chan + c] = leftBorder [borderSize*chan + c];
46 rightBorder[b*chan + c] = rightBorder[-chan + c];
52 void fillBorderReflectRow(uint8_t* row, int length, int chan, int borderSize)
54 auto leftBorder = reinterpret_cast<T*>(row);
55 auto rightBorder = leftBorder + (length - borderSize) * chan;
56 for (int b = 0; b < borderSize; b++)
58 for (int c = 0; c < chan; c++)
60 leftBorder [b*chan + c] = leftBorder [(2*borderSize - b)*chan + c];
61 rightBorder[b*chan + c] = rightBorder[(-b - 2)*chan + c];
67 void fillConstBorderRow(uint8_t* row, int length, int chan, int borderSize, cv::gapi::own::Scalar borderValue)
69 GAPI_DbgAssert(chan > 0 && chan <= 4);
71 auto leftBorder = reinterpret_cast<T*>(row);
72 auto rightBorder = leftBorder + (length - borderSize) * chan;
73 for (int b = 0; b < borderSize; b++)
75 for (int c = 0; c < chan; c++)
77 leftBorder [b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
78 rightBorder[b*chan + c] = fluid::saturate<T>(borderValue[c], fluid::roundd);
83 // Fills const border pixels in the whole mat
84 void fillBorderConstant(int borderSize, cv::gapi::own::Scalar borderValue, cv::gapi::own::Mat& mat)
86 // cv::Scalar can contain maximum 4 chan
87 GAPI_Assert(mat.channels() > 0 && mat.channels() <= 4);
89 auto getFillBorderRowFunc = [&](int type) {
92 case CV_8U: return &fillConstBorderRow< uint8_t>; break;
93 case CV_16S: return &fillConstBorderRow< int16_t>; break;
94 case CV_16U: return &fillConstBorderRow<uint16_t>; break;
95 case CV_32F: return &fillConstBorderRow< float >; break;
96 default: GAPI_Assert(false); return &fillConstBorderRow<uint8_t>;
100 auto fillBorderRow = getFillBorderRowFunc(mat.depth());
101 for (int y = 0; y < mat.rows; y++)
103 fillBorderRow(mat.ptr(y), mat.cols, mat.channels(), borderSize, borderValue);
106 } // anonymous namespace
108 fluid::BorderHandler::BorderHandler(int border_size)
110 GAPI_Assert(border_size > 0);
111 m_border_size = border_size;
114 template <int BorderType>
115 fluid::BorderHandlerT<BorderType>::BorderHandlerT(int border_size, int data_type)
116 : BorderHandler(border_size)
118 auto getFillBorderRowFunc = [&](int border, int depth) {
119 if (border == cv::BORDER_REPLICATE)
123 case CV_8U: return &fillBorderReplicateRow< uint8_t>; break;
124 case CV_16S: return &fillBorderReplicateRow< int16_t>; break;
125 case CV_16U: return &fillBorderReplicateRow<uint16_t>; break;
126 case CV_32F: return &fillBorderReplicateRow< float >; break;
127 default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReplicateRow<uint8_t>;
130 else if (border == cv::BORDER_REFLECT_101)
134 case CV_8U: return &fillBorderReflectRow< uint8_t>; break;
135 case CV_16S: return &fillBorderReflectRow< int16_t>; break;
136 case CV_16U: return &fillBorderReflectRow<uint16_t>; break;
137 case CV_32F: return &fillBorderReflectRow< float >; break;
138 default: GAPI_Assert(!"Unsupported data type"); return &fillBorderReflectRow<uint8_t>;
143 GAPI_Assert(!"Unsupported border type");
144 return &fillBorderReflectRow<uint8_t>;
148 m_fill_border_row = getFillBorderRowFunc(BorderType, CV_MAT_DEPTH(data_type));
152 template <int BorderType> int getBorderIdx(int log_idx, int desc_height);
154 template<> int getBorderIdx<cv::BORDER_REPLICATE>(int log_idx, int desc_height)
156 return log_idx < 0 ? 0 : desc_height - 1;
159 template<> int getBorderIdx<cv::BORDER_REFLECT_101>(int log_idx, int desc_height)
161 return log_idx < 0 ? -log_idx : 2*(desc_height - 1) - log_idx;
165 template <int BorderType>
166 const uint8_t* fluid::BorderHandlerT<BorderType>::inLineB(int log_idx, const BufferStorageWithBorder& data, int desc_height) const
168 auto idx = getBorderIdx<BorderType>(log_idx, desc_height);
169 return data.ptr(idx);
172 fluid::BorderHandlerT<cv::BORDER_CONSTANT>::BorderHandlerT(int border_size, cv::gapi::own::Scalar border_value)
173 : BorderHandler(border_size), m_border_value(border_value)
176 const uint8_t* fluid::BorderHandlerT<cv::BORDER_CONSTANT>::inLineB(int /*log_idx*/, const BufferStorageWithBorder& /*data*/, int /*desc_height*/) const
178 return m_const_border.ptr(0, m_border_size);
181 void fluid::BorderHandlerT<cv::BORDER_CONSTANT>::fillCompileTimeBorder(BufferStorageWithBorder& data)
183 m_const_border.create(1, data.cols(), data.data().type());
184 m_const_border = m_border_value;
186 cv::gapi::fillBorderConstant(m_border_size, m_border_value, data.data());
189 template <int BorderType>
190 void fluid::BorderHandlerT<BorderType>::updateBorderPixels(BufferStorageWithBorder &data, int startLine, int nLines) const
192 auto& mat = data.data();
193 auto length = mat.cols;
194 auto chan = mat.channels();
196 for (int l = startLine; l < startLine + nLines; l++)
198 auto row = mat.ptr(data.physIdx(l));
199 m_fill_border_row(row, length, chan, m_border_size);
203 std::size_t fluid::BorderHandlerT<cv::BORDER_CONSTANT>::size() const
205 return m_const_border.total() * m_const_border.elemSize();
208 // Fluid BufferStorage implementation //////////////////////////////////////////
210 void fluid::BufferStorage::updateInCache(View::Cache& cache, int start_log_idx, int nLines) const
212 for (int i = 0; i < nLines; i++)
214 cache.m_linePtrs[i] = inLineB(start_log_idx + i, cache.m_desc.size.height);
218 void fluid::BufferStorage::updateOutCache(Buffer::Cache& cache, int start_log_idx, int nLines)
220 for (int i = 0; i < nLines; i++)
222 cache.m_linePtrs[i] = ptr(start_log_idx + i);
226 void fluid::BufferStorageWithBorder::init(int dtype, int border_size, Border border)
230 case cv::BORDER_CONSTANT:
231 m_borderHandler.reset(new BorderHandlerT<cv::BORDER_CONSTANT>(border_size, border.value)); break;
232 case cv::BORDER_REPLICATE:
233 m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REPLICATE>(border_size, dtype)); break;
234 case cv::BORDER_REFLECT_101:
235 m_borderHandler.reset(new BorderHandlerT<cv::BORDER_REFLECT_101>(border_size, dtype)); break;
241 void fluid::BufferStorageWithBorder::create(int capacity, int desc_width, int dtype)
243 auto borderSize = m_borderHandler->borderSize();
244 auto width = (desc_width + 2*borderSize);
245 m_data.create(capacity, width, dtype);
247 m_borderHandler->fillCompileTimeBorder(*this);
250 void fluid::BufferStorageWithoutBorder::create(int capacity, int desc_width, int dtype)
252 auto width = desc_width;
253 m_data.create(capacity, width, dtype);
258 const uint8_t* fluid::BufferStorageWithBorder::inLineB(int log_idx, int desc_height) const
260 if (log_idx < 0 || log_idx >= desc_height)
262 return m_borderHandler->inLineB(log_idx, *this, desc_height);
270 static 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)
272 auto subSrc = src(cv::gapi::own::Rect{src_border_size, startSrcLine, src.cols - 2*src_border_size, lpi});
273 auto subDst = dst(cv::gapi::own::Rect{dst_border_size, startDstLine, dst.cols - 2*dst_border_size, lpi});
275 subSrc.copyTo(subDst);
278 void fluid::BufferStorageWithoutBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
280 for (int l = startLine; l < startLine + nLines; l++)
282 copyWithoutBorder(m_data, 0, dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
286 void fluid::BufferStorageWithBorder::copyTo(BufferStorageWithBorder &dst, int startLine, int nLines) const
288 // Copy required lpi lines line by line (to avoid wrap if invoked for multiple lines)
289 for (int l = startLine; l < startLine + nLines; l++)
291 copyWithoutBorder(m_data, borderSize(), dst.data(), dst.borderSize(), physIdx(l), dst.physIdx(l), 1);
295 // FIXME? remember parent and remove src parameter?
296 void fluid::BufferStorageWithBorder::updateBeforeRead(int startLine, int nLines, const BufferStorage& src)
299 // Cover with tests!!
300 // (Ensure that there are no redundant copies done
301 // and only required (not fetched before) lines are copied)
303 GAPI_DbgAssert(startLine >= 0);
305 src.copyTo(*this, startLine, nLines);
306 m_borderHandler->updateBorderPixels(*this, startLine, nLines);
309 void fluid::BufferStorageWithoutBorder::updateBeforeRead(int /*startLine*/, int /*lpi*/, const BufferStorage& /*src*/)
314 void fluid::BufferStorageWithBorder::updateAfterWrite(int startLine, int nLines)
317 // Actually startLine + nLines can be > logical height so
318 // redundant end lines which will never be read
319 // can be filled in the ring buffer
320 m_borderHandler->updateBorderPixels(*this, startLine, nLines);
323 void fluid::BufferStorageWithoutBorder::updateAfterWrite(int /*startLine*/, int /*lpi*/)
328 size_t fluid::BufferStorageWithBorder::size() const
330 return m_data.total()*m_data.elemSize() + m_borderHandler->size();
333 size_t fluid::BufferStorageWithoutBorder::size() const
335 return m_data.total()*m_data.elemSize();
340 std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
341 int border_size, fluid::BorderOpt border);
342 std::unique_ptr<fluid::BufferStorage> createStorage(int capacity, int desc_width, int type,
343 int border_size, fluid::BorderOpt border)
347 std::unique_ptr<fluid::BufferStorageWithBorder> storage(new BufferStorageWithBorder);
348 storage->init(type, border_size, border.value());
349 storage->create(capacity, desc_width, type);
350 return std::move(storage);
353 std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
354 storage->create(capacity, desc_width, type);
355 return std::move(storage);
358 std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi);
359 std::unique_ptr<BufferStorage> createStorage(const cv::gapi::own::Mat& data, cv::gapi::own::Rect roi)
361 std::unique_ptr<BufferStorageWithoutBorder> storage(new BufferStorageWithoutBorder);
362 storage->attach(data, roi);
363 return std::move(storage);
368 // Fluid View implementation ///////////////////////////////////////////////////
370 void fluid::View::Priv::reset(int linesForFirstIteration)
374 m_lines_next_iter = linesForFirstIteration;
375 m_read_caret = m_p->priv().readStart();
378 void fluid::View::Priv::readDone(int linesRead, int linesForNextIteration)
381 m_read_caret += linesRead;
382 m_lines_next_iter = linesForNextIteration;
385 bool fluid::View::Priv::ready() const
387 auto lastWrittenLine = m_p->priv().writeStart() + m_p->linesReady();
389 if (lastWrittenLine == m_p->meta().size.height) lastWrittenLine += m_border_size;
391 lastWrittenLine += m_border_size;
393 auto lastRequiredLine = m_read_caret + m_lines_next_iter;
395 return lastWrittenLine >= lastRequiredLine;
398 fluid::ViewPrivWithoutOwnBorder::ViewPrivWithoutOwnBorder(const Buffer *parent, int borderSize)
402 m_border_size = borderSize;
405 const uint8_t* fluid::ViewPrivWithoutOwnBorder::InLineB(int index) const
409 const auto &p_priv = m_p->priv();
411 GAPI_DbgAssert(index >= -m_border_size
412 && index < -m_border_size + m_lines_next_iter);
414 const int log_idx = m_read_caret + index;
416 return p_priv.storage().inLineB(log_idx, m_p->meta().size.height);
419 void fluid::ViewPrivWithoutOwnBorder::allocate(int lineConsumption, BorderOpt)
421 initCache(lineConsumption);
424 void fluid::ViewPrivWithoutOwnBorder::prepareToRead()
426 const auto &storage = m_p->priv().storage();
428 const int start_log_idx = m_read_caret - m_border_size;
429 storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter);
432 fluid::ViewPrivWithOwnBorder::ViewPrivWithOwnBorder(const Buffer *parent, int borderSize)
436 m_border_size = borderSize;
439 void fluid::ViewPrivWithOwnBorder::allocate(int lineConsumption, BorderOpt border)
441 initCache(lineConsumption);
443 const auto& desc = m_cache.m_desc;
444 int type = CV_MAKETYPE(desc.depth, desc.chan);
445 m_own_storage.init(type, m_border_size, border.value());
446 m_own_storage.create(lineConsumption, desc.size.width, type);
449 void fluid::ViewPrivWithOwnBorder::prepareToRead()
454 if (m_read_caret == m_p->priv().readStart())
456 // Need to fetch full window on the first iteration
457 startLine = (m_read_caret > m_border_size) ? m_read_caret - m_border_size : 0;
458 nLines = m_lines_next_iter;
462 startLine = m_read_caret + m_border_size;
463 nLines = m_lines_next_iter - 2*m_border_size;
466 m_own_storage.updateBeforeRead(startLine, nLines, m_p->priv().storage());
468 const int start_log_idx = m_read_caret - m_border_size;
469 m_own_storage.updateInCache(m_cache, start_log_idx, m_lines_next_iter);
472 std::size_t fluid::ViewPrivWithOwnBorder::size() const
475 return m_own_storage.size();
478 const uint8_t* fluid::ViewPrivWithOwnBorder::InLineB(int index) const
481 GAPI_DbgAssert(index >= -m_border_size
482 && index < -m_border_size + m_lines_next_iter);
484 const int log_idx = m_read_caret + index;
486 return m_own_storage.inLineB(log_idx, m_p->meta().size.height);
489 bool fluid::View::ready() const
491 return m_priv->ready();
494 int fluid::View::y() const
496 return m_priv->m_read_caret - m_priv->m_border_size;
499 fluid::View::Priv& fluid::View::priv()
504 const fluid::View::Priv& fluid::View::priv() const
509 void fluid::View::Priv::initCache(int lineConsumption)
511 m_cache.m_linePtrs.resize(lineConsumption);
512 m_cache.m_desc = m_p->priv().meta();
513 m_cache.m_border_size = m_border_size;
516 // Fluid Buffer implementation /////////////////////////////////////////////////
518 fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
519 : m_readStart(read_start)
523 void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
526 cv::gapi::own::Rect roi)
528 m_writer_lpi = writer_lpi;
530 m_readStart = readStartPos;
531 m_roi = roi == own::Rect{} ? own::Rect{ 0, 0, desc.size.width, desc.size.height }
533 m_cache.m_linePtrs.resize(writer_lpi);
534 m_cache.m_desc = desc;
537 void fluid::Buffer::Priv::allocate(BorderOpt border,
539 int line_consumption,
542 GAPI_Assert(line_consumption > 0);
544 // Init physical buffer
546 // FIXME? combine line_consumption with skew?
547 auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1;
549 m_storage = createStorage(data_height,
551 CV_MAKETYPE(m_desc.depth, m_desc.chan),
555 // Finally, initialize carets
556 m_write_caret = writeStart();
558 m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
561 void fluid::Buffer::Priv::bindTo(const cv::gapi::own::Mat &data, bool is_input)
563 // FIXME: move all these fields into a separate structure
564 GAPI_Assert(m_desc == descr_of(data));
566 // Currently m_writer_lpi is obtained from metadata which is shared between islands
567 // and this assert can trigger for slot which connects two fluid islands.
568 // m_writer_lpi is used only in write-related functions and doesn't affect
569 // buffer which is island's input so it's safe to skip this check.
571 // Bring back this check when we move to 1 buffer <-> 1 metadata model
572 // if (is_input) GAPI_Assert(m_writer_lpi == 1);
574 m_storage = createStorage(data, m_roi);
576 m_is_input = is_input;
577 m_write_caret = is_input ? writeEnd(): writeStart();
578 // NB: views remain the same!
580 m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
583 bool fluid::Buffer::Priv::full() const
585 int slowest_y = writeEnd();
586 if (!m_views.empty())
588 // reset with maximum possible value and then find minimum
589 slowest_y = m_desc.size.height;
590 for (const auto &v : m_views) slowest_y = std::min(slowest_y, v.y());
593 return m_write_caret + lpi() - slowest_y > m_storage->rows();
596 void fluid::Buffer::Priv::writeDone()
598 // There are possible optimizations which can be done to fill a border values
599 // in compile time of the graph (for example border is const),
600 // so there is no need to update border values after each write.
601 // If such optimizations weren't applied, fill border for lines
602 // which have been just written
603 m_storage->updateAfterWrite(m_write_caret, m_writer_lpi);
605 // Final write may produce less LPI, so
606 // write caret may exceed logical buffer size
607 m_write_caret += m_writer_lpi;
608 // FIXME: add consistency check!
610 m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
613 void fluid::Buffer::Priv::reset()
615 m_write_caret = m_is_input ? writeEnd() : writeStart();
616 m_storage->updateOutCache(m_cache, m_write_caret, m_writer_lpi);
619 int fluid::Buffer::Priv::size() const
621 std::size_t view_sz = 0;
622 for (const auto &v : m_views) view_sz += v.priv().size();
624 auto total = view_sz;
625 if (m_storage) total += m_storage->size();
627 // FIXME: Change API to return size_t!!!
628 return static_cast<int>(total);
631 int fluid::Buffer::Priv::linesReady() const
635 return m_storage->rows();
639 const int writes = std::min(m_write_caret - writeStart(), outputLines());
644 uint8_t* fluid::Buffer::Priv::OutLineB(int index)
646 GAPI_DbgAssert(index >= 0 && index < m_writer_lpi);
648 return m_storage->ptr(m_write_caret + index);
651 int fluid::Buffer::Priv::lpi() const
654 // m_write_caret can be greater than m_writeRoi.y + m_writeRoi.height, so return value can be negative !!!
655 return std::min(writeEnd() - m_write_caret, m_writer_lpi);
658 fluid::Buffer::Buffer()
660 , m_cache(&m_priv->cache())
664 fluid::Buffer::Buffer(const cv::GMatDesc &desc)
666 , m_cache(&m_priv->cache())
668 int lineConsumption = 1;
669 int border = 0, skew = 0, wlpi = 1, readStart = 0;
670 cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
671 m_priv->init(desc, wlpi, readStart, roi);
672 m_priv->allocate({}, border, lineConsumption, skew);
675 fluid::Buffer::Buffer(const cv::GMatDesc &desc,
676 int max_line_consumption,
682 , m_cache(&m_priv->cache())
685 cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
686 m_priv->init(desc, wlpi, readStart, roi);
687 m_priv->allocate(border, border_size, max_line_consumption, skew);
690 fluid::Buffer::Buffer(const cv::gapi::own::Mat &data, bool is_input)
692 , m_cache(&m_priv->cache())
694 int wlpi = 1, readStart = 0;
695 cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};
696 m_priv->init(descr_of(data), wlpi, readStart, roi);
697 m_priv->bindTo(data, is_input);
700 int fluid::Buffer::linesReady() const
702 return m_priv->linesReady();
705 int fluid::Buffer::lpi() const
707 return m_priv->lpi();
710 fluid::View::View(Priv* p)
711 : m_priv(p), m_cache(&p->cache())
714 fluid::View fluid::Buffer::mkView(int borderSize, bool ownStorage)
716 // FIXME: logic outside of Priv (because View takes pointer to Buffer)
717 auto view = ownStorage ? View(new ViewPrivWithOwnBorder(this, borderSize))
718 : View(new ViewPrivWithoutOwnBorder(this, borderSize));
719 m_priv->addView(view);
723 void fluid::debugBufferPriv(const fluid::Buffer& buffer, std::ostream &os)
725 // FIXME Use cv::gapi::own Size and Rect with operator<<, when merged ADE-285
726 const auto& p = buffer.priv();
727 os << "Fluid buffer " << std::hex << &buffer << std::dec
728 << " " << p.m_desc.size.width << " x " << p.m_desc.size.height << "]"
729 << " readStart:" << p.m_readStart
730 << " roi:" << "[" << p.m_roi.width << " x " << p.m_roi.height << " from (" << p.m_roi.x << ", " << p.m_roi.y << ")]"
731 <<" (phys " << "[" << p.storage().cols() << " x " << p.storage().rows() << "]" << ") :"
732 << " w: " << p.m_write_caret
734 for (const auto &v : p.m_views) { os << &v.priv() << ":" << v.y() << " "; }
735 os << "], avail: " << buffer.linesReady()
739 void fluid::Buffer::debug(std::ostream &os) const
741 debugBufferPriv(*this, os);
744 fluid::Buffer::Priv& fluid::Buffer::priv()
749 const fluid::Buffer::Priv& fluid::Buffer::priv() const
754 int fluid::Buffer::y() const
759 } // namespace cv::gapi