Merge pull request #18857 from OrestChura:oc/kmeans

[G-API]: kmeans() Standard Kernel Implementation

* cv::gapi::kmeans kernel implementation
 - 4 overloads:
    - standard GMat - for any dimensionality
    - GMat without bestLabels initialization
    - GArray<Point2f> - for 2D
    - GArray<Point3f> - for 3D
 - Accuracy tests:
   - for every input - 2 tests
   1) without initializing. In this case, no comparison with cv::kmeans is done as kmeans uses random auto-initialization
   2) with initialization
   - in both cases, only 1 attempt is done as after first attempt kmeans initializes bestLabels randomly

* Addressing comments
 - bestLabels is returned to its original place among parameters
 - checkVector and isPointsVector functions are merged into one, shared between core.hpp & imgproc.hpp by placing it into gmat.hpp (and implementation - to gmat.cpp)
 - typos corrected

* addressing comments
 - unified names in tests
 - const added
 - typos

* Addressing comments
 - fixed the doc note
 - ddepth -> expectedDepth, `< 0 ` -> `== -1`

* Fix unsupported cases of input Mat
 - supported: multiple channels, reversed width
 - added test cases for those
 - added notes in docs
 - refactored checkVector to return dimentionality along with quantity

* Addressing comments
 - makes chackVector smaller and (maybe) clearer

* Addressing comments

* Addressing comments
 - cv::checkVector -> cv::gapi::detail

* Addressing comments
 - Changed checkVector: returns bool, quantity & dimensionality as references

* Addressing comments
 - Polishing checkVector
 - FIXME added

* Addressing discussion
 - checkVector: added overload, separate two different functionalities
 - depth assert - out of the function

* Addressing comments
 - quantity -> amount, dimensionality -> dim
 - Fix typos

* Addressing comments
 - fix docs
 - use 2 variable's definitions instead of one (for all non-trivial variables)
pull/18967/head
Orest Chura 4 years ago committed by GitHub
parent 95ce8f45ea
commit 986ad4ff06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 145
      modules/gapi/include/opencv2/gapi/core.hpp
  2. 21
      modules/gapi/include/opencv2/gapi/gmat.hpp
  3. 32
      modules/gapi/include/opencv2/gapi/imgproc.hpp
  4. 32
      modules/gapi/src/api/gmat.cpp
  5. 34
      modules/gapi/src/api/kernels_core.cpp
  6. 61
      modules/gapi/src/backends/cpu/gcpucore.cpp
  7. 10
      modules/gapi/test/common/gapi_core_tests.hpp
  8. 191
      modules/gapi/test/common/gapi_core_tests_inl.hpp
  9. 22
      modules/gapi/test/common/gapi_tests_common.hpp
  10. 66
      modules/gapi/test/cpu/gapi_core_tests_cpu.cpp

@ -26,6 +26,7 @@
@defgroup gapi_transform Graph API: Image and channel composition functions
@}
*/
namespace cv { namespace gapi {
namespace core {
using GMat2 = std::tuple<GMat,GMat>;
@ -508,6 +509,77 @@ namespace core {
return in.withType(in.depth, in.chan).withSize(dsize);
}
};
G_TYPED_KERNEL(
GKMeansND,
<std::tuple<GOpaque<double>,GMat,GMat>(GMat,int,GMat,TermCriteria,int,KmeansFlags)>,
"org.opencv.core.kmeansND") {
static std::tuple<GOpaqueDesc,GMatDesc,GMatDesc>
outMeta(const GMatDesc& in, int K, const GMatDesc& bestLabels, const TermCriteria&, int,
KmeansFlags flags) {
GAPI_Assert(in.depth == CV_32F);
std::vector<int> amount_n_dim = detail::checkVector(in);
int amount = amount_n_dim[0], dim = amount_n_dim[1];
if (amount == -1) // Mat with height != 1, width != 1, channels != 1 given
{ // which means that kmeans will consider the following:
amount = in.size.height;
dim = in.size.width * in.chan;
}
// kmeans sets these labels' sizes when no bestLabels given:
GMatDesc out_labels(CV_32S, 1, Size{1, amount});
// kmeans always sets these centers' sizes:
GMatDesc centers (CV_32F, 1, Size{dim, K});
if (flags & KMEANS_USE_INITIAL_LABELS)
{
GAPI_Assert(bestLabels.depth == CV_32S);
int labels_amount = detail::checkVector(bestLabels, 1u);
GAPI_Assert(labels_amount == amount);
out_labels = bestLabels; // kmeans preserves bestLabels' sizes if given
}
return std::make_tuple(empty_gopaque_desc(), out_labels, centers);
}
};
G_TYPED_KERNEL(
GKMeansNDNoInit,
<std::tuple<GOpaque<double>,GMat,GMat>(GMat,int,TermCriteria,int,KmeansFlags)>,
"org.opencv.core.kmeansNDNoInit") {
static std::tuple<GOpaqueDesc,GMatDesc,GMatDesc>
outMeta(const GMatDesc& in, int K, const TermCriteria&, int, KmeansFlags flags) {
GAPI_Assert( !(flags & KMEANS_USE_INITIAL_LABELS) );
GAPI_Assert(in.depth == CV_32F);
std::vector<int> amount_n_dim = detail::checkVector(in);
int amount = amount_n_dim[0], dim = amount_n_dim[1];
if (amount == -1) // Mat with height != 1, width != 1, channels != 1 given
{ // which means that kmeans will consider the following:
amount = in.size.height;
dim = in.size.width * in.chan;
}
GMatDesc out_labels(CV_32S, 1, Size{1, amount});
GMatDesc centers (CV_32F, 1, Size{dim, K});
return std::make_tuple(empty_gopaque_desc(), out_labels, centers);
}
};
G_TYPED_KERNEL(GKMeans2D, <std::tuple<GOpaque<double>,GArray<int>,GArray<Point2f>>
(GArray<Point2f>,int,GArray<int>,TermCriteria,int,KmeansFlags)>,
"org.opencv.core.kmeans2D") {
static std::tuple<GOpaqueDesc,GArrayDesc,GArrayDesc>
outMeta(const GArrayDesc&,int,const GArrayDesc&,const TermCriteria&,int,KmeansFlags) {
return std::make_tuple(empty_gopaque_desc(), empty_array_desc(), empty_array_desc());
}
};
G_TYPED_KERNEL(GKMeans3D, <std::tuple<GOpaque<double>,GArray<int>,GArray<Point3f>>
(GArray<Point3f>,int,GArray<int>,TermCriteria,int,KmeansFlags)>,
"org.opencv.core.kmeans3D") {
static std::tuple<GOpaqueDesc,GArrayDesc,GArrayDesc>
outMeta(const GArrayDesc&,int,const GArrayDesc&,const TermCriteria&,int,KmeansFlags) {
return std::make_tuple(empty_gopaque_desc(), empty_array_desc(), empty_array_desc());
}
};
} // namespace core
namespace streaming {
@ -1757,6 +1829,79 @@ GAPI_EXPORTS GMat warpAffine(const GMat& src, const Mat& M, const Size& dsize, i
int borderMode = cv::BORDER_CONSTANT, const Scalar& borderValue = Scalar());
//! @} gapi_transform
/** @brief Finds centers of clusters and groups input samples around the clusters.
The function kmeans implements a k-means algorithm that finds the centers of K clusters
and groups the input samples around the clusters. As an output, \f$\texttt{bestLabels}_i\f$
contains a 0-based cluster index for the \f$i^{th}\f$ sample.
@note
- Function textual ID is "org.opencv.core.kmeansND"
- In case of an N-dimentional points' set given, input GMat can have the following traits:
2 dimensions, a single row or column if there are N channels,
or N columns if there is a single channel. Mat should have @ref CV_32F depth.
- Although, if GMat with height != 1, width != 1, channels != 1 given as data, n-dimensional
samples are considered given in amount of A, where A = height, n = width * channels.
- In case of GMat given as data:
- the output labels are returned as 1-channel GMat with sizes
width = 1, height = A, where A is samples amount, or width = bestLabels.width,
height = bestLabels.height if bestLabels given;
- the cluster centers are returned as 1-channel GMat with sizes
width = n, height = K, where n is samples' dimentionality and K is clusters' amount.
- As one of possible usages, if you want to control the initial labels for each attempt
by yourself, you can utilize just the core of the function. To do that, set the number
of attempts to 1, initialize labels each time using a custom algorithm, pass them with the
( flags = #KMEANS_USE_INITIAL_LABELS ) flag, and then choose the best (most-compact) clustering.
@param data Data for clustering. An array of N-Dimensional points with float coordinates is needed.
Function can take GArray<Point2f>, GArray<Point3f> for 2D and 3D cases or GMat for any
dimentionality and channels.
@param K Number of clusters to split the set by.
@param bestLabels Optional input integer array that can store the supposed initial cluster indices
for every sample. Used when ( flags = #KMEANS_USE_INITIAL_LABELS ) flag is set.
@param criteria The algorithm termination criteria, that is, the maximum number of iterations
and/or the desired accuracy. The accuracy is specified as criteria.epsilon. As soon as each of
the cluster centers moves by less than criteria.epsilon on some iteration, the algorithm stops.
@param attempts Flag to specify the number of times the algorithm is executed using different
initial labellings. The algorithm returns the labels that yield the best compactness (see the first
function return value).
@param flags Flag that can take values of cv::KmeansFlags .
@return
- Compactness measure that is computed as
\f[\sum _i \| \texttt{samples} _i - \texttt{centers} _{ \texttt{labels} _i} \| ^2\f]
after every attempt. The best (minimum) value is chosen and the corresponding labels and the
compactness value are returned by the function.
- Integer array that stores the cluster indices for every sample.
- Array of the cluster centers.
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GMat,GMat>
kmeans(const GMat& data, const int K, const GMat& bestLabels,
const TermCriteria& criteria, const int attempts, const KmeansFlags flags);
/** @overload
@note
- Function textual ID is "org.opencv.core.kmeansNDNoInit"
- #KMEANS_USE_INITIAL_LABELS flag must not be set while using this overload.
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GMat,GMat>
kmeans(const GMat& data, const int K, const TermCriteria& criteria, const int attempts,
const KmeansFlags flags);
/** @overload
@note Function textual ID is "org.opencv.core.kmeans2D"
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GArray<int>,GArray<Point2f>>
kmeans(const GArray<Point2f>& data, const int K, const GArray<int>& bestLabels,
const TermCriteria& criteria, const int attempts, const KmeansFlags flags);
/** @overload
@note Function textual ID is "org.opencv.core.kmeans3D"
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GArray<int>,GArray<Point3f>>
kmeans(const GArray<Point3f>& data, const int K, const GArray<int>& bestLabels,
const TermCriteria& criteria, const int attempts, const KmeansFlags flags);
namespace streaming {
/** @brief Gets dimensions from Mat.

@ -203,6 +203,27 @@ struct GAPI_EXPORTS GMatDesc
static inline GMatDesc empty_gmat_desc() { return GMatDesc{-1,-1,{-1,-1}}; }
namespace gapi { namespace detail {
/** Checks GMatDesc fields if the passed matrix is a set of n-dimentional points.
@param in GMatDesc to check.
@param n expected dimensionality.
@return the amount of points. In case input matrix can't be described as vector of points
of expected dimensionality, returns -1.
*/
int checkVector(const GMatDesc& in, const size_t n);
/** @overload
Checks GMatDesc fields if the passed matrix can be described as a set of points of any
dimensionality.
@return array of two elements in form of std::vector<int>: the amount of points
and their calculated dimensionality. In case input matrix can't be described as vector of points,
returns {-1, -1}.
*/
std::vector<int> checkVector(const GMatDesc& in);
}} // namespace gapi::detail
#if !defined(GAPI_STANDALONE)
GAPI_EXPORTS GMatDesc descr_of(const cv::UMat &mat);
#endif // !defined(GAPI_STANDALONE)

@ -43,15 +43,6 @@ void validateFindingContoursMeta(const int depth, const int chan, const int mode
break;
}
}
// Checks if the passed mat is a set of n-dimentional points of the given depth
bool isPointsVector(const int chan, const cv::Size &size, const int depth,
const int n, const int ddepth = -1)
{
return (ddepth == depth || ddepth < 0) &&
((chan == n && (size.height == 1 || size.width == 1)) ||
(chan == 1 && size.width == n));
}
} // anonymous namespace
namespace cv { namespace gapi {
@ -212,10 +203,17 @@ namespace imgproc {
G_TYPED_KERNEL(GBoundingRectMat, <GOpaque<Rect>(GMat)>,
"org.opencv.imgproc.shape.boundingRectMat") {
static GOpaqueDesc outMeta(GMatDesc in) {
GAPI_Assert((in.depth == CV_8U && in.chan == 1) ||
(isPointsVector(in.chan, in.size, in.depth, 2, CV_32S) ||
isPointsVector(in.chan, in.size, in.depth, 2, CV_32F)));
if (in.depth == CV_8U)
{
GAPI_Assert(in.chan == 1);
}
else
{
GAPI_Assert (in.depth == CV_32S || in.depth == CV_32F);
int amount = detail::checkVector(in, 2u);
GAPI_Assert(amount != -1 &&
"Input Mat can't be described as vector of 2-dimentional points");
}
return empty_gopaque_desc();
}
};
@ -237,7 +235,9 @@ namespace imgproc {
G_TYPED_KERNEL(GFitLine2DMat, <GOpaque<Vec4f>(GMat,DistanceTypes,double,double,double)>,
"org.opencv.imgproc.shape.fitLine2DMat") {
static GOpaqueDesc outMeta(GMatDesc in,DistanceTypes,double,double,double) {
GAPI_Assert(isPointsVector(in.chan, in.size, in.depth, 2, -1));
int amount = detail::checkVector(in, 2u);
GAPI_Assert(amount != -1 &&
"Input Mat can't be described as vector of 2-dimentional points");
return empty_gopaque_desc();
}
};
@ -269,7 +269,9 @@ namespace imgproc {
G_TYPED_KERNEL(GFitLine3DMat, <GOpaque<Vec6f>(GMat,DistanceTypes,double,double,double)>,
"org.opencv.imgproc.shape.fitLine3DMat") {
static GOpaqueDesc outMeta(GMatDesc in,int,double,double,double) {
GAPI_Assert(isPointsVector(in.chan, in.size, in.depth, 3, -1));
int amount = detail::checkVector(in, 3u);
GAPI_Assert(amount != -1 &&
"Input Mat can't be described as vector of 3-dimentional points");
return empty_gopaque_desc();
}
};

@ -36,6 +36,38 @@ const cv::GOrigin& cv::GMat::priv() const
return *m_priv;
}
static std::vector<int> checkVectorImpl(const int width, const int height, const int chan,
const int n)
{
if (width == 1 && (n == -1 || n == chan))
{
return {height, chan};
}
else if (height == 1 && (n == -1 || n == chan))
{
return {width, chan};
}
else if (chan == 1 && (n == -1 || n == width))
{
return {height, width};
}
else // input Mat can't be described as vector of points of given dimensionality
{
return {-1, -1};
}
}
int cv::gapi::detail::checkVector(const cv::GMatDesc& in, const size_t n)
{
GAPI_Assert(n != 0u);
return checkVectorImpl(in.size.width, in.size.height, in.chan, static_cast<int>(n))[0];
}
std::vector<int> cv::gapi::detail::checkVector(const cv::GMatDesc& in)
{
return checkVectorImpl(in.size.width, in.size.height, in.chan, -1);
}
namespace{
template <typename T> cv::GMetaArgs vec_descr_of(const std::vector<T> &vec)
{

@ -388,6 +388,40 @@ GMat warpAffine(const GMat& src, const Mat& M, const Size& dsize, int flags,
return core::GWarpAffine::on(src, M, dsize, flags, borderMode, borderValue);
}
std::tuple<GOpaque<double>,GMat,GMat> kmeans(const GMat& data, const int K, const GMat& bestLabels,
const TermCriteria& criteria, const int attempts,
const KmeansFlags flags)
{
return core::GKMeansND::on(data, K, bestLabels, criteria, attempts, flags);
}
std::tuple<GOpaque<double>,GMat,GMat> kmeans(const GMat& data, const int K,
const TermCriteria& criteria, const int attempts,
const KmeansFlags flags)
{
return core::GKMeansNDNoInit::on(data, K, criteria, attempts, flags);
}
std::tuple<GOpaque<double>,GArray<int>,GArray<Point2f>> kmeans(const GArray<Point2f>& data,
const int K,
const GArray<int>& bestLabels,
const TermCriteria& criteria,
const int attempts,
const KmeansFlags flags)
{
return core::GKMeans2D::on(data, K, bestLabels, criteria, attempts, flags);
}
std::tuple<GOpaque<double>,GArray<int>,GArray<Point3f>> kmeans(const GArray<Point3f>& data,
const int K,
const GArray<int>& bestLabels,
const TermCriteria& criteria,
const int attempts,
const KmeansFlags flags)
{
return core::GKMeans3D::on(data, K, bestLabels, criteria, attempts, flags);
}
GOpaque<Size> streaming::size(const GMat& src)
{
return streaming::GSize::on(src);

@ -585,6 +585,63 @@ GAPI_OCV_KERNEL(GCPUWarpAffine, cv::gapi::core::GWarpAffine)
}
};
GAPI_OCV_KERNEL(GCPUKMeansND, cv::gapi::core::GKMeansND)
{
static void run(const cv::Mat& data, const int K, const cv::Mat& inBestLabels,
const cv::TermCriteria& criteria, const int attempts,
const cv::KmeansFlags flags,
double& compactness, cv::Mat& outBestLabels, cv::Mat& centers)
{
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
inBestLabels.copyTo(outBestLabels);
}
compactness = cv::kmeans(data, K, outBestLabels, criteria, attempts, flags, centers);
}
};
GAPI_OCV_KERNEL(GCPUKMeansNDNoInit, cv::gapi::core::GKMeansNDNoInit)
{
static void run(const cv::Mat& data, const int K, const cv::TermCriteria& criteria,
const int attempts, const cv::KmeansFlags flags,
double& compactness, cv::Mat& outBestLabels, cv::Mat& centers)
{
compactness = cv::kmeans(data, K, outBestLabels, criteria, attempts, flags, centers);
}
};
GAPI_OCV_KERNEL(GCPUKMeans2D, cv::gapi::core::GKMeans2D)
{
static void run(const std::vector<cv::Point2f>& data, const int K,
const std::vector<int>& inBestLabels, const cv::TermCriteria& criteria,
const int attempts, const cv::KmeansFlags flags,
double& compactness, std::vector<int>& outBestLabels,
std::vector<cv::Point2f>& centers)
{
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
outBestLabels = inBestLabels;
}
compactness = cv::kmeans(data, K, outBestLabels, criteria, attempts, flags, centers);
}
};
GAPI_OCV_KERNEL(GCPUKMeans3D, cv::gapi::core::GKMeans3D)
{
static void run(const std::vector<cv::Point3f>& data, const int K,
const std::vector<int>& inBestLabels, const cv::TermCriteria& criteria,
const int attempts, const cv::KmeansFlags flags,
double& compactness, std::vector<int>& outBestLabels,
std::vector<cv::Point3f>& centers)
{
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
outBestLabels = inBestLabels;
}
compactness = cv::kmeans(data, K, outBestLabels, criteria, attempts, flags, centers);
}
};
GAPI_OCV_KERNEL(GCPUParseSSDBL, cv::gapi::nn::parsers::GParseSSDBL)
{
static void run(const cv::Mat& in_ssd_result,
@ -714,6 +771,10 @@ cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels()
, GCPUNormalize
, GCPUWarpPerspective
, GCPUWarpAffine
, GCPUKMeansND
, GCPUKMeansNDNoInit
, GCPUKMeans2D
, GCPUKMeans3D
, GCPUParseSSDBL
, GOCVParseSSD
, GCPUParseYolo

@ -151,6 +151,16 @@ GAPI_TEST_FIXTURE(WarpPerspectiveTest, initMatrixRandU,
GAPI_TEST_FIXTURE(WarpAffineTest, initMatrixRandU,
FIXTURE_API(CompareMats, double , double, int, int, cv::Scalar),
6, cmpF, angle, scale, flags, border_mode, border_value)
GAPI_TEST_FIXTURE(KMeansNDNoInitTest, initMatrixRandU, FIXTURE_API(int, cv::KmeansFlags),
2, K, flags)
GAPI_TEST_FIXTURE(KMeansNDInitTest, initMatrixRandU,
FIXTURE_API(CompareMats, int, cv::KmeansFlags), 3, cmpF, K, flags)
GAPI_TEST_FIXTURE(KMeans2DNoInitTest, initNothing, FIXTURE_API(int, cv::KmeansFlags),
2, K, flags)
GAPI_TEST_FIXTURE(KMeans2DInitTest, initNothing, FIXTURE_API(int, cv::KmeansFlags), 2, K, flags)
GAPI_TEST_FIXTURE(KMeans3DNoInitTest, initNothing, FIXTURE_API(int, cv::KmeansFlags),
2, K, flags)
GAPI_TEST_FIXTURE(KMeans3DInitTest, initNothing, FIXTURE_API(int, cv::KmeansFlags), 2, K, flags)
GAPI_TEST_EXT_BASE_FIXTURE(ParseSSDBLTest, ParserSSDTest, initNothing,
FIXTURE_API(float, int), 2, confidence_threshold, filter_label)

@ -15,6 +15,16 @@
namespace opencv_test
{
namespace
{
template <typename Elem>
inline bool compareVectorsAbsExact(const std::vector<Elem>& outOCV,
const std::vector<Elem>& outGAPI)
{
return AbsExactVector<Elem>().to_compare_f()(outOCV, outGAPI);
}
}
TEST_P(MathOpTest, MatricesAccuracyTest)
{
// G-API code & corresponding OpenCV code ////////////////////////////////
@ -1377,6 +1387,187 @@ TEST_P(NormalizeTest, Test)
}
}
TEST_P(KMeansNDNoInitTest, AccuracyTest)
{
const int amount = sz.height != 1 ? sz.height : sz.width,
dim = sz.height != 1 ? sz.width : (type >> CV_CN_SHIFT) + 1;
// amount of channels
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
double compact_gapi = -1.;
cv::Mat labels_gapi, centers_gapi;
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
cv::GOpaque<double> compactness;
cv::GMat outLabels, centers;
std::tie(compactness, outLabels, centers) = cv::gapi::kmeans(in, K, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_mat1), cv::gout(compact_gapi, labels_gapi, centers_gapi), getCompileArgs());
// Validation //////////////////////////////////////////////////////////////
{
EXPECT_GE(compact_gapi, 0.);
EXPECT_EQ(labels_gapi.cols, 1);
EXPECT_EQ(labels_gapi.rows, amount);
EXPECT_FALSE(labels_gapi.empty());
EXPECT_EQ(centers_gapi.cols, dim);
EXPECT_EQ(centers_gapi.rows, K);
EXPECT_FALSE(centers_gapi.empty());
}
}
TEST_P(KMeansNDInitTest, AccuracyTest)
{
const int amount = sz.height != 1 ? sz.height : sz.width;
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
cv::Mat bestLabels(cv::Size{1, amount}, CV_32SC1);
double compact_ocv = -1., compact_gapi = -1.;
cv::Mat labels_ocv, labels_gapi, centers_ocv, centers_gapi;
cv::randu(bestLabels, 0, K);
bestLabels.copyTo(labels_ocv);
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in, inLabels;
cv::GOpaque<double> compactness;
cv::GMat outLabels, centers;
std::tie(compactness, outLabels, centers) =
cv::gapi::kmeans(in, K, inLabels, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in, inLabels), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_mat1, bestLabels), cv::gout(compact_gapi, labels_gapi, centers_gapi),
getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
compact_ocv = cv::kmeans(in_mat1, K, labels_ocv, criteria, attempts, flags, centers_ocv);
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(compact_gapi == compact_ocv);
EXPECT_TRUE(cmpF(labels_gapi, labels_ocv));
EXPECT_TRUE(cmpF(centers_gapi, centers_ocv));
}
}
TEST_P(KMeans2DNoInitTest, AccuracyTest)
{
const int amount = sz.height;
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
std::vector<cv::Point2f> in_vector{};
double compact_gapi = -1.;
std::vector<int> labels_gapi{};
std::vector<cv::Point2f> centers_gapi{};
initPointsVectorRandU(amount, in_vector);
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point2f> in;
cv::GArray<int> inLabels(std::vector<int>{});
cv::GOpaque<double> compactness;
cv::GArray<int> outLabels;
cv::GArray<cv::Point2f> centers;
std::tie(compactness, outLabels, centers) =
cv::gapi::kmeans(in, K, inLabels, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_vector), cv::gout(compact_gapi, labels_gapi, centers_gapi), getCompileArgs());
// Validation //////////////////////////////////////////////////////////////
{
EXPECT_GE(compact_gapi, 0.);
EXPECT_EQ(labels_gapi.size(), static_cast<size_t>(amount));
EXPECT_EQ(centers_gapi.size(), static_cast<size_t>(K));
}
}
TEST_P(KMeans2DInitTest, AccuracyTest)
{
const int amount = sz.height;
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
std::vector<cv::Point2f> in_vector{};
std::vector<int> bestLabels(amount);
double compact_ocv = -1., compact_gapi = -1.;
std::vector<int> labels_ocv{}, labels_gapi{};
std::vector<cv::Point2f> centers_ocv{}, centers_gapi{};
initPointsVectorRandU(amount, in_vector);
cv::randu(bestLabels, 0, K);
labels_ocv = bestLabels;
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point2f> in;
cv::GArray<int> inLabels;
cv::GOpaque<double> compactness;
cv::GArray<int> outLabels;
cv::GArray<cv::Point2f> centers;
std::tie(compactness, outLabels, centers) =
cv::gapi::kmeans(in, K, inLabels, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in, inLabels), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_vector, bestLabels), cv::gout(compact_gapi, labels_gapi, centers_gapi),
getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
compact_ocv = cv::kmeans(in_vector, K, labels_ocv, criteria, attempts, flags, centers_ocv);
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(compact_gapi == compact_ocv);
EXPECT_TRUE(compareVectorsAbsExact(labels_gapi, labels_ocv));
EXPECT_TRUE(compareVectorsAbsExact(centers_gapi, centers_ocv));
}
}
TEST_P(KMeans3DNoInitTest, AccuracyTest)
{
const int amount = sz.height;
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
std::vector<cv::Point3f> in_vector{};
double compact_gapi = -1.;
std::vector<int> labels_gapi{};
std::vector<cv::Point3f> centers_gapi{};
initPointsVectorRandU(amount, in_vector);
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point3f> in;
cv::GArray<int> inLabels(std::vector<int>{});
cv::GOpaque<double> compactness;
cv::GArray<int> outLabels;
cv::GArray<cv::Point3f> centers;
std::tie(compactness, outLabels, centers) =
cv::gapi::kmeans(in, K, inLabels, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_vector), cv::gout(compact_gapi, labels_gapi, centers_gapi), getCompileArgs());
// Validation //////////////////////////////////////////////////////////////
{
EXPECT_GE(compact_gapi, 0.);
EXPECT_EQ(labels_gapi.size(), static_cast<size_t>(amount));
EXPECT_EQ(centers_gapi.size(), static_cast<size_t>(K));
}
}
TEST_P(KMeans3DInitTest, AccuracyTest)
{
const int amount = sz.height;
const cv::TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0);
const int attempts = 1;
std::vector<cv::Point3f> in_vector{};
std::vector<int> bestLabels(amount);
double compact_ocv = -1., compact_gapi = -1.;
std::vector<int> labels_ocv{}, labels_gapi{};
std::vector<cv::Point3f> centers_ocv{}, centers_gapi{};
initPointsVectorRandU(amount, in_vector);
cv::randu(bestLabels, 0, K);
labels_ocv = bestLabels;
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point3f> in;
cv::GArray<int> inLabels;
cv::GOpaque<double> compactness;
cv::GArray<int> outLabels;
cv::GArray<cv::Point3f> centers;
std::tie(compactness, outLabels, centers) =
cv::gapi::kmeans(in, K, inLabels, criteria, attempts, flags);
cv::GComputation c(cv::GIn(in, inLabels), cv::GOut(compactness, outLabels, centers));
c.apply(cv::gin(in_vector, bestLabels), cv::gout(compact_gapi, labels_gapi, centers_gapi),
getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
compact_ocv = cv::kmeans(in_vector, K, labels_ocv, criteria, attempts, flags, centers_ocv);
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(compact_gapi == compact_ocv);
EXPECT_TRUE(compareVectorsAbsExact(labels_gapi, labels_ocv));
EXPECT_TRUE(compareVectorsAbsExact(centers_gapi, centers_ocv));
}
}
// PLEASE DO NOT PUT NEW ACCURACY TESTS BELOW THIS POINT! //////////////////////
TEST_P(BackendOutputAllocationTest, EmptyOutput)

@ -1174,6 +1174,28 @@ inline std::ostream& operator<<(std::ostream& os, DistanceTypes op)
#undef CASE
return os;
}
inline std::ostream& operator<<(std::ostream& os, KmeansFlags op)
{
int op_(op);
switch (op_)
{
case KmeansFlags::KMEANS_RANDOM_CENTERS:
os << "KMEANS_RANDOM_CENTERS";
break;
case KmeansFlags::KMEANS_PP_CENTERS:
os << "KMEANS_PP_CENTERS";
break;
case KmeansFlags::KMEANS_RANDOM_CENTERS | KmeansFlags::KMEANS_USE_INITIAL_LABELS:
os << "KMEANS_RANDOM_CENTERS | KMEANS_USE_INITIAL_LABELS";
break;
case KmeansFlags::KMEANS_PP_CENTERS | KmeansFlags::KMEANS_USE_INITIAL_LABELS:
os << "KMEANS_PP_CENTERS | KMEANS_USE_INITIAL_LABELS";
break;
default: GAPI_Assert(false && "unknown KmeansFlags value");
}
return os;
}
} // namespace cv
#endif //OPENCV_GAPI_TESTS_COMMON_HPP

@ -484,6 +484,72 @@ INSTANTIATE_TEST_CASE_P(NormalizeTestCPU, NormalizeTest,
Values(NORM_MINMAX, NORM_INF, NORM_L1, NORM_L2),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F)));
INSTANTIATE_TEST_CASE_P(KMeansNDNoInitTestCPU, KMeansNDNoInitTest,
Combine(Values(CV_32FC1),
Values(cv::Size(2, 20)),
Values(-1),
Values(CORE_CPU),
Values(5),
Values(cv::KMEANS_RANDOM_CENTERS, cv::KMEANS_PP_CENTERS)));
INSTANTIATE_TEST_CASE_P(KMeansNDInitTestCPU, KMeansNDInitTest,
Combine(Values(CV_32FC1, CV_32FC3),
Values(cv::Size(1, 20),
cv::Size(2, 20),
cv::Size(5, 720)),
Values(-1),
Values(CORE_CPU),
Values(AbsTolerance(0.01).to_compare_obj()),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS)));
INSTANTIATE_TEST_CASE_P(KMeansNDInitReverseTestCPU, KMeansNDInitTest,
Combine(Values(CV_32FC3),
Values(cv::Size(20, 1)),
Values(-1),
Values(CORE_CPU),
Values(AbsTolerance(0.01).to_compare_obj()),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS)));
INSTANTIATE_TEST_CASE_P(KMeans2DNoInitTestCPU, KMeans2DNoInitTest,
Combine(Values(-1),
Values(cv::Size(-1, 20)),
Values(-1),
Values(CORE_CPU),
Values(5),
Values(cv::KMEANS_RANDOM_CENTERS, cv::KMEANS_PP_CENTERS)));
INSTANTIATE_TEST_CASE_P(KMeans2DInitTestCPU, KMeans2DInitTest,
Combine(Values(-1),
Values(cv::Size(-1, 720),
cv::Size(-1, 20)),
Values(-1),
Values(CORE_CPU),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS)));
INSTANTIATE_TEST_CASE_P(KMeans3DNoInitTestCPU, KMeans3DNoInitTest,
Combine(Values(-1),
Values(cv::Size(-1, 20)),
Values(-1),
Values(CORE_CPU),
Values(5),
Values(cv::KMEANS_RANDOM_CENTERS, cv::KMEANS_PP_CENTERS)));
INSTANTIATE_TEST_CASE_P(KMeans3DInitTestCPU, KMeans3DInitTest,
Combine(Values(-1),
Values(cv::Size(-1, 720),
cv::Size(-1, 20)),
Values(-1),
Values(CORE_CPU),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS)));
// PLEASE DO NOT PUT NEW ACCURACY TESTS BELOW THIS POINT! //////////////////////
INSTANTIATE_TEST_CASE_P(BackendOutputAllocationTestCPU, BackendOutputAllocationTest,

Loading…
Cancel
Save