mirror of
https://github.com/opencv/opencv.git
synced 2026-07-28 23:03:03 +04:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6d9486a6c | |||
| 9a8e47a766 | |||
| dbed39a931 | |||
| ea31c09384 | |||
| 9c23f2f1a6 | |||
| d8ffddd075 | |||
| a692d87cfb | |||
| 5e9750d1f5 | |||
| e7015f6ae8 | |||
| 9c9fb1c6d0 | |||
| 80c64fce11 | |||
| a7dfa261d8 | |||
| 4d1f0ef2d9 | |||
| df6728e64c | |||
| 7a73941f22 | |||
| 6d9e66eca1 | |||
| 2180a67670 | |||
| 365451dab0 | |||
| 1188c6fe14 | |||
| a2e754c136 | |||
| c09a3cf098 | |||
| 842341fc16 | |||
| 32a5e5fae1 | |||
| 85b9960f62 | |||
| 5640b36f6d | |||
| c8e6ce304f | |||
| 1c468add20 | |||
| 5339ef3004 | |||
| a66fd527b0 | |||
| 922d5796b9 |
@@ -38,8 +38,8 @@
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_ML_HPP
|
||||
#define OPENCV_ML_HPP
|
||||
#ifndef OPENCV_OLD_ML_HPP
|
||||
#define OPENCV_OLD_ML_HPP
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "opencv2/core.hpp"
|
||||
@@ -2038,6 +2038,6 @@ template<> struct DefaultDeleter<CvDTreeSplit>{ void operator ()(CvDTreeSplit* o
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // OPENCV_ML_HPP
|
||||
#endif // OPENCV_OLD_ML_HPP
|
||||
|
||||
/* End of file. */
|
||||
|
||||
@@ -103,7 +103,9 @@ if(CV_GCC OR CV_CLANG)
|
||||
add_extra_compiler_option(-Wundef)
|
||||
add_extra_compiler_option(-Winit-self)
|
||||
add_extra_compiler_option(-Wpointer-arith)
|
||||
add_extra_compiler_option(-Wshadow)
|
||||
if(NOT (CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0"))
|
||||
add_extra_compiler_option(-Wshadow) # old GCC emits warnings for variables + methods combination
|
||||
endif()
|
||||
add_extra_compiler_option(-Wsign-promo)
|
||||
add_extra_compiler_option(-Wuninitialized)
|
||||
add_extra_compiler_option(-Winit-self)
|
||||
|
||||
@@ -87,6 +87,8 @@ if(CUDA_FOUND)
|
||||
set(__cuda_arch_bin "6.0 6.1")
|
||||
elseif(CUDA_GENERATION STREQUAL "Volta")
|
||||
set(__cuda_arch_bin "7.0")
|
||||
elseif(CUDA_GENERATION STREQUAL "Turing")
|
||||
set(__cuda_arch_bin "7.5")
|
||||
elseif(CUDA_GENERATION STREQUAL "Auto")
|
||||
execute_process( COMMAND ${DETECT_ARCHS_COMMAND}
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/"
|
||||
@@ -113,7 +115,7 @@ if(CUDA_FOUND)
|
||||
string(REGEX REPLACE ".*\n" "" _nvcc_out "${_nvcc_out}") #Strip leading warning messages, if any
|
||||
if(NOT _nvcc_res EQUAL 0)
|
||||
message(STATUS "Automatic detection of CUDA generation failed. Going to build for all known architectures.")
|
||||
set(__cuda_arch_bin "5.3 6.2 7.0")
|
||||
set(__cuda_arch_bin "5.3 6.2 7.0 7.5")
|
||||
else()
|
||||
set(__cuda_arch_bin "${_nvcc_out}")
|
||||
string(REPLACE "2.1" "2.1(2.0)" __cuda_arch_bin "${__cuda_arch_bin}")
|
||||
@@ -122,8 +124,10 @@ if(CUDA_FOUND)
|
||||
else()
|
||||
if(${CUDA_VERSION} VERSION_LESS "9.0")
|
||||
set(__cuda_arch_bin "2.0 3.0 3.5 3.7 5.0 5.2 6.0 6.1")
|
||||
else()
|
||||
elseif(${CUDA_VERSION} VERSION_LESS "10.0")
|
||||
set(__cuda_arch_bin "3.0 3.5 3.7 5.0 5.2 6.0 6.1 7.0")
|
||||
else()
|
||||
set(__cuda_arch_bin "3.0 3.5 3.7 5.0 5.2 6.0 6.1 7.0 7.5")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1133,6 +1133,7 @@ CV_IMPL void cvFindExtrinsicCameraParams2( const CvMat* objectPoints,
|
||||
if( cvDet(&_RR) < 0 )
|
||||
cvScale( &_RRt, &_RRt, -1 );
|
||||
sc = cvNorm(&_RR);
|
||||
CV_Assert(fabs(sc) > DBL_EPSILON);
|
||||
cvSVD( &_RR, &matW, &matU, &matV, CV_SVD_MODIFY_A + CV_SVD_U_T + CV_SVD_V_T );
|
||||
cvGEMM( &matU, &matV, 1, 0, 0, &matR, CV_GEMM_A_T );
|
||||
cvScale( &_tt, &_t, cvNorm(&matR)/sc );
|
||||
|
||||
@@ -164,7 +164,9 @@ cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst)
|
||||
Mat H = dst_T.inv()*Mat(H_, false)*src_T;
|
||||
|
||||
// enforce frobeniusnorm of one
|
||||
double scale = 1.0/cv::norm(H);
|
||||
double scale = cv::norm(H);
|
||||
CV_Assert(fabs(scale) > DBL_EPSILON);
|
||||
scale = 1.0 / scale;
|
||||
return H*scale;
|
||||
}
|
||||
void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order)
|
||||
|
||||
@@ -654,7 +654,7 @@ class float16_t
|
||||
public:
|
||||
#if CV_FP16_TYPE
|
||||
|
||||
float16_t() {}
|
||||
float16_t() : h(0) {}
|
||||
explicit float16_t(float x) { h = (__fp16)x; }
|
||||
operator float() const { return (float)h; }
|
||||
static float16_t fromBits(ushort w)
|
||||
@@ -681,7 +681,7 @@ protected:
|
||||
__fp16 h;
|
||||
|
||||
#else
|
||||
float16_t() {}
|
||||
float16_t() : w(0) {}
|
||||
explicit float16_t(float x)
|
||||
{
|
||||
#if CV_AVX2
|
||||
|
||||
@@ -1353,7 +1353,7 @@ CvSlice;
|
||||
|
||||
CV_INLINE CvSlice cvSlice( int start, int end )
|
||||
{
|
||||
#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus))
|
||||
#if !(defined(CV__ENABLE_C_API_CTORS) && defined(__cplusplus) && !defined(__CUDACC__))
|
||||
CvSlice slice = { start, end };
|
||||
#else
|
||||
CvSlice slice(start, end);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define CV_VERSION_MAJOR 4
|
||||
#define CV_VERSION_MINOR 0
|
||||
#define CV_VERSION_REVISION 0
|
||||
#define CV_VERSION_STATUS "-beta"
|
||||
#define CV_VERSION_STATUS "-openvino.2018R4"
|
||||
|
||||
#define CVAUX_STR_EXP(__A) #__A
|
||||
#define CVAUX_STR(__A) CVAUX_STR_EXP(__A)
|
||||
|
||||
@@ -729,7 +729,7 @@ static bool ipp_flip(Mat &src, Mat &dst, int flip_mode)
|
||||
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiMirror, iwSrc, iwDst, ippMode);
|
||||
}
|
||||
catch(::ipp::IwException)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1032,7 @@ void Core_SeqBaseTest::run( int )
|
||||
cvClearMemStorage( storage );
|
||||
}
|
||||
}
|
||||
catch(int)
|
||||
catch(const int &)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1200,7 +1200,7 @@ void Core_SeqSortInvTest::run( int )
|
||||
storage.release();
|
||||
}
|
||||
}
|
||||
catch (int)
|
||||
catch (const int &)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1416,7 +1416,7 @@ void Core_SetTest::run( int )
|
||||
storage.release();
|
||||
}
|
||||
}
|
||||
catch(int)
|
||||
catch(const int &)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1859,7 +1859,7 @@ void Core_GraphTest::run( int )
|
||||
storage.release();
|
||||
}
|
||||
}
|
||||
catch(int)
|
||||
catch(const int &)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2121,7 +2121,7 @@ void Core_GraphScanTest::run( int )
|
||||
storage.release();
|
||||
}
|
||||
}
|
||||
catch(int)
|
||||
catch(const int &)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +332,8 @@ public:
|
||||
int poolingType;
|
||||
float spatialScale;
|
||||
|
||||
PoolingInvoker() : src(0), rois(0), dst(0), mask(0), avePoolPaddedArea(false), nstripes(0),
|
||||
PoolingInvoker() : src(0), rois(0), dst(0), mask(0), pad_l(0), pad_t(0), pad_r(0), pad_b(0),
|
||||
avePoolPaddedArea(false), nstripes(0),
|
||||
computeMaxIdx(0), poolingType(MAX), spatialScale(0) {}
|
||||
|
||||
static void run(const Mat& src, const Mat& rois, Mat& dst, Mat& mask, Size kernel,
|
||||
|
||||
@@ -331,8 +331,18 @@ InferenceEngine::StatusCode InfEngineBackendNet::setBatchSize(size_t size, Infer
|
||||
|
||||
size_t InfEngineBackendNet::getBatchSize() const CV_NOEXCEPT
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "");
|
||||
return 0;
|
||||
size_t batchSize = 0;
|
||||
for (const auto& inp : inputs)
|
||||
{
|
||||
CV_Assert(inp.second);
|
||||
std::vector<size_t> dims = inp.second->getDims();
|
||||
CV_Assert(!dims.empty());
|
||||
if (batchSize != 0)
|
||||
CV_Assert(batchSize == dims.back());
|
||||
else
|
||||
batchSize = dims.back();
|
||||
}
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
#if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2018R2)
|
||||
|
||||
@@ -287,6 +287,46 @@ TEST_P(Test_Torch_nets, OpenFace_accuracy)
|
||||
normAssert(out, outRef, "", default_l1, default_lInf);
|
||||
}
|
||||
|
||||
static Mat getSegmMask(const Mat& scores)
|
||||
{
|
||||
const int rows = scores.size[2];
|
||||
const int cols = scores.size[3];
|
||||
const int numClasses = scores.size[1];
|
||||
|
||||
Mat maxCl = Mat::zeros(rows, cols, CV_8UC1);
|
||||
Mat maxVal(rows, cols, CV_32FC1, Scalar(0));
|
||||
for (int ch = 0; ch < numClasses; ch++)
|
||||
{
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
const float *ptrScore = scores.ptr<float>(0, ch, row);
|
||||
uint8_t *ptrMaxCl = maxCl.ptr<uint8_t>(row);
|
||||
float *ptrMaxVal = maxVal.ptr<float>(row);
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
if (ptrScore[col] > ptrMaxVal[col])
|
||||
{
|
||||
ptrMaxVal[col] = ptrScore[col];
|
||||
ptrMaxCl[col] = (uchar)ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxCl;
|
||||
}
|
||||
|
||||
// Computer per-class intersection over union metric.
|
||||
static void normAssertSegmentation(const Mat& ref, const Mat& test)
|
||||
{
|
||||
CV_Assert_N(ref.dims == 4, test.dims == 4);
|
||||
const int numClasses = ref.size[1];
|
||||
CV_Assert(numClasses == test.size[1]);
|
||||
|
||||
Mat refMask = getSegmMask(ref);
|
||||
Mat testMask = getSegmMask(test);
|
||||
EXPECT_EQ(countNonZero(refMask != testMask), 0);
|
||||
}
|
||||
|
||||
TEST_P(Test_Torch_nets, ENet_accuracy)
|
||||
{
|
||||
checkBackend();
|
||||
@@ -313,14 +353,16 @@ TEST_P(Test_Torch_nets, ENet_accuracy)
|
||||
// Due to numerical instability in Pooling-Unpooling layers (indexes jittering)
|
||||
// thresholds for ENet must be changed. Accuracy of results was checked on
|
||||
// Cityscapes dataset and difference in mIOU with Torch is 10E-4%
|
||||
normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.5);
|
||||
normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.552);
|
||||
normAssertSegmentation(ref, out);
|
||||
|
||||
const int N = 3;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
net.setInput(inputBlob, "");
|
||||
Mat out = net.forward();
|
||||
normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.5);
|
||||
normAssert(ref, out, "", 0.00044, /*target == DNN_TARGET_CPU ? 0.453 : */0.552);
|
||||
normAssertSegmentation(ref, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* @author Pablo F. Alcantarilla
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__
|
||||
#define __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__
|
||||
#ifndef __OPENCV_FEATURES_2D_KAZE_CONFIG_H__
|
||||
#define __OPENCV_FEATURES_2D_KAZE_CONFIG_H__
|
||||
|
||||
// OpenCV Includes
|
||||
#include "../precomp.hpp"
|
||||
|
||||
@@ -103,6 +103,58 @@ using ::cvflann::KL_Divergence;
|
||||
|
||||
/** @brief The FLANN nearest neighbor index class. This class is templated with the type of elements for which
|
||||
the index is built.
|
||||
|
||||
`Distance` functor specifies the metric to be used to calculate the distance between two points.
|
||||
There are several `Distance` functors that are readily available:
|
||||
|
||||
@link cvflann::L2_Simple cv::flann::L2_Simple @endlink- Squared Euclidean distance functor.
|
||||
This is the simpler, unrolled version. This is preferable for very low dimensionality data (eg 3D points)
|
||||
|
||||
@link cvflann::L2 cv::flann::L2 @endlink- Squared Euclidean distance functor, optimized version.
|
||||
|
||||
@link cvflann::L1 cv::flann::L1 @endlink - Manhattan distance functor, optimized version.
|
||||
|
||||
@link cvflann::MinkowskiDistance cv::flann::MinkowskiDistance @endlink - The Minkowsky distance functor.
|
||||
This is highly optimised with loop unrolling.
|
||||
The computation of squared root at the end is omitted for efficiency.
|
||||
|
||||
@link cvflann::MaxDistance cv::flann::MaxDistance @endlink - The max distance functor. It computes the
|
||||
maximum distance between two vectors. This distance is not a valid kdtree distance, it's not
|
||||
dimensionwise additive.
|
||||
|
||||
@link cvflann::HammingLUT cv::flann::HammingLUT @endlink - %Hamming distance functor. It counts the bit
|
||||
differences between two strings using a lookup table implementation.
|
||||
|
||||
@link cvflann::Hamming cv::flann::Hamming @endlink - %Hamming distance functor. Population count is
|
||||
performed using library calls, if available. Lookup table implementation is used as a fallback.
|
||||
|
||||
@link cvflann::Hamming2 cv::flann::Hamming2 @endlink- %Hamming distance functor. Population count is
|
||||
implemented in 12 arithmetic operations (one of which is multiplication).
|
||||
|
||||
@link cvflann::HistIntersectionDistance cv::flann::HistIntersectionDistance @endlink - The histogram
|
||||
intersection distance functor.
|
||||
|
||||
@link cvflann::HellingerDistance cv::flann::HellingerDistance @endlink - The Hellinger distance functor.
|
||||
|
||||
@link cvflann::ChiSquareDistance cv::flann::ChiSquareDistance @endlink - The chi-square distance functor.
|
||||
|
||||
@link cvflann::KL_Divergence cv::flann::KL_Divergence @endlink - The Kullback-Leibler divergence functor.
|
||||
|
||||
Although the provided implementations cover a vast range of cases, it is also possible to use
|
||||
a custom implementation. The distance functor is a class whose `operator()` computes the distance
|
||||
between two features. If the distance is also a kd-tree compatible distance, it should also provide an
|
||||
`accum_dist()` method that computes the distance between individual feature dimensions.
|
||||
|
||||
In addition to `operator()` and `accum_dist()`, a distance functor should also define the
|
||||
`ElementType` and the `ResultType` as the types of the elements it operates on and the type of the
|
||||
result it computes. If a distance functor can be used as a kd-tree distance (meaning that the full
|
||||
distance between a pair of features can be accumulated from the partial distances between the
|
||||
individual dimensions) a typedef `is_kdtree_distance` should be present inside the distance functor.
|
||||
If the distance is not a kd-tree distance, but it's a distance in a vector space (the individual
|
||||
dimensions of the elements it operates on can be accessed independently) a typedef
|
||||
`is_vector_space_distance` should be defined inside the functor. If neither typedef is defined, the
|
||||
distance is assumed to be a metric distance and will only be used with indexes operating on
|
||||
generic metric distances.
|
||||
*/
|
||||
template <typename Distance>
|
||||
class GenericIndex
|
||||
@@ -217,6 +269,17 @@ public:
|
||||
std::vector<DistanceType>& dists, int knn, const ::cvflann::SearchParams& params);
|
||||
void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const ::cvflann::SearchParams& params);
|
||||
|
||||
/** @brief Performs a radius nearest neighbor search for a given query point using the index.
|
||||
|
||||
@param query The query point.
|
||||
@param indices Vector that will contain the indices of the nearest neighbors found.
|
||||
@param dists Vector that will contain the distances to the nearest neighbors found. It has the same
|
||||
number of elements as indices.
|
||||
@param radius The search radius.
|
||||
@param params SearchParams
|
||||
|
||||
This function returns the number of nearest neighbors found.
|
||||
*/
|
||||
int radiusSearch(const std::vector<ElementType>& query, std::vector<int>& indices,
|
||||
std::vector<DistanceType>& dists, DistanceType radius, const ::cvflann::SearchParams& params);
|
||||
int radiusSearch(const Mat& query, Mat& indices, Mat& dists,
|
||||
|
||||
@@ -92,3 +92,4 @@ if(TARGET opencv_test_gapi)
|
||||
endif()
|
||||
|
||||
ocv_add_perf_tests()
|
||||
ocv_add_samples()
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Graph API {#gapi}
|
||||
|
||||
# Introduction {#gapi_root_intro}
|
||||
|
||||
OpenCV Graph API (or G-API) is a new OpenCV module targeted to make
|
||||
regular image processing fast and portable. These two goals are
|
||||
achieved by introducing a new graph-based model of execution.
|
||||
|
||||
G-API is a special module in OpenCV -- in contrast with the majority
|
||||
of other main modules, this one acts as a framework rather than some
|
||||
specific CV algorithm. G-API provides means to define CV operations,
|
||||
construct graphs (in form of expressions) using it, and finally
|
||||
implement and run the operations for a particular backend.
|
||||
|
||||
# Contents
|
||||
|
||||
G-API documentation is organized into the following chapters:
|
||||
|
||||
- @subpage gapi_purposes
|
||||
|
||||
The motivation behind G-API and its goals.
|
||||
|
||||
- @subpage gapi_hld
|
||||
|
||||
General overview of G-API architecture and its major internal
|
||||
components.
|
||||
|
||||
- @subpage gapi_kernel_api
|
||||
|
||||
Learn how to introduce new operations in G-API and implement it for
|
||||
various backends.
|
||||
|
||||
- @subpage gapi_impl
|
||||
|
||||
Low-level implementation details of G-API, for those who want to
|
||||
contribute.
|
||||
|
||||
- API Reference: functions and classes
|
||||
|
||||
- @subpage gapi_core
|
||||
|
||||
Core G-API operations - arithmetic, boolean, and other matrix
|
||||
operations;
|
||||
|
||||
- @subpage gapi_imgproc
|
||||
|
||||
Image processing functions: color space conversions, various
|
||||
filters, etc.
|
||||
|
||||
# API Example {#gapi_example}
|
||||
|
||||
A very basic example of G-API pipeline is shown below:
|
||||
|
||||
@include modules/gapi/samples/api_example.cpp
|
||||
|
||||
<!-- TODO align this code with text using marks and itemized list -->
|
||||
|
||||
G-API is a separate OpenCV module so its header files have to be
|
||||
included explicitly. The first four lines of `main()` create and
|
||||
initialize OpenCV's standard video capture object, which fetches
|
||||
video frames from either an attached camera or a specified file.
|
||||
|
||||
G-API pipelie is constructed next. In fact, it is a series of G-API
|
||||
operation calls on cv::GMat data. The important aspect of G-API is
|
||||
that this code block is just a declaration of actions, but not the
|
||||
actions themselves. No processing happens at this point, G-API only
|
||||
tracks which operations form pipeline and how it is connected. G-API
|
||||
_Data objects_ (here it is cv::GMat) are used to connect operations
|
||||
each other. `in` is an _empty_ cv::GMat signalling that it is a
|
||||
beginning of computation.
|
||||
|
||||
After G-API code is written, it is captured into a call graph with
|
||||
instantiation of cv::GComputation object. This object takes
|
||||
input/output data references (in this example, `in` and `out`
|
||||
cv::GMat objects, respectively) as parameters and reconstructs the
|
||||
call graph based on all the data flow between `in` and `out`.
|
||||
|
||||
cv::GComputation is a thin object in sense that it just captures which
|
||||
operations form up a computation. However, it can be used to execute
|
||||
computations -- in the following processing loop, every captured frame (a
|
||||
cv::Mat `input_frame`) is passed to cv::GComputation::apply().
|
||||
|
||||

|
||||
|
||||
cv::GComputation::apply() is a polimorphic method which accepts a
|
||||
variadic number of arguments. Since this computation is defined on one
|
||||
input, one output, a special overload of cv::GComputation::apply() is
|
||||
used to pass input data and get output data.
|
||||
|
||||
Internally, cv::GComputation::apply() compiles the captured graph for
|
||||
the given input parameters and executes the compiled graph on data
|
||||
immediately.
|
||||
|
||||
There is a number important concepts can be outlines with this examle:
|
||||
* Graph declaration and graph execution are distinct steps;
|
||||
* Graph is built implicitly from a sequence of G-API expressions;
|
||||
* G-API supports function-like calls -- e.g. cv::gapi::resize(), and
|
||||
operators, e.g operator|() which is used to compute bitwise OR;
|
||||
* G-API syntax aims to look pure: every operation call within a graph
|
||||
yields a new result, thus forming a directed acyclic graph (DAG);
|
||||
* Graph declaration is not bound to any data -- real data objects
|
||||
(cv::Mat) come into picture after the graph is already declared.
|
||||
|
||||
<!-- FIXME: The above operator|() link links to MatExpr not GAPI -->
|
||||
|
||||
See Tutorial[TBD] and Porting examples[TBD] to learn more on various
|
||||
G-API features and concepts.
|
||||
|
||||
<!-- TODO Add chapter on declaration, compilation, execution -->
|
||||
@@ -0,0 +1,76 @@
|
||||
# Why Graph API? {#gapi_purposes}
|
||||
|
||||
# Motivation behind G-API {#gapi_intro_why}
|
||||
|
||||
G-API module brings graph-based model of execution to OpenCV. This
|
||||
chapter briefly describes how this new model can help software
|
||||
developers in two aspects: optimizing and porting image processing
|
||||
algorithms.
|
||||
|
||||
## Optimizing with Graph API {#gapi_intro_opt}
|
||||
|
||||
Traditionally OpenCV provided a lot of stand-alone image processing
|
||||
functions (see modules `core` and `imgproc`). Many of that functions
|
||||
are well-optimized (e.g. vectorized for specific CPUs, parallel, etc)
|
||||
but still the out-of-box optimization scope has been limited to a
|
||||
single function only -- optimizing the whole algorithm built atop of that
|
||||
functions was a responsibility of a programmer.
|
||||
|
||||
OpenCV 3.0 introduced _Transparent API_ (or _T-API_) which allowed to
|
||||
offload OpenCV function calls transparently to OpenCL devices and save
|
||||
on Host/Device data transfers with cv::UMat -- and it was a great step
|
||||
forward. However, T-API is a dynamic API -- user code still remains
|
||||
unconstrained and OpenCL kernels are enqueued in arbitrary order, thus
|
||||
eliminating further pipeline-level optimization potential.
|
||||
|
||||
G-API brings implicit graph model to OpenCV 4.0. Graph model captures
|
||||
all operations and its data dependencies in a pipeline and so provides
|
||||
G-API framework with extra information to do pipeline-level
|
||||
optimizations.
|
||||
|
||||
The cornerstone of graph-based optimizations is _Tiling_. Tiling
|
||||
allows to break the processing into smaller parts and reorganize
|
||||
operations to enable data parallelism, improve data locality, and save
|
||||
memory footprint. Data locality is an especially important aspect of
|
||||
software optimization due to diffent costs of memory access on modern
|
||||
computer architectures -- the more data is reused in the first level
|
||||
cache, the more efficient pipeline is.
|
||||
|
||||
Definitely the aforementioned techinques can be applied manually --
|
||||
but it requires extra skills and knowledge of the target platform and
|
||||
the algorithm implementation changes irrevocably -- becoming more
|
||||
specific, less flexible, and harder to extend and maintain.
|
||||
|
||||
G-API takes this responsiblity and complexity from user and does the
|
||||
majority of the work by itself, keeping the algorithm code clean from
|
||||
device or optimization details. This approach has its own limitations,
|
||||
though, as graph model is a _constrained_ model and not every
|
||||
algorithm can be represented as a graph, so the G-API scope is limited
|
||||
only to regular image processing -- various filters, arithmentic,
|
||||
binary operations, and well-defined geometrical transformations.
|
||||
|
||||
## Porting with Graph API {#gapi_intro_port}
|
||||
|
||||
The essense of G-API is declaring a sequence of operations to run, and
|
||||
then executing that sequence. G-API is a constrained API, so it puts a
|
||||
number of limitations on which operations can form a pipeline and
|
||||
which data these operations may exchange each other.
|
||||
|
||||
This formalization in fact helps to make an algorithm portable. G-API
|
||||
clearly separates operation _interfaces_ from its _implementations_.
|
||||
|
||||
One operation (_kernel_) may have multiple implementations even for a
|
||||
single device (e.g., OpenCV-based "reference" implementation and a
|
||||
tiled optimized implementation, both running on CPU). Graphs (or
|
||||
_Computations_ in G-API terms) are built only using operation
|
||||
interfaces, not implementations -- thus the same graph can be executed
|
||||
on different devices (and, of course, using different optimization
|
||||
techniques) with little-to-no changes in the graph itself.
|
||||
|
||||
G-API supports plugins (_Backends_) which aggreate logic and
|
||||
intelligence on what is the best way to execute on a particular
|
||||
platform. Once a pipeline is built with G-API, it can be parametrized
|
||||
to use either of the backends (or a combination of it) and so a graph
|
||||
can be ported easily to a new platform.
|
||||
|
||||
@sa @ref gapi_hld
|
||||
@@ -0,0 +1,159 @@
|
||||
# High-level design overview {#gapi_hld}
|
||||
|
||||
# G-API High-level design overview
|
||||
|
||||
[TOC]
|
||||
|
||||
G-API is a heterogeneous framework and provides single API to program
|
||||
image processing pipelines with a number of supported backends.
|
||||
|
||||
The key design idea is to keep pipeline code itself platform-neutral
|
||||
while specifying which kernels to use and which devices to utilize
|
||||
using extra parameters at graph compile (configuration) time. This
|
||||
requirement has led to the following architecture:
|
||||
|
||||
<!-- FIXME: Render from dot directly -->
|
||||
|
||||

|
||||
|
||||
There are three layers in this architecture:
|
||||
* **API Layer** -- this is the top layer, which implements G-API
|
||||
public interface, its building blocks and semantics.
|
||||
When user constructs a pipeline with G-API, he interacts with this
|
||||
layer directly, and the entities the user operates on (like cv::GMat
|
||||
or cv::GComputation) are provided by this layer.
|
||||
* **Graph Compiler Layer** -- this is the intermediate layer which
|
||||
unrolls user computation into a graph and then applies a number of
|
||||
transformations to it (e.g. optimizations). This layer is built atop
|
||||
of [ADE Framework](@ref gapi_detail_ade).
|
||||
* **Backends Layer** -- this is the lowest level layer, which lists a
|
||||
number of _Backends_. In contrast with the above two layers,
|
||||
backends are highly coupled with low-level platform details, with
|
||||
every backend standing for every platform. A backend operates on a
|
||||
processed graph (coming from the graph compiler) and executes this
|
||||
graph optimally for a specific platform or device.
|
||||
|
||||
# API layer {#gapi_api_layer}
|
||||
|
||||
API layer is what user interacts with when defining and using a
|
||||
pipeline (a Computation in G-API terms). API layer defines a set of
|
||||
G-API _dynamic_ objects which can be used as inputs, outputs, and
|
||||
intermediate data objects within a graph:
|
||||
* cv::GMat
|
||||
* cv::GScalar
|
||||
* cv::GArray (template class)
|
||||
|
||||
API layer specifies a list of Operations which are defined on these
|
||||
data objects -- so called kernels. See G-API [core](@ref gapi_core)
|
||||
and [imgproc](@ref gapi_imgproc) namespaces for details on which
|
||||
operations G-API provides by default.
|
||||
|
||||
G-API is not limited to these operations only -- users can define
|
||||
their own kernels easily using a special macro G_TYPED_KERNEL().
|
||||
|
||||
API layer is also responsible for marshalling and storing operation
|
||||
parameters on pipeline creation. In addition to the aforementioned
|
||||
G-API dynamic objects, operations may also accept arbitrary
|
||||
parameters (more on this [below](@ref gapi_detail_params)), so API
|
||||
layer captures its values and stores internally upon the moment of
|
||||
execution.
|
||||
|
||||
Finally, cv::GComputation and cv::GCompiled are the remaining
|
||||
important components of API layer. The former wraps a series of G-API
|
||||
expressions into an object (graph), and the latter is a product of
|
||||
graph _compilation_ (see [this chapter](@ref gapi_detail_compiler) for
|
||||
details).
|
||||
|
||||
# Graph compiler layer {#gapi_compiler}
|
||||
|
||||
Every G-API computation is compiled before it executes. Compilation
|
||||
process is triggered in two ways:
|
||||
* _implicitly_, when cv::GComputation::apply() is used. In this case,
|
||||
graph compilation is then immediately followed by execution.
|
||||
* _explicitly_, when cv::GComputation::compile() is used. In this case,
|
||||
a cv::GCompiled object is returned which then can be invoked as a
|
||||
C++ functor.
|
||||
|
||||
The first way is recommended for cases when input data format is not
|
||||
known in advance -- e.g. when it comes from an arbitrary input file.
|
||||
The second way is recommended for deployment (production) scenarios
|
||||
where input data characteristics are usually predefined.
|
||||
|
||||
Graph compilation process is built atop of ADE Framework. Initially, a
|
||||
bipartite graph is generated from expressions captured by API layer.
|
||||
This graph contains nodes of two types: _Data_ and _Operations_. Graph
|
||||
always starts and ends with a Data node(s), with Operations nodes
|
||||
in-between. Every Operation node has inputs and outputs, both are Data
|
||||
nodes.
|
||||
|
||||
After the initial graph is generated, it is actually processed by a
|
||||
number of graph transformations, called _passes_. ADE Framework acts
|
||||
as a compiler pass management engine, and passes are written
|
||||
specifically for G-API.
|
||||
|
||||
There are different passes which check graph validity, refine details
|
||||
on operations and data, organize nodes into clusters ("Islands") based
|
||||
on affinity or user-specified regioning[TBD], and more. Backends also
|
||||
are able to inject backend-specific passes into the compilation
|
||||
process, see more on this in the [dedicated chapter](@ref gapi_detail_meta).
|
||||
|
||||
Result of graph compilation is a compiled object, represented by class
|
||||
cv::GCompiled. A new cv::GCompiled object is always created regardless
|
||||
if there was an explicit or implicit compilation request (see
|
||||
above). Actual graph execution happens within cv::GCompiled and is
|
||||
determined by backends which participated in the graph compilation.
|
||||
|
||||
@sa cv::GComputation::apply(), cv::GComputation::compile(), cv::GCompiled
|
||||
|
||||
# Backends layer {#gapi_backends}
|
||||
|
||||
The above diagram lists two backends, _OpenCV_ and _Fluid_. _OpenCV_
|
||||
is so-called "reference backend", which implements G-API operations
|
||||
using plain old OpenCV functions. This backend is useful for
|
||||
prototyping on a familiar development system. _Fluid_ is a plugin for
|
||||
cache-efficient execution on CPU -- it implements a different
|
||||
execution policy and operates with its own, special kernels. Fluid
|
||||
backend allows to achieve less memory footprint and better memory
|
||||
locality when running on CPU.
|
||||
|
||||
There may be more backends available, e.g. Halide, OpenCL, etc. --
|
||||
G-API provides an uniform internal API to develop backends so any
|
||||
enthusiast or a company are free to scale G-API on a new platform or
|
||||
accelerator. In terms of OpenCV infrastructure, every new backend is a
|
||||
new distinct OpenCV module, which extends G-API when build as a part
|
||||
of OpenCV.
|
||||
|
||||
# Graph execution {#gapi_compiled}
|
||||
|
||||
The way graph executed is defined by backends selected for
|
||||
compilation. In fact, every backend builds its own execution script as
|
||||
the final stage of graph compilation process, when an executable
|
||||
(compiled) object is being generated. For example, in OpenCV backend,
|
||||
this script is just a topologically-sorted sequence of OpenCV
|
||||
functions to call; for Fluid backend, it is a similar thing -- a
|
||||
topologically sorted list of _Agents_ processing lines of input on
|
||||
every iteration.
|
||||
|
||||
Graph execution is triggered in two ways:
|
||||
* via cv::GComputation::apply(), with graph compiled in-place exactly
|
||||
for the given input data;
|
||||
* via cv::GCompiled::operator()(), when the graph has been precompiled.
|
||||
|
||||
Both methods are polimorphic and take a variadic number of arguments,
|
||||
with validity checks performed in runtime. If a number, shapes, and
|
||||
formats of passed data objects differ from expected, a run-time
|
||||
exception is thrown. G-API also provides _typed_ wrappers to move
|
||||
these checks to the compile time -- see cv::GComputationT<>.
|
||||
|
||||
G-API graph execution is declared stateless -- it means that a
|
||||
compiled functor (cv::GCompiled) acts like a pure C++ function and
|
||||
provides the same result for the same set of input arguments.
|
||||
|
||||
Both execution methods take \f$N+M\f$ parameters, where \f$N\f$ is a
|
||||
number of inputs, and \f$M\f$ is a number of outputs on which a
|
||||
cv::GComputation is defined. Note that while G-API types (cv::GMat,
|
||||
etc) are used in definition, the execution methods accept OpenCV's
|
||||
traditional data types (like cv::Mat) which hold actual data -- see
|
||||
table in [parameter marshalling](@#gapi_detail_params).
|
||||
|
||||
@sa @ref gapi_impl, @ref gapi_kernel_api
|
||||
@@ -0,0 +1,170 @@
|
||||
# Kernel API {#gapi_kernel_api}
|
||||
|
||||
[TOC]
|
||||
|
||||
# G-API Kernel API
|
||||
|
||||
The core idea behind G-API is portability -- a pipeline built with
|
||||
G-API must be portable (or at least able to be portable). It means
|
||||
that either it works out-of-the box when compiled for new platform,
|
||||
_or_ G-API provides necessary tools to make it running there, with
|
||||
little-to-no changes in the algorithm itself.
|
||||
|
||||
This idea can be achieved by separating kernel interface from its
|
||||
implementation. Once a pipeline is built using kernel interfaces, it
|
||||
becomes implementation-neutral -- the implementation details
|
||||
(i.e. which kernels to use) are passed on a separate stage (graph
|
||||
compilation).
|
||||
|
||||
Kernel-implementation hierarchy may look like:
|
||||
|
||||

|
||||
|
||||
A pipeline itself then can be expressed only in terms of `A`, `B`, and
|
||||
so on, and choosing which implementation to use in execution becomes
|
||||
an external parameter.
|
||||
|
||||
# Defining a kernel {#gapi_defining_kernel}
|
||||
|
||||
G-API provides a macro to define a new kernel interface --
|
||||
G_TYPED_KERNEL():
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_api
|
||||
|
||||
This macro is a shortcut to a new type definition. It takes three
|
||||
arguments to register a new type, and requires type body to be present
|
||||
(see [below](@ref gapi_kernel_supp_info)). The macro arguments are:
|
||||
1. Kernel interface name -- also serves as a name of new type defined
|
||||
with this macro;
|
||||
2. Kernel signature -- an `std::function<>`-like signature which defines
|
||||
API of the kernel;
|
||||
3. Kernel's unique name -- used to identify kernel when its type
|
||||
informattion is stripped within the system.
|
||||
|
||||
Kernel declaration may be seen as function declaration -- in both cases
|
||||
a new entity must be used then according to the way it was defined.
|
||||
|
||||
Kernel signature defines kernel's usage syntax -- which parameters
|
||||
it takes during graph construction. Implementations can also use this
|
||||
signature to derive it into backend-specific callback signatures (see
|
||||
next chapter).
|
||||
|
||||
Kernel may accept values of any type, and G-API _dynamic_ types are
|
||||
handled in a special way. All other types are opaque to G-API and
|
||||
passed to kernel in `outMeta()` or in execution callbacks as-is.
|
||||
|
||||
Kernel's return value can _only_ be of G-API dynamic type -- cv::GMat,
|
||||
cv::GScalar, or cv::GArray<T>. If an operation has more than one output,
|
||||
it should be wrapped into an `std::tuple<>` (which can contain only
|
||||
mentioned G-API types). Arbitrary-output-number operations are not
|
||||
supported.
|
||||
|
||||
Once a kernel is defined, it can be used in pipelines with special,
|
||||
G-API-supplied method "::on()". This method has the same signature as
|
||||
defined in kernel, so this code:
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_on
|
||||
|
||||
is a perfectly legal construction. This example has some verbosity,
|
||||
though, so usually a kernel declaration comes with a C++ function
|
||||
wrapper ("factory method") which enables optional parameters, more
|
||||
compact syntax, Doxygen comments, etc:
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap
|
||||
|
||||
so now it can be used like:
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap_call
|
||||
|
||||
# Extra information {#gapi_kernel_supp_info}
|
||||
|
||||
In the current version, kernel declaration body (everything within the
|
||||
curly braces) must contain a static function `outMeta()`. This function
|
||||
establishes a functional dependency between operation's input and
|
||||
output metadata.
|
||||
|
||||
_Metadata_ is an information about data kernel operates on. Since
|
||||
non-G-API types are opaque to G-API, G-API cares only about `G*` data
|
||||
descriptors (i.e. dimensions and format of cv::GMat, etc).
|
||||
|
||||
`outMeta()` is also an example of how kernel's signature can be
|
||||
transformed into a derived callback -- note that in this example,
|
||||
`outMeta()` signature exactly follows the kernel signature (defined
|
||||
within the macro) but is different -- where kernel expects cv::GMat,
|
||||
`outMeta()` takes and returns cv::GMatDesc (a G-API structure metadata
|
||||
for cv::GMat).
|
||||
|
||||
The point of `outMeta()` is to propagate metadata information within
|
||||
computation from inputs to outputs and infer metadata of internal
|
||||
(intermediate, temporary) data objects. This information is required
|
||||
for further pipeline optimizations, memory allocation, and other
|
||||
operations done by G-API framework during graph compilation.
|
||||
|
||||
<!-- TODO add examples -->
|
||||
|
||||
# Implementing a kernel {#gapi_kernel_implementing}
|
||||
|
||||
Once a kernel is declared, its interface can be used to implement
|
||||
versions of this kernel in different backends. This concept is
|
||||
naturally projected from object-oriented programming
|
||||
"Interface/Implementation" idiom: an interface can be implemented
|
||||
multiple times, and different implementations of a kernel should be
|
||||
substitutable with each other without breaking the algorithm
|
||||
(pipeline) logic (Liskov Substitution Principle).
|
||||
|
||||
Every backend defines its own way to implement a kernel interface.
|
||||
This way is regular, though -- whatever plugin is, its kernel
|
||||
implementation must be "derived" from a kernel interface type.
|
||||
|
||||
Kernel implementation are then organized into _kernel
|
||||
packages_. Kernel packages are passed to cv::GComputation::compile()
|
||||
as compile arguments, with some hints to G-API on how to select proper
|
||||
kernels (see more on this in "Heterogeneity"[TBD]).
|
||||
|
||||
For example, the aforementioned `Filter2D` is implemented in
|
||||
"reference" CPU (OpenCV) plugin this way (*NOTE* -- this is a
|
||||
simplified form with improper border handling):
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_ocv
|
||||
|
||||
Note how CPU (OpenCV) plugin has transformed the original kernel
|
||||
signature:
|
||||
- Input cv::GMat has been substituted with cv::Mat, holding actual input
|
||||
data for the underlying OpenCV function call;
|
||||
- Output cv::GMat has been transformed into extra output parameter, thus
|
||||
`GCPUFilter2D::run()` takes one argument more than the original
|
||||
kernel signature.
|
||||
|
||||
The basic intuition for kernel developer here is _not to care_ where
|
||||
that cv::Mat objects come from instead of the original cv::GMat -- and
|
||||
just follow the signature conventions defined by the plugin. G-API
|
||||
will call this method during execution and supply all the necessary
|
||||
information (and forward the original opaque data as-is).
|
||||
|
||||
# Compound kernels
|
||||
|
||||
Sometimes kernel is a single thing only on API level. It is convenient
|
||||
for users, but on a particular implementation side it would be better to
|
||||
have multiple kernels (a subgraph) doing the thing instead. An example
|
||||
is goodFeaturesToTrack() -- while in OpenCV backend it may remain a
|
||||
single kernel, with Fluid it becomes compound -- Fluid can handle Harris
|
||||
response calculation but can't do sparse non-maxima suppression and
|
||||
point extraction to an STL vector:
|
||||
|
||||
<!-- PIC -->
|
||||
|
||||
A compound kernel _implementation_ can be defined using a generic
|
||||
macro GAPI_COMPOUND_KERNEL():
|
||||
|
||||
@snippet modules/gapi/samples/kernel_api_snippets.cpp compound
|
||||
|
||||
<!-- TODO: ADD on how Compound kernels may simplify dispatching -->
|
||||
<!-- TODO: Add details on when expand() is called! -->
|
||||
|
||||
It is important to distinguish a compound kernel from G-API high-order
|
||||
function, i.e. a C++ function which looks like a kernel but in fact
|
||||
generates a subgraph. The core difference is that a compound kernel is
|
||||
an _implementation detail_ and a kernel implementation may be either
|
||||
compound or not (depending on backend capabilities), while a
|
||||
high-order function is a "macro" in terms of G-API and so cannot act as
|
||||
an interface which then needs to be implemented by a backend.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Implementation details {#gapi_impl}
|
||||
|
||||
[TOC]
|
||||
|
||||
# G-API Implementation details {#gapi_impl_header}
|
||||
|
||||
## Api layer details {#gapi_detail_api}
|
||||
|
||||
### Expression unrolling {#gapi_detail_expr}
|
||||
|
||||
### Parameter marshalling {#gapi_detail_params}
|
||||
|
||||
### Operations representation {#gapi_detail_operations}
|
||||
|
||||
## Graph compiler details {#gapi_detail_compiler}
|
||||
|
||||
### ADE basics {#gapi_detail_ade}
|
||||
|
||||
### Graph model representation {#gapi_detail_gmodel}
|
||||
|
||||
### G-API metadata and passes {#gapi_detail_meta}
|
||||
|
||||
## Backends details {#gapi_detail_backends}
|
||||
|
||||
### Backend scope of work {#gapi_backend_scope}
|
||||
|
||||
### Graph transformation {#gapi_backend_pass}
|
||||
@@ -0,0 +1,17 @@
|
||||
digraph {
|
||||
rankdir=BT;
|
||||
node [shape=record];
|
||||
|
||||
ki_a [label="{<f0> interface\nA}"];
|
||||
ki_b [label="{<f0> interface\nB}"];
|
||||
|
||||
{rank=same; ki_a ki_b};
|
||||
|
||||
"CPU::A" -> ki_a [dir="forward"];
|
||||
"OpenCL::A" -> ki_a [dir="forward"];
|
||||
"Halide::A" -> ki_a [dir="forward"];
|
||||
|
||||
"CPU::B" -> ki_b [dir="forward"];
|
||||
"OpenCL::B" -> ki_b [dir="forward"];
|
||||
"Halide::B" -> ki_b [dir="forward"];
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Graph API {#gapi}
|
||||
|
||||
Introduction to G-API (WIP).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -8,10 +8,10 @@
|
||||
#ifndef OPENCV_GAPI_GCPUKERNEL_HPP
|
||||
#define OPENCV_GAPI_GCPUKERNEL_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <opencv2/gapi/gcommon.hpp>
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <unordered_map> // map (for GKernelPackage)
|
||||
#include <vector> // lookup order
|
||||
#include <string> // string
|
||||
#include <utility> // tuple
|
||||
#include <type_traits> // false_type, true_type
|
||||
#include <unordered_map> // map (for GKernelPackage)
|
||||
#include <utility> // tuple
|
||||
#include <vector> // lookup order
|
||||
|
||||
#include <opencv2/gapi/gcommon.hpp> // CompileArgTag
|
||||
#include <opencv2/gapi/util/util.hpp> // Seq
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/gapi.hpp>
|
||||
#include <opencv2/gapi/core.hpp>
|
||||
#include <opencv2/gapi/imgproc.hpp>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cv::VideoCapture cap;
|
||||
if (argc > 1) cap.open(argv[1]);
|
||||
else cap.open(0);
|
||||
CV_Assert(cap.isOpened());
|
||||
|
||||
cv::GMat in;
|
||||
cv::GMat vga = cv::gapi::resize(in, cv::Size(), 0.5, 0.5);
|
||||
cv::GMat gray = cv::gapi::BGR2Gray(vga);
|
||||
cv::GMat blurred = cv::gapi::blur(gray, cv::Size(5,5));
|
||||
cv::GMat edges = cv::gapi::Canny(blurred, 32, 128, 3);
|
||||
cv::GMat b,g,r;
|
||||
std::tie(b,g,r) = cv::gapi::split3(vga);
|
||||
cv::GMat out = cv::gapi::merge3(b, g | edges, r);
|
||||
cv::GComputation ac(in, out);
|
||||
|
||||
cv::Mat input_frame;
|
||||
cv::Mat output_frame;
|
||||
CV_Assert(cap.read(input_frame));
|
||||
do
|
||||
{
|
||||
ac.apply(input_frame, output_frame);
|
||||
cv::imshow("output", output_frame);
|
||||
} while (cap.read(input_frame) && cv::waitKey(30) < 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// [filter2d_api]
|
||||
#include <opencv2/gapi.hpp>
|
||||
|
||||
G_TYPED_KERNEL(GFilter2D,
|
||||
<cv::GMat(cv::GMat,int,cv::Mat,cv::Point,double,int,cv::Scalar)>,
|
||||
"org.opencv.imgproc.filters.filter2D")
|
||||
{
|
||||
static cv::GMatDesc // outMeta's return value type
|
||||
outMeta(cv::GMatDesc in , // descriptor of input GMat
|
||||
int ddepth , // depth parameter
|
||||
cv::Mat /* coeffs */, // (unused)
|
||||
cv::Point /* anchor */, // (unused)
|
||||
double /* scale */, // (unused)
|
||||
int /* border */, // (unused)
|
||||
cv::Scalar /* bvalue */ ) // (unused)
|
||||
{
|
||||
return in.withDepth(ddepth);
|
||||
}
|
||||
};
|
||||
// [filter2d_api]
|
||||
|
||||
cv::GMat filter2D(cv::GMat ,
|
||||
int ,
|
||||
cv::Mat ,
|
||||
cv::Point ,
|
||||
double ,
|
||||
int ,
|
||||
cv::Scalar);
|
||||
|
||||
// [filter2d_wrap]
|
||||
cv::GMat filter2D(cv::GMat in,
|
||||
int ddepth,
|
||||
cv::Mat k,
|
||||
cv::Point anchor = cv::Point(-1,-1),
|
||||
double scale = 0.,
|
||||
int border = cv::BORDER_DEFAULT,
|
||||
cv::Scalar bval = cv::Scalar(0))
|
||||
{
|
||||
return GFilter2D::on(in, ddepth, k, anchor, scale, border, bval);
|
||||
}
|
||||
// [filter2d_wrap]
|
||||
|
||||
// [compound]
|
||||
#include <opencv2/gapi/gcompoundkernel.hpp> // GAPI_COMPOUND_KERNEL()
|
||||
|
||||
using PointArray2f = cv::GArray<cv::Point2f>;
|
||||
|
||||
G_TYPED_KERNEL(HarrisCorners,
|
||||
<PointArray2f(cv::GMat,int,double,double,int,double)>,
|
||||
"org.opencv.imgproc.harris_corner")
|
||||
{
|
||||
static cv::GArrayDesc outMeta(const cv::GMatDesc &,
|
||||
int,
|
||||
double,
|
||||
double,
|
||||
int,
|
||||
double)
|
||||
{
|
||||
// No special metadata for arrays in G-API (yet)
|
||||
return cv::empty_array_desc();
|
||||
}
|
||||
};
|
||||
|
||||
// Define Fluid-backend-local kernels which form GoodFeatures
|
||||
G_TYPED_KERNEL(HarrisResponse,
|
||||
<cv::GMat(cv::GMat,double,int,double)>,
|
||||
"org.opencv.fluid.harris_response")
|
||||
{
|
||||
static cv::GMatDesc outMeta(const cv::GMatDesc &in,
|
||||
double,
|
||||
int,
|
||||
double)
|
||||
{
|
||||
return in.withType(CV_32F, 1);
|
||||
}
|
||||
};
|
||||
|
||||
G_TYPED_KERNEL(ArrayNMS,
|
||||
<PointArray2f(cv::GMat,int,double)>,
|
||||
"org.opencv.cpu.nms_array")
|
||||
{
|
||||
static cv::GArrayDesc outMeta(const cv::GMatDesc &,
|
||||
int,
|
||||
double)
|
||||
{
|
||||
return cv::empty_array_desc();
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_COMPOUND_KERNEL(GFluidHarrisCorners, HarrisCorners)
|
||||
{
|
||||
static PointArray2f
|
||||
expand(cv::GMat in,
|
||||
int maxCorners,
|
||||
double quality,
|
||||
double minDist,
|
||||
int blockSize,
|
||||
double k)
|
||||
{
|
||||
cv::GMat response = HarrisResponse::on(in, quality, blockSize, k);
|
||||
return ArrayNMS::on(response, maxCorners, minDist);
|
||||
}
|
||||
};
|
||||
|
||||
// Then implement HarrisResponse as Fluid kernel and NMSresponse
|
||||
// as a generic (OpenCV) kernel
|
||||
// [compound]
|
||||
|
||||
// [filter2d_ocv]
|
||||
#include <opencv2/gapi/cpu/gcpukernel.hpp> // GAPI_OCV_KERNEL()
|
||||
#include <opencv2/imgproc.hpp> // cv::filter2D()
|
||||
|
||||
GAPI_OCV_KERNEL(GCPUFilter2D, GFilter2D)
|
||||
{
|
||||
static void
|
||||
run(const cv::Mat &in, // in - derived from GMat
|
||||
const int ddepth, // opaque (passed as-is)
|
||||
const cv::Mat &k, // opaque (passed as-is)
|
||||
const cv::Point &anchor, // opaque (passed as-is)
|
||||
const double delta, // opaque (passed as-is)
|
||||
const int border, // opaque (passed as-is)
|
||||
const cv::Scalar &, // opaque (passed as-is)
|
||||
cv::Mat &out) // out - derived from GMat (retval)
|
||||
{
|
||||
cv::filter2D(in, out, ddepth, k, anchor, delta, border);
|
||||
}
|
||||
};
|
||||
// [filter2d_ocv]
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
std::cout << "This sample is non-complete. It is used as code snippents in documentation." << std::endl;
|
||||
|
||||
cv::Mat conv_kernel_mat;
|
||||
|
||||
{
|
||||
// [filter2d_on]
|
||||
cv::GMat in;
|
||||
cv::GMat out = GFilter2D::on(/* GMat */ in,
|
||||
/* int */ -1,
|
||||
/* Mat */ conv_kernel_mat,
|
||||
/* Point */ cv::Point(-1,-1),
|
||||
/* double */ 0.,
|
||||
/* int */ cv::BORDER_DEFAULT,
|
||||
/* Scalar */ cv::Scalar(0));
|
||||
// [filter2d_on]
|
||||
}
|
||||
|
||||
{
|
||||
// [filter2d_wrap_call]
|
||||
cv::GMat in;
|
||||
cv::GMat out = filter2D(in, -1, conv_kernel_mat);
|
||||
// [filter2d_wrap_call]
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -76,8 +76,23 @@ namespace
|
||||
const cv::GCompileArgs &args,
|
||||
const std::vector<ade::NodeHandle> &nodes) const override
|
||||
{
|
||||
const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args).value_or(cv::GFluidOutputRois());
|
||||
return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, out_rois.rois)};
|
||||
using namespace cv::gimpl;
|
||||
GModel::ConstGraph g(graph);
|
||||
auto isl_graph = g.metadata().get<IslandModel>().model;
|
||||
GIslandModel::Graph gim(*isl_graph);
|
||||
|
||||
const auto num_islands = std::count_if
|
||||
(gim.nodes().begin(), gim.nodes().end(),
|
||||
[&](const ade::NodeHandle &nh) {
|
||||
return gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND;
|
||||
});
|
||||
|
||||
const auto out_rois = cv::gimpl::getCompileArg<cv::GFluidOutputRois>(args);
|
||||
if (num_islands > 1 && out_rois.has_value())
|
||||
cv::util::throw_error(std::logic_error("GFluidOutputRois feature supports only one-island graphs"));
|
||||
|
||||
auto rois = out_rois.value_or(cv::GFluidOutputRois());
|
||||
return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, std::move(rois.rois))};
|
||||
}
|
||||
|
||||
virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override;
|
||||
@@ -102,6 +117,7 @@ private:
|
||||
virtual int linesRead() const override;
|
||||
public:
|
||||
using FluidAgent::FluidAgent;
|
||||
virtual void setInHeight(int) override { /* nothing */ }
|
||||
};
|
||||
|
||||
struct FluidResizeAgent : public FluidAgent
|
||||
@@ -112,6 +128,7 @@ private:
|
||||
virtual int linesRead() const override;
|
||||
public:
|
||||
using FluidAgent::FluidAgent;
|
||||
virtual void setInHeight(int) override { /* nothing */ }
|
||||
};
|
||||
|
||||
struct FluidUpscaleAgent : public FluidAgent
|
||||
@@ -120,8 +137,11 @@ private:
|
||||
virtual int firstWindow() const override;
|
||||
virtual int nextWindow() const override;
|
||||
virtual int linesRead() const override;
|
||||
|
||||
int m_inH;
|
||||
public:
|
||||
using FluidAgent::FluidAgent;
|
||||
virtual void setInHeight(int h) override { m_inH = h; }
|
||||
};
|
||||
}} // namespace cv::gimpl
|
||||
|
||||
@@ -207,21 +227,24 @@ static int calcResizeWindow(int inH, int outH)
|
||||
}
|
||||
}
|
||||
|
||||
static int maxReadWindow(const cv::GFluidKernel& k, int inH, int outH)
|
||||
static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi)
|
||||
{
|
||||
switch (k.m_kind)
|
||||
{
|
||||
case cv::GFluidKernel::Kind::Filter: return k.m_window; break;
|
||||
case cv::GFluidKernel::Kind::Filter: return k.m_window + lpi - 1; break;
|
||||
case cv::GFluidKernel::Kind::Resize:
|
||||
{
|
||||
if (inH >= outH)
|
||||
{
|
||||
return calcResizeWindow(inH, outH);
|
||||
// FIXME:
|
||||
// This is a suboptimal value, can be reduced
|
||||
return calcResizeWindow(inH, outH) * lpi;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upscale always has window of 2
|
||||
return (inH == 1) ? 1 : 2;
|
||||
// FIXME:
|
||||
// This is a suboptimal value, can be reduced
|
||||
return (inH == 1) ? 1 : 2 + lpi - 1;
|
||||
}
|
||||
} break;
|
||||
default: GAPI_Assert(false); return 0;
|
||||
@@ -297,37 +320,45 @@ int cv::gimpl::FluidFilterAgent::linesRead() const
|
||||
int cv::gimpl::FluidResizeAgent::firstWindow() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return windowEnd(outIdx, m_ratio) - windowStart(outIdx, m_ratio);
|
||||
auto lpi = std::min(m_outputLines - m_producedLines, k.m_lpi);
|
||||
return windowEnd(outIdx + lpi - 1, m_ratio) - windowStart(outIdx, m_ratio);
|
||||
}
|
||||
|
||||
int cv::gimpl::FluidResizeAgent::nextWindow() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return windowEnd(outIdx + 1, m_ratio) - windowStart(outIdx + 1, m_ratio);
|
||||
auto lpi = std::min(m_outputLines - m_producedLines - k.m_lpi, k.m_lpi);
|
||||
auto nextStartIdx = outIdx + 1 + k.m_lpi - 1;
|
||||
auto nextEndIdx = nextStartIdx + lpi - 1;
|
||||
return windowEnd(nextEndIdx, m_ratio) - windowStart(nextStartIdx, m_ratio);
|
||||
}
|
||||
|
||||
int cv::gimpl::FluidResizeAgent::linesRead() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return windowStart(outIdx + 1, m_ratio) - windowStart(outIdx, m_ratio);
|
||||
return windowStart(outIdx + 1 + k.m_lpi - 1, m_ratio) - windowStart(outIdx, m_ratio);
|
||||
}
|
||||
|
||||
int cv::gimpl::FluidUpscaleAgent::firstWindow() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return upscaleWindowEnd(outIdx, m_ratio, in_views[0].meta().size.height) - upscaleWindowStart(outIdx, m_ratio);
|
||||
auto lpi = std::min(m_outputLines - m_producedLines, k.m_lpi);
|
||||
return upscaleWindowEnd(outIdx + lpi - 1, m_ratio, m_inH) - upscaleWindowStart(outIdx, m_ratio);
|
||||
}
|
||||
|
||||
int cv::gimpl::FluidUpscaleAgent::nextWindow() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return upscaleWindowEnd(outIdx + 1, m_ratio, in_views[0].meta().size.height) - upscaleWindowStart(outIdx + 1, m_ratio);
|
||||
auto lpi = std::min(m_outputLines - m_producedLines - k.m_lpi, k.m_lpi);
|
||||
auto nextStartIdx = outIdx + 1 + k.m_lpi - 1;
|
||||
auto nextEndIdx = nextStartIdx + lpi - 1;
|
||||
return upscaleWindowEnd(nextEndIdx, m_ratio, m_inH) - upscaleWindowStart(nextStartIdx, m_ratio);
|
||||
}
|
||||
|
||||
int cv::gimpl::FluidUpscaleAgent::linesRead() const
|
||||
{
|
||||
auto outIdx = out_buffers[0]->priv().y();
|
||||
return upscaleWindowStart(outIdx + 1, m_ratio) - upscaleWindowStart(outIdx, m_ratio);
|
||||
return upscaleWindowStart(outIdx + 1 + k.m_lpi - 1, m_ratio) - upscaleWindowStart(outIdx, m_ratio);
|
||||
}
|
||||
|
||||
bool cv::gimpl::FluidAgent::canRead() const
|
||||
@@ -416,111 +447,17 @@ void cv::gimpl::FluidAgent::debug(std::ostream &os)
|
||||
}
|
||||
|
||||
// GCPUExcecutable implementation //////////////////////////////////////////////
|
||||
cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
const std::vector<ade::NodeHandle> &nodes,
|
||||
const std::vector<cv::gapi::own::Rect> &outputRois)
|
||||
: m_g(g), m_gm(m_g), m_outputRois(outputRois)
|
||||
|
||||
void cv::gimpl::GFluidExecutable::initBufferRois(std::vector<int>& readStarts, std::vector<cv::gapi::own::Rect>& rois)
|
||||
{
|
||||
GConstFluidModel fg(m_g);
|
||||
|
||||
// Initialize vector of data buffers, build list of operations
|
||||
// FIXME: There _must_ be a better way to [query] count number of DATA nodes
|
||||
std::size_t mat_count = 0;
|
||||
std::size_t last_agent = 0;
|
||||
std::map<std::size_t, ade::NodeHandle> all_gmat_ids;
|
||||
|
||||
auto grab_mat_nh = [&](ade::NodeHandle nh) {
|
||||
auto rc = m_gm.metadata(nh).get<Data>().rc;
|
||||
if (m_id_map.count(rc) == 0)
|
||||
{
|
||||
all_gmat_ids[mat_count] = nh;
|
||||
m_id_map[rc] = mat_count++;
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &nh : nodes)
|
||||
{
|
||||
switch (m_gm.metadata(nh).get<NodeType>().t)
|
||||
{
|
||||
case NodeType::DATA:
|
||||
if (m_gm.metadata(nh).get<Data>().shape == GShape::GMAT)
|
||||
grab_mat_nh(nh);
|
||||
break;
|
||||
|
||||
case NodeType::OP:
|
||||
{
|
||||
const auto& fu = fg.metadata(nh).get<FluidUnit>();
|
||||
switch (fu.k.m_kind)
|
||||
{
|
||||
case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break;
|
||||
case GFluidKernel::Kind::Resize:
|
||||
{
|
||||
if (fu.ratio >= 1.0)
|
||||
{
|
||||
m_agents.emplace_back(new FluidResizeAgent(m_g, nh));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_agents.emplace_back(new FluidUpscaleAgent(m_g, nh));
|
||||
}
|
||||
} break;
|
||||
default: GAPI_Assert(false);
|
||||
}
|
||||
// NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!!
|
||||
m_agents.back()->in_buffer_ids.resize(m_gm.metadata(nh).get<Op>().args.size(), -1);
|
||||
for (auto eh : nh->inEdges())
|
||||
{
|
||||
// FIXME Only GMats are currently supported (which can be represented
|
||||
// as fluid buffers
|
||||
if (m_gm.metadata(eh->srcNode()).get<Data>().shape == GShape::GMAT)
|
||||
{
|
||||
const auto in_port = m_gm.metadata(eh).get<Input>().port;
|
||||
const auto in_buf = m_gm.metadata(eh->srcNode()).get<Data>().rc;
|
||||
|
||||
m_agents.back()->in_buffer_ids[in_port] = in_buf;
|
||||
grab_mat_nh(eh->srcNode());
|
||||
}
|
||||
}
|
||||
// FIXME: Assumption that all operation outputs MUST be connected
|
||||
m_agents.back()->out_buffer_ids.resize(nh->outEdges().size(), -1);
|
||||
for (auto eh : nh->outEdges())
|
||||
{
|
||||
const auto& data = m_gm.metadata(eh->dstNode()).get<Data>();
|
||||
const auto out_port = m_gm.metadata(eh).get<Output>().port;
|
||||
const auto out_buf = data.rc;
|
||||
|
||||
m_agents.back()->out_buffer_ids[out_port] = out_buf;
|
||||
if (data.shape == GShape::GMAT) grab_mat_nh(eh->dstNode());
|
||||
}
|
||||
if (fu.k.m_scratch)
|
||||
m_scratch_users.push_back(last_agent);
|
||||
last_agent++;
|
||||
break;
|
||||
}
|
||||
default: GAPI_Assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that IDs form a continiuos set (important for further indexing)
|
||||
GAPI_Assert(m_id_map.size() > 0u);
|
||||
GAPI_Assert(m_id_map.size() == mat_count);
|
||||
|
||||
// Actually initialize Fluid buffers
|
||||
GAPI_LOG_INFO(NULL, "Initializing " << mat_count << " fluid buffer(s)" << std::endl);
|
||||
m_num_int_buffers = mat_count;
|
||||
const std::size_t num_scratch = m_scratch_users.size();
|
||||
|
||||
// Calculate rois for each fluid buffer
|
||||
|
||||
auto proto = m_gm.metadata().get<Protocol>();
|
||||
std::vector<int> readStarts(mat_count);
|
||||
std::vector<cv::gapi::own::Rect> rois(mat_count);
|
||||
std::stack<ade::NodeHandle> nodesToVisit;
|
||||
|
||||
if (proto.outputs.size() != m_outputRois.size())
|
||||
{
|
||||
GAPI_Assert(m_outputRois.size() == 0);
|
||||
m_outputRois.resize(proto.outputs.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// First, initialize rois for output nodes, add them to traversal stack
|
||||
@@ -569,6 +506,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
{
|
||||
GAPI_Assert(startNode->inNodes().size() == 1);
|
||||
const auto& oh = startNode->inNodes().front();
|
||||
|
||||
const auto& data = m_gm.metadata(startNode).get<Data>();
|
||||
// only GMats participate in the process so it's valid to obtain GMatDesc
|
||||
const auto& meta = util::get<GMatDesc>(data.meta);
|
||||
@@ -577,7 +515,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
{
|
||||
const auto& in_data = m_gm.metadata(inNode).get<Data>();
|
||||
|
||||
if (in_data.shape == GShape::GMAT)
|
||||
if (in_data.shape == GShape::GMAT && fg.metadata(inNode).contains<FluidData>())
|
||||
{
|
||||
const auto& in_meta = util::get<GMatDesc>(in_data.meta);
|
||||
const auto& fd = fg.metadata(inNode).get<FluidData>();
|
||||
@@ -636,7 +574,8 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
{
|
||||
readStarts[in_id] = readStart;
|
||||
rois[in_id] = roi;
|
||||
nodesToVisit.push(inNode);
|
||||
// Continue traverse on internal (w.r.t Island) data nodes only.
|
||||
if (fd.internal) nodesToVisit.push(inNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -647,6 +586,106 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
} // for (const auto& inNode : oh->inNodes())
|
||||
} // if (!startNode->inNodes().empty())
|
||||
} // while (!nodesToVisit.empty())
|
||||
}
|
||||
|
||||
cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
const std::vector<ade::NodeHandle> &nodes,
|
||||
const std::vector<cv::gapi::own::Rect> &outputRois)
|
||||
: m_g(g), m_gm(m_g), m_nodes(nodes), m_outputRois(outputRois)
|
||||
{
|
||||
GConstFluidModel fg(m_g);
|
||||
|
||||
// Initialize vector of data buffers, build list of operations
|
||||
// FIXME: There _must_ be a better way to [query] count number of DATA nodes
|
||||
std::size_t mat_count = 0;
|
||||
std::size_t last_agent = 0;
|
||||
std::map<std::size_t, ade::NodeHandle> all_gmat_ids;
|
||||
|
||||
auto grab_mat_nh = [&](ade::NodeHandle nh) {
|
||||
auto rc = m_gm.metadata(nh).get<Data>().rc;
|
||||
if (m_id_map.count(rc) == 0)
|
||||
{
|
||||
all_gmat_ids[mat_count] = nh;
|
||||
m_id_map[rc] = mat_count++;
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &nh : m_nodes)
|
||||
{
|
||||
switch (m_gm.metadata(nh).get<NodeType>().t)
|
||||
{
|
||||
case NodeType::DATA:
|
||||
if (m_gm.metadata(nh).get<Data>().shape == GShape::GMAT)
|
||||
grab_mat_nh(nh);
|
||||
break;
|
||||
|
||||
case NodeType::OP:
|
||||
{
|
||||
const auto& fu = fg.metadata(nh).get<FluidUnit>();
|
||||
switch (fu.k.m_kind)
|
||||
{
|
||||
case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break;
|
||||
case GFluidKernel::Kind::Resize:
|
||||
{
|
||||
if (fu.ratio >= 1.0)
|
||||
{
|
||||
m_agents.emplace_back(new FluidResizeAgent(m_g, nh));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_agents.emplace_back(new FluidUpscaleAgent(m_g, nh));
|
||||
}
|
||||
} break;
|
||||
default: GAPI_Assert(false);
|
||||
}
|
||||
// NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!!
|
||||
m_agents.back()->in_buffer_ids.resize(m_gm.metadata(nh).get<Op>().args.size(), -1);
|
||||
for (auto eh : nh->inEdges())
|
||||
{
|
||||
// FIXME Only GMats are currently supported (which can be represented
|
||||
// as fluid buffers
|
||||
if (m_gm.metadata(eh->srcNode()).get<Data>().shape == GShape::GMAT)
|
||||
{
|
||||
const auto in_port = m_gm.metadata(eh).get<Input>().port;
|
||||
const int in_buf = m_gm.metadata(eh->srcNode()).get<Data>().rc;
|
||||
|
||||
m_agents.back()->in_buffer_ids[in_port] = in_buf;
|
||||
grab_mat_nh(eh->srcNode());
|
||||
}
|
||||
}
|
||||
// FIXME: Assumption that all operation outputs MUST be connected
|
||||
m_agents.back()->out_buffer_ids.resize(nh->outEdges().size(), -1);
|
||||
for (auto eh : nh->outEdges())
|
||||
{
|
||||
const auto& data = m_gm.metadata(eh->dstNode()).get<Data>();
|
||||
const auto out_port = m_gm.metadata(eh).get<Output>().port;
|
||||
const int out_buf = data.rc;
|
||||
|
||||
m_agents.back()->out_buffer_ids[out_port] = out_buf;
|
||||
if (data.shape == GShape::GMAT) grab_mat_nh(eh->dstNode());
|
||||
}
|
||||
if (fu.k.m_scratch)
|
||||
m_scratch_users.push_back(last_agent);
|
||||
last_agent++;
|
||||
break;
|
||||
}
|
||||
default: GAPI_Assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that IDs form a continiuos set (important for further indexing)
|
||||
GAPI_Assert(m_id_map.size() > 0);
|
||||
GAPI_Assert(m_id_map.size() == static_cast<size_t>(mat_count));
|
||||
|
||||
// Actually initialize Fluid buffers
|
||||
GAPI_LOG_INFO(NULL, "Initializing " << mat_count << " fluid buffer(s)" << std::endl);
|
||||
m_num_int_buffers = mat_count;
|
||||
const std::size_t num_scratch = m_scratch_users.size();
|
||||
|
||||
std::vector<int> readStarts(mat_count);
|
||||
std::vector<cv::gapi::own::Rect> rois(mat_count);
|
||||
|
||||
initBufferRois(readStarts, rois);
|
||||
|
||||
// NB: Allocate ALL buffer object at once, and avoid any further reallocations
|
||||
// (since raw pointers-to-elements are taken)
|
||||
@@ -659,12 +698,13 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
const auto &fd = fg.metadata(nh).get<FluidData>();
|
||||
const auto meta = cv::util::get<GMatDesc>(d.meta);
|
||||
|
||||
// FIXME: Only continuous set...
|
||||
m_buffers[id].priv().init(meta, fd.max_consumption, fd.border_size, fd.skew, fd.lpi_write, readStarts[id], rois[id]);
|
||||
m_buffers[id].priv().init(meta, fd.lpi_write, readStarts[id], rois[id]);
|
||||
|
||||
if (d.storage == Data::Storage::INTERNAL)
|
||||
// TODO:
|
||||
// Introduce Storage::INTERNAL_GRAPH and Storage::INTERNAL_ISLAND?
|
||||
if (fd.internal == true)
|
||||
{
|
||||
m_buffers[id].priv().allocate(fd.border);
|
||||
m_buffers[id].priv().allocate(fd.border, fd.border_size, fd.max_consumption, fd.skew);
|
||||
std::stringstream stream;
|
||||
m_buffers[id].debug(stream);
|
||||
GAPI_LOG_INFO(NULL, stream.str());
|
||||
@@ -707,6 +747,13 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
}
|
||||
}
|
||||
|
||||
// cache input height to avoid costly meta() call
|
||||
// (actually cached and used only in upscale)
|
||||
if (agent->in_views[0])
|
||||
{
|
||||
agent->setInHeight(agent->in_views[0].meta().size.height);
|
||||
}
|
||||
|
||||
// b. Agent output parameters with Buffer pointers.
|
||||
agent->out_buffers.resize(agent->op_handle->outEdges().size(), nullptr);
|
||||
for (auto it : ade::util::zip(ade::util::iota(agent->out_buffers.size()),
|
||||
@@ -723,7 +770,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
if (num_scratch)
|
||||
{
|
||||
GAPI_LOG_INFO(NULL, "Initializing " << num_scratch << " scratch buffer(s)" << std::endl);
|
||||
unsigned last_scratch_id = 0;
|
||||
std::size_t last_scratch_id = 0;
|
||||
|
||||
for (auto i : m_scratch_users)
|
||||
{
|
||||
@@ -751,14 +798,14 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g,
|
||||
}
|
||||
}
|
||||
|
||||
int total_size = 0;
|
||||
std::size_t total_size = 0;
|
||||
for (const auto &i : ade::util::indexed(m_buffers))
|
||||
{
|
||||
// Check that all internal and scratch buffers are allocated
|
||||
auto idx = ade::util::index(i);
|
||||
auto b = ade::util::value(i);
|
||||
const auto idx = ade::util::index(i);
|
||||
const auto b = ade::util::value(i);
|
||||
if (idx >= m_num_int_buffers ||
|
||||
m_gm.metadata(all_gmat_ids[idx]).get<Data>().storage == Data::Storage::INTERNAL)
|
||||
fg.metadata(all_gmat_ids[idx]).get<FluidData>().internal == true)
|
||||
{
|
||||
GAPI_Assert(b.priv().size() > 0);
|
||||
}
|
||||
@@ -888,7 +935,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
// limited), and only then continue with all other passes.
|
||||
//
|
||||
// The passes/stages API must be streamlined!
|
||||
ectx.addPass("exec", "fluid_sanity_check", [](ade::passes::PassContext &ctx)
|
||||
ectx.addPass("exec", "init_fluid_data", [](ade::passes::PassContext &ctx)
|
||||
{
|
||||
GModel::Graph g(ctx.graph);
|
||||
if (!GModel::isActive(g, cv::gapi::fluid::backend())) // FIXME: Rearchitect this!
|
||||
@@ -897,32 +944,46 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto isl_graph = g.metadata().get<IslandModel>().model;
|
||||
GIslandModel::Graph gim(*isl_graph);
|
||||
|
||||
const auto num_non_fluid_islands = std::count_if
|
||||
(gim.nodes().begin(),
|
||||
gim.nodes().end(),
|
||||
[&](const ade::NodeHandle &nh) {
|
||||
return gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND &&
|
||||
gim.metadata(nh).get<FusedIsland>().object->backend() != cv::gapi::fluid::backend();
|
||||
});
|
||||
|
||||
// FIXME: Break this limitation!
|
||||
if (num_non_fluid_islands > 0)
|
||||
cv::util::throw_error(std::logic_error("Fluid doesn't support heterogeneous execution"));
|
||||
});
|
||||
ectx.addPass("exec", "init_fluid_data", [](ade::passes::PassContext &ctx)
|
||||
{
|
||||
GModel::Graph g(ctx.graph);
|
||||
if (!GModel::isActive(g, cv::gapi::fluid::backend())) // FIXME: Rearchitect this!
|
||||
return;
|
||||
|
||||
GFluidModel fg(ctx.graph);
|
||||
for (const auto node : g.nodes())
|
||||
|
||||
const auto setFluidData = [&](ade::NodeHandle nh, bool internal) {
|
||||
FluidData fd;
|
||||
fd.internal = internal;
|
||||
fg.metadata(nh).set(fd);
|
||||
};
|
||||
|
||||
for (const auto& nh : gim.nodes())
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
|
||||
if (gim.metadata(nh).get<NodeKind>().k == NodeKind::ISLAND)
|
||||
{
|
||||
fg.metadata(node).set(FluidData());
|
||||
}
|
||||
}
|
||||
const auto isl = gim.metadata(nh).get<FusedIsland>().object;
|
||||
if (isl->backend() == cv::gapi::fluid::backend())
|
||||
{
|
||||
// add FluidData to all data nodes inside island
|
||||
for (const auto node : isl->contents())
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
|
||||
setFluidData(node, true);
|
||||
}
|
||||
|
||||
// add FluidData to slot if it's read/written by fluid
|
||||
std::vector<ade::NodeHandle> io_handles;
|
||||
for (const auto &in_op : isl->in_ops())
|
||||
{
|
||||
ade::util::copy(in_op->inNodes(), std::back_inserter(io_handles));
|
||||
}
|
||||
for (const auto &out_op : isl->out_ops())
|
||||
{
|
||||
ade::util::copy(out_op->outNodes(), std::back_inserter(io_handles));
|
||||
}
|
||||
for (const auto &io_node : io_handles)
|
||||
{
|
||||
if (!fg.metadata(io_node).contains<FluidData>())
|
||||
setFluidData(io_node, false);
|
||||
}
|
||||
} // if (fluid backend)
|
||||
} // if (ISLAND)
|
||||
} // for (gim.nodes())
|
||||
});
|
||||
// FIXME:
|
||||
// move to unpackKernel method
|
||||
@@ -938,7 +999,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
|
||||
for (auto node : sorted)
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::OP)
|
||||
if (fg.metadata(node).contains<FluidUnit>())
|
||||
{
|
||||
// FIXME: check that op has only one data node on input
|
||||
auto &fu = fg.metadata(node).get<FluidUnit>();
|
||||
@@ -960,7 +1021,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
|
||||
for (auto node : sorted)
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::OP)
|
||||
if (fg.metadata(node).contains<FluidUnit>())
|
||||
{
|
||||
std::set<int> in_hs, out_ws, out_hs;
|
||||
|
||||
@@ -993,8 +1054,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto &fu = fg.metadata(node).get<FluidUnit>();
|
||||
fu.ratio = (double)in_h / out_h;
|
||||
|
||||
int w = maxReadWindow(fu.k, in_h, out_h);
|
||||
int line_consumption = fu.k.m_lpi + w - 1;
|
||||
int line_consumption = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi);
|
||||
int border_size = borderSize(fu.k);
|
||||
|
||||
fu.border_size = border_size;
|
||||
@@ -1014,7 +1074,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
GFluidModel fg(ctx.graph);
|
||||
for (const auto node : g.nodes())
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::OP)
|
||||
if (fg.metadata(node).contains<FluidUnit>())
|
||||
{
|
||||
const auto &fu = fg.metadata(node).get<FluidUnit>();
|
||||
|
||||
@@ -1045,7 +1105,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
|
||||
for (auto node : sorted)
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::OP)
|
||||
if (fg.metadata(node).contains<FluidUnit>())
|
||||
{
|
||||
const auto &fu = fg.metadata(node).get<FluidUnit>();
|
||||
|
||||
@@ -1083,7 +1143,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
|
||||
for (auto node : sorted)
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::OP)
|
||||
if (fg.metadata(node).contains<FluidUnit>())
|
||||
{
|
||||
int max_latency = 0;
|
||||
for (auto in_data_node : node->inNodes())
|
||||
@@ -1105,6 +1165,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ectx.addPass("exec", "init_buffer_borders", [](ade::passes::PassContext &ctx)
|
||||
{
|
||||
GModel::Graph g(ctx.graph);
|
||||
@@ -1115,7 +1176,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
auto sorted = g.metadata().get<ade::passes::TopologicalSortData>().nodes();
|
||||
for (auto node : sorted)
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
|
||||
if (fg.metadata(node).contains<FluidData>())
|
||||
{
|
||||
auto &fd = fg.metadata(node).get<FluidData>();
|
||||
|
||||
@@ -1123,7 +1184,7 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
|
||||
// In/out data nodes are bound to user data directly,
|
||||
// so cannot be extended with a border
|
||||
if (g.metadata(node).get<Data>().storage == Data::Storage::INTERNAL)
|
||||
if (fd.internal == true)
|
||||
{
|
||||
// For now border of the buffer's storage is the border
|
||||
// of the first reader whose border size is the same.
|
||||
@@ -1134,9 +1195,10 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
// on this criteria)
|
||||
auto readers = node->outNodes();
|
||||
const auto &candidate = ade::util::find_if(readers, [&](ade::NodeHandle nh) {
|
||||
const auto &fu = fg.metadata(nh).get<FluidUnit>();
|
||||
return fu.border_size == fd.border_size;
|
||||
return fg.metadata(nh).contains<FluidUnit>() &&
|
||||
fg.metadata(nh).get<FluidUnit>().border_size == fd.border_size;
|
||||
});
|
||||
|
||||
GAPI_Assert(candidate != readers.end());
|
||||
|
||||
const auto &fu = fg.metadata(*candidate).get<FluidUnit>();
|
||||
@@ -1159,26 +1221,30 @@ void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx)
|
||||
GFluidModel fg(ctx.graph);
|
||||
for (auto node : g.nodes())
|
||||
{
|
||||
if (g.metadata(node).get<NodeType>().t == NodeType::DATA)
|
||||
if (fg.metadata(node).contains<FluidData>())
|
||||
{
|
||||
auto &fd = fg.metadata(node).get<FluidData>();
|
||||
for (auto out_edge : node->outEdges())
|
||||
{
|
||||
const auto &fu = fg.metadata(out_edge->dstNode()).get<FluidUnit>();
|
||||
const auto dstNode = out_edge->dstNode();
|
||||
if (fg.metadata(dstNode).contains<FluidUnit>())
|
||||
{
|
||||
const auto &fu = fg.metadata(dstNode).get<FluidUnit>();
|
||||
|
||||
// There is no need in own storage for view if it's border is
|
||||
// the same as the buffer's (view can have equal or smaller border
|
||||
// size in this case)
|
||||
if (fu.border_size == 0 ||
|
||||
(fu.border && fd.border && (*fu.border == *fd.border)))
|
||||
{
|
||||
GAPI_Assert(fu.border_size <= fd.border_size);
|
||||
fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{false});
|
||||
}
|
||||
else
|
||||
{
|
||||
fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{true});
|
||||
GModel::log(g, out_edge, "OwnBufferStorage: true");
|
||||
// There is no need in own storage for view if it's border is
|
||||
// the same as the buffer's (view can have equal or smaller border
|
||||
// size in this case)
|
||||
if (fu.border_size == 0 ||
|
||||
(fu.border && fd.border && (*fu.border == *fd.border)))
|
||||
{
|
||||
GAPI_Assert(fu.border_size <= fd.border_size);
|
||||
fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{false});
|
||||
}
|
||||
else
|
||||
{
|
||||
fg.metadata(out_edge).set(FluidUseOwnBorderBuffer{true});
|
||||
GModel::log(g, out_edge, "OwnBufferStorage: true");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace cv { namespace gimpl {
|
||||
|
||||
struct FluidUnit
|
||||
{
|
||||
static const char *name() { return "FluidKernel"; }
|
||||
static const char *name() { return "FluidUnit"; }
|
||||
GFluidKernel k;
|
||||
gapi::fluid::BorderOpt border;
|
||||
int border_size;
|
||||
@@ -40,11 +40,12 @@ struct FluidData
|
||||
static const char *name() { return "FluidData"; }
|
||||
|
||||
// FIXME: This structure starts looking like "FluidBuffer" meta
|
||||
int latency = 0;
|
||||
int skew = 0;
|
||||
int max_consumption = 1;
|
||||
int border_size = 0;
|
||||
int lpi_write = 1;
|
||||
int latency = 0;
|
||||
int skew = 0;
|
||||
int max_consumption = 1;
|
||||
int border_size = 0;
|
||||
int lpi_write = 1;
|
||||
bool internal = false; // is node internal to any fluid island
|
||||
gapi::fluid::BorderOpt border;
|
||||
};
|
||||
|
||||
@@ -82,7 +83,10 @@ 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;
|
||||
private:
|
||||
// FIXME!!!
|
||||
// move to another class
|
||||
@@ -95,6 +99,7 @@ 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;
|
||||
@@ -114,6 +119,8 @@ class GFluidExecutable final: public GIslandExecutable
|
||||
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);
|
||||
|
||||
public:
|
||||
GFluidExecutable(const ade::Graph &g,
|
||||
const std::vector<ade::NodeHandle> &nodes,
|
||||
|
||||
@@ -20,12 +20,6 @@
|
||||
|
||||
namespace cv {
|
||||
namespace gapi {
|
||||
|
||||
//namespace own {
|
||||
// class Mat;
|
||||
// CV_EXPORTS cv::GMatDesc descr_of(const Mat &mat);
|
||||
//}//own
|
||||
|
||||
namespace fluid {
|
||||
bool operator == (const fluid::Border& b1, const fluid::Border& b2)
|
||||
{
|
||||
@@ -503,38 +497,34 @@ fluid::Buffer::Priv::Priv(int read_start, cv::gapi::own::Rect roi)
|
||||
{}
|
||||
|
||||
void fluid::Buffer::Priv::init(const cv::GMatDesc &desc,
|
||||
int line_consumption,
|
||||
int border_size,
|
||||
int skew,
|
||||
int wlpi,
|
||||
int readStartPos,
|
||||
cv::gapi::own::Rect roi)
|
||||
{
|
||||
GAPI_Assert(m_line_consumption == -1);
|
||||
GAPI_Assert(line_consumption > 0);
|
||||
|
||||
m_line_consumption = line_consumption;
|
||||
m_border_size = border_size;
|
||||
m_skew = skew;
|
||||
m_writer_lpi = wlpi;
|
||||
m_desc = desc;
|
||||
m_readStart = readStartPos;
|
||||
m_roi = roi;
|
||||
m_writer_lpi = wlpi;
|
||||
m_desc = desc;
|
||||
m_readStart = readStartPos;
|
||||
m_roi = roi == cv::Rect{} ? cv::Rect{0, 0, desc.size.width, desc.size.height}
|
||||
: roi;
|
||||
}
|
||||
|
||||
void fluid::Buffer::Priv::allocate(BorderOpt border)
|
||||
void fluid::Buffer::Priv::allocate(BorderOpt border,
|
||||
int border_size,
|
||||
int line_consumption,
|
||||
int skew)
|
||||
{
|
||||
GAPI_Assert(!m_storage);
|
||||
GAPI_Assert(line_consumption > 0);
|
||||
|
||||
// Init physical buffer
|
||||
|
||||
// FIXME? combine line_consumption with skew?
|
||||
auto data_height = std::max(m_line_consumption, m_skew) + m_writer_lpi - 1;
|
||||
auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1;
|
||||
|
||||
m_storage = createStorage(data_height,
|
||||
m_desc.size.width,
|
||||
CV_MAKETYPE(m_desc.depth, m_desc.chan),
|
||||
m_border_size,
|
||||
border_size,
|
||||
border);
|
||||
|
||||
// Finally, initialize carets
|
||||
@@ -544,9 +534,15 @@ void fluid::Buffer::Priv::allocate(BorderOpt border)
|
||||
void fluid::Buffer::Priv::bindTo(const cv::gapi::own::Mat &data, bool is_input)
|
||||
{
|
||||
// FIXME: move all these fields into a separate structure
|
||||
GAPI_Assert(m_skew == 0);
|
||||
GAPI_Assert(m_desc == descr_of(data));
|
||||
if ( is_input) GAPI_Assert(m_writer_lpi == 1);
|
||||
|
||||
// Currently m_writer_lpi is obtained from metadata which is shared between islands
|
||||
// and this assert can trigger for slot which connects two fluid islands.
|
||||
// m_writer_lpi is used only in write-related functions and doesn't affect
|
||||
// buffer which is island's input so it's safe to skip this check.
|
||||
// FIXME:
|
||||
// Bring back this check when we move to 1 buffer <-> 1 metadata model
|
||||
// if (is_input) GAPI_Assert(m_writer_lpi == 1);
|
||||
|
||||
m_storage = createStorage(data, m_roi);
|
||||
|
||||
@@ -638,8 +634,8 @@ fluid::Buffer::Buffer(const cv::GMatDesc &desc)
|
||||
int lineConsumption = 1;
|
||||
int border = 0, skew = 0, wlpi = 1, readStart = 0;
|
||||
cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
|
||||
m_priv->init(desc, lineConsumption, border, skew, wlpi, readStart, roi);
|
||||
m_priv->allocate({});
|
||||
m_priv->init(desc, wlpi, readStart, roi);
|
||||
m_priv->allocate({}, border, lineConsumption, skew);
|
||||
}
|
||||
|
||||
fluid::Buffer::Buffer(const cv::GMatDesc &desc,
|
||||
@@ -652,17 +648,16 @@ fluid::Buffer::Buffer(const cv::GMatDesc &desc,
|
||||
{
|
||||
int readStart = 0;
|
||||
cv::gapi::own::Rect roi = {0, 0, desc.size.width, desc.size.height};
|
||||
m_priv->init(desc, max_line_consumption, border_size, skew, wlpi, readStart, roi);
|
||||
m_priv->allocate(border);
|
||||
m_priv->init(desc, wlpi, readStart, roi);
|
||||
m_priv->allocate(border, border_size, max_line_consumption, skew);
|
||||
}
|
||||
|
||||
fluid::Buffer::Buffer(const cv::gapi::own::Mat &data, bool is_input)
|
||||
: m_priv(new Priv())
|
||||
{
|
||||
int lineConsumption = 1;
|
||||
int border = 0, skew = 0, wlpi = 1, readStart = 0;
|
||||
int wlpi = 1, readStart = 0;
|
||||
cv::gapi::own::Rect roi{0, 0, data.cols, data.rows};
|
||||
m_priv->init(descr_of(data), lineConsumption, border, skew, wlpi, readStart, roi);
|
||||
m_priv->init(descr_of(data), wlpi, readStart, roi);
|
||||
m_priv->bindTo(data, is_input);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/gapi/fluid/gfluidbuffer.hpp"
|
||||
#include "opencv2/gapi/own/convert.hpp" // cv::gapi::own::to_ocv
|
||||
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
|
||||
|
||||
namespace gapi { namespace own {
|
||||
class Mat;
|
||||
GAPI_EXPORTS cv::GMatDesc descr_of(const Mat &mat);
|
||||
}}//gapi::own
|
||||
|
||||
namespace cv {
|
||||
namespace gapi {
|
||||
namespace fluid {
|
||||
@@ -233,9 +227,6 @@ void debugBufferPriv(const Buffer& buffer, std::ostream &os);
|
||||
// like readDone/writeDone in low-level tests
|
||||
class GAPI_EXPORTS Buffer::Priv
|
||||
{
|
||||
int m_line_consumption = -1;
|
||||
int m_border_size = -1;
|
||||
int m_skew = -1;
|
||||
int m_writer_lpi = 1;
|
||||
|
||||
cv::GMatDesc m_desc = cv::GMatDesc{-1,-1,{-1,-1}};
|
||||
@@ -262,14 +253,11 @@ public:
|
||||
|
||||
// API used by actors/backend
|
||||
void init(const cv::GMatDesc &desc,
|
||||
int line_consumption,
|
||||
int border_size,
|
||||
int skew,
|
||||
int wlpi,
|
||||
int readStart,
|
||||
cv::gapi::own::Rect roi);
|
||||
|
||||
void allocate(BorderOpt border);
|
||||
void allocate(BorderOpt border, int border_size, int line_consumption, int skew);
|
||||
void bindTo(const cv::gapi::own::Mat &data, bool is_input);
|
||||
|
||||
inline void addView(const View& view) { m_views.push_back(view); }
|
||||
|
||||
@@ -31,6 +31,22 @@ namespace
|
||||
}
|
||||
};
|
||||
|
||||
void FluidFooRow(const uint8_t* in, uint8_t* out, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
out[i] = in[i] + 3;
|
||||
}
|
||||
}
|
||||
|
||||
void FluidBarRow(const uint8_t* in1, const uint8_t* in2, uint8_t* out, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
out[i] = 3*(in1[i] + in2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
GAPI_FLUID_KERNEL(FFoo, I::Foo, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
@@ -38,12 +54,7 @@ namespace
|
||||
static void run(const cv::gapi::fluid::View &in,
|
||||
cv::gapi::fluid::Buffer &out)
|
||||
{
|
||||
const uint8_t* in_ptr = in.InLine<uint8_t>(0);
|
||||
uint8_t *out_ptr = out.OutLine<uint8_t>();
|
||||
for (int i = 0; i < in.length(); i++)
|
||||
{
|
||||
out_ptr[i] = in_ptr[i] + 3;
|
||||
}
|
||||
FluidFooRow(in.InLineB(0), out.OutLineB(), in.length());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,15 +66,88 @@ namespace
|
||||
const cv::gapi::fluid::View &in2,
|
||||
cv::gapi::fluid::Buffer &out)
|
||||
{
|
||||
const uint8_t* in1_ptr = in1.InLine<uint8_t>(0);
|
||||
const uint8_t* in2_ptr = in2.InLine<uint8_t>(0);
|
||||
uint8_t *out_ptr = out.OutLine<uint8_t>();
|
||||
for (int i = 0; i < in1.length(); i++)
|
||||
FluidBarRow(in1.InLineB(0), in2.InLineB(0), out.OutLineB(), in1.length());
|
||||
}
|
||||
};
|
||||
|
||||
G_TYPED_KERNEL(FluidFooI, <cv::GMat(cv::GMat)>, "test.kernels.fluid_foo")
|
||||
{
|
||||
static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; }
|
||||
};
|
||||
|
||||
G_TYPED_KERNEL(FluidBarI, <cv::GMat(cv::GMat,cv::GMat)>, "test.kernels.fluid_bar")
|
||||
{
|
||||
static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) { return in; }
|
||||
};
|
||||
|
||||
GAPI_FLUID_KERNEL(FluidFoo, FluidFooI, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
|
||||
static void run(const cv::gapi::fluid::View &in,
|
||||
cv::gapi::fluid::Buffer &out)
|
||||
{
|
||||
FluidFooRow(in.InLineB(0), out.OutLineB(), in.length());
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_FLUID_KERNEL(FluidBar, FluidBarI, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
|
||||
static void run(const cv::gapi::fluid::View &in1,
|
||||
const cv::gapi::fluid::View &in2,
|
||||
cv::gapi::fluid::Buffer &out)
|
||||
{
|
||||
FluidBarRow(in1.InLineB(0), in2.InLineB(0), out.OutLineB(), in1.length());
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_FLUID_KERNEL(FluidFoo2lpi, FluidFooI, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
static const int LPI = 2;
|
||||
|
||||
static void run(const cv::gapi::fluid::View &in,
|
||||
cv::gapi::fluid::Buffer &out)
|
||||
{
|
||||
for (int l = 0; l < out.lpi(); l++)
|
||||
{
|
||||
out_ptr[i] = 3*(in1_ptr[i] + in2_ptr[i]);
|
||||
FluidFooRow(in.InLineB(l), out.OutLineB(l), in.length());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cv::Mat ocvFoo(const cv::Mat &in)
|
||||
{
|
||||
cv::Mat out;
|
||||
OCVFoo::run(in, out);
|
||||
return out;
|
||||
}
|
||||
cv::Mat ocvBar(const cv::Mat &in1, const cv::Mat &in2)
|
||||
{
|
||||
cv::Mat out;
|
||||
OCVBar::run(in1, in2, out);
|
||||
return out;
|
||||
}
|
||||
cv::Mat fluidFoo(const cv::Mat &in)
|
||||
{
|
||||
cv::Mat out(in.rows, in.cols, in.type());
|
||||
for (int y = 0; y < in.rows; y++)
|
||||
{
|
||||
FluidFooRow(in.ptr(y), out.ptr(y), in.cols);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
cv::Mat fluidBar(const cv::Mat &in1, const cv::Mat &in2)
|
||||
{
|
||||
cv::Mat out(in1.rows, in1.cols, in1.type());
|
||||
for (int y = 0; y < in1.rows; y++)
|
||||
{
|
||||
FluidBarRow(in1.ptr(y), in2.ptr(y), out.ptr(y), in1.cols);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
struct GAPIHeteroTest: public ::testing::Test
|
||||
@@ -98,7 +182,7 @@ TEST_F(GAPIHeteroTest, TestOCV)
|
||||
EXPECT_TRUE(cv::gapi::cpu::backend() == m_ocv_kernels.lookup<I::Foo>());
|
||||
EXPECT_TRUE(cv::gapi::cpu::backend() == m_ocv_kernels.lookup<I::Bar>());
|
||||
|
||||
cv::Mat ref = 4*(m_in_mat+2 + m_in_mat+2);
|
||||
cv::Mat ref = ocvBar(ocvFoo(m_in_mat), ocvFoo(m_in_mat));
|
||||
EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_ocv_kernels)));
|
||||
EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat));
|
||||
}
|
||||
@@ -108,17 +192,121 @@ TEST_F(GAPIHeteroTest, TestFluid)
|
||||
EXPECT_TRUE(cv::gapi::fluid::backend() == m_fluid_kernels.lookup<I::Foo>());
|
||||
EXPECT_TRUE(cv::gapi::fluid::backend() == m_fluid_kernels.lookup<I::Bar>());
|
||||
|
||||
cv::Mat ref = 3*(m_in_mat+3 + m_in_mat+3);
|
||||
cv::Mat ref = fluidBar(fluidFoo(m_in_mat), fluidFoo(m_in_mat));
|
||||
EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_fluid_kernels)));
|
||||
EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat));
|
||||
}
|
||||
|
||||
TEST_F(GAPIHeteroTest, TestBoth_ExpectFailure)
|
||||
TEST_F(GAPIHeteroTest, TestBoth)
|
||||
{
|
||||
EXPECT_TRUE(cv::gapi::cpu::backend() == m_hetero_kernels.lookup<I::Foo>());
|
||||
EXPECT_TRUE(cv::gapi::fluid::backend() == m_hetero_kernels.lookup<I::Bar>());
|
||||
EXPECT_ANY_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_hetero_kernels)));
|
||||
|
||||
cv::Mat ref = fluidBar(ocvFoo(m_in_mat), ocvFoo(m_in_mat));
|
||||
EXPECT_NO_THROW(m_comp.apply(m_in_mat, m_out_mat, cv::compile_args(m_hetero_kernels)));
|
||||
EXPECT_EQ(0, cv::countNonZero(ref != m_out_mat));
|
||||
}
|
||||
|
||||
struct GAPIBigHeteroTest : public ::testing::TestWithParam<std::array<int, 9>>
|
||||
{
|
||||
cv::GComputation m_comp;
|
||||
cv::gapi::GKernelPackage m_kernels;
|
||||
|
||||
cv::Mat m_in_mat;
|
||||
cv::Mat m_out_mat1;
|
||||
cv::Mat m_out_mat2;
|
||||
|
||||
cv::Mat m_ref_mat1;
|
||||
cv::Mat m_ref_mat2;
|
||||
|
||||
GAPIBigHeteroTest();
|
||||
};
|
||||
|
||||
// Foo7
|
||||
// .-> Foo2 -> Foo3 -<
|
||||
// Foo0 -> Foo1 Bar -> Foo6
|
||||
// `-> Foo4 -> Foo5 -`
|
||||
|
||||
GAPIBigHeteroTest::GAPIBigHeteroTest()
|
||||
: m_comp([&](){
|
||||
auto flags = GetParam();
|
||||
std::array<std::function<cv::GMat(cv::GMat)>, 8> foos;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
foos[i] = flags[i] ? &I::Foo::on : &FluidFooI::on;
|
||||
}
|
||||
auto bar = flags[8] ? &I::Bar::on : &FluidBarI::on;
|
||||
|
||||
cv::GMat in;
|
||||
auto foo1Out = foos[1](foos[0](in));
|
||||
auto foo3Out = foos[3](foos[2](foo1Out));
|
||||
auto foo6Out = foos[6](bar(foo3Out,
|
||||
foos[5](foos[4](foo1Out))));
|
||||
auto foo7Out = foos[7](foo3Out);
|
||||
|
||||
return cv::GComputation(GIn(in), GOut(foo6Out, foo7Out));
|
||||
})
|
||||
, m_kernels(cv::gapi::kernels<OCVFoo, OCVBar, FluidFoo, FluidBar>())
|
||||
, m_in_mat(cv::Mat::eye(cv::Size(64, 64), CV_8UC1))
|
||||
{
|
||||
auto flags = GetParam();
|
||||
std::array<std::function<cv::Mat(cv::Mat)>, 8> foos;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
foos[i] = flags[i] ? ocvFoo : fluidFoo;
|
||||
}
|
||||
auto bar = flags[8] ? ocvBar : fluidBar;
|
||||
|
||||
cv::Mat foo1OutMat = foos[1](foos[0](m_in_mat));
|
||||
cv::Mat foo3OutMat = foos[3](foos[2](foo1OutMat));
|
||||
|
||||
m_ref_mat1 = foos[6](bar(foo3OutMat,
|
||||
foos[5](foos[4](foo1OutMat))));
|
||||
|
||||
m_ref_mat2 = foos[7](foo3OutMat);
|
||||
}
|
||||
|
||||
TEST_P(GAPIBigHeteroTest, Test)
|
||||
{
|
||||
EXPECT_NO_THROW(m_comp.apply(gin(m_in_mat), gout(m_out_mat1, m_out_mat2), cv::compile_args(m_kernels)));
|
||||
EXPECT_EQ(0, cv::countNonZero(m_ref_mat1 != m_out_mat1));
|
||||
EXPECT_EQ(0, cv::countNonZero(m_ref_mat2 != m_out_mat2));
|
||||
}
|
||||
|
||||
static auto configurations = []()
|
||||
{
|
||||
// Fill all possible configurations
|
||||
// from 000000000 to 111111111
|
||||
std::array<std::array<int, 9>, 512> arr;
|
||||
for (auto n = 0; n < 512; n++)
|
||||
{
|
||||
for (auto i = 0; i < 9; i++)
|
||||
{
|
||||
arr[n][i] = (n >> (8 - i)) & 1;
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}();
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(GAPIBigHeteroTest, GAPIBigHeteroTest,
|
||||
::testing::ValuesIn(configurations));
|
||||
|
||||
TEST(GAPIHeteroTestLPI, Test)
|
||||
{
|
||||
cv::GMat in;
|
||||
auto mid = FluidFooI::on(in);
|
||||
auto out = FluidFooI::on(mid);
|
||||
cv::gapi::island("isl0", GIn(in), GOut(mid));
|
||||
cv::gapi::island("isl1", GIn(mid), GOut(out));
|
||||
cv::GComputation c(in, out);
|
||||
|
||||
cv::Mat in_mat = cv::Mat::eye(cv::Size(64, 64), CV_8UC1);
|
||||
cv::Mat out_mat;
|
||||
EXPECT_NO_THROW(c.apply(in_mat, out_mat, cv::compile_args(cv::gapi::kernels<FluidFoo2lpi>())));
|
||||
cv::Mat ref = fluidFoo(fluidFoo(in_mat));
|
||||
EXPECT_EQ(0, cv::countNonZero(ref != out_mat));
|
||||
}
|
||||
|
||||
} // namespace opencv_test
|
||||
|
||||
@@ -40,7 +40,7 @@ GAPI_FLUID_KERNEL(FCopy, TCopy, false)
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_FLUID_KERNEL(FResizeNN, cv::gapi::core::GResize, false)
|
||||
GAPI_FLUID_KERNEL(FResizeNN1Lpi, cv::gapi::core::GResize, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
static const auto Kind = GFluidKernel::Kind::Resize;
|
||||
@@ -49,22 +49,25 @@ GAPI_FLUID_KERNEL(FResizeNN, cv::gapi::core::GResize, false)
|
||||
cv::gapi::fluid::Buffer& out)
|
||||
|
||||
{
|
||||
auto length = out.length();
|
||||
double vRatio = (double)in.meta().size.height / out.meta().size.height;
|
||||
double hRatio = (double)in.length() / length;
|
||||
auto y = out.y();
|
||||
auto inY = in.y();
|
||||
|
||||
auto sy = static_cast<int>(y * vRatio);
|
||||
int idx = sy - inY;
|
||||
|
||||
const auto src = in.InLine <unsigned char>(idx);
|
||||
auto dst = out.OutLine<unsigned char>();
|
||||
|
||||
double horRatio = (double)in.length() / out.length();
|
||||
|
||||
for (int x = 0; x < out.length(); x++)
|
||||
for (int l = 0; l < out.lpi(); l++)
|
||||
{
|
||||
auto inX = static_cast<int>(x * horRatio);
|
||||
dst[x] = src[inX];
|
||||
auto sy = static_cast<int>((y+l) * vRatio);
|
||||
int idx = sy - inY;
|
||||
|
||||
const auto src = in.InLine <unsigned char>(idx);
|
||||
auto dst = out.OutLine<unsigned char>(l);
|
||||
|
||||
for (int x = 0; x < length; x++)
|
||||
{
|
||||
auto inX = static_cast<int>(x * hRatio);
|
||||
dst[x] = src[inX];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -101,25 +104,33 @@ template <class Mapper>
|
||||
inline void calcRow(const cv::gapi::fluid::View& in, cv::gapi::fluid::Buffer& out, cv::gapi::fluid::Buffer &scratch)
|
||||
{
|
||||
double vRatio = (double)in.meta().size.height / out.meta().size.height;
|
||||
auto mapY = Mapper::map(vRatio, in.y(), in.meta().size.height, out.y());
|
||||
|
||||
const auto src0 = in.InLine <unsigned char>(mapY.s0);
|
||||
const auto src1 = in.InLine <unsigned char>(mapY.s1);
|
||||
|
||||
auto dst = out.OutLine<unsigned char>();
|
||||
auto mapX = scratch.OutLine<typename Mapper::Unit>();
|
||||
auto inY = in.y();
|
||||
auto inH = in.meta().size.height;
|
||||
auto outY = out.y();
|
||||
auto length = out.length();
|
||||
|
||||
for (int x = 0; x < out.length(); x++)
|
||||
for (int l = 0; l < out.lpi(); l++)
|
||||
{
|
||||
auto alpha0 = mapX[x].alpha0;
|
||||
auto alpha1 = mapX[x].alpha1;
|
||||
auto sx0 = mapX[x].s0;
|
||||
auto sx1 = mapX[x].s1;
|
||||
auto mapY = Mapper::map(vRatio, inY, inH, outY + l);
|
||||
|
||||
int res0 = src0[sx0]*alpha0 + src0[sx1]*alpha1;
|
||||
int res1 = src1[sx0]*alpha0 + src1[sx1]*alpha1;
|
||||
const auto src0 = in.InLine <unsigned char>(mapY.s0);
|
||||
const auto src1 = in.InLine <unsigned char>(mapY.s1);
|
||||
|
||||
dst[x] = uchar(( ((mapY.alpha0 * (res0 >> 4)) >> 16) + ((mapY.alpha1 * (res1 >> 4)) >> 16) + 2)>>2);
|
||||
auto dst = out.OutLine<unsigned char>(l);
|
||||
|
||||
for (int x = 0; x < length; x++)
|
||||
{
|
||||
auto alpha0 = mapX[x].alpha0;
|
||||
auto alpha1 = mapX[x].alpha1;
|
||||
auto sx0 = mapX[x].s0;
|
||||
auto sx1 = mapX[x].s1;
|
||||
|
||||
int res0 = src0[sx0]*alpha0 + src0[sx1]*alpha1;
|
||||
int res1 = src1[sx0]*alpha0 + src1[sx1]*alpha1;
|
||||
|
||||
dst[x] = uchar(( ((mapY.alpha0 * (res0 >> 4)) >> 16) + ((mapY.alpha1 * (res1 >> 4)) >> 16) + 2)>>2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace func
|
||||
@@ -191,7 +202,7 @@ struct Mapper
|
||||
} // namespace areaUpscale
|
||||
} // anonymous namespace
|
||||
|
||||
GAPI_FLUID_KERNEL(FResizeLinear, cv::gapi::core::GResize, true)
|
||||
GAPI_FLUID_KERNEL(FResizeLinear1Lpi, cv::gapi::core::GResize, true)
|
||||
{
|
||||
static const int Window = 1;
|
||||
static const auto Kind = GFluidKernel::Kind::Resize;
|
||||
@@ -226,7 +237,7 @@ auto endInCoord = [](int outCoord, double ratio) {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
GAPI_FLUID_KERNEL(FResizeArea, cv::gapi::core::GResize, false)
|
||||
GAPI_FLUID_KERNEL(FResizeArea1Lpi, cv::gapi::core::GResize, false)
|
||||
{
|
||||
static const int Window = 1;
|
||||
static const auto Kind = GFluidKernel::Kind::Resize;
|
||||
@@ -235,57 +246,62 @@ GAPI_FLUID_KERNEL(FResizeArea, cv::gapi::core::GResize, false)
|
||||
cv::gapi::fluid::Buffer& out)
|
||||
|
||||
{
|
||||
auto y = out.y();
|
||||
auto firstOutLineIdx = out.y();
|
||||
auto firstViewLineIdx = in.y();
|
||||
auto length = out.length();
|
||||
double vRatio = (double)in.meta().size.height / out.meta().size.height;
|
||||
double hRatio = (double)in.length() / length;
|
||||
|
||||
int startY = startInCoord(y, vRatio);
|
||||
int endY = endInCoord (y, vRatio);
|
||||
|
||||
auto dst = out.OutLine<unsigned char>();
|
||||
|
||||
double hRatio = (double)in.length() / out.length();
|
||||
|
||||
for (int x = 0; x < out.length(); x++)
|
||||
for (int l = 0; l < out.lpi(); l++)
|
||||
{
|
||||
float res = 0.0;
|
||||
int outY = firstOutLineIdx + l;
|
||||
int startY = startInCoord(outY, vRatio);
|
||||
int endY = endInCoord (outY, vRatio);
|
||||
|
||||
int startX = startInCoord(x, hRatio);
|
||||
int endX = endInCoord (x, hRatio);
|
||||
auto dst = out.OutLine<unsigned char>(l);
|
||||
|
||||
for (int inY = startY; inY < endY; inY++)
|
||||
for (int x = 0; x < length; x++)
|
||||
{
|
||||
double startCoordY = inY / vRatio;
|
||||
double endCoordY = startCoordY + 1/vRatio;
|
||||
float res = 0.0;
|
||||
|
||||
if (startCoordY < y) startCoordY = y;
|
||||
if (endCoordY > y + 1) endCoordY = y + 1;
|
||||
int startX = startInCoord(x, hRatio);
|
||||
int endX = endInCoord (x, hRatio);
|
||||
|
||||
float fracY = static_cast<float>((inY == startY || inY == endY - 1) ? endCoordY - startCoordY : 1/vRatio);
|
||||
|
||||
const auto src = in.InLine <unsigned char>(inY - startY);
|
||||
|
||||
float rowSum = 0.0f;
|
||||
|
||||
for (int inX = startX; inX < endX; inX++)
|
||||
for (int inY = startY; inY < endY; inY++)
|
||||
{
|
||||
double startCoordX = inX / hRatio;
|
||||
double endCoordX = startCoordX + 1/hRatio;
|
||||
double startCoordY = inY / vRatio;
|
||||
double endCoordY = startCoordY + 1/vRatio;
|
||||
|
||||
if (startCoordX < x) startCoordX = x;
|
||||
if (endCoordX > x + 1) endCoordX = x + 1;
|
||||
if (startCoordY < outY) startCoordY = outY;
|
||||
if (endCoordY > outY + 1) endCoordY = outY + 1;
|
||||
|
||||
float fracX = static_cast<float>((inX == startX || inX == endX - 1) ? endCoordX - startCoordX : 1/hRatio);
|
||||
float fracY = static_cast<float>((inY == startY || inY == endY - 1) ? endCoordY - startCoordY : 1/vRatio);
|
||||
|
||||
rowSum += src[inX] * fracX;
|
||||
const auto src = in.InLine <unsigned char>(inY - firstViewLineIdx);
|
||||
|
||||
float rowSum = 0.0f;
|
||||
|
||||
for (int inX = startX; inX < endX; inX++)
|
||||
{
|
||||
double startCoordX = inX / hRatio;
|
||||
double endCoordX = startCoordX + 1/hRatio;
|
||||
|
||||
if (startCoordX < x) startCoordX = x;
|
||||
if (endCoordX > x + 1) endCoordX = x + 1;
|
||||
|
||||
float fracX = static_cast<float>((inX == startX || inX == endX - 1) ? endCoordX - startCoordX : 1/hRatio);
|
||||
|
||||
rowSum += src[inX] * fracX;
|
||||
}
|
||||
res += rowSum * fracY;
|
||||
}
|
||||
res += rowSum * fracY;
|
||||
dst[x] = static_cast<unsigned char>(std::rint(res));
|
||||
}
|
||||
dst[x] = static_cast<unsigned char>(std::rint(res));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_FLUID_KERNEL(FResizeAreaUpscale, cv::gapi::core::GResize, true)
|
||||
GAPI_FLUID_KERNEL(FResizeAreaUpscale1Lpi, cv::gapi::core::GResize, true)
|
||||
{
|
||||
static const int Window = 1;
|
||||
static const auto Kind = GFluidKernel::Kind::Resize;
|
||||
@@ -307,31 +323,80 @@ GAPI_FLUID_KERNEL(FResizeAreaUpscale, cv::gapi::core::GResize, true)
|
||||
}
|
||||
};
|
||||
|
||||
static auto fluidResizeTestPackage = [](int interpolation, cv::Size szIn, cv::Size szOut)
|
||||
#define ADD_RESIZE_KERNEL_WITH_LPI(interp, lpi, scratch) \
|
||||
struct Resize##interp##lpi##LpiHelper : public FResize##interp##1Lpi { static const int LPI = lpi; }; \
|
||||
struct FResize##interp##lpi##Lpi : public cv::GFluidKernelImpl<Resize##interp##lpi##LpiHelper, cv::gapi::core::GResize, scratch>{};
|
||||
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(NN, 2, false)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(NN, 3, false)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(NN, 4, false)
|
||||
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Linear, 2, true)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Linear, 3, true)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Linear, 4, true)
|
||||
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Area, 2, false)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Area, 3, false)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(Area, 4, false)
|
||||
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 2, true)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 3, true)
|
||||
ADD_RESIZE_KERNEL_WITH_LPI(AreaUpscale, 4, true)
|
||||
#undef ADD_RESIZE_KERNEL_WITH_LPI
|
||||
|
||||
static auto fluidResizeTestPackage = [](int interpolation, cv::Size szIn, cv::Size szOut, int lpi = 1)
|
||||
{
|
||||
using namespace cv;
|
||||
using namespace cv::gapi;
|
||||
bool upscale = szIn.width < szOut.width || szIn.height < szOut.height;
|
||||
|
||||
cv::gapi::GKernelPackage pkg;
|
||||
#define RESIZE_CASE(interp, lpi) \
|
||||
case lpi: pkg = kernels<FCopy, FResize##interp##lpi##Lpi>(); break;
|
||||
|
||||
#define RESIZE_SWITCH(interp) \
|
||||
switch(lpi) \
|
||||
{ \
|
||||
RESIZE_CASE(interp, 1) \
|
||||
RESIZE_CASE(interp, 2) \
|
||||
RESIZE_CASE(interp, 3) \
|
||||
RESIZE_CASE(interp, 4) \
|
||||
default: CV_Assert(false); \
|
||||
}
|
||||
|
||||
GKernelPackage pkg;
|
||||
switch (interpolation)
|
||||
{
|
||||
case cv::INTER_NEAREST: pkg = cv::gapi::kernels<FCopy, FResizeNN >(); break;
|
||||
case cv::INTER_LINEAR: pkg = cv::gapi::kernels<FCopy, FResizeLinear>(); break;
|
||||
case cv::INTER_AREA: pkg = upscale ? cv::gapi::kernels<FCopy, FResizeAreaUpscale>()
|
||||
: cv::gapi::kernels<FCopy, FResizeArea>(); break;
|
||||
case INTER_NEAREST: RESIZE_SWITCH(NN); break;
|
||||
case INTER_LINEAR: RESIZE_SWITCH(Linear); break;
|
||||
case INTER_AREA:
|
||||
{
|
||||
if (upscale)
|
||||
{
|
||||
RESIZE_SWITCH(AreaUpscale)
|
||||
}
|
||||
else
|
||||
{
|
||||
RESIZE_SWITCH(Area);
|
||||
}
|
||||
}break;
|
||||
default: CV_Assert(false);
|
||||
}
|
||||
return cv::gapi::combine(pkg, fluidTestPackage, cv::unite_policy::KEEP);
|
||||
return combine(pkg, fluidTestPackage, unite_policy::KEEP);
|
||||
|
||||
#undef RESIZE_SWITCH
|
||||
#undef RESIZE_CASE
|
||||
};
|
||||
|
||||
struct ResizeTestFluid : public TestWithParam<std::tuple<int, int, cv::Size, std::tuple<cv::Size, cv::Rect>, double>> {};
|
||||
struct ResizeTestFluid : public TestWithParam<std::tuple<int, int, cv::Size, std::tuple<cv::Size, cv::Rect>, int, double>> {};
|
||||
TEST_P(ResizeTestFluid, SanityTest)
|
||||
{
|
||||
int type = 0, interp = 0;
|
||||
cv::Size sz_in, sz_out;
|
||||
int lpi = 0;
|
||||
double tolerance = 0.0;
|
||||
cv::Rect outRoi;
|
||||
std::tuple<cv::Size, cv::Rect> outSizeAndRoi;
|
||||
std::tie(type, interp, sz_in, outSizeAndRoi, tolerance) = GetParam();
|
||||
std::tie(type, interp, sz_in, outSizeAndRoi, lpi, tolerance) = GetParam();
|
||||
std::tie(sz_out, outRoi) = outSizeAndRoi;
|
||||
if (outRoi == cv::Rect{}) outRoi = {0,0,sz_out.width,sz_out.height};
|
||||
if (outRoi.width == 0) outRoi.width = sz_out.width;
|
||||
@@ -351,7 +416,7 @@ TEST_P(ResizeTestFluid, SanityTest)
|
||||
auto out = cv::gapi::resize(mid, sz_out, fx, fy, interp);
|
||||
|
||||
cv::GComputation c(in, out);
|
||||
c.apply(in_mat1, out_mat, cv::compile_args(GFluidOutputRois{{outRoi}}, fluidResizeTestPackage(interp, sz_in, sz_out)));
|
||||
c.apply(in_mat1, out_mat, cv::compile_args(GFluidOutputRois{{outRoi}}, fluidResizeTestPackage(interp, sz_in, sz_out, lpi)));
|
||||
|
||||
cv::Mat mid_mat;
|
||||
cv::blur(in_mat1, mid_mat, {3,3}, {-1,-1}, cv::BORDER_REPLICATE);
|
||||
@@ -383,6 +448,7 @@ INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTestFluid,
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 0, 0, 3}),
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 1, 0, 2}),
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 3, 0, 1})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
Values(0.0)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ResizeAreaTestCPU, ResizeTestFluid,
|
||||
@@ -406,6 +472,7 @@ INSTANTIATE_TEST_CASE_P(ResizeAreaTestCPU, ResizeTestFluid,
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 0, 0, 3}),
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 1, 0, 2}),
|
||||
std::make_tuple(cv::Size(8, 4), cv::Rect{0, 3, 0, 1})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
// Actually this tolerance only for cases where OpenCV
|
||||
// uses ResizeAreaFast
|
||||
Values(1.0)));
|
||||
@@ -441,6 +508,7 @@ INSTANTIATE_TEST_CASE_P(ResizeUpscaleTestCPU, ResizeTestFluid,
|
||||
std::make_tuple(cv::Size(16, 25), cv::Rect{0, 0,16,25}),
|
||||
std::make_tuple(cv::Size(16, 7), cv::Rect{}),
|
||||
std::make_tuple(cv::Size(16, 8), cv::Rect{})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
Values(0.0)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ResizeUpscaleOneDimDownscaleAnother, ResizeTestFluid,
|
||||
@@ -473,6 +541,7 @@ INSTANTIATE_TEST_CASE_P(ResizeUpscaleOneDimDownscaleAnother, ResizeTestFluid,
|
||||
std::make_tuple(cv::Size(5, 11), cv::Rect{0, 3, 0, 3}),
|
||||
std::make_tuple(cv::Size(5, 11), cv::Rect{0, 6, 0, 3}),
|
||||
std::make_tuple(cv::Size(5, 11), cv::Rect{0, 9, 0, 2})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
Values(0.0)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Resize400_384TestCPU, ResizeTestFluid,
|
||||
@@ -480,6 +549,7 @@ INSTANTIATE_TEST_CASE_P(Resize400_384TestCPU, ResizeTestFluid,
|
||||
Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
|
||||
Values(cv::Size(128, 400)),
|
||||
Values(std::make_tuple(cv::Size(128, 384), cv::Rect{})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
Values(0.0)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Resize220_400TestCPU, ResizeTestFluid,
|
||||
@@ -487,6 +557,7 @@ INSTANTIATE_TEST_CASE_P(Resize220_400TestCPU, ResizeTestFluid,
|
||||
Values(cv::INTER_LINEAR),
|
||||
Values(cv::Size(220, 220)),
|
||||
Values(std::make_tuple(cv::Size(400, 400), cv::Rect{})),
|
||||
Values(1, 2, 3, 4), // lpi
|
||||
Values(0.0)));
|
||||
|
||||
static auto cvBlur = [](const cv::Mat& in, cv::Mat& out, int kernelSize)
|
||||
|
||||
@@ -79,7 +79,7 @@ PFMDecoder::~PFMDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
PFMDecoder::PFMDecoder()
|
||||
PFMDecoder::PFMDecoder() : m_scale_factor(0), m_swap_byte_order(false)
|
||||
{
|
||||
m_strm.close();
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ static bool ipp_Canny(const Mat& src , const Mat& dx_, const Mat& dy_, Mat& dst,
|
||||
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCannyDeriv, iwSrcDx, iwSrcDy, iwDst, low, high, ::ipp::IwiFilterCannyDerivParams(norm));
|
||||
}
|
||||
catch (::ipp::IwException ex)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ static bool ipp_Canny(const Mat& src , const Mat& dx_, const Mat& dy_, Mat& dst,
|
||||
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterCanny, iwSrc, iwDst, low, high, ::ipp::IwiFilterCannyParams(ippFilterSobel, kernel, norm), ippBorderRepl);
|
||||
}
|
||||
catch (::ipp::IwException)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1532,6 +1532,7 @@ icvFindContoursInInterval( const CvArr* src,
|
||||
tmp_prev->link = 0;
|
||||
|
||||
// First line. None of runs is binded
|
||||
tmp.pt.x = 0;
|
||||
tmp.pt.y = 0;
|
||||
CV_WRITE_SEQ_ELEM( tmp, writer );
|
||||
upper_line = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
|
||||
|
||||
@@ -337,7 +337,7 @@ static bool ipp_Deriv(InputArray _src, OutputArray _dst, int dx, int dy, int ksi
|
||||
if(useScale)
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta, ::ipp::IwiScaleParams(ippAlgHintFast));
|
||||
}
|
||||
catch (::ipp::IwException)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -765,7 +765,7 @@ static bool ipp_Laplacian(InputArray _src, OutputArray _dst, int ksize, double s
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta);
|
||||
|
||||
}
|
||||
catch (::ipp::IwException ex)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -295,11 +295,19 @@ static bool between( Point2f a, Point2f b, Point2f c )
|
||||
((a.y >= c.y) && (c.y >= b.y));
|
||||
}
|
||||
|
||||
static char parallelInt( Point2f a, Point2f b, Point2f c, Point2f d, Point2f& p, Point2f& q )
|
||||
enum LineSegmentIntersection
|
||||
{
|
||||
char code = 'e';
|
||||
LS_NO_INTERSECTION = 0,
|
||||
LS_SINGLE_INTERSECTION = 1,
|
||||
LS_OVERLAP = 2,
|
||||
LS_ENDPOINT_INTERSECTION = 3
|
||||
};
|
||||
|
||||
static LineSegmentIntersection parallelInt( Point2f a, Point2f b, Point2f c, Point2f d, Point2f& p, Point2f& q )
|
||||
{
|
||||
LineSegmentIntersection code = LS_OVERLAP;
|
||||
if( areaSign(a, b, c) != 0 )
|
||||
code = '0';
|
||||
code = LS_NO_INTERSECTION;
|
||||
else if( between(a, b, c) && between(a, b, d))
|
||||
p = c, q = d;
|
||||
else if( between(c, d, a) && between(c, d, b))
|
||||
@@ -313,60 +321,33 @@ static char parallelInt( Point2f a, Point2f b, Point2f c, Point2f d, Point2f& p,
|
||||
else if( between(a, b, d) && between(c, d, a))
|
||||
p = d, q = a;
|
||||
else
|
||||
code = '0';
|
||||
code = LS_NO_INTERSECTION;
|
||||
return code;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// segSegInt: Finds the point of intersection p between two closed
|
||||
// segments ab and cd. Returns p and a char with the following meaning:
|
||||
// 'e': The segments collinearly overlap, sharing a point.
|
||||
// 'v': An endpoint (vertex) of one segment is on the other segment,
|
||||
// but 'e' doesn't hold.
|
||||
// '1': The segments intersect properly (i.e., they share a point and
|
||||
// neither 'v' nor 'e' holds).
|
||||
// '0': The segments do not intersect (i.e., they share no points).
|
||||
// Note that two collinear segments that share just one point, an endpoint
|
||||
// of each, returns 'e' rather than 'v' as one might expect.
|
||||
//---------------------------------------------------------------------
|
||||
static char segSegInt( Point2f a, Point2f b, Point2f c, Point2f d, Point2f& p, Point2f& q )
|
||||
// Finds intersection of two line segments: (a, b) and (c, d).
|
||||
static LineSegmentIntersection intersectLineSegments( Point2f a, Point2f b, Point2f c,
|
||||
Point2f d, Point2f& p, Point2f& q )
|
||||
{
|
||||
double s, t; // The two parameters of the parametric eqns.
|
||||
double num, denom; // Numerator and denoninator of equations.
|
||||
char code = '?'; // Return char characterizing intersection.
|
||||
|
||||
denom = a.x * (double)( d.y - c.y ) +
|
||||
b.x * (double)( c.y - d.y ) +
|
||||
d.x * (double)( b.y - a.y ) +
|
||||
c.x * (double)( a.y - b.y );
|
||||
double denom = a.x * (double)(d.y - c.y) + b.x * (double)(c.y - d.y) +
|
||||
d.x * (double)(b.y - a.y) + c.x * (double)(a.y - b.y);
|
||||
|
||||
// If denom is zero, then segments are parallel: handle separately.
|
||||
if (denom == 0.0)
|
||||
if( denom == 0. )
|
||||
return parallelInt(a, b, c, d, p, q);
|
||||
|
||||
num = a.x * (double)( d.y - c.y ) +
|
||||
c.x * (double)( a.y - d.y ) +
|
||||
d.x * (double)( c.y - a.y );
|
||||
if ( (num == 0.0) || (num == denom) ) code = 'v';
|
||||
s = num / denom;
|
||||
double num = a.x * (double)(d.y - c.y) + c.x * (double)(a.y - d.y) + d.x * (double)(c.y - a.y);
|
||||
double s = num / denom;
|
||||
|
||||
num = -( a.x * (double)( c.y - b.y ) +
|
||||
b.x * (double)( a.y - c.y ) +
|
||||
c.x * (double)( b.y - a.y ) );
|
||||
if ( (num == 0.0) || (num == denom) ) code = 'v';
|
||||
t = num / denom;
|
||||
|
||||
if ( (0.0 < s) && (s < 1.0) &&
|
||||
(0.0 < t) && (t < 1.0) )
|
||||
code = '1';
|
||||
else if ( (0.0 > s) || (s > 1.0) ||
|
||||
(0.0 > t) || (t > 1.0) )
|
||||
code = '0';
|
||||
num = a.x * (double)(b.y - c.y) + b.x * (double)(c.y - a.y) + c.x * (double)(a.y - b.y);
|
||||
double t = num / denom;
|
||||
|
||||
p.x = (float)(a.x + s*(b.x - a.x));
|
||||
p.y = (float)(a.y + s*(b.y - a.y));
|
||||
q = p;
|
||||
|
||||
return code;
|
||||
return s < 0. || s > 1. || t < 0. || t > 1. ? LS_NO_INTERSECTION :
|
||||
s == 0. || s == 1. || t == 0. || t == 1. ? LS_ENDPOINT_INTERSECTION : LS_SINGLE_INTERSECTION;
|
||||
}
|
||||
|
||||
static tInFlag inOut( Point2f p, tInFlag inflag, int aHB, int bHA, Point2f*& result )
|
||||
@@ -424,8 +405,8 @@ static int intersectConvexConvex_( const Point2f* P, int n, const Point2f* Q, in
|
||||
|
||||
// If A & B intersect, update inflag.
|
||||
Point2f p, q;
|
||||
int code = segSegInt( P[a1], P[a], Q[b1], Q[b], p, q );
|
||||
if( code == '1' || code == 'v' )
|
||||
LineSegmentIntersection code = intersectLineSegments( P[a1], P[a], Q[b1], Q[b], p, q );
|
||||
if( code == LS_SINGLE_INTERSECTION || code == LS_ENDPOINT_INTERSECTION )
|
||||
{
|
||||
if( inflag == Unknown && FirstPoint )
|
||||
{
|
||||
@@ -440,7 +421,7 @@ static int intersectConvexConvex_( const Point2f* P, int n, const Point2f* Q, in
|
||||
//-----Advance rules-----
|
||||
|
||||
// Special case: A & B overlap and oppositely oriented.
|
||||
if( code == 'e' && A.ddot(B) < 0 )
|
||||
if( code == LS_OVERLAP && A.ddot(B) < 0 )
|
||||
{
|
||||
addSharedSeg( p, q, result );
|
||||
return (int)(result - result0);
|
||||
|
||||
@@ -1299,7 +1299,7 @@ static bool ippMorph(int op, int src_type, int dst_type,
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterMorphology, iwSrc, iwDst, morphType, iwMask, ::ipp::IwDefault(), iwBorderType);
|
||||
}
|
||||
}
|
||||
catch(::ipp::IwException ex)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3241,7 +3241,7 @@ public:
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(iwiResize, m_src, m_dst, ippBorderRepl, tile);
|
||||
}
|
||||
catch(::ipp::IwException)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
m_ok = false;
|
||||
return;
|
||||
@@ -3291,7 +3291,7 @@ public:
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(iwiWarpAffine, m_src, m_dst, tile);
|
||||
}
|
||||
catch(::ipp::IwException)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
m_ok = false;
|
||||
return;
|
||||
@@ -3387,7 +3387,7 @@ static bool ipp_resize(const uchar * src_data, size_t src_step, int src_width, i
|
||||
if(!ok)
|
||||
return false;
|
||||
}
|
||||
catch(::ipp::IwException)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1510,7 +1510,7 @@ static bool ipp_boxfilter(Mat &src, Mat &dst, Size ksize, Point anchor, bool nor
|
||||
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBox, iwSrc, iwDst, iwKSize, ::ipp::IwDefault(), ippBorder);
|
||||
}
|
||||
catch (::ipp::IwException)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -4000,7 +4000,7 @@ public:
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterGaussian, m_src, m_dst, m_kernelSize, m_sigma, ::ipp::IwDefault(), m_border, tile);
|
||||
}
|
||||
catch(::ipp::IwException e)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*m_pOk = false;
|
||||
return;
|
||||
@@ -4067,7 +4067,7 @@ static bool ipp_GaussianBlur(InputArray _src, OutputArray _dst, Size ksize,
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterGaussian, iwSrc, iwDst, ksize.width, sigma1, ::ipp::IwDefault(), ippBorder);
|
||||
}
|
||||
}
|
||||
catch (::ipp::IwException ex)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -5873,7 +5873,7 @@ public:
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, src, dst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), borderType, tile);
|
||||
}
|
||||
catch(::ipp::IwException)
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
return;
|
||||
@@ -5928,7 +5928,7 @@ static bool ipp_bilateralFilter(Mat &src, Mat &dst, int d, double sigmaColor, do
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterBilateral, iwSrc, iwDst, radius, valSquareSigma, posSquareSigma, ::ipp::IwDefault(), ippBorder);
|
||||
}
|
||||
}
|
||||
catch (::ipp::IwException)
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@ void HOGDescriptor::computeGradient(InputArray _img, InputOutputArray _grad, Inp
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat img = _img.getMat();
|
||||
CV_Assert(!img.empty());
|
||||
CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
|
||||
|
||||
Size gradsize(img.cols + paddingTL.width + paddingBR.width,
|
||||
|
||||
@@ -103,7 +103,7 @@ PERF_TEST_P( match, bestOf2Nearest, TEST_DETECTORS)
|
||||
Mat R (pairwise_matches.H, Range::all(), Range(0, 2));
|
||||
// separate transform matrix, use lower error on rotations
|
||||
SANITY_CHECK(dist, 1., ERROR_ABSOLUTE);
|
||||
SANITY_CHECK(R, .015, ERROR_ABSOLUTE);
|
||||
SANITY_CHECK(R, .06, ERROR_ABSOLUTE);
|
||||
}
|
||||
|
||||
PERF_TEST_P( matchVector, bestOf2NearestVectorFeatures, testing::Combine(
|
||||
|
||||
@@ -1519,8 +1519,6 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo
|
||||
_opencv_ffmpeg_av_image_fill_arrays(picture, picture_buf,
|
||||
(AVPixelFormat) pix_fmt, width, height);
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user