1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 08:13:04 +04:00

Merge pull request #18510 from OrestChura:oc/boundingRect

[G-API]: findContours() and boundingRect() Standard Kernels Implementation

* Add findContours() standard kernel
 - API and documentation provided:
   - as OpenCV provides two overloads whether to calculate hierarchy or not, but they differ by only the output in sight of G-API, two different G-API functions and kernels implemented
   - G-API Imgproc documentation divided into more parts according to imgproc module parts
   - some typos connected with division into parts corrected
 - `GArray<GArray<U>>` overload for `get_out` function provided to coonvert correctly into `vector<vector<U>>`
 - OCV backend supported
 - accuracy tests provided

* Add boundingRect() standard kernel
     - API and documentation provided:
       - GOpaque<Rect> used as an output
       - as OpenCV provides two possibilities whether to take a gray-scale image or a set of 2D points (`Point2i` or `Point2f` supported), three different overloads of a single G-API function and three kernels implemented
          - for a gray-scale image the overload via `GMat`
          - for a set of `Point2i` - the one via GArray<`Point2i`>
          - set of `Point2f` -> GArray<`Point2f`>
     - OCV backend supported
     - accuracy tests provided
       - comparison function for Rects provided
     - some typos in `gapi_tests_common` corrected

* Fix precommit windows warnings

* - Addressing comments:
   - split tests
 - Fix Windows warnings

* Static_cast for warnings

* - Remove randomness
 - Fix unnecessary precision losses

* - Forgot reference for RNG

* addressing comments

* equalizeHist -> no group

* `const` addedin new functions

* Address suggestions:
 - Hierarchical -> H
 - added cv::GMatDesc::isVectorPoins()
 - added support of giving a set of points to boundingRect()

* Addressing comments
 - IoU comparison function added for Rects
 - isPointsVector moved from a GMatDesc method to a separate function in imgproc.hpp
 - enums instead of int
 - typos corrected

* Addressing comments
 - findContours: Point offset -> GOpaque<Point>
 - removed "straight" comparison for Rects, IoU available only
 - changed vectors initialization -> fix Debug test run
 - Some typos

* added comment for later upgrades

* Fix not to corrupt docs by FIXME

* Addressing commens
 - overload without offset added (as a temporary workaround)
 - checkMetaForFindingContours -> validateFindingContoursMeta
 - added ostream overload for enums used in tests
This commit is contained in:
Orest Chura
2020-11-11 15:13:10 +03:00
committed by GitHub
parent ef32d7fd16
commit 3fc1c73064
8 changed files with 821 additions and 14 deletions
@@ -66,6 +66,21 @@ GAPI_TEST_FIXTURE_SPEC_PARAMS(GoodFeaturesTest,
double,int,bool),
8, cmpF, fileName, type, maxCorners, qualityLevel, minDistance,
blockSize, useHarrisDetector)
GAPI_TEST_FIXTURE_SPEC_PARAMS(FindContoursNoOffsetTest,
FIXTURE_API(cv::Size,MatType2,cv::RetrievalModes,
cv::ContourApproximationModes),
4, sz, type, mode, method)
GAPI_TEST_FIXTURE_SPEC_PARAMS(FindContoursOffsetTest, <>, 0)
GAPI_TEST_FIXTURE_SPEC_PARAMS(FindContoursHNoOffsetTest,
FIXTURE_API(cv::Size,MatType2,cv::RetrievalModes,
cv::ContourApproximationModes),
4, sz, type, mode, method)
GAPI_TEST_FIXTURE_SPEC_PARAMS(FindContoursHOffsetTest, <>, 0)
GAPI_TEST_FIXTURE(BoundingRectMatTest, initMatrixRandU, FIXTURE_API(CompareRects), 1, cmpF)
GAPI_TEST_FIXTURE(BoundingRectMatVector32STest, initNothing, FIXTURE_API(CompareRects), 1, cmpF)
GAPI_TEST_FIXTURE(BoundingRectMatVector32FTest, initNothing, FIXTURE_API(CompareRects), 1, cmpF)
GAPI_TEST_FIXTURE(BoundingRectVector32STest, initNothing, FIXTURE_API(CompareRects), 1, cmpF)
GAPI_TEST_FIXTURE(BoundingRectVector32FTest, initNothing, FIXTURE_API(CompareRects), 1, cmpF)
GAPI_TEST_FIXTURE(BGR2RGBTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF)
GAPI_TEST_FIXTURE(RGB2GrayTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF)
GAPI_TEST_FIXTURE(BGR2GrayTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF)
@@ -50,6 +50,27 @@ namespace
rgb2yuyv(in_line_p, out_line_p, in.cols);
}
}
// Draw random ellipses on given mat of given size and type
void initMatForFindingContours(cv::Mat& mat, const cv::Size& sz, const int type)
{
cv::RNG& rng = theRNG();
mat = cv::Mat(sz, type, cv::Scalar::all(0));
size_t numEllipses = rng.uniform(1, 10);
for( size_t i = 0; i < numEllipses; i++ )
{
cv::Point center;
cv::Size axes;
center.x = rng.uniform(0, sz.width);
center.y = rng.uniform(0, sz.height);
axes.width = rng.uniform(2, sz.width);
axes.height = rng.uniform(2, sz.height);
int color = rng.uniform(1, 256);
double angle = rng.uniform(0., 180.);
cv::ellipse(mat, center, axes, angle, 0., 360., color, 1, FILLED);
}
}
}
TEST_P(Filter2DTest, AccuracyTest)
@@ -470,6 +491,267 @@ TEST_P(GoodFeaturesTest, AccuracyTest)
}
}
TEST_P(FindContoursNoOffsetTest, AccuracyTest)
{
std::vector<std::vector<cv::Point>> outCtsOCV, outCtsGAPI;
initMatForFindingContours(in_mat1, sz, type);
out_mat_gapi = cv::Mat(sz, type, cv::Scalar::all(0));
out_mat_ocv = cv::Mat(sz, type, cv::Scalar::all(0));
// OpenCV code /////////////////////////////////////////////////////////////
{
cv::findContours(in_mat1, outCtsOCV, mode, method);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
cv::GArray<cv::GArray<cv::Point>> outCts;
outCts = cv::gapi::findContours(in, mode, method);
cv::GComputation c(GIn(in), GOut(outCts));
c.apply(gin(in_mat1), gout(outCtsGAPI), getCompileArgs());
// Comparison //////////////////////////////////////////////////////////////
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
cv::fillPoly(out_mat_ocv, outCtsOCV, cv::Scalar::all(1));
cv::fillPoly(out_mat_gapi, outCtsGAPI, cv::Scalar::all(1));
EXPECT_TRUE(AbsExact().to_compare_f()(out_mat_ocv, out_mat_gapi));
}
TEST_P(FindContoursOffsetTest, AccuracyTest)
{
const cv::Size sz(1280, 720);
const MatType2 type = CV_8UC1;
const cv::RetrievalModes mode = cv::RETR_EXTERNAL;
const cv::ContourApproximationModes method = cv::CHAIN_APPROX_NONE;
const cv::Point offset(15, 15);
std::vector<std::vector<cv::Point>> outCtsOCV, outCtsGAPI;
initMatForFindingContours(in_mat1, sz, type);
out_mat_gapi = cv::Mat(sz, type, cv::Scalar::all(0));
out_mat_ocv = cv::Mat(sz, type, cv::Scalar::all(0));
// OpenCV code /////////////////////////////////////////////////////////////
{
cv::findContours(in_mat1, outCtsOCV, mode, method, offset);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
GOpaque<Point> gOffset;
cv::GArray<cv::GArray<cv::Point>> outCts;
outCts = cv::gapi::findContours(in, mode, method, gOffset);
cv::GComputation c(GIn(in, gOffset), GOut(outCts));
c.apply(gin(in_mat1, offset), gout(outCtsGAPI), getCompileArgs());
// Comparison //////////////////////////////////////////////////////////////
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
cv::fillPoly(out_mat_ocv, outCtsOCV, cv::Scalar::all(1));
cv::fillPoly(out_mat_gapi, outCtsGAPI, cv::Scalar::all(1));
EXPECT_TRUE(AbsExact().to_compare_f()(out_mat_ocv, out_mat_gapi));
}
TEST_P(FindContoursHNoOffsetTest, AccuracyTest)
{
std::vector<std::vector<cv::Point>> outCtsOCV, outCtsGAPI;
std::vector<cv::Vec4i> outHierOCV, outHierGAPI;
initMatForFindingContours(in_mat1, sz, type);
out_mat_gapi = cv::Mat(sz, type, cv::Scalar::all(0));
out_mat_ocv = cv::Mat(sz, type, cv::Scalar::all(0));
// OpenCV code /////////////////////////////////////////////////////////////
{
cv::findContours(in_mat1, outCtsOCV, outHierOCV, mode, method);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
cv::GArray<cv::GArray<cv::Point>> outCts;
cv::GArray<cv::Vec4i> outHier;
std::tie(outCts, outHier) = cv::gapi::findContoursH(in, mode, method);
cv::GComputation c(GIn(in), GOut(outCts, outHier));
c.apply(gin(in_mat1), gout(outCtsGAPI, outHierGAPI), getCompileArgs());
// Comparison //////////////////////////////////////////////////////////////
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
cv::fillPoly(out_mat_ocv, outCtsOCV, cv::Scalar::all(1));
cv::fillPoly(out_mat_gapi, outCtsGAPI, cv::Scalar::all(1));
EXPECT_TRUE(AbsExact().to_compare_f()(out_mat_ocv, out_mat_gapi));
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
EXPECT_TRUE(AbsExactVector<cv::Vec4i>().to_compare_f()(outHierOCV, outHierGAPI));
}
TEST_P(FindContoursHOffsetTest, AccuracyTest)
{
const cv::Size sz(1280, 720);
const MatType2 type = CV_8UC1;
const cv::RetrievalModes mode = cv::RETR_EXTERNAL;
const cv::ContourApproximationModes method = cv::CHAIN_APPROX_NONE;
const cv::Point offset(15, 15);
std::vector<std::vector<cv::Point>> outCtsOCV, outCtsGAPI;
std::vector<cv::Vec4i> outHierOCV, outHierGAPI;
initMatForFindingContours(in_mat1, sz, type);
out_mat_gapi = cv::Mat(sz, type, cv::Scalar::all(0));
out_mat_ocv = cv::Mat(sz, type, cv::Scalar::all(0));
// OpenCV code /////////////////////////////////////////////////////////////
{
cv::findContours(in_mat1, outCtsOCV, outHierOCV, mode, method, offset);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
GOpaque<Point> gOffset;
cv::GArray<cv::GArray<cv::Point>> outCts;
cv::GArray<cv::Vec4i> outHier;
std::tie(outCts, outHier) = cv::gapi::findContoursH(in, mode, method, gOffset);
cv::GComputation c(GIn(in, gOffset), GOut(outCts, outHier));
c.apply(gin(in_mat1, offset), gout(outCtsGAPI, outHierGAPI), getCompileArgs());
// Comparison //////////////////////////////////////////////////////////////
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
cv::fillPoly(out_mat_ocv, outCtsOCV, cv::Scalar::all(1));
cv::fillPoly(out_mat_gapi, outCtsGAPI, cv::Scalar::all(1));
EXPECT_TRUE(AbsExact().to_compare_f()(out_mat_ocv, out_mat_gapi));
EXPECT_TRUE(outCtsGAPI.size() == outCtsOCV.size());
EXPECT_TRUE(AbsExactVector<cv::Vec4i>().to_compare_f()(outHierOCV, outHierGAPI));
}
TEST_P(BoundingRectMatTest, AccuracyTest)
{
cv::Rect out_rect_gapi, out_rect_ocv;
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::boundingRect(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
c.apply(cv::gin(in_mat1), cv::gout(out_rect_gapi), getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
{
out_rect_ocv = cv::boundingRect(in_mat1);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_rect_gapi, out_rect_ocv));
}
}
TEST_P(BoundingRectMatVector32STest, AccuracyTest)
{
cv::Rect out_rect_gapi, out_rect_ocv;
std::vector<cv::Point2i> in_vectorS(sz.width);
cv::randu(in_vectorS, cv::Scalar::all(0), cv::Scalar::all(255));
in_mat1 = cv::Mat(in_vectorS);
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::boundingRect(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
c.apply(cv::gin(in_mat1), cv::gout(out_rect_gapi), getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
{
out_rect_ocv = cv::boundingRect(in_mat1);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_rect_gapi, out_rect_ocv));
}
}
TEST_P(BoundingRectMatVector32FTest, AccuracyTest)
{
cv::RNG& rng = theRNG();
cv::Rect out_rect_gapi, out_rect_ocv;
std::vector<cv::Point2f> in_vectorF(sz.width);
const int fscale = 256; // avoid bits near ULP, generate stable test input
for (int i = 0; i < sz.width; i++)
{
cv::Point2f pt(rng.uniform(0, 255 * fscale) / static_cast<float>(fscale),
rng.uniform(0, 255 * fscale) / static_cast<float>(fscale));
in_vectorF.push_back(pt);
}
in_mat1 = cv::Mat(in_vectorF);
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::boundingRect(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
c.apply(cv::gin(in_mat1), cv::gout(out_rect_gapi), getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
{
out_rect_ocv = cv::boundingRect(in_mat1);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_rect_gapi, out_rect_ocv));
}
}
TEST_P(BoundingRectVector32STest, AccuracyTest)
{
cv::Rect out_rect_gapi, out_rect_ocv;
std::vector<cv::Point2i> in_vectorS(sz.width);
cv::randu(in_vectorS, cv::Scalar::all(0), cv::Scalar::all(255));
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point2i> in;
auto out = cv::gapi::boundingRect(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
c.apply(cv::gin(in_vectorS), cv::gout(out_rect_gapi), getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
{
out_rect_ocv = cv::boundingRect(in_vectorS);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_rect_gapi, out_rect_ocv));
}
}
TEST_P(BoundingRectVector32FTest, AccuracyTest)
{
cv::RNG& rng = theRNG();
cv::Rect out_rect_gapi, out_rect_ocv;
std::vector<cv::Point2f> in_vectorF(sz.width);
const int fscale = 256; // avoid bits near ULP, generate stable test input
for (int i = 0; i < sz.width; i++)
{
cv::Point2f pt(rng.uniform(0, 255 * fscale) / static_cast<float>(fscale),
rng.uniform(0, 255 * fscale) / static_cast<float>(fscale));
in_vectorF.push_back(pt);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GArray<cv::Point2f> in;
auto out = cv::gapi::boundingRect(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
c.apply(cv::gin(in_vectorF), cv::gout(out_rect_gapi), getCompileArgs());
// OpenCV code /////////////////////////////////////////////////////////////
{
out_rect_ocv = cv::boundingRect(in_vectorF);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_rect_gapi, out_rect_ocv));
}
}
TEST_P(BGR2RGBTest, AccuracyTest)
{
// G-API code //////////////////////////////////////////////////////////////
+105 -3
View File
@@ -463,6 +463,7 @@ struct TestWithParamsSpecific : public TestWithParamsBase<ParamsSpecific<Specifi
using compare_f = std::function<bool(const cv::Mat &a, const cv::Mat &b)>;
using compare_scalar_f = std::function<bool(const cv::Scalar &a, const cv::Scalar &b)>;
using compare_rect_f = std::function<bool(const cv::Rect &a, const cv::Rect &b)>;
template<typename Elem>
using compare_vector_f = std::function<bool(const std::vector<Elem> &a,
@@ -489,6 +490,7 @@ private:
using CompareMats = CompareF<cv::Mat, cv::Mat>;
using CompareScalars = CompareF<cv::Scalar, cv::Scalar>;
using CompareRects = CompareF<cv::Rect, cv::Rect>;
template<typename Elem>
using CompareVectors = CompareF<std::vector<Elem>, std::vector<Elem>>;
@@ -535,6 +537,27 @@ struct WrappableScalar
}
};
template<typename T>
struct WrappableRect
{
compare_rect_f to_compare_f()
{
T t = *static_cast<T*const>(this);
return [t](const cv::Rect &a, const cv::Rect &b)
{
return t(a, b);
};
}
CompareRects to_compare_obj()
{
T t = *static_cast<T*const>(this);
std::stringstream ss;
ss << t;
return CompareRects(to_compare_f(), ss.str());
}
};
template<typename T, typename Elem>
struct WrappableVector
{
@@ -719,13 +742,15 @@ public:
double err_Inf = cv::norm(in1, in2, NORM_INF);
if (err_Inf > _inf_tol)
{
std::cout << "ToleranceColor error: err_Inf=" << err_Inf << " tolerance=" << _inf_tol << std::endl;;
std::cout << "ToleranceColor error: err_Inf=" << err_Inf
<< " tolerance=" << _inf_tol << std::endl;
return false;
}
double err = cv::norm(in1, in2, NORM_L1 | NORM_RELATIVE);
if (err > _tol)
{
std::cout << "ToleranceColor error: err=" << err << " tolerance=" << _tol << std::endl;;
std::cout << "ToleranceColor error: err=" << err
<< " tolerance=" << _tol << std::endl;
return false;
}
}
@@ -749,7 +774,8 @@ public:
double abs_err = std::abs(in1[0] - in2[0]) / std::max(1.0, std::abs(in2[0]));
if (abs_err > _tol)
{
std::cout << "AbsToleranceScalar error: abs_err=" << abs_err << " tolerance=" << _tol << " in1[0]" << in1[0] << " in2[0]" << in2[0] << std::endl;;
std::cout << "AbsToleranceScalar error: abs_err=" << abs_err << " tolerance=" << _tol
<< " in1[0]" << in1[0] << " in2[0]" << in2[0] << std::endl;
return false;
}
else
@@ -765,6 +791,46 @@ private:
double _tol;
};
class IoUToleranceRect : public WrappableRect<IoUToleranceRect>
{
public:
IoUToleranceRect(double tol) : _tol(tol) {}
bool operator() (const cv::Rect& in1, const cv::Rect& in2) const
{
// determine the (x, y)-coordinates of the intersection rectangle
int xA = max(in1.x, in2.x);
int yA = max(in1.y, in2.y);
int xB = min(in1.br().x, in2.br().x);
int yB = min(in1.br().y, in2.br().y);
// compute the area of intersection rectangle
int interArea = max(0, xB - xA) * max(0, yB - yA);
// compute the area of union rectangle
int unionArea = in1.area() + in2.area() - interArea;
double iou = interArea / unionArea;
double err = 1 - iou;
if (err > _tol)
{
std::cout << "IoUToleranceRect error: err=" << err << " tolerance=" << _tol
<< " in1.x=" << in1.x << " in2.x=" << in2.x
<< " in1.y=" << in1.y << " in2.y=" << in2.y
<< " in1.width=" << in1.width << " in2.width=" << in2.width
<< " in1.height=" << in1.height << " in2.height=" << in2.height << std::endl;
return false;
}
else
{
return true;
}
}
friend std::ostream& operator<<(std::ostream& os, const IoUToleranceRect& obj)
{
return os << "IoUToleranceRect(" << std::to_string(obj._tol) << ")";
}
private:
double _tol;
};
template<typename Elem>
class AbsExactVector : public WrappableVector<AbsExactVector<Elem>, Elem>
{
@@ -803,6 +869,11 @@ inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_sca
return os << "compare_scalar_f";
}
inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_rect_f&)
{
return os << "compare_rect_f";
}
template<typename Elem>
inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_vector_f<Elem>&)
{
@@ -849,6 +920,37 @@ inline std::ostream& operator<<(std::ostream& os, NormTypes op)
return os;
}
inline std::ostream& operator<<(std::ostream& os, RetrievalModes op)
{
#define CASE(v) case RetrievalModes::v: os << #v; break
switch (op)
{
CASE(RETR_EXTERNAL);
CASE(RETR_LIST);
CASE(RETR_CCOMP);
CASE(RETR_TREE);
CASE(RETR_FLOODFILL);
default: GAPI_Assert(false && "unknown RetrievalModes value");
}
#undef CASE
return os;
}
inline std::ostream& operator<<(std::ostream& os, ContourApproximationModes op)
{
#define CASE(v) case ContourApproximationModes::v: os << #v; break
switch (op)
{
CASE(CHAIN_APPROX_NONE);
CASE(CHAIN_APPROX_SIMPLE);
CASE(CHAIN_APPROX_TC89_L1);
CASE(CHAIN_APPROX_TC89_KCOS);
default: GAPI_Assert(false && "unknown ContourApproximationModes value");
}
#undef CASE
return os;
}
inline std::ostream& operator<<(std::ostream& os, MorphTypes op)
{
#define CASE(v) case MorphTypes::v: os << #v; break