Skip to content

Commit

Permalink
Merge pull request opencv#17082 from OrestChura:oc/buildPyramid
Browse files Browse the repository at this point in the history
  • Loading branch information
alalek committed Apr 23, 2020
2 parents a1641f9 + 05d5c28 commit 189fc43
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 43 deletions.
6 changes: 6 additions & 0 deletions modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ template<typename U> struct get_out<cv::GArray<U>>
return ctx.outVecR<U>(idx);
}
};

//FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
template<> struct get_out<cv::GArray<cv::GMat> >: public get_out<cv::GArray<cv::Mat> >
{
};

template<typename U> struct get_out<cv::GOpaque<U>>
{
static U& get(GCPUContext &ctx, int idx)
Expand Down
105 changes: 73 additions & 32 deletions modules/gapi/include/opencv2/gapi/video.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,83 @@
namespace cv { namespace gapi {
namespace video
{
using GOptFlowLKOutput = std::tuple<cv::GArray<cv::Point2f>,
cv::GArray<uchar>,
cv::GArray<float>>;

G_TYPED_KERNEL(GCalcOptFlowLK,
<GOptFlowLKOutput(GMat,GMat,cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,
int,TermCriteria,int,double)>,
"org.opencv.video.calcOpticalFlowPyrLK")
using GBuildPyrOutput = std::tuple<GArray<GMat>, GScalar>;

using GOptFlowLKOutput = std::tuple<cv::GArray<cv::Point2f>,
cv::GArray<uchar>,
cv::GArray<float>>;

G_TYPED_KERNEL(GBuildOptFlowPyramid, <GBuildPyrOutput(GMat,Size,GScalar,bool,int,int,bool)>,
"org.opencv.video.buildOpticalFlowPyramid")
{
static std::tuple<GArrayDesc,GScalarDesc>
outMeta(GMatDesc,const Size&,GScalarDesc,bool,int,int,bool)
{
return std::make_tuple(empty_array_desc(), empty_scalar_desc());
}
};

G_TYPED_KERNEL(GCalcOptFlowLK,
<GOptFlowLKOutput(GMat,GMat,cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,
GScalar,TermCriteria,int,double)>,
"org.opencv.video.calcOpticalFlowPyrLK")
{
static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GMatDesc,GMatDesc,GArrayDesc,
GArrayDesc,const Size&,GScalarDesc,
const TermCriteria&,int,double)
{
static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GMatDesc,GMatDesc,GArrayDesc,
GArrayDesc,const Size&,int,
const TermCriteria&,int,double)
{
return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc());
}

};

G_TYPED_KERNEL(GCalcOptFlowLKForPyr,
<GOptFlowLKOutput(cv::GArray<cv::GMat>,cv::GArray<cv::GMat>,
cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,int,
TermCriteria,int,double)>,
"org.opencv.video.calcOpticalFlowPyrLKForPyr")
return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc());
}

};

G_TYPED_KERNEL(GCalcOptFlowLKForPyr,
<GOptFlowLKOutput(cv::GArray<cv::GMat>,cv::GArray<cv::GMat>,
cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,GScalar,
TermCriteria,int,double)>,
"org.opencv.video.calcOpticalFlowPyrLKForPyr")
{
static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GArrayDesc,GArrayDesc,
GArrayDesc,GArrayDesc,
const Size&,GScalarDesc,
const TermCriteria&,int,double)
{
static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GArrayDesc,GArrayDesc,
GArrayDesc,GArrayDesc,
const Size&,int,
const TermCriteria&,int,double)
{
return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc());
}
};
return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc());
}
};
} //namespace video

//! @addtogroup gapi_video
//! @{
/** @brief Constructs the image pyramid which can be passed to calcOpticalFlowPyrLK.
@note Function textual ID is "org.opencv.video.buildOpticalFlowPyramid"
@param img 8-bit input image.
@param winSize window size of optical flow algorithm. Must be not less than winSize
argument of calcOpticalFlowPyrLK. It is needed to calculate required
padding for pyramid levels.
@param maxLevel 0-based maximal pyramid level number.
@param withDerivatives set to precompute gradients for the every pyramid level. If pyramid is
constructed without the gradients then calcOpticalFlowPyrLK will calculate
them internally.
@param pyrBorder the border mode for pyramid layers.
@param derivBorder the border mode for gradients.
@param tryReuseInputImage put ROI of input image into the pyramid if possible. You can pass false
to force data copying.
@return output pyramid.
@return number of levels in constructed pyramid. Can be less than maxLevel.
*/
GAPI_EXPORTS std::tuple<GArray<GMat>, GScalar>
buildOpticalFlowPyramid(const GMat &img,
const Size &winSize,
const GScalar &maxLevel,
bool withDerivatives = true,
int pyrBorder = BORDER_REFLECT_101,
int derivBorder = BORDER_CONSTANT,
bool tryReuseInputImage = true);

/** @brief Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade
method with pyramids.
Expand Down Expand Up @@ -104,7 +145,7 @@ calcOpticalFlowPyrLK(const GMat &prevImg,
const GArray<Point2f> &prevPts,
const GArray<Point2f> &predPts,
const Size &winSize = Size(21, 21),
int maxLevel = 3,
const GScalar &maxLevel = 3,
const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT |
TermCriteria::EPS,
30, 0.01),
Expand All @@ -121,7 +162,7 @@ calcOpticalFlowPyrLK(const GArray<GMat> &prevPyr,
const GArray<Point2f> &prevPts,
const GArray<Point2f> &predPts,
const Size &winSize = Size(21, 21),
int maxLevel = 3,
const GScalar &maxLevel = 3,
const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT |
TermCriteria::EPS,
30, 0.01),
Expand Down
5 changes: 5 additions & 0 deletions modules/gapi/perf/common/gapi_video_perf_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ using namespace perf;

//------------------------------------------------------------------------------

class BuildOptFlowPyramidPerfTest : public TestPerfParams<tuple<std::string,int,int,bool,int,int,
bool,GCompileArgs>> {};
class OptFlowLKPerfTest : public TestPerfParams<tuple<std::string,int,tuple<int,int>,int,
cv::TermCriteria,cv::GCompileArgs>> {};
class OptFlowLKForPyrPerfTest : public TestPerfParams<tuple<std::string,int,tuple<int,int>,int,
cv::TermCriteria,bool,
cv::GCompileArgs>> {};
class BuildPyr_CalcOptFlow_PipelinePerfTest : public TestPerfParams<tuple<std::string,int,int,bool,
cv::GCompileArgs>> {};

} // opencv_test

#endif // OPENCV_GAPI_VIDEO_PERF_TESTS_HPP
69 changes: 69 additions & 0 deletions modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,37 @@ namespace opencv_test

//------------------------------------------------------------------------------

PERF_TEST_P_(BuildOptFlowPyramidPerfTest, TestPerformance)
{
std::vector<Mat> outPyrOCV, outPyrGAPI;
int outMaxLevelOCV = 0, outMaxLevelGAPI = 0;
Scalar outMaxLevelSc;

BuildOpticalFlowPyramidTestParams params;
std::tie(params.fileName, params.winSize,
params.maxLevel, params.withDerivatives,
params.pyrBorder, params.derivBorder,
params.tryReuseInputImage, params.compileArgs) = GetParam();

BuildOpticalFlowPyramidTestOutput outOCV { outPyrOCV, outMaxLevelOCV };
BuildOpticalFlowPyramidTestOutput outGAPI { outPyrGAPI, outMaxLevelGAPI };

GComputation c = runOCVnGAPIBuildOptFlowPyramid(*this, params, outOCV, outGAPI);

declare.in(in_mat1).out(outPyrGAPI);

TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(outPyrGAPI, outMaxLevelSc));
}
outMaxLevelGAPI = static_cast<int>(outMaxLevelSc[0]);

// Comparison //////////////////////////////////////////////////////////////
compareOutputPyramids(outOCV, outGAPI);

SANITY_CHECK_NOTHING();
}

PERF_TEST_P_(OptFlowLKPerfTest, TestPerformance)
{
std::vector<cv::Point2f> outPtsOCV, outPtsGAPI, inPts;
Expand Down Expand Up @@ -83,6 +114,44 @@ PERF_TEST_P_(OptFlowLKForPyrPerfTest, TestPerformance)
SANITY_CHECK_NOTHING();
}

PERF_TEST_P_(BuildPyr_CalcOptFlow_PipelinePerfTest, TestPerformance)
{
std::vector<Point2f> outPtsOCV, outPtsGAPI, inPts;
std::vector<uchar> outStatusOCV, outStatusGAPI;
std::vector<float> outErrOCV, outErrGAPI;

BuildOpticalFlowPyramidTestParams params;
params.pyrBorder = BORDER_DEFAULT;
params.derivBorder = BORDER_DEFAULT;
params.tryReuseInputImage = true;
std::tie(params.fileName, params.winSize,
params.maxLevel, params.withDerivatives,
params.compileArgs) = GetParam();

auto customKernel = gapi::kernels<GCPUMinScalar>();
auto kernels = gapi::combine(customKernel,
params.compileArgs[0].get<gapi::GKernelPackage>());
params.compileArgs = compile_args(kernels);

OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV };
OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI };

cv::GComputation c = runOCVnGAPIOptFlowPipeline(*this, params, outOCV, outGAPI, inPts);

declare.in(in_mat1, in_mat2, inPts).out(outPtsGAPI, outStatusGAPI, outErrGAPI);

TEST_CYCLE()
{
c.apply(cv::gin(in_mat1, in_mat2, inPts, std::vector<cv::Point2f>{ }),
cv::gout(outPtsGAPI, outStatusGAPI, outErrGAPI));
}

// Comparison //////////////////////////////////////////////////////////////
compareOutputsOptFlow(outOCV, outGAPI);

SANITY_CHECK_NOTHING();
}

//------------------------------------------------------------------------------

} // opencv_test
Expand Down
39 changes: 39 additions & 0 deletions modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ namespace

namespace opencv_test
{
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidPerfTestCPU),
BuildOptFlowPyramidPerfTest,
Combine(Values("cv/optflow/rock_1.bmp",
"cv/optflow/frames/1080p_01.png"),
Values(7, 11),
Values(1000),
testing::Bool(),
Values(BORDER_DEFAULT, BORDER_TRANSPARENT),
Values(BORDER_DEFAULT, BORDER_TRANSPARENT),
testing::Bool(),
Values(cv::compile_args(VIDEO_CPU))));

INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidInternalPerfTestCPU),
BuildOptFlowPyramidPerfTest,
Combine(Values("cv/optflow/rock_1.bmp"),
Values(15),
Values(3),
Values(true),
Values(BORDER_REFLECT_101),
Values(BORDER_CONSTANT),
Values(true),
Values(cv::compile_args(VIDEO_CPU))));

INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKPerfTestCPU), OptFlowLKPerfTest,
Combine(Values("cv/optflow/rock_%01d.bmp",
"cv/optflow/frames/1080p_%02d.png"),
Expand Down Expand Up @@ -61,4 +84,20 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalPerfTestCPU),
21, 0.05)),
Values(true),
Values(cv::compile_args(VIDEO_CPU))));

INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelinePerfTestCPU),
BuildPyr_CalcOptFlow_PipelinePerfTest,
Combine(Values("cv/optflow/frames/1080p_%02d.png"),
Values(7, 11),
Values(1000),
Values(true, false),
Values(cv::compile_args(VIDEO_CPU))));

INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineInternalTestPerfCPU),
BuildPyr_CalcOptFlow_PipelinePerfTest,
Combine(Values("cv/optflow/rock_%01d.bmp"),
Values(15),
Values(3),
Values(true),
Values(cv::compile_args(VIDEO_CPU))));
} // opencv_test
15 changes: 13 additions & 2 deletions modules/gapi/src/api/kernels_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,24 @@
namespace cv { namespace gapi {
using namespace video;

GBuildPyrOutput buildOpticalFlowPyramid(const GMat &img,
const Size &winSize,
const GScalar &maxLevel,
bool withDerivatives,
int pyrBorder,
int derivBorder,
bool tryReuseInputImage)
{
return GBuildOptFlowPyramid::on(img, winSize, maxLevel, withDerivatives, pyrBorder,
derivBorder, tryReuseInputImage);
}

GOptFlowLKOutput calcOpticalFlowPyrLK(const GMat &prevImg,
const GMat &nextImg,
const cv::GArray<cv::Point2f> &prevPts,
const cv::GArray<cv::Point2f> &predPts,
const Size &winSize,
int maxLevel,
const GScalar &maxLevel,
const TermCriteria &criteria,
int flags,
double minEigThresh)
Expand All @@ -32,7 +43,7 @@ GOptFlowLKOutput calcOpticalFlowPyrLK(const cv::GArray<cv::GMat> &prevPyr,
const cv::GArray<cv::Point2f> &prevPts,
const cv::GArray<cv::Point2f> &predPts,
const Size &winSize,
int maxLevel,
const GScalar &maxLevel,
const TermCriteria &criteria,
int flags,
double minEigThresh)
Expand Down
Loading

0 comments on commit 189fc43

Please sign in to comment.