Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
06067efa
Commit
06067efa
authored
Sep 10, 2019
by
Talamanov, Anatoliy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new render primitives
parent
bc927f97
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
535 additions
and
233 deletions
+535
-233
CMakeLists.txt
modules/gapi/CMakeLists.txt
+1
-0
render.hpp
modules/gapi/include/opencv2/gapi/render.hpp
+69
-4
render.cpp
modules/gapi/src/api/render.cpp
+34
-75
render_ocv.cpp
modules/gapi/src/api/render_ocv.cpp
+226
-0
render_ocv.hpp
modules/gapi/src/api/render_ocv.hpp
+25
-0
render_priv.hpp
modules/gapi/src/api/render_priv.hpp
+1
-1
gapi_render_tests.hpp
modules/gapi/test/common/gapi_render_tests.hpp
+81
-39
gapi_render_tests_inl.hpp
modules/gapi/test/common/gapi_render_tests_inl.hpp
+14
-66
gapi_render_tests_cpu.cpp
modules/gapi/test/cpu/gapi_render_tests_cpu.cpp
+84
-48
No files found.
modules/gapi/CMakeLists.txt
View file @
06067efa
...
...
@@ -48,6 +48,7 @@ set(gapi_srcs
src/api/kernels_core.cpp
src/api/kernels_imgproc.cpp
src/api/render.cpp
src/api/render_ocv.cpp
src/api/ginfer.cpp
# Compiler part
...
...
modules/gapi/include/opencv2/gapi/render.hpp
View file @
06067efa
...
...
@@ -2,7 +2,7 @@
// 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) 2018 Intel Corporation
// Copyright (C) 2018
-2019
Intel Corporation
#ifndef OPENCV_GAPI_RENDER_HPP
...
...
@@ -11,6 +11,8 @@
#include <string>
#include <vector>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/opencv_includes.hpp>
#include <opencv2/gapi/util/variant.hpp>
#include <opencv2/gapi/own/exports.hpp>
...
...
@@ -20,6 +22,9 @@ namespace cv
{
namespace
gapi
{
namespace
ocv
{
GAPI_EXPORTS
cv
::
gapi
::
GKernelPackage
kernels
();
}
namespace
wip
{
namespace
draw
...
...
@@ -78,7 +83,38 @@ struct Line
int
thick
;
//!< The thickness of line
int
lt
;
//!< The Type of the line. See #LineTypes
int
shift
;
//!< The number of fractional bits in the point coordinates
};
/**
* A structure to represent parameters for drawing a mosaic
*/
struct
Mosaic
{
cv
::
Rect
mos
;
//!< Coordinates of the mosaic
int
cellSz
;
//!< Cell size (same for X, Y). Note: mos size must be multiple of cell size
int
decim
;
//!< Decimation (0 stands for no decimation)
};
/**
* A structure to represent parameters for drawing an image
*/
struct
Image
{
cv
::
Point
org
;
//!< The bottom-left corner of the image
cv
::
Mat
img
;
//!< Image to draw
cv
::
Mat
alpha
;
//!< Alpha channel for image to draw (same size and number of channels)
};
/**
* A structure to represent parameters for drawing a polygon
*/
struct
Poly
{
std
::
vector
<
cv
::
Point
>
points
;
//!< Points to connect
cv
::
Scalar
color
;
//!< The line color
int
thick
;
//!< The thickness of line
int
lt
;
//!< The Type of the line. See #LineTypes
int
shift
;
//!< The number of fractional bits in the point coordinates
};
using
Prim
=
util
::
variant
...
...
@@ -86,27 +122,56 @@ using Prim = util::variant
,
Rect
,
Circle
,
Line
,
Mosaic
,
Image
,
Poly
>
;
using
Prims
=
std
::
vector
<
Prim
>
;
using
Prims
=
std
::
vector
<
Prim
>
;
using
GMat2
=
std
::
tuple
<
cv
::
GMat
,
cv
::
GMat
>
;
using
GMatDesc2
=
std
::
tuple
<
cv
::
GMatDesc
,
cv
::
GMatDesc
>
;
G_TYPED_KERNEL_M
(
GRenderNV12
,
<
GMat2
(
cv
::
GMat
,
cv
::
GMat
,
cv
::
GArray
<
wip
::
draw
::
Prim
>
)
>
,
"org.opencv.render.nv12"
)
{
static
GMatDesc2
outMeta
(
GMatDesc
y_plane
,
GMatDesc
uv_plane
,
GArrayDesc
)
{
return
std
::
make_tuple
(
y_plane
,
uv_plane
);
}
};
G_TYPED_KERNEL
(
GRenderBGR
,
<
cv
::
GMat
(
cv
::
GMat
,
cv
::
GArray
<
wip
::
draw
::
Prim
>
)
>
,
"org.opencv.render.bgr"
)
{
static
GMatDesc
outMeta
(
GMatDesc
bgr
,
GArrayDesc
)
{
return
bgr
;
}
};
/** @brief The function renders on the input image passed drawing primitivies
@param bgr input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
@param prims vector of drawing primitivies
@param pkg contains render kernel implementation
*/
GAPI_EXPORTS
void
render
(
cv
::
Mat
&
bgr
,
const
Prims
&
prims
);
GAPI_EXPORTS
void
render
(
cv
::
Mat
&
bgr
,
const
Prims
&
prims
,
const
cv
::
gapi
::
GKernelPackage
&
pkg
=
ocv
::
kernels
());
/** @brief The function renders on two NV12 planes passed drawing primitivies
@param y_plane input image: 8-bit unsigned 1-channel image @ref CV_8UC1.
@param uv_plane input image: 8-bit unsigned 2-channel image @ref CV_8UC2.
@param prims vector of drawing primitivies
@param pkg contains render kernel implementation
*/
GAPI_EXPORTS
void
render
(
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
,
const
Prims
&
prims
);
GAPI_EXPORTS
void
render
(
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
,
const
Prims
&
prims
,
const
cv
::
gapi
::
GKernelPackage
&
pkg
=
ocv
::
kernels
());
}
// namespace draw
}
// namespace wip
}
// namespace gapi
}
// namespace cv
...
...
modules/gapi/src/api/render.cpp
View file @
06067efa
#include <opencv2/imgproc.hpp>
#include "opencv2/gapi/render.hpp"
#include "opencv2/gapi/own/assert.hpp"
#include <opencv2/gapi/render.hpp>
#include <opencv2/gapi/own/assert.hpp>
#include "api/render_priv.hpp"
using
namespace
cv
::
gapi
::
wip
::
draw
;
// FXIME util::visitor ?
void
cv
::
gapi
::
wip
::
draw
::
render
(
cv
::
Mat
&
bgr
,
const
Prims
&
prims
)
void
cv
::
gapi
::
wip
::
draw
::
render
(
cv
::
Mat
&
bgr
,
const
cv
::
gapi
::
wip
::
draw
::
Prims
&
prims
,
const
cv
::
gapi
::
GKernelPackage
&
pkg
)
{
for
(
const
auto
&
p
:
prims
)
{
switch
(
p
.
index
())
{
case
Prim
:
:
index_of
<
Rect
>
()
:
{
const
auto
&
t_p
=
cv
::
util
::
get
<
Rect
>
(
p
);
cv
::
rectangle
(
bgr
,
t_p
.
rect
,
t_p
.
color
,
t_p
.
thick
,
t_p
.
lt
,
t_p
.
shift
);
break
;
}
case
Prim
:
:
index_of
<
Text
>
()
:
{
const
auto
&
t_p
=
cv
::
util
::
get
<
Text
>
(
p
);
cv
::
putText
(
bgr
,
t_p
.
text
,
t_p
.
org
,
t_p
.
ff
,
t_p
.
fs
,
t_p
.
color
,
t_p
.
thick
,
t_p
.
lt
,
t_p
.
bottom_left_origin
);
break
;
}
case
Prim
:
:
index_of
<
Circle
>
()
:
{
const
auto
&
c_p
=
cv
::
util
::
get
<
Circle
>
(
p
);
cv
::
circle
(
bgr
,
c_p
.
center
,
c_p
.
radius
,
c_p
.
color
,
c_p
.
thick
,
c_p
.
lt
,
c_p
.
shift
);
break
;
}
cv
::
GMat
in
;
cv
::
GArray
<
Prim
>
arr
;
case
Prim
:
:
index_of
<
Line
>
()
:
{
const
auto
&
l_p
=
cv
::
util
::
get
<
Line
>
(
p
);
cv
::
line
(
bgr
,
l_p
.
pt1
,
l_p
.
pt2
,
l_p
.
color
,
l_p
.
thick
,
l_p
.
lt
,
l_p
.
shift
);
break
;
}
default
:
util
::
throw_error
(
std
::
logic_error
(
"Unsupported draw operation"
));
}
}
cv
::
GComputation
comp
(
cv
::
GIn
(
in
,
arr
),
cv
::
GOut
(
cv
::
gapi
::
wip
::
draw
::
GRenderBGR
::
on
(
in
,
arr
)));
comp
.
apply
(
cv
::
gin
(
bgr
,
prims
),
cv
::
gout
(
bgr
),
cv
::
compile_args
(
pkg
));
}
void
cv
::
gapi
::
wip
::
draw
::
render
(
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
,
const
Prims
&
prims
)
void
cv
::
gapi
::
wip
::
draw
::
render
(
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
,
const
Prims
&
prims
,
const
GKernelPackage
&
pkg
)
{
cv
::
Mat
bgr
;
cv
::
cvtColorTwoPlane
(
y_plane
,
uv_plane
,
bgr
,
cv
::
COLOR_YUV2BGR_NV12
);
render
(
bgr
,
prims
);
BGR2NV12
(
bgr
,
y_plane
,
uv_plane
);
}
void
cv
::
gapi
::
wip
::
draw
::
splitNV12TwoPlane
(
const
cv
::
Mat
&
yuv
,
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
)
{
y_plane
.
create
(
yuv
.
size
(),
CV_8UC1
);
uv_plane
.
create
(
yuv
.
size
()
/
2
,
CV_8UC2
);
// Fill Y plane
for
(
int
i
=
0
;
i
<
yuv
.
rows
;
++
i
)
{
const
uchar
*
in
=
yuv
.
ptr
<
uchar
>
(
i
);
uchar
*
out
=
y_plane
.
ptr
<
uchar
>
(
i
);
for
(
int
j
=
0
;
j
<
yuv
.
cols
;
j
++
)
{
out
[
j
]
=
in
[
3
*
j
];
}
}
// Fill UV plane
for
(
int
i
=
0
;
i
<
uv_plane
.
rows
;
i
++
)
{
const
uchar
*
in
=
yuv
.
ptr
<
uchar
>
(
2
*
i
);
uchar
*
out
=
uv_plane
.
ptr
<
uchar
>
(
i
);
for
(
int
j
=
0
;
j
<
uv_plane
.
cols
;
j
++
)
{
out
[
j
*
2
]
=
in
[
6
*
j
+
1
];
out
[
j
*
2
+
1
]
=
in
[
6
*
j
+
2
];
}
}
cv
::
GMat
y_in
,
uv_in
,
y_out
,
uv_out
;
cv
::
GArray
<
Prim
>
arr
;
std
::
tie
(
y_out
,
uv_out
)
=
cv
::
gapi
::
wip
::
draw
::
GRenderNV12
::
on
(
y_in
,
uv_in
,
arr
);
cv
::
GComputation
comp
(
cv
::
GIn
(
y_in
,
uv_in
,
arr
),
cv
::
GOut
(
y_out
,
uv_out
));
comp
.
apply
(
cv
::
gin
(
y_plane
,
uv_plane
,
prims
),
cv
::
gout
(
y_plane
,
uv_plane
),
cv
::
compile_args
(
pkg
));
}
void
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
const
cv
::
Mat
&
bgr
,
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
)
void
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
const
cv
::
Mat
&
bgr
,
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
)
{
GAPI_Assert
(
bgr
.
size
().
width
%
2
==
0
);
GAPI_Assert
(
bgr
.
size
().
height
%
2
==
0
);
cvtColor
(
bgr
,
bgr
,
cv
::
COLOR_BGR2YUV
);
splitNV12TwoPlane
(
bgr
,
y_plane
,
uv_plane
);
cv
::
Mat
yuv
;
cvtColor
(
bgr
,
yuv
,
cv
::
COLOR_BGR2YUV
);
std
::
vector
<
cv
::
Mat
>
chs
(
3
);
cv
::
split
(
yuv
,
chs
);
y_plane
=
chs
[
0
];
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
{
chs
[
1
],
chs
[
2
]},
uv_plane
);
cv
::
resize
(
uv_plane
,
uv_plane
,
uv_plane
.
size
()
/
2
,
cv
::
INTER_LINEAR
);
}
modules/gapi/src/api/render_ocv.cpp
0 → 100644
View file @
06067efa
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/gapi/render.hpp> // Kernel API's
#include "api/render_ocv.hpp"
namespace
cv
{
namespace
gapi
{
namespace
ocv
{
GAPI_OCV_KERNEL
(
GOCVRenderNV12
,
cv
::
gapi
::
wip
::
draw
::
GRenderNV12
)
{
static
void
run
(
const
cv
::
Mat
&
y
,
const
cv
::
Mat
&
uv
,
const
cv
::
gapi
::
wip
::
draw
::
Prims
&
prims
,
cv
::
Mat
&
out_y
,
cv
::
Mat
&
out_uv
)
{
/* FIXME How to render correctly on NV12 format ?
*
* Rendering on NV12 via OpenCV looks like this:
*
* y --------> 1)(NV12 -> YUV) -> yuv -> 2)draw -> yuv -> 3)split -------> out_y
* ^ |
* | |
* uv -------------- `----------> out_uv
*
*
* 1) Collect yuv mat from two planes, uv plain in two times less than y plane
* so, upsample uv in tow times, with bilinear interpolation
*
* 2) Render primitives on YUV
*
* 3) Convert yuv to NV12 (using bilinear interpolation)
*
*/
// NV12 -> YUV
cv
::
Mat
upsample_uv
,
yuv
;
cv
::
resize
(
uv
,
upsample_uv
,
uv
.
size
()
*
2
,
cv
::
INTER_LINEAR
);
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
{
y
,
upsample_uv
},
yuv
);
cv
::
gapi
::
wip
::
draw
::
drawPrimitivesOCVYUV
(
yuv
,
prims
);
// YUV -> NV12
cv
::
Mat
out_u
,
out_v
,
uv_plane
;
std
::
vector
<
cv
::
Mat
>
chs
=
{
out_y
,
out_u
,
out_v
};
cv
::
split
(
yuv
,
chs
);
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
{
chs
[
1
],
chs
[
2
]},
uv_plane
);
cv
::
resize
(
uv_plane
,
out_uv
,
uv_plane
.
size
()
/
2
,
cv
::
INTER_LINEAR
);
}
};
GAPI_OCV_KERNEL
(
GOCVRenderBGR
,
cv
::
gapi
::
wip
::
draw
::
GRenderBGR
)
{
static
void
run
(
const
cv
::
Mat
&
,
const
cv
::
gapi
::
wip
::
draw
::
Prims
&
prims
,
cv
::
Mat
&
out
)
{
cv
::
gapi
::
wip
::
draw
::
drawPrimitivesOCVBGR
(
out
,
prims
);
}
};
cv
::
gapi
::
GKernelPackage
kernels
()
{
static
const
auto
pkg
=
cv
::
gapi
::
kernels
<
GOCVRenderNV12
,
GOCVRenderBGR
>
();
return
pkg
;
}
}
// namespace ocv
namespace
wip
{
namespace
draw
{
void
mosaic
(
cv
::
Mat
mat
,
const
cv
::
Rect
&
rect
,
int
cellSz
);
void
image
(
cv
::
Mat
mat
,
cv
::
Point
org
,
cv
::
Mat
img
,
cv
::
Mat
alpha
);
void
poly
(
cv
::
Mat
mat
,
std
::
vector
<
cv
::
Point
>
,
cv
::
Scalar
color
,
int
lt
,
int
shift
);
void
mosaic
(
cv
::
Mat
mat
,
const
cv
::
Rect
&
rect
,
int
cellSz
)
{
cv
::
Mat
msc_roi
=
mat
(
rect
);
int
crop_x
=
msc_roi
.
cols
-
msc_roi
.
cols
%
cellSz
;
int
crop_y
=
msc_roi
.
rows
-
msc_roi
.
rows
%
cellSz
;
for
(
int
i
=
0
;
i
<
crop_y
;
i
+=
cellSz
)
for
(
int
j
=
0
;
j
<
crop_x
;
j
+=
cellSz
)
{
auto
cell_roi
=
msc_roi
(
cv
::
Rect
(
j
,
i
,
cellSz
,
cellSz
));
cell_roi
=
cv
::
mean
(
cell_roi
);
}
};
void
image
(
cv
::
Mat
mat
,
cv
::
Point
org
,
cv
::
Mat
img
,
cv
::
Mat
alpha
)
{
auto
roi
=
mat
(
cv
::
Rect
(
org
.
x
,
org
.
y
,
img
.
size
().
width
,
img
.
size
().
height
));
cv
::
Mat
img32f_w
;
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
(
3
,
alpha
),
img32f_w
);
cv
::
Mat
roi32f_w
(
roi
.
size
(),
CV_32FC3
,
cv
::
Scalar
::
all
(
1.0
));
roi32f_w
-=
img32f_w
;
cv
::
Mat
img32f
,
roi32f
;
img
.
convertTo
(
img32f
,
CV_32F
,
1.0
/
255
);
roi
.
convertTo
(
roi32f
,
CV_32F
,
1.0
/
255
);
cv
::
multiply
(
img32f
,
img32f_w
,
img32f
);
cv
::
multiply
(
roi32f
,
roi32f_w
,
roi32f
);
roi32f
+=
img32f
;
roi32f
.
convertTo
(
roi
,
CV_8U
,
255.0
);
};
void
poly
(
cv
::
Mat
mat
,
std
::
vector
<
cv
::
Point
>
points
,
cv
::
Scalar
color
,
int
lt
,
int
shift
)
{
std
::
vector
<
std
::
vector
<
cv
::
Point
>>
pp
{
points
};
cv
::
fillPoly
(
mat
,
pp
,
color
,
lt
,
shift
);
};
struct
BGR2YUVConverter
{
cv
::
Scalar
cvtColor
(
const
cv
::
Scalar
&
bgr
)
const
{
double
y
=
bgr
[
2
]
*
0.299000
+
bgr
[
1
]
*
0.587000
+
bgr
[
0
]
*
0.114000
;
double
u
=
bgr
[
2
]
*
-
0.168736
+
bgr
[
1
]
*
-
0.331264
+
bgr
[
0
]
*
0.500000
+
128
;
double
v
=
bgr
[
2
]
*
0.500000
+
bgr
[
1
]
*
-
0.418688
+
bgr
[
0
]
*
-
0.081312
+
128
;
return
{
y
,
u
,
v
};
}
void
cvtImg
(
const
cv
::
Mat
&
in
,
cv
::
Mat
&
out
)
{
cv
::
cvtColor
(
in
,
out
,
cv
::
COLOR_BGR2YUV
);
};
};
struct
EmptyConverter
{
cv
::
Scalar
cvtColor
(
const
cv
::
Scalar
&
bgr
)
const
{
return
bgr
;
};
void
cvtImg
(
const
cv
::
Mat
&
in
,
cv
::
Mat
&
out
)
const
{
out
=
in
;
};
};
// FIXME util::visitor ?
template
<
typename
ColorConverter
>
void
drawPrimitivesOCV
(
cv
::
Mat
&
in
,
const
Prims
&
prims
)
{
ColorConverter
converter
;
for
(
const
auto
&
p
:
prims
)
{
switch
(
p
.
index
())
{
case
Prim
:
:
index_of
<
Rect
>
()
:
{
const
auto
&
t_p
=
cv
::
util
::
get
<
Rect
>
(
p
);
const
auto
color
=
converter
.
cvtColor
(
t_p
.
color
);
cv
::
rectangle
(
in
,
t_p
.
rect
,
color
,
t_p
.
thick
,
t_p
.
lt
,
t_p
.
shift
);
break
;
}
case
Prim
:
:
index_of
<
Text
>
()
:
{
const
auto
&
t_p
=
cv
::
util
::
get
<
Text
>
(
p
);
const
auto
color
=
converter
.
cvtColor
(
t_p
.
color
);
cv
::
putText
(
in
,
t_p
.
text
,
t_p
.
org
,
t_p
.
ff
,
t_p
.
fs
,
color
,
t_p
.
thick
,
t_p
.
lt
,
t_p
.
bottom_left_origin
);
break
;
}
case
Prim
:
:
index_of
<
Circle
>
()
:
{
const
auto
&
c_p
=
cv
::
util
::
get
<
Circle
>
(
p
);
const
auto
color
=
converter
.
cvtColor
(
c_p
.
color
);
cv
::
circle
(
in
,
c_p
.
center
,
c_p
.
radius
,
color
,
c_p
.
thick
,
c_p
.
lt
,
c_p
.
shift
);
break
;
}
case
Prim
:
:
index_of
<
Line
>
()
:
{
const
auto
&
l_p
=
cv
::
util
::
get
<
Line
>
(
p
);
const
auto
color
=
converter
.
cvtColor
(
l_p
.
color
);
cv
::
line
(
in
,
l_p
.
pt1
,
l_p
.
pt2
,
color
,
l_p
.
thick
,
l_p
.
lt
,
l_p
.
shift
);
break
;
}
case
Prim
:
:
index_of
<
Mosaic
>
()
:
{
const
auto
&
l_p
=
cv
::
util
::
get
<
Mosaic
>
(
p
);
mosaic
(
in
,
l_p
.
mos
,
l_p
.
cellSz
);
break
;
}
case
Prim
:
:
index_of
<
Image
>
()
:
{
const
auto
&
i_p
=
cv
::
util
::
get
<
Image
>
(
p
);
cv
::
Mat
img
;
converter
.
cvtImg
(
i_p
.
img
,
img
);
image
(
in
,
i_p
.
org
,
img
,
i_p
.
alpha
);
break
;
}
case
Prim
:
:
index_of
<
Poly
>
()
:
{
const
auto
&
p_p
=
cv
::
util
::
get
<
Poly
>
(
p
);
const
auto
color
=
converter
.
cvtColor
(
p_p
.
color
);
poly
(
in
,
p_p
.
points
,
color
,
p_p
.
lt
,
p_p
.
shift
);
break
;
}
default
:
cv
::
util
::
throw_error
(
std
::
logic_error
(
"Unsupported draw operation"
));
}
}
}
void
drawPrimitivesOCVBGR
(
cv
::
Mat
&
in
,
const
Prims
&
prims
)
{
drawPrimitivesOCV
<
EmptyConverter
>
(
in
,
prims
);
}
void
drawPrimitivesOCVYUV
(
cv
::
Mat
&
in
,
const
Prims
&
prims
)
{
drawPrimitivesOCV
<
BGR2YUVConverter
>
(
in
,
prims
);
}
}
// namespace draw
}
// namespace wip
}
// namespace gapi
}
// namespace cv
modules/gapi/src/api/render_ocv.hpp
0 → 100644
View file @
06067efa
#include <vector>
#include "render_priv.hpp"
#ifndef OPENCV_RENDER_OCV_HPP
#define OPENCV_RENDER_OCV_HPP
namespace
cv
{
namespace
gapi
{
namespace
wip
{
namespace
draw
{
// FIXME only for tests
void
GAPI_EXPORTS
drawPrimitivesOCVYUV
(
cv
::
Mat
&
yuv
,
const
Prims
&
prims
);
void
GAPI_EXPORTS
drawPrimitivesOCVBGR
(
cv
::
Mat
&
bgr
,
const
Prims
&
prims
);
}
// namespace draw
}
// namespace wip
}
// namespace gapi
}
// namespace cv
#endif // OPENCV_RENDER_OCV_HPP
modules/gapi/src/api/render_priv.hpp
View file @
06067efa
...
...
@@ -18,9 +18,9 @@ namespace wip
{
namespace
draw
{
// FIXME only for tests
GAPI_EXPORTS
void
BGR2NV12
(
const
cv
::
Mat
&
bgr
,
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
);
void
splitNV12TwoPlane
(
const
cv
::
Mat
&
yuv
,
cv
::
Mat
&
y_plane
,
cv
::
Mat
&
uv_plane
);
}
// namespace draw
}
// namespace wip
...
...
modules/gapi/test/common/gapi_render_tests.hpp
View file @
06067efa
...
...
@@ -10,14 +10,53 @@
#include "gapi_tests_common.hpp"
#include "api/render_priv.hpp"
#include "api/render_ocv.hpp"
#define rect1 Prim{cv::gapi::wip::draw::Rect{cv::Rect{101, 101, 199, 199}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define rect2 Prim{cv::gapi::wip::draw::Rect{cv::Rect{100, 100, 199, 199}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define rect3 Prim{cv::gapi::wip::draw::Rect{cv::Rect{0 , 0 , 199, 199}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define rect4 Prim{cv::gapi::wip::draw::Rect{cv::Rect{100, 100, 0, 199 }, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define rect5 Prim{cv::gapi::wip::draw::Rect{cv::Rect{0 , -1 , 199, 199}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define rect6 Prim{cv::gapi::wip::draw::Rect{cv::Rect{100, 100, 199, 199}, cv::Scalar{153, 172, 58}, 10, LINE_8, 0}}
#define rect7 Prim{cv::gapi::wip::draw::Rect{cv::Rect{100, 100, 200, 200}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define box1 Prim{cv::gapi::wip::draw::Rect{cv::Rect{101, 101, 200, 200}, cv::Scalar{153, 172, 58}, -1, LINE_8, 0}}
#define box2 Prim{cv::gapi::wip::draw::Rect{cv::Rect{100, 100, 199, 199}, cv::Scalar{153, 172, 58}, -1, LINE_8, 0}}
#define rects Prims{rect1, rect2, rect3, rect4, rect5, rect6, rect7, box1, box2}
#define circle1 Prim{cv::gapi::wip::draw::Circle{cv::Point{200, 200}, 100, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define circle2 Prim{cv::gapi::wip::draw::Circle{cv::Point{10, 30} , 2 , cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define circle3 Prim{cv::gapi::wip::draw::Circle{cv::Point{75, 100} , 50 , cv::Scalar{153, 172, 58}, 5, LINE_8, 0}}
#define circles Prims{circle1, circle2, circle3}
#define line1 Prim{cv::gapi::wip::draw::Line{cv::Point{50, 50}, cv::Point{250, 200}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define line2 Prim{cv::gapi::wip::draw::Line{cv::Point{51, 51}, cv::Point{51, 100}, cv::Scalar{153, 172, 58}, 1, LINE_8, 0}}
#define lines Prims{line1, line2}
#define mosaic1 Prim{cv::gapi::wip::draw::Mosaic{cv::Rect{100, 100, 200, 200}, 5, 0}}
#define mosaics Prims{mosaic1}
#define image1 Prim{cv::gapi::wip::draw::Image{cv::Point(100, 100), cv::Mat(cv::Size(200, 200), CV_8UC3, cv::Scalar::all(255)),\
cv::Mat(cv::Size(200, 200), CV_32FC1, cv::Scalar::all(1))}}
#define image2 Prim{cv::gapi::wip::draw::Image{cv::Point(100, 100), cv::Mat(cv::Size(200, 200), CV_8UC3, cv::Scalar::all(255)),\
cv::Mat(cv::Size(200, 200), CV_32FC1, cv::Scalar::all(0.5))}}
#define image3 Prim{cv::gapi::wip::draw::Image{cv::Point(100, 100), cv::Mat(cv::Size(200, 200), CV_8UC3, cv::Scalar::all(255)),\
cv::Mat(cv::Size(200, 200), CV_32FC1, cv::Scalar::all(0.0))}}
#define images Prims{image1, image2, image3}
#define polygon1 Prim{cv::gapi::wip::draw::Poly{ {cv::Point{100, 100}, cv::Point{50, 200}, cv::Point{200, 30}, cv::Point{150, 50} }, cv::Scalar{153, 172, 58}, 1, LINE_8, 0} }
#define polygons Prims{polygon1}
#define text1 Prim{cv::gapi::wip::draw::Text{"TheBrownFoxJump", cv::Point{100, 100}, FONT_HERSHEY_SIMPLEX, 2, cv::Scalar{102, 178, 240}, 1, LINE_8, false} }
#define texts Prims{text1}
namespace
opencv_test
{
using
Points
=
std
::
vector
<
cv
::
Point
>
;
using
Rects
=
std
::
vector
<
cv
::
Rect
>
;
using
PairOfPoints
=
std
::
pair
<
cv
::
Point
,
cv
::
Point
>
;
using
VecOfPairOfPoints
=
std
::
vector
<
PairOfPoints
>
;
using
Prims
=
cv
::
gapi
::
wip
::
draw
::
Prims
;
using
Prim
=
cv
::
gapi
::
wip
::
draw
::
Prim
;
template
<
class
T
>
class
RenderWithParam
:
public
TestWithParam
<
T
>
...
...
@@ -26,47 +65,50 @@ protected:
void
Init
()
{
MatType
type
=
CV_8UC3
;
out_mat_ocv
=
cv
::
Mat
(
sz
,
type
,
cv
::
Scalar
(
255
));
out_mat_gapi
=
cv
::
Mat
(
sz
,
type
,
cv
::
Scalar
(
255
));
if
(
isNV12Format
)
{
/* NB: When converting data from BGR to NV12, data loss occurs,
* so the reference data is subjected to the same transformation
* for correct comparison of the test results */
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
out_mat_ocv
,
y
,
uv
);
cv
::
cvtColorTwoPlane
(
y
,
uv
,
out_mat_ocv
,
cv
::
COLOR_YUV2BGR_NV12
);
}
}
void
Run
()
{
if
(
isNV12Format
)
{
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
out_mat_gapi
,
y
,
uv
);
cv
::
gapi
::
wip
::
draw
::
render
(
y
,
uv
,
prims
);
cv
::
cvtColorTwoPlane
(
y
,
uv
,
out_mat_gapi
,
cv
::
COLOR_YUV2BGR_NV12
);
// NB: Also due to data loss
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
out_mat_ocv
,
y
,
uv
);
cv
::
cvtColorTwoPlane
(
y
,
uv
,
out_mat_ocv
,
cv
::
COLOR_YUV2BGR_NV12
);
}
else
{
cv
::
gapi
::
wip
::
draw
::
render
(
out_mat_gapi
,
prims
);
}
mat_ocv
.
create
(
sz
,
type
);
mat_gapi
.
create
(
sz
,
type
);
cv
::
randu
(
mat_ocv
,
cv
::
Scalar
::
all
(
0
),
cv
::
Scalar
::
all
(
255
));
mat_ocv
.
copyTo
(
mat_gapi
);
}
cv
::
Size
sz
;
cv
::
Scalar
color
;
int
thick
;
int
lt
;
bool
isNV12Format
;
std
::
vector
<
cv
::
gapi
::
wip
::
draw
::
Prim
>
prims
;
cv
::
Mat
y
,
uv
;
cv
::
Mat
out_mat_ocv
,
out_mat_gapi
;
cv
::
gapi
::
GKernelPackage
pkg
;
cv
::
Mat
y_mat_ocv
,
uv_mat_ocv
,
y_mat_gapi
,
uv_mat_gapi
,
mat_ocv
,
mat_gapi
;
};
struct
RenderTextTest
:
public
RenderWithParam
<
std
::
tuple
<
cv
::
Size
,
std
::
string
,
Points
,
int
,
double
,
cv
::
Scalar
,
int
,
int
,
bool
,
bool
>>
{};
struct
RenderRectTest
:
public
RenderWithParam
<
std
::
tuple
<
cv
::
Size
,
Rects
,
cv
::
Scalar
,
int
,
int
,
int
,
bool
>>
{};
struct
RenderCircleTest
:
public
RenderWithParam
<
std
::
tuple
<
cv
::
Size
,
Points
,
int
,
cv
::
Scalar
,
int
,
int
,
int
,
bool
>>
{};
struct
RenderLineTest
:
public
RenderWithParam
<
std
::
tuple
<
cv
::
Size
,
VecOfPairOfPoints
,
cv
::
Scalar
,
int
,
int
,
int
,
bool
>>
{};
using
TestArgs
=
std
::
tuple
<
cv
::
Size
,
cv
::
gapi
::
wip
::
draw
::
Prims
,
cv
::
gapi
::
GKernelPackage
>
;
struct
RenderNV12
:
public
RenderWithParam
<
TestArgs
>
{
void
ComputeRef
()
{
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
mat_ocv
,
y_mat_ocv
,
uv_mat_ocv
);
// NV12 -> YUV
cv
::
Mat
upsample_uv
,
yuv
;
cv
::
resize
(
uv_mat_ocv
,
upsample_uv
,
uv_mat_ocv
.
size
()
*
2
,
cv
::
INTER_LINEAR
);
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
{
y_mat_ocv
,
upsample_uv
},
yuv
);
cv
::
gapi
::
wip
::
draw
::
drawPrimitivesOCVYUV
(
yuv
,
prims
);
// YUV -> NV12
std
::
vector
<
cv
::
Mat
>
chs
(
3
);
cv
::
split
(
yuv
,
chs
);
cv
::
merge
(
std
::
vector
<
cv
::
Mat
>
{
chs
[
1
],
chs
[
2
]},
uv_mat_ocv
);
y_mat_ocv
=
chs
[
0
];
cv
::
resize
(
uv_mat_ocv
,
uv_mat_ocv
,
uv_mat_ocv
.
size
()
/
2
,
cv
::
INTER_LINEAR
);
}
};
struct
RenderBGR
:
public
RenderWithParam
<
TestArgs
>
{
void
ComputeRef
()
{
cv
::
gapi
::
wip
::
draw
::
drawPrimitivesOCVBGR
(
mat_ocv
,
prims
);
}
};
}
// opencv_test
...
...
modules/gapi/test/common/gapi_render_tests_inl.hpp
View file @
06067efa
...
...
@@ -2,93 +2,41 @@
// 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) 2018 Intel Corporation
// Copyright (C) 2018
-2019
Intel Corporation
#ifndef OPENCV_GAPI_RENDER_TESTS_INL_HPP
#define OPENCV_GAPI_RENDER_TESTS_INL_HPP
#include "gapi_render_tests.hpp"
#include <opencv2/gapi/render.hpp>
#include "gapi_render_tests.hpp"
namespace
opencv_test
{
TEST_P
(
RenderTextTest
,
AccuracyTest
)
{
std
::
vector
<
cv
::
Point
>
points
;
std
::
string
text
;
int
ff
;
double
fs
;
bool
blo
;
std
::
tie
(
sz
,
text
,
points
,
ff
,
fs
,
color
,
thick
,
lt
,
blo
,
isNV12Format
)
=
GetParam
();
Init
();
for
(
const
auto
&
p
:
points
)
{
cv
::
putText
(
out_mat_ocv
,
text
,
p
,
ff
,
fs
,
color
,
thick
,
lt
,
blo
);
prims
.
emplace_back
(
cv
::
gapi
::
wip
::
draw
::
Text
{
text
,
p
,
ff
,
fs
,
color
,
thick
,
lt
,
blo
});
}
Run
();
EXPECT_EQ
(
0
,
cv
::
countNonZero
(
out_mat_gapi
!=
out_mat_ocv
));
}
TEST_P
(
RenderRectTest
,
AccuracyTest
)
{
std
::
vector
<
cv
::
Rect
>
rects
;
int
shift
;
std
::
tie
(
sz
,
rects
,
color
,
thick
,
lt
,
shift
,
isNV12Format
)
=
GetParam
();
Init
();
for
(
const
auto
&
r
:
rects
)
{
cv
::
rectangle
(
out_mat_ocv
,
r
,
color
,
thick
,
lt
,
shift
);
prims
.
emplace_back
(
cv
::
gapi
::
wip
::
draw
::
Rect
{
r
,
color
,
thick
,
lt
,
shift
});
}
Run
();
EXPECT_EQ
(
0
,
cv
::
countNonZero
(
out_mat_gapi
!=
out_mat_ocv
));
}
TEST_P
(
RenderCircleTest
,
AccuracyTest
)
TEST_P
(
RenderNV12
,
AccuracyTest
)
{
std
::
vector
<
cv
::
Point
>
points
;
int
radius
;
int
shift
;
std
::
tie
(
sz
,
points
,
radius
,
color
,
thick
,
lt
,
shift
,
isNV12Format
)
=
GetParam
();
std
::
tie
(
sz
,
prims
,
pkg
)
=
GetParam
();
Init
();
for
(
const
auto
&
p
:
points
)
{
cv
::
circle
(
out_mat_ocv
,
p
,
radius
,
color
,
thick
,
lt
,
shift
);
prims
.
emplace_back
(
cv
::
gapi
::
wip
::
draw
::
Circle
{
p
,
radius
,
color
,
thick
,
lt
,
shift
});
}
cv
::
gapi
::
wip
::
draw
::
BGR2NV12
(
mat_gapi
,
y_mat_gapi
,
uv_mat_gapi
);
cv
::
gapi
::
wip
::
draw
::
render
(
y_mat_gapi
,
uv_mat_gapi
,
prims
,
pkg
);
Run
();
ComputeRef
();
EXPECT_EQ
(
0
,
cv
::
countNonZero
(
out_mat_gapi
!=
out_mat_ocv
));
EXPECT_EQ
(
0
,
cv
::
norm
(
y_mat_gapi
,
y_mat_ocv
));
EXPECT_EQ
(
0
,
cv
::
norm
(
uv_mat_gapi
,
uv_mat_ocv
));
}
TEST_P
(
Render
LineTest
,
AccuracyTest
)
TEST_P
(
Render
BGR
,
AccuracyTest
)
{
std
::
vector
<
std
::
pair
<
cv
::
Point
,
cv
::
Point
>>
points
;
int
shift
;
std
::
tie
(
sz
,
points
,
color
,
thick
,
lt
,
shift
,
isNV12Format
)
=
GetParam
();
std
::
tie
(
sz
,
prims
,
pkg
)
=
GetParam
();
Init
();
for
(
const
auto
&
p
:
points
)
{
cv
::
line
(
out_mat_ocv
,
p
.
first
,
p
.
second
,
color
,
thick
,
lt
,
shift
);
prims
.
emplace_back
(
cv
::
gapi
::
wip
::
draw
::
Line
{
p
.
first
,
p
.
second
,
color
,
thick
,
lt
,
shift
});
}
Run
();
cv
::
gapi
::
wip
::
draw
::
render
(
mat_gapi
,
prims
,
pkg
);
ComputeRef
();
EXPECT_EQ
(
0
,
cv
::
countNonZero
(
out_mat_gapi
!=
out_
mat_ocv
));
EXPECT_EQ
(
0
,
cv
::
norm
(
mat_gapi
,
mat_ocv
));
}
}
// opencv_test
...
...
modules/gapi/test/cpu/gapi_render_tests_cpu.cpp
View file @
06067efa
...
...
@@ -11,56 +11,92 @@
namespace
opencv_test
{
INSTANTIATE_TEST_CASE_P
(
RenderTextTestCPU
,
RenderTextTest
,
#define OCV cv::gapi::ocv::kernels()
/* NV12 test cases */
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVRects
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
rects
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVCircles
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
circles
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVLines
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
lines
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVMosaics
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
mosaics
),
Values
(
OCV
)));
// FIXME difference in color
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVImages
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
images
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVPolygons
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
polygons
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderNV12OCVTexts
,
RenderNV12
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
texts
),
Values
(
OCV
)));
/* BGR test cases */
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVRects
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
rects
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVCircles
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
circles
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVLines
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
)),
Values
(
lines
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVMosaics
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
),
cv
::
Size
(
128
,
128
)),
Values
(
"text"
),
Values
(
Points
{
Point
(
5
,
30
),
Point
(
40
,
70
),
Point
(
-
1
,
-
1
)}),
/* Font face */
Values
(
FONT_HERSHEY_SIMPLEX
),
/* Font scale */
Values
(
2
),
/* Color */
Values
(
cv
::
Scalar
(
255
,
0
,
0
)),
/* Thickness */
Values
(
1
),
/* Line type */
Values
(
LINE_8
),
/* Bottom left origin */
testing
::
Bool
(),
/* NV12 format or not */
testing
::
Bool
()));
INSTANTIATE_TEST_CASE_P
(
RenderRectTestCPU
,
RenderRectTest
,
cv
::
Size
(
640
,
480
)),
Values
(
mosaics
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVImages
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
),
cv
::
Size
(
128
,
128
)),
Values
(
Rects
{
Rect
(
5
,
30
,
40
,
50
),
Rect
(
40
,
70
,
40
,
50
),
/* Edge case, rectangle will not be drawn */
Rect
(
75
,
110
,
-
40
,
50
),
/* Edge case, rectangle will not be drawn */
Rect
(
70
,
100
,
0
,
50
)}),
/* Color */
Values
(
cv
::
Scalar
(
255
,
0
,
0
)),
/* Thickness */
Values
(
1
),
/* Line type */
Values
(
LINE_8
),
/* Shift */
Values
(
0
),
/* NV12 format or not */
testing
::
Bool
()));
INSTANTIATE_TEST_CASE_P
(
RenderCircleTestCPU
,
RenderCircleTest
,
cv
::
Size
(
640
,
480
)),
Values
(
images
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVPolygons
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
),
cv
::
Size
(
128
,
128
)),
Values
(
Points
{
Point
(
5
,
30
),
Point
(
40
,
70
),
Point
(
75
,
110
)}),
/* Radius */
Values
(
5
),
/* Color */
Values
(
cv
::
Scalar
(
255
,
0
,
0
)),
/* Thickness */
Values
(
1
),
/* Line type */
Values
(
LINE_8
),
/* Shift */
Values
(
0
),
/* NV12 format or not */
testing
::
Bool
()));
INSTANTIATE_TEST_CASE_P
(
RenderLineTestCPU
,
RenderLineTest
,
cv
::
Size
(
640
,
480
)),
Values
(
polygons
),
Values
(
OCV
)));
INSTANTIATE_TEST_CASE_P
(
RenderBGROCVTexts
,
RenderBGR
,
Combine
(
Values
(
cv
::
Size
(
1280
,
720
),
cv
::
Size
(
640
,
480
),
cv
::
Size
(
128
,
128
)),
Values
(
VecOfPairOfPoints
{
{
Point
(
5
,
30
)
,
Point
(
5
,
40
)
},
{
Point
(
40
,
70
)
,
Point
(
50
,
70
)
},
{
Point
(
75
,
110
),
Point
(
100
,
115
)}
}),
/* Color */
Values
(
cv
::
Scalar
(
255
,
0
,
0
)),
/* Thickness */
Values
(
1
),
/* Line type */
Values
(
LINE_8
),
/* Shift */
Values
(
0
),
/* NV12 format or not */
testing
::
Bool
()));
cv
::
Size
(
640
,
480
)),
Values
(
texts
),
Values
(
OCV
)));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment