/* * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ #include "test_precomp.hpp" namespace opencv_test { namespace { // nPts, nDims, nClusters typedef std::tuple ClusterEuclideanTestParams; class ClusterEuclideanTest : public ::testing::TestWithParam {}; TEST_P(ClusterEuclideanTest, accuracy) { auto p = GetParam(); int nPts = std::get<0>(p); int nDims = std::get<1>(p); int nClusters = std::get<2>(p); Mat points(nPts, nDims, CV_8U); Mat clusterCenters(nClusters, nDims, CV_32F); Mat trueMeans(nClusters, nDims, CV_32F); Mat stddevs(nClusters, nDims, CV_32F); std::vector trueClusterSizes(nClusters, 0); std::vector trueClusterBindings(nPts, 0); std::vector trueSumDists(nClusters, 0); cv::RNG& rng = cv::theRNG(); for (int i = 0; i < nClusters; i++) { Mat mean(1, nDims, CV_64F), stdev(1, nDims, CV_64F); rng.fill(mean, cv::RNG::UNIFORM, 0, 256); rng.fill(stdev, cv::RNG::UNIFORM, 5.f, 16); int lo = i * nPts / nClusters; int hi = (i + 1) * nPts / nClusters; for (int d = 0; d < nDims; d++) { rng.fill(points.col(d).rowRange(lo, hi), cv::RNG::NORMAL, mean.at(d), stdev.at(d)); } float sd = 0; for (int j = lo; j < hi; j++) { Mat pts64f; points.row(j).convertTo(pts64f, CV_64F); sd += cv::norm(mean, pts64f, NORM_L2); trueClusterBindings.at(j) = i; trueClusterSizes.at(i)++; } trueSumDists.at(i) = sd; // let's shift initial cluster center a bit Mat(mean + stdev * 0.5).copyTo(clusterCenters.row(i)); mean.copyTo(trueMeans.row(i)); stdev.copyTo(stddevs.row(i)); } Mat newClusterCenters; std::vector clusterSizes, clusterBindings; std::vector clusterSumDists; cv::fastcv::clusterEuclidean(points, clusterCenters, newClusterCenters, clusterSizes, clusterBindings, clusterSumDists); if (cvtest::debugLevel > 0 && nDims == 2) { Mat draw(256, 256, CV_8UC3, Scalar(0)); for (int i = 0; i < nPts; i++) { int x = std::rint(points.at(i, 0)); int y = std::rint(points.at(i, 1)); draw.at(y, x) = Vec3b::all(128); } for (int i = 0; i < nClusters; i++) { float cx = trueMeans.at(i, 0); float cy = trueMeans.at(i, 1); draw.at(cy, cx) = Vec3b(0, 255, 0); float sx = stddevs.at(i, 0); float sy = stddevs.at(i, 1); cv::ellipse(draw, Point(cx, cy), Size(sx, sy), 0, 0, 360, Scalar(255, 0, 0)); float ox = clusterCenters.at(i, 0); float oy = clusterCenters.at(i, 1); draw.at(oy, ox) = Vec3b(0, 0, 255); float nx = newClusterCenters.at(i, 0); float ny = newClusterCenters.at(i, 1); draw.at(ny, nx) = Vec3b(255, 255, 0); } cv::imwrite(cv::format("draw_%d_%d_%d.png", nPts, nDims, nClusters), draw); } { std::vector diffs; for (int i = 0; i < nClusters; i++) { double cs = std::abs((trueClusterSizes[i] - clusterSizes[i]) / double(trueClusterSizes[i])); diffs.push_back(cs); } double normL2 = cv::norm(diffs, NORM_L2) / nClusters; EXPECT_LT(normL2, 0.392); } { Mat bindings8u, trueBindings8u; Mat(clusterBindings).convertTo(bindings8u, CV_8U); Mat(trueClusterBindings).convertTo(trueBindings8u, CV_8U); double normH = cv::norm(bindings8u, trueBindings8u, NORM_HAMMING) / nPts; EXPECT_LT(normH, 0.658); } } INSTANTIATE_TEST_CASE_P(FastCV_Extension, ClusterEuclideanTest, ::testing::Combine(::testing::Values(100, 1000, 10000), // nPts ::testing::Values(2, 10, 32), // nDims ::testing::Values(5, 10, 16))); // nClusters }} // namespaces opencv_test, ::