Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Greenplum
Opencv
提交
b083c20e
O
Opencv
项目概览
Greenplum
/
Opencv
11 个月 前同步成功
通知
7
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
O
Opencv
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
b083c20e
编写于
6月 04, 2020
作者:
A
AsyaPronina
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Enable stateful kernels in G-API OCV Backend
上级
c3e8a82c
变更
15
隐藏空白更改
内联
并排
Showing
15 changed file
with
527 addition
and
44 deletion
+527
-44
modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
+107
-20
modules/gapi/include/opencv2/gapi/gcompiled.hpp
modules/gapi/include/opencv2/gapi/gcompiled.hpp
+13
-0
modules/gapi/src/backends/cpu/gcpubackend.cpp
modules/gapi/src/backends/cpu/gcpubackend.cpp
+57
-8
modules/gapi/src/backends/cpu/gcpubackend.hpp
modules/gapi/src/backends/cpu/gcpubackend.hpp
+10
-1
modules/gapi/src/backends/cpu/gcpukernel.cpp
modules/gapi/src/backends/cpu/gcpukernel.cpp
+2
-8
modules/gapi/src/backends/ocl/goclbackend.cpp
modules/gapi/src/backends/ocl/goclbackend.cpp
+4
-4
modules/gapi/src/backends/ocl/goclbackend.hpp
modules/gapi/src/backends/ocl/goclbackend.hpp
+1
-1
modules/gapi/src/backends/render/grenderocvbackend.cpp
modules/gapi/src/backends/render/grenderocvbackend.cpp
+1
-1
modules/gapi/src/compiler/gcompiled.cpp
modules/gapi/src/compiler/gcompiled.cpp
+11
-0
modules/gapi/src/compiler/gcompiled_priv.hpp
modules/gapi/src/compiler/gcompiled_priv.hpp
+1
-0
modules/gapi/src/executor/gexecutor.cpp
modules/gapi/src/executor/gexecutor.cpp
+8
-0
modules/gapi/src/executor/gexecutor.hpp
modules/gapi/src/executor/gexecutor.hpp
+2
-0
modules/gapi/src/executor/gstreamingexecutor.cpp
modules/gapi/src/executor/gstreamingexecutor.cpp
+12
-1
modules/gapi/test/cpu/gapi_ocv_stateful_kernel_test_utils.hpp
...les/gapi/test/cpu/gapi_ocv_stateful_kernel_test_utils.hpp
+47
-0
modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp
modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp
+251
-0
未找到文件。
modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
浏览文件 @
b083c20e
...
...
@@ -17,6 +17,7 @@
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/gkernel.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gmetaarg.hpp>
#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
#include <opencv2/gapi/util/util.hpp>
...
...
@@ -109,11 +110,17 @@ public:
return
outOpaqueRef
(
output
).
wref
<
T
>
();
}
GArg
state
()
{
return
m_state
;
}
protected:
detail
::
VectorRef
&
outVecRef
(
int
output
);
detail
::
OpaqueRef
&
outOpaqueRef
(
int
output
);
std
::
vector
<
GArg
>
m_args
;
GArg
m_state
;
//FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call
//to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
...
...
@@ -127,16 +134,18 @@ protected:
class
GAPI_EXPORTS
GCPUKernel
{
public:
// This function is kernel's execution entry point (does the processing work)
using
F
=
std
::
function
<
void
(
GCPUContext
&
)
>
;
// This function is a kernel's execution entry point (does the processing work)
using
RunF
=
std
::
function
<
void
(
GCPUContext
&
)
>
;
// This function is a stateful kernel's setup routine (configures state)
using
SetupF
=
std
::
function
<
void
(
const
GMetaArgs
&
,
const
GArgs
&
,
GArg
&
)
>
;
GCPUKernel
();
explicit
GCPUKernel
(
const
F
&
f
);
GCPUKernel
(
const
RunF
&
runF
,
const
SetupF
&
setupF
=
nullptr
);
void
apply
(
GCPUContext
&
ctx
);
RunF
m_runF
=
nullptr
;
SetupF
m_setupF
=
nullptr
;
protected:
F
m_f
;
bool
m_isStateful
=
false
;
};
// FIXME: This is an ugly ad-hoc implementation. TODO: refactor
...
...
@@ -269,12 +278,38 @@ template<typename U> struct get_out<cv::GOpaque<U>>
}
};
template
<
typename
,
typename
>
struct
OCVSetupHelper
;
template
<
typename
Impl
,
typename
...
Ins
>
struct
OCVSetupHelper
<
Impl
,
std
::
tuple
<
Ins
...
>>
{
template
<
int
...
IIs
>
static
void
setup_impl
(
const
GMetaArgs
&
metaArgs
,
const
GArgs
&
args
,
GArg
&
state
,
detail
::
Seq
<
IIs
...
>
)
{
// TODO: unique_ptr <-> shared_ptr conversion ?
// To check: Conversion is possible only if the state which should be passed to
// 'setup' user callback isn't required to have previous value
std
::
shared_ptr
<
typename
Impl
::
State
>
stPtr
;
Impl
::
setup
(
detail
::
get_in_meta
<
Ins
>
(
metaArgs
,
args
,
IIs
)...,
stPtr
);
state
=
GArg
(
stPtr
);
}
static
void
setup
(
const
GMetaArgs
&
metaArgs
,
const
GArgs
&
args
,
GArg
&
state
)
{
setup_impl
(
metaArgs
,
args
,
state
,
typename
detail
::
MkSeq
<
sizeof
...(
Ins
)
>::
type
());
}
};
// OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors.
template
<
typename
,
typename
,
typename
>
struct
OCVCallHelper
;
// FIXME: probably can be simplified with std::apply or analogue.
template
<
typename
Impl
,
typename
...
Ins
,
typename
...
Outs
>
struct
OCVCallHelper
<
Impl
,
std
::
tuple
<
Ins
...
>
,
std
::
tuple
<
Outs
...
>
>
struct
OCVCallHelper
<
Impl
,
std
::
tuple
<
Ins
...
>
,
std
::
tuple
<
Outs
...
>>
{
template
<
typename
...
Inputs
>
struct
call_and_postprocess
...
...
@@ -302,19 +337,16 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
//by comparing it's state (data ptr) before and after the call.
//This is done by converting each output Mat into tracked_cv_mat object, and binding
//them to parameters of ad-hoc function
//Convert own::Scalar to cv::Scalar before call kernel and run kernel
//convert cv::Scalar to own::Scalar after call kernel and write back results
call_and_postprocess
<
decltype
(
get_in
<
Ins
>::
get
(
ctx
,
IIs
))...
>
::
call
(
get_in
<
Ins
>::
get
(
ctx
,
IIs
)...,
get_out
<
Outs
>::
get
(
ctx
,
OIs
)...);
::
call
(
get_in
<
Ins
>::
get
(
ctx
,
IIs
)...,
get_out
<
Outs
>::
get
(
ctx
,
OIs
)...);
}
template
<
int
...
IIs
,
int
...
OIs
>
static
void
call_impl
(
cv
::
GCPUContext
&
ctx
,
Impl
&
impl
,
detail
::
Seq
<
IIs
...
>
,
detail
::
Seq
<
OIs
...
>
)
static
void
call_impl
(
cv
::
GCPUContext
&
ctx
,
Impl
&
impl
,
detail
::
Seq
<
IIs
...
>
,
detail
::
Seq
<
OIs
...
>
)
{
call_and_postprocess
<
decltype
(
cv
::
detail
::
get_in
<
Ins
>::
get
(
ctx
,
IIs
))...
>
::
call
(
impl
,
cv
::
detail
::
get_in
<
Ins
>::
get
(
ctx
,
IIs
)...,
cv
::
detail
::
get_out
<
Outs
>::
get
(
ctx
,
OIs
)...);
call_and_postprocess
<
decltype
(
get_in
<
Ins
>::
get
(
ctx
,
IIs
))...
>
::
call
(
impl
,
get_in
<
Ins
>::
get
(
ctx
,
IIs
)...,
get_out
<
Outs
>::
get
(
ctx
,
OIs
)...);
}
static
void
call
(
GCPUContext
&
ctx
)
...
...
@@ -335,23 +367,78 @@ struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...> >
}
};
// OCVStCallHelper is a helper class to call stateful OCV kernels.
template
<
typename
,
typename
,
typename
>
struct
OCVStCallHelper
;
template
<
typename
Impl
,
typename
...
Ins
,
typename
...
Outs
>
struct
OCVStCallHelper
<
Impl
,
std
::
tuple
<
Ins
...
>
,
std
::
tuple
<
Outs
...
>>
:
OCVCallHelper
<
Impl
,
std
::
tuple
<
Ins
...
>
,
std
::
tuple
<
Outs
...
>>
{
template
<
typename
...
Inputs
>
struct
call_and_postprocess
{
template
<
typename
...
Outputs
>
static
void
call
(
typename
Impl
::
State
&
st
,
Inputs
&&
...
ins
,
Outputs
&&
...
outs
)
{
Impl
::
run
(
std
::
forward
<
Inputs
>
(
ins
)...,
outs
...,
st
);
postprocess
(
outs
...);
}
};
template
<
int
...
IIs
,
int
...
OIs
>
static
void
call_impl
(
GCPUContext
&
ctx
,
detail
::
Seq
<
IIs
...
>
,
detail
::
Seq
<
OIs
...
>
)
{
auto
&
st
=
*
ctx
.
state
().
get
<
std
::
shared_ptr
<
typename
Impl
::
State
>>
();
call_and_postprocess
<
decltype
(
get_in
<
Ins
>::
get
(
ctx
,
IIs
))...
>
::
call
(
st
,
get_in
<
Ins
>::
get
(
ctx
,
IIs
)...,
get_out
<
Outs
>::
get
(
ctx
,
OIs
)...);
}
static
void
call
(
GCPUContext
&
ctx
)
{
call_impl
(
ctx
,
typename
detail
::
MkSeq
<
sizeof
...(
Ins
)
>::
type
(),
typename
detail
::
MkSeq
<
sizeof
...(
Outs
)
>::
type
());
}
};
}
// namespace detail
template
<
class
Impl
,
class
K
>
class
GCPUKernelImpl
:
public
cv
::
detail
::
OCVCallHelper
<
Impl
,
typename
K
::
InArgs
,
typename
K
::
OutArgs
>
,
public
cv
::
detail
::
KernelTag
class
GCPUKernelImpl
:
public
cv
::
detail
::
KernelTag
{
using
CallHelper
=
detail
::
OCVCallHelper
<
Impl
,
typename
K
::
InArgs
,
typename
K
::
OutArgs
>
;
public:
using
API
=
K
;
static
cv
::
gapi
::
GBackend
backend
()
{
return
cv
::
gapi
::
cpu
::
backend
();
}
static
cv
::
GCPUKernel
kernel
()
{
return
GCPUKernel
(
&
CallHelper
::
call
);
}
};
template
<
class
Impl
,
class
K
,
class
S
>
class
GCPUStKernelImpl
:
public
cv
::
detail
::
KernelTag
{
using
P
=
detail
::
OCVCallHelper
<
Impl
,
typename
K
::
InArgs
,
typename
K
::
OutArgs
>
;
using
StSetupHelper
=
detail
::
OCVSetupHelper
<
Impl
,
typename
K
::
InArgs
>
;
using
StCallHelper
=
detail
::
OCVStCallHelper
<
Impl
,
typename
K
::
InArgs
,
typename
K
::
OutArgs
>
;
public:
using
API
=
K
;
using
State
=
S
;
static
cv
::
gapi
::
GBackend
backend
()
{
return
cv
::
gapi
::
cpu
::
backend
();
}
static
cv
::
GCPUKernel
kernel
()
{
return
GCPUKernel
(
&
P
::
call
);
}
static
cv
::
gapi
::
GBackend
backend
()
{
return
cv
::
gapi
::
cpu
::
backend
();
}
static
cv
::
GCPUKernel
kernel
()
{
return
GCPUKernel
(
&
StCallHelper
::
call
,
&
StSetupHelper
::
setup
);
}
};
#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
// TODO: Reuse Anatoliy's logic for support of types with commas in macro.
// Retrieve the common part from Anatoliy's logic to the separate place.
#define GAPI_OCV_KERNEL_ST(Name, API, State) \
struct Name:public cv::GCPUStKernelImpl<Name, API, State> \
class
gapi
::
cpu
::
GOCVFunctor
:
public
gapi
::
GFunctor
{
public:
...
...
modules/gapi/include/opencv2/gapi/gcompiled.hpp
浏览文件 @
b083c20e
...
...
@@ -208,6 +208,19 @@ public:
// FIXME: Why it requires compile args?
void
reshape
(
const
GMetaArgs
&
inMetas
,
const
GCompileArgs
&
args
);
/**
* @brief Prepare inner kernels states for a new video-stream.
*
* GCompiled objects may be used to process video streams frame by frame.
* In this case, a GCompiled is called on every image frame individually.
* Starting OpenCV 4.4, some kernels in the graph may have their internal
* states (see GAPI_OCV_KERNEL_ST for the OpenCV backend).
* In this case, if user starts processing another video stream with
* this GCompiled, this method needs to be called to let kernels re-initialize
* their internal states to a new video stream.
*/
void
prepareForNewStream
();
protected:
/// @private
std
::
shared_ptr
<
Priv
>
m_priv
;
...
...
modules/gapi/src/backends/cpu/gcpubackend.cpp
浏览文件 @
b083c20e
...
...
@@ -33,13 +33,13 @@
//
// If not, we need to introduce that!
using
GCPUModel
=
ade
::
TypedGraph
<
cv
::
gimpl
::
Unit
<
cv
::
gimpl
::
CPU
Unit
,
cv
::
gimpl
::
Protocol
>
;
// FIXME: Same issue with Typed and ConstTyped
using
GConstGCPUModel
=
ade
::
ConstTypedGraph
<
cv
::
gimpl
::
Unit
<
cv
::
gimpl
::
CPU
Unit
,
cv
::
gimpl
::
Protocol
>
;
...
...
@@ -53,7 +53,7 @@ namespace
{
GCPUModel
gm
(
graph
);
auto
cpu_impl
=
cv
::
util
::
any_cast
<
cv
::
GCPUKernel
>
(
impl
.
opaque
);
gm
.
metadata
(
op_node
).
set
(
cv
::
gimpl
::
Unit
{
cpu_impl
});
gm
.
metadata
(
op_node
).
set
(
cv
::
gimpl
::
CPU
Unit
{
cpu_impl
});
}
virtual
EPtr
compile
(
const
ade
::
Graph
&
graph
,
...
...
@@ -78,11 +78,23 @@ cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g,
{
// Convert list of operations (which is topologically sorted already)
// into an execution script.
GConstGCPUModel
gcm
(
m_g
);
for
(
auto
&
nh
:
nodes
)
{
switch
(
m_gm
.
metadata
(
nh
).
get
<
NodeType
>
().
t
)
{
case
NodeType
::
OP
:
m_script
.
push_back
({
nh
,
GModel
::
collectOutputMeta
(
m_gm
,
nh
)});
break
;
case
NodeType
::
OP
:
{
m_script
.
push_back
({
nh
,
GModel
::
collectOutputMeta
(
m_gm
,
nh
)});
// If kernel is stateful then prepare storage for its state.
GCPUKernel
k
=
gcm
.
metadata
(
nh
).
get
<
CPUUnit
>
().
k
;
if
(
k
.
m_isStateful
)
{
m_nodesToStates
[
nh
]
=
GArg
{
};
}
break
;
}
case
NodeType
::
DATA
:
{
m_dataNodes
.
push_back
(
nh
);
...
...
@@ -104,6 +116,9 @@ cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g,
default:
util
::
throw_error
(
std
::
logic_error
(
"Unsupported NodeType type"
));
}
}
// For each stateful kernel call 'setup' user callback to initialize state.
setupKernelStates
();
}
// FIXME: Document what it does
...
...
@@ -140,6 +155,26 @@ cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg)
}
}
void
cv
::
gimpl
::
GCPUExecutable
::
setupKernelStates
()
{
GConstGCPUModel
gcm
(
m_g
);
for
(
auto
&
nodeToState
:
m_nodesToStates
)
{
auto
&
kernelNode
=
nodeToState
.
first
;
auto
&
kernelState
=
nodeToState
.
second
;
const
GCPUKernel
&
kernel
=
gcm
.
metadata
(
kernelNode
).
get
<
CPUUnit
>
().
k
;
kernel
.
m_setupF
(
GModel
::
collectInputMeta
(
m_gm
,
kernelNode
),
m_gm
.
metadata
(
kernelNode
).
get
<
Op
>
().
args
,
kernelState
);
}
}
void
cv
::
gimpl
::
GCPUExecutable
::
handleNewStream
()
{
m_newStreamStarted
=
true
;
}
void
cv
::
gimpl
::
GCPUExecutable
::
run
(
std
::
vector
<
InObj
>
&&
input_objs
,
std
::
vector
<
OutObj
>
&&
output_objs
)
{
...
...
@@ -167,6 +202,14 @@ void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
}
}
// In case if new video-stream happens - for each stateful kernel
// call 'setup' user callback to re-initialize state.
if
(
m_newStreamStarted
)
{
setupKernelStates
();
m_newStreamStarted
=
false
;
}
// OpenCV backend execution is not a rocket science at all.
// Simply invoke our kernels in the proper order.
GConstGCPUModel
gcm
(
m_g
);
...
...
@@ -176,7 +219,7 @@ void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
// Obtain our real execution unit
// TODO: Should kernels be copyable?
GCPUKernel
k
=
gcm
.
metadata
(
op_info
.
nh
).
get
<
Unit
>
().
k
;
GCPUKernel
k
=
gcm
.
metadata
(
op_info
.
nh
).
get
<
CPU
Unit
>
().
k
;
// Initialize kernel's execution context:
// - Input parameters
...
...
@@ -185,8 +228,8 @@ void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
using
namespace
std
::
placeholders
;
ade
::
util
::
transform
(
op
.
args
,
std
::
back_inserter
(
context
.
m_args
),
std
::
bind
(
&
GCPUExecutable
::
packArg
,
this
,
_1
));
std
::
back_inserter
(
context
.
m_args
),
std
::
bind
(
&
GCPUExecutable
::
packArg
,
this
,
_1
));
// - Output parameters.
// FIXME: pre-allocate internal Mats, etc, according to the known meta
...
...
@@ -198,8 +241,14 @@ void cv::gimpl::GCPUExecutable::run(std::vector<InObj> &&input_objs,
context
.
m_results
[
out_port
]
=
magazine
::
getObjPtr
(
m_res
,
out_desc
);
}
// For stateful kernel add state to its execution context
if
(
k
.
m_isStateful
)
{
context
.
m_state
=
m_nodesToStates
.
at
(
op_info
.
nh
);
}
// Now trigger the executable unit
k
.
apply
(
context
);
k
.
m_runF
(
context
);
//As Kernels are forbidden to allocate memory for (Mat) outputs,
//this code seems redundant, at least for Mats
...
...
modules/gapi/src/backends/cpu/gcpubackend.hpp
浏览文件 @
b083c20e
...
...
@@ -23,7 +23,7 @@
namespace
cv
{
namespace
gimpl
{
struct
Unit
struct
CPU
Unit
{
static
const
char
*
name
()
{
return
"HostKernel"
;
}
GCPUKernel
k
;
...
...
@@ -48,6 +48,13 @@ class GCPUExecutable final: public GIslandExecutable
// Actual data of all resources in graph (both internal and external)
Mag
m_res
;
GArg
packArg
(
const
GArg
&
arg
);
void
setupKernelStates
();
// TODO: Check that it is thread-safe
std
::
unordered_map
<
ade
::
NodeHandle
,
GArg
,
ade
::
HandleHasher
<
ade
::
Node
>>
m_nodesToStates
;
bool
m_newStreamStarted
=
false
;
public:
GCPUExecutable
(
const
ade
::
Graph
&
graph
,
...
...
@@ -62,6 +69,8 @@ public:
util
::
throw_error
(
std
::
logic_error
(
"GCPUExecutable::reshape() should never be called"
));
}
virtual
void
handleNewStream
()
override
;
virtual
void
run
(
std
::
vector
<
InObj
>
&&
input_objs
,
std
::
vector
<
OutObj
>
&&
output_objs
)
override
;
};
...
...
modules/gapi/src/backends/cpu/gcpukernel.cpp
浏览文件 @
b083c20e
...
...
@@ -45,13 +45,7 @@ cv::GCPUKernel::GCPUKernel()
{
}
cv
::
GCPUKernel
::
GCPUKernel
(
const
GCPUKernel
::
F
&
f
)
:
m_
f
(
f
)
cv
::
GCPUKernel
::
GCPUKernel
(
const
GCPUKernel
::
RunF
&
runF
,
const
GCPUKernel
::
SetupF
&
setupF
)
:
m_
runF
(
runF
),
m_setupF
(
setupF
),
m_isStateful
(
m_setupF
!=
nullptr
)
{
}
void
cv
::
GCPUKernel
::
apply
(
GCPUContext
&
ctx
)
{
GAPI_Assert
(
m_f
);
m_f
(
ctx
);
}
modules/gapi/src/backends/ocl/goclbackend.cpp
浏览文件 @
b083c20e
...
...
@@ -33,13 +33,13 @@
//
// If not, we need to introduce that!
using
GOCLModel
=
ade
::
TypedGraph
<
cv
::
gimpl
::
Unit
<
cv
::
gimpl
::
OCL
Unit
,
cv
::
gimpl
::
Protocol
>
;
// FIXME: Same issue with Typed and ConstTyped
using
GConstGOCLModel
=
ade
::
ConstTypedGraph
<
cv
::
gimpl
::
Unit
<
cv
::
gimpl
::
OCL
Unit
,
cv
::
gimpl
::
Protocol
>
;
...
...
@@ -53,7 +53,7 @@ namespace
{
GOCLModel
gm
(
graph
);
auto
ocl_impl
=
cv
::
util
::
any_cast
<
cv
::
GOCLKernel
>
(
impl
.
opaque
);
gm
.
metadata
(
op_node
).
set
(
cv
::
gimpl
::
Unit
{
ocl_impl
});
gm
.
metadata
(
op_node
).
set
(
cv
::
gimpl
::
OCL
Unit
{
ocl_impl
});
}
virtual
EPtr
compile
(
const
ade
::
Graph
&
graph
,
...
...
@@ -198,7 +198,7 @@ void cv::gimpl::GOCLExecutable::run(std::vector<InObj> &&input_objs,
// Obtain our real execution unit
// TODO: Should kernels be copyable?
GOCLKernel
k
=
gcm
.
metadata
(
op_info
.
nh
).
get
<
Unit
>
().
k
;
GOCLKernel
k
=
gcm
.
metadata
(
op_info
.
nh
).
get
<
OCL
Unit
>
().
k
;
// Initialize kernel's execution context:
// - Input parameters
...
...
modules/gapi/src/backends/ocl/goclbackend.hpp
浏览文件 @
b083c20e
...
...
@@ -23,7 +23,7 @@
namespace
cv
{
namespace
gimpl
{
struct
Unit
struct
OCL
Unit
{
static
const
char
*
name
()
{
return
"OCLKernel"
;
}
GOCLKernel
k
;
...
...
modules/gapi/src/backends/render/grenderocvbackend.cpp
浏览文件 @
b083c20e
...
...
@@ -93,7 +93,7 @@ void cv::gimpl::render::ocv::GRenderExecutable::run(std::vector<InObj> &&input_
context
.
m_args
.
emplace_back
(
m_ftpr
.
get
());
k
.
apply
(
context
);
k
.
m_runF
(
context
);
for
(
auto
&
it
:
output_objs
)
magazine
::
writeBack
(
m_res
,
it
.
first
,
it
.
second
);
}
...
...
modules/gapi/src/compiler/gcompiled.cpp
浏览文件 @
b083c20e
...
...
@@ -72,6 +72,12 @@ void cv::GCompiled::Priv::reshape(const GMetaArgs& inMetas, const GCompileArgs&
m_metas
=
inMetas
;
}
void
cv
::
GCompiled
::
Priv
::
prepareForNewStream
()
{
GAPI_Assert
(
m_exec
);
m_exec
->
prepareForNewStream
();
}
const
cv
::
gimpl
::
GModel
::
Graph
&
cv
::
GCompiled
::
Priv
::
model
()
const
{
GAPI_Assert
(
nullptr
!=
m_exec
);
...
...
@@ -155,3 +161,8 @@ void cv::GCompiled::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
{
m_priv
->
reshape
(
inMetas
,
args
);
}
void
cv
::
GCompiled
::
prepareForNewStream
()
{
m_priv
->
prepareForNewStream
();
}
modules/gapi/src/compiler/gcompiled_priv.hpp
浏览文件 @
b083c20e
...
...
@@ -48,6 +48,7 @@ public:
bool
canReshape
()
const
;
void
reshape
(
const
GMetaArgs
&
inMetas
,
const
GCompileArgs
&
args
);
void
prepareForNewStream
();
void
run
(
cv
::
gimpl
::
GRuntimeArgs
&&
args
);
const
GMetaArgs
&
metas
()
const
;
...
...
modules/gapi/src/executor/gexecutor.cpp
浏览文件 @
b083c20e
...
...
@@ -265,3 +265,11 @@ void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs&
passes
::
inferMeta
(
ctx
,
true
);
m_ops
[
0
].
isl_exec
->
reshape
(
g
,
args
);
}
void
cv
::
gimpl
::
GExecutor
::
prepareForNewStream
()
{
for
(
auto
&
op
:
m_ops
)
{
op
.
isl_exec
->
handleNewStream
();
}
}
modules/gapi/src/executor/gexecutor.hpp
浏览文件 @
b083c20e
...
...
@@ -91,6 +91,8 @@ public:
bool
canReshape
()
const
;
void
reshape
(
const
GMetaArgs
&
inMetas
,
const
GCompileArgs
&
args
);
void
prepareForNewStream
();
const
GModel
::
Graph
&
model
()
const
;
// FIXME: make it ConstGraph?
};
...
...
modules/gapi/src/executor/gstreamingexecutor.cpp
浏览文件 @
b083c20e
...
...
@@ -800,6 +800,7 @@ void cv::gimpl::GStreamingExecutor::setSource(GRunArgs &&ins)
}
}
};
bool
islandsRecompiled
=
false
;
const
auto
new_meta
=
cv
::
descr_of
(
ins
);
// 0
if
(
gm
.
metadata
().
contains
<
OriginalInputMeta
>
())
// (1)
{
...
...
@@ -821,6 +822,8 @@ void cv::gimpl::GStreamingExecutor::setSource(GRunArgs &&ins)
}
update_int_metas
();
// (7)
m_reshapable
=
util
::
make_optional
(
is_reshapable
);
islandsRecompiled
=
true
;
}
else
// (8)
{
...
...
@@ -929,7 +932,15 @@ void cv::gimpl::GStreamingExecutor::setSource(GRunArgs &&ins)
for
(
auto
&&
out_eh
:
op
.
nh
->
outNodes
())
{
out_queues
.
push_back
(
reader_queues
(
*
m_island_graph
,
out_eh
));
}
op
.
isl_exec
->
handleNewStream
();
// If Island Executable is recompiled, all its stuff including internal kernel states
// are recreated and re-initialized automatically.
// But if not, we should notify Island Executable about new started stream to let it update
// its internal variables.
if
(
!
islandsRecompiled
)
{
op
.
isl_exec
->
handleNewStream
();
}
m_threads
.
emplace_back
(
islandActorThread
,
op
.
in_objects
,
...
...
modules/gapi/test/cpu/gapi_ocv_stateful_kernel_test_utils.hpp
0 → 100644
浏览文件 @
b083c20e
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
#ifndef OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
#define OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
#include "../test_precomp.hpp"
// TODO: Reuse Anatoliy's logic for support of types with commas in macro.
// Retrieve the common part from Anatoliy's logic to the separate place.
#define DEFINE_INITIALIZER(Name, StateType, ...) \
struct Name \
{ \
static StateType value() \
{ \
return __VA_ARGS__; \
} \
} \
namespace
opencv_test
{
namespace
{
struct
UserStruct
{
UserStruct
()
=
default
;
UserStruct
(
short
myShortVal
,
float
myFloatVal
)
:
_myShortVal
(
myShortVal
),
_myFloatVal
(
myFloatVal
)
{
}
bool
operator
==
(
const
UserStruct
&
rhs
)
const
{
return
((
_myShortVal
==
rhs
.
_myShortVal
)
&&
(
_myFloatVal
==
rhs
.
_myFloatVal
));
}
private:
short
_myShortVal
;
float
_myFloatVal
;
};
}
// anonymous namespace
}
// opencv_test
#endif // OPENCV_GAPI_OCV_STATEFUL_KERNEL_TESTS_UTILS_HPP
modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp
0 → 100644
浏览文件 @
b083c20e
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
#include "gapi_ocv_stateful_kernel_test_utils.hpp"
#include <opencv2/gapi/cpu/core.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
namespace
opencv_test
{
//TODO: test OT, Background Subtractor, Kalman with 3rd version of API
//----------------------------------------------- Simple tests ------------------------------------------------
namespace
{
inline
void
initTestDataPath
()
{
#ifndef WINRT
static
bool
initialized
=
false
;
if
(
!
initialized
)
{
// Since G-API has no own test data (yet), it is taken from the common space
const
char
*
testDataPath
=
getenv
(
"OPENCV_TEST_DATA_PATH"
);
GAPI_Assert
(
testDataPath
!=
nullptr
);
cvtest
::
addDataSearchPath
(
testDataPath
);
initialized
=
true
;
}
#endif // WINRT
}
G_TYPED_KERNEL
(
GCountCalls
,
<
cv
::
GOpaque
<
int
>
(
GMat
)
>
,
"org.opencv.test.count_calls"
)
{
static
GOpaqueDesc
outMeta
(
GMatDesc
/* in */
)
{
return
empty_gopaque_desc
();
}
};
GAPI_OCV_KERNEL_ST
(
GOCVCountCalls
,
GCountCalls
,
int
)
{
static
void
setup
(
const
cv
::
GMatDesc
&
/* in */
,
std
::
shared_ptr
<
int
>
&
state
)
{
state
.
reset
(
new
int
{
});
}
static
void
run
(
const
cv
::
Mat
&
/* in */
,
int
&
out
,
int
&
state
)
{
out
=
++
state
;
}
};
G_TYPED_KERNEL
(
GIsStateUpToDate
,
<
cv
::
GOpaque
<
bool
>
(
GMat
)
>
,
"org.opencv.test.is_state_up-to-date"
)
{
static
GOpaqueDesc
outMeta
(
GMatDesc
/* in */
)
{
return
empty_gopaque_desc
();
}
};
GAPI_OCV_KERNEL_ST
(
GOCVIsStateUpToDate
,
GIsStateUpToDate
,
cv
::
Size
)
{
static
void
setup
(
const
cv
::
GMatDesc
&
in
,
std
::
shared_ptr
<
cv
::
Size
>
&
state
)
{
state
.
reset
(
new
cv
::
Size
(
in
.
size
));
}
static
void
run
(
const
cv
::
Mat
&
in
,
bool
&
out
,
cv
::
Size
&
state
)
{
out
=
in
.
size
()
==
state
;
}
};
G_TYPED_KERNEL
(
GStInvalidResize
,
<
GMat
(
GMat
,
Size
,
double
,
double
,
int
)
>
,
"org.opencv.test.st_invalid_resize"
)
{
static
GMatDesc
outMeta
(
GMatDesc
in
,
Size
,
double
,
double
,
int
)
{
return
in
;
}
};
GAPI_OCV_KERNEL_ST
(
GOCVStInvalidResize
,
GStInvalidResize
,
int
)
{
static
void
setup
(
const
cv
::
GMatDesc
,
cv
::
Size
,
double
,
double
,
int
,
std
::
shared_ptr
<
int
>
&
/* state */
)
{
}
static
void
run
(
const
cv
::
Mat
&
in
,
cv
::
Size
sz
,
double
fx
,
double
fy
,
int
interp
,
cv
::
Mat
&
out
,
int
&
/* state */
)
{
cv
::
resize
(
in
,
out
,
sz
,
fx
,
fy
,
interp
);
}
};
};
TEST
(
StatefulKernel
,
StateIsMutableInRuntime
)
{
constexpr
int
expectedCallsCount
=
10
;
cv
::
Mat
dummyIn
{
1
,
1
,
CV_8UC1
};
int
actualCallsCount
=
0
;
// Declaration of G-API expression
GMat
in
;
GOpaque
<
int
>
out
=
GCountCalls
::
on
(
in
);
cv
::
GComputation
comp
(
cv
::
GIn
(
in
),
cv
::
GOut
(
out
));
const
auto
pkg
=
cv
::
gapi
::
kernels
<
GOCVCountCalls
>
();
// Compilation of G-API expression
auto
callsCounter
=
comp
.
compile
(
cv
::
descr_of
(
dummyIn
),
cv
::
compile_args
(
pkg
));
// Simulating video stream: call GCompiled multiple times
for
(
int
i
=
0
;
i
<
expectedCallsCount
;
i
++
)
{
callsCounter
(
cv
::
gin
(
dummyIn
),
cv
::
gout
(
actualCallsCount
));
EXPECT_EQ
(
i
+
1
,
actualCallsCount
);
}
// End of "video stream"
EXPECT_EQ
(
expectedCallsCount
,
actualCallsCount
);
// User asks G-API to prepare for a new stream
callsCounter
.
prepareForNewStream
();
callsCounter
(
cv
::
gin
(
dummyIn
),
cv
::
gout
(
actualCallsCount
));
EXPECT_EQ
(
1
,
actualCallsCount
);
}
TEST
(
StatefulKernel
,
StateIsAutoResetForNewStream
)
{
initTestDataPath
();
cv
::
GMat
in
;
GOpaque
<
bool
>
out
=
GIsStateUpToDate
::
on
(
in
);
cv
::
GComputation
c
(
cv
::
GIn
(
in
),
cv
::
GOut
(
out
));
const
auto
pkg
=
cv
::
gapi
::
kernels
<
GOCVIsStateUpToDate
>
();
// Compilation & testing
auto
ccomp
=
c
.
compileStreaming
(
cv
::
compile_args
(
pkg
));
ccomp
.
setSource
(
gapi
::
wip
::
make_src
<
cv
::
gapi
::
wip
::
GCaptureSource
>
(
findDataFile
(
"cv/video/768x576.avi"
)));
ccomp
.
start
();
EXPECT_TRUE
(
ccomp
.
running
());
// Process the full video
bool
isStateUpToDate
=
false
;
while
(
ccomp
.
pull
(
cv
::
gout
(
isStateUpToDate
)))
{
EXPECT_TRUE
(
isStateUpToDate
);
}
EXPECT_FALSE
(
ccomp
.
running
());
ccomp
.
setSource
(
gapi
::
wip
::
make_src
<
cv
::
gapi
::
wip
::
GCaptureSource
>
(
findDataFile
(
"cv/video/1920x1080.avi"
)));
ccomp
.
start
();
EXPECT_TRUE
(
ccomp
.
running
());
while
(
ccomp
.
pull
(
cv
::
gout
(
isStateUpToDate
)))
{
EXPECT_TRUE
(
isStateUpToDate
);
}
EXPECT_FALSE
(
ccomp
.
running
());
}
TEST
(
StatefulKernel
,
InvalidReallocatingKernel
)
{
cv
::
GMat
in
,
out
;
cv
::
Mat
in_mat
(
500
,
500
,
CV_8UC1
),
out_mat
;
out
=
GStInvalidResize
::
on
(
in
,
cv
::
Size
(
300
,
300
),
0.0
,
0.0
,
cv
::
INTER_LINEAR
);
const
auto
pkg
=
cv
::
gapi
::
kernels
<
GOCVStInvalidResize
>
();
cv
::
GComputation
comp
(
cv
::
GIn
(
in
),
cv
::
GOut
(
out
));
EXPECT_THROW
(
comp
.
apply
(
in_mat
,
out_mat
,
cv
::
compile_args
(
pkg
)),
std
::
logic_error
);
}
//-------------------------------------------------------------------------------------------------------------
//------------------------------------------- Typed tests on setup() ------------------------------------------
namespace
{
template
<
typename
Tuple
>
struct
SetupStateTypedTest
:
public
::
testing
::
Test
{
using
StateT
=
typename
std
::
tuple_element
<
0
,
Tuple
>::
type
;
using
SetupT
=
typename
std
::
tuple_element
<
1
,
Tuple
>::
type
;
G_TYPED_KERNEL
(
GReturnState
,
<
cv
::
GOpaque
<
StateT
>
(
GMat
)
>
,
"org.opencv.test.return_state"
)
{
static
GOpaqueDesc
outMeta
(
GMatDesc
/* in */
)
{
return
empty_gopaque_desc
();
}
};
GAPI_OCV_KERNEL_ST
(
GOCVReturnState
,
GReturnState
,
StateT
)
{
static
void
setup
(
const
cv
::
GMatDesc
&
/* in */
,
std
::
shared_ptr
<
StateT
>
&
state
)
{
// Don't use input cv::GMatDesc intentionally
state
.
reset
(
new
StateT
(
SetupT
::
value
()));
}
static
void
run
(
const
cv
::
Mat
&
/* in */
,
StateT
&
out
,
StateT
&
state
)
{
out
=
state
;
}
};
};
TYPED_TEST_CASE_P
(
SetupStateTypedTest
);
}
// namespace
TYPED_TEST_P
(
SetupStateTypedTest
,
ReturnInitializedState
)
{
using
StateType
=
typename
TestFixture
::
StateT
;
using
SetupType
=
typename
TestFixture
::
SetupT
;
cv
::
Mat
dummyIn
{
1
,
1
,
CV_8UC1
};
StateType
retState
{
};
GMat
in
;
auto
out
=
TestFixture
::
GReturnState
::
on
(
in
);
cv
::
GComputation
comp
(
cv
::
GIn
(
in
),
cv
::
GOut
(
out
));
const
auto
pkg
=
cv
::
gapi
::
kernels
<
typename
TestFixture
::
GOCVReturnState
>
();
comp
.
apply
(
cv
::
gin
(
dummyIn
),
cv
::
gout
(
retState
),
cv
::
compile_args
(
pkg
));
EXPECT_EQ
(
SetupType
::
value
(),
retState
);
}
REGISTER_TYPED_TEST_CASE_P
(
SetupStateTypedTest
,
ReturnInitializedState
);
DEFINE_INITIALIZER
(
CharValue
,
char
,
'z'
);
DEFINE_INITIALIZER
(
IntValue
,
int
,
7
);
DEFINE_INITIALIZER
(
FloatValue
,
float
,
42.
f
);
DEFINE_INITIALIZER
(
UcharPtrValue
,
uchar
*
,
nullptr
);
namespace
{
using
Std3IntArray
=
std
::
array
<
int
,
3
>
;
}
DEFINE_INITIALIZER
(
StdArrayValue
,
Std3IntArray
,
{
1
,
2
,
3
});
DEFINE_INITIALIZER
(
UserValue
,
UserStruct
,
{
5
,
7.
f
});
using
TypesToVerify
=
::
testing
::
Types
<
std
::
tuple
<
char
,
CharValue
>
,
std
::
tuple
<
int
,
IntValue
>
,
std
::
tuple
<
float
,
FloatValue
>
,
std
::
tuple
<
uchar
*
,
UcharPtrValue
>
,
std
::
tuple
<
std
::
array
<
int
,
3
>
,
StdArrayValue
>
,
std
::
tuple
<
UserStruct
,
UserValue
>>
;
INSTANTIATE_TYPED_TEST_CASE_P
(
SetupStateTypedInst
,
SetupStateTypedTest
,
TypesToVerify
);
//-------------------------------------------------------------------------------------------------------------
}
// opencv_test
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录