Skip to content

Commit 62b1038

Browse files
committed
implement fast guided filter
1 parent 9d733ef commit 62b1038

File tree

4 files changed

+91
-29
lines changed

4 files changed

+91
-29
lines changed

modules/ximgproc/include/opencv2/ximgproc/edge_filter.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,12 @@ channels then only first 3 channels will be used.
153153
@param eps regularization term of Guided Filter. \f${eps}^2\f$ is similar to the sigma in the color
154154
space into bilateralFilter.
155155
156+
@param scale subsample factor of source image and guide image, to reduce computation time. bigger scale means
157+
smaller image after subsampling.
158+
156159
For more details about Guided Filter parameters, see the original article @cite Kaiming10 .
157160
*/
158-
CV_EXPORTS_W Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps);
161+
CV_EXPORTS_W Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps, double scale = 1.0);
159162

160163
/** @brief Simple one-line Guided Filter call.
161164
@@ -176,8 +179,11 @@ space into bilateralFilter.
176179
177180
@param dDepth optional depth of the output image.
178181
182+
@param scale subsample factor of source image and guide image, to reduce computation time. bigger scale means
183+
smaller image after subsampling.
184+
179185
@sa bilateralFilter, dtFilter, amFilter */
180-
CV_EXPORTS_W void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth = -1);
186+
CV_EXPORTS_W void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth = -1, double scale = 1.0);
181187

182188
//////////////////////////////////////////////////////////////////////////
183189
//////////////////////////////////////////////////////////////////////////

modules/ximgproc/perf/perf_guided_filter.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ namespace opencv_test { namespace {
77

88
CV_ENUM(GuideTypes, CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3);
99
CV_ENUM(SrcTypes, CV_8UC1, CV_8UC3, CV_32FC1, CV_32FC3);
10-
typedef tuple<GuideTypes, SrcTypes, Size> GFParams;
10+
CV_ENUM(Scales, 1, 2, 3, 4);
11+
typedef tuple<GuideTypes, SrcTypes, Size, Scales> GFParams;
1112

1213
typedef TestBaseWithParam<GFParams> GuidedFilterPerfTest;
1314

14-
PERF_TEST_P( GuidedFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::all(), Values(sz1080p, sz2K)) )
15+
PERF_TEST_P( GuidedFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::all(), Values(sz1080p, sz2K), Scales::all()) )
1516
{
1617
RNG rng(0);
1718

1819
GFParams params = GetParam();
1920
int guideType = get<0>(params);
2021
int srcType = get<1>(params);
2122
Size sz = get<2>(params);
23+
double scale = get<3>(params);
2224

2325
Mat guide(sz, guideType);
2426
Mat src(sz, srcType);
@@ -30,7 +32,7 @@ PERF_TEST_P( GuidedFilterPerfTest, perf, Combine(GuideTypes::all(), SrcTypes::al
3032
{
3133
int radius = rng.uniform(5, 30);
3234
double eps = rng.uniform(0.1, 1e5);
33-
guidedFilter(guide, src, dst, radius, eps);
35+
guidedFilter(guide, src, dst, radius, eps, -1, scale);
3436
}
3537

3638
SANITY_CHECK_NOTHING();

modules/ximgproc/src/guided_filter.cpp

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,21 @@ class GuidedFilterImpl : public GuidedFilter
128128
{
129129
public:
130130

131-
static Ptr<GuidedFilterImpl> create(InputArray guide, int radius, double eps);
131+
static Ptr<GuidedFilterImpl> create(InputArray guide, int radius, double eps, double scale);
132132

133133
void filter(InputArray src, OutputArray dst, int dDepth = -1) CV_OVERRIDE;
134134

135135
protected:
136136

137137
int radius;
138138
double eps;
139+
double scale;
139140
int h, w;
141+
int hOriginal, wOriginal;
140142

141143
vector<Mat> guideCn;
142144
vector<Mat> guideCnMean;
145+
vector<Mat> guideCnOriginal;
143146

144147
SymArray2D<Mat> covarsInv;
145148

@@ -149,7 +152,7 @@ class GuidedFilterImpl : public GuidedFilter
149152

150153
GuidedFilterImpl() {}
151154

152-
void init(InputArray guide, int radius, double eps);
155+
void init(InputArray guide, int radius, double eps, double scale);
153156

154157
void computeCovGuide(SymArray2D<Mat>& covars);
155158

@@ -167,6 +170,16 @@ class GuidedFilterImpl : public GuidedFilter
167170
src.convertTo(dst, CV_32F);
168171
}
169172

173+
inline void subsample(Mat& src, Mat& dst)
174+
{
175+
resize(src, dst, Size(w, h), 0, 0, INTER_NEAREST);
176+
}
177+
178+
inline void upsample(Mat& src, Mat& dst)
179+
{
180+
resize(src, dst, Size(wOriginal, hOriginal), 0, 0, INTER_LINEAR);
181+
}
182+
170183
private: /*Routines to parallelize boxFilter and convertTo*/
171184

172185
typedef void (GuidedFilterImpl::*TransformFunc)(Mat& src, Mat& dst);
@@ -203,6 +216,20 @@ class GuidedFilterImpl : public GuidedFilter
203216
parallel_for_(pb.getRange(), pb);
204217
}
205218

219+
template<typename V>
220+
void parSubsample(V &src, V &dst)
221+
{
222+
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::subsample);
223+
parallel_for_(pb.getRange(), pb);
224+
}
225+
226+
template<typename V>
227+
void parUpsample(V &src, V &dst)
228+
{
229+
GFTransform_ParBody pb(*this, src, dst, &GuidedFilterImpl::upsample);
230+
parallel_for_(pb.getRange(), pb);
231+
}
232+
206233
private: /*Parallel body classes*/
207234

208235
inline void runParBody(const ParallelLoopBody& pb)
@@ -582,7 +609,7 @@ void GuidedFilterImpl::ApplyTransform_ParBody::operator()(const Range& range) co
582609
{
583610
float *_g[4];
584611
for (int gi = 0; gi < gf.gCnNum; gi++)
585-
_g[gi] = gf.guideCn[gi].ptr<float>(i);
612+
_g[gi] = gf.guideCnOriginal[gi].ptr<float>(i);
586613

587614
float *betaDst, *g, *a;
588615
for (int si = 0; si < srcCnNum; si++)
@@ -593,7 +620,7 @@ void GuidedFilterImpl::ApplyTransform_ParBody::operator()(const Range& range) co
593620
a = alpha[si][gi].ptr<float>(i);
594621
g = _g[gi];
595622

596-
add_mul(betaDst, a, g, gf.w);
623+
add_mul(betaDst, a, g, gf.wOriginal);
597624
}
598625
}
599626
}
@@ -666,28 +693,42 @@ void GuidedFilterImpl::getWalkPattern(int eid, int &cn1, int &cn2)
666693
cn2 = wdata[6 * 2 * (gCnNum-1) + 6 + eid];
667694
}
668695

669-
Ptr<GuidedFilterImpl> GuidedFilterImpl::create(InputArray guide, int radius, double eps)
696+
Ptr<GuidedFilterImpl> GuidedFilterImpl::create(InputArray guide, int radius, double eps, double scale)
670697
{
671698
GuidedFilterImpl *gf = new GuidedFilterImpl();
672-
gf->init(guide, radius, eps);
699+
gf->init(guide, radius, eps, scale);
673700
return Ptr<GuidedFilterImpl>(gf);
674701
}
675702

676-
void GuidedFilterImpl::init(InputArray guide, int radius_, double eps_)
703+
void GuidedFilterImpl::init(InputArray guide, int radius_, double eps_, double scale_)
677704
{
678705
CV_Assert( !guide.empty() && radius_ >= 0 && eps_ >= 0 );
679706
CV_Assert( (guide.depth() == CV_32F || guide.depth() == CV_8U || guide.depth() == CV_16U) && (guide.channels() <= 3) );
707+
CV_Assert( scale_ >= 1.0 );
680708

681709
radius = radius_;
682710
eps = eps_;
711+
scale = scale_;
683712

684-
splitFirstNChannels(guide, guideCn, 3);
685-
gCnNum = (int)guideCn.size();
686-
h = guideCn[0].rows;
687-
w = guideCn[0].cols;
713+
splitFirstNChannels(guide, guideCnOriginal, 3);
714+
gCnNum = (int)guideCnOriginal.size();
715+
hOriginal = guideCnOriginal[0].rows;
716+
wOriginal = guideCnOriginal[0].cols;
717+
h = int(hOriginal / scale);
718+
w = int(wOriginal / scale);
719+
720+
parConvertToWorkType(guideCnOriginal, guideCnOriginal);
721+
if (scale > 1.0)
722+
{
723+
guideCn.resize(gCnNum);
724+
parSubsample(guideCnOriginal, guideCn);
725+
}
726+
else
727+
{
728+
guideCn = guideCnOriginal;
729+
}
688730

689731
guideCnMean.resize(gCnNum);
690-
parConvertToWorkType(guideCn, guideCn);
691732
parMeanFilter(guideCn, guideCnMean);
692733

693734
SymArray2D<Mat> covars;
@@ -712,7 +753,7 @@ void GuidedFilterImpl::computeCovGuide(SymArray2D<Mat>& covars)
712753
void GuidedFilterImpl::filter(InputArray src, OutputArray dst, int dDepth /*= -1*/)
713754
{
714755
CV_Assert( !src.empty() && (src.depth() == CV_32F || src.depth() == CV_8U) );
715-
if (src.rows() != h || src.cols() != w)
756+
if (src.rows() != hOriginal || src.cols() != wOriginal)
716757
{
717758
CV_Error(Error::StsBadSize, "Size of filtering image must be equal to size of guide image");
718759
return;
@@ -725,6 +766,11 @@ void GuidedFilterImpl::filter(InputArray src, OutputArray dst, int dDepth /*= -1
725766
vector<Mat>& srcCnMean = srcCn;
726767
split(src, srcCn);
727768

769+
if (scale > 1.0)
770+
{
771+
parSubsample(srcCn, srcCn);
772+
}
773+
728774
if (src.depth() != CV_32F)
729775
{
730776
parConvertToWorkType(srcCn, srcCn);
@@ -749,7 +795,13 @@ void GuidedFilterImpl::filter(InputArray src, OutputArray dst, int dDepth /*= -1
749795
parMeanFilter(beta, beta);
750796
parMeanFilter(alpha, alpha);
751797

752-
runParBody(ApplyTransform_ParBody(*this, alpha, beta));
798+
if (scale > 1.0)
799+
{
800+
parUpsample(beta, beta);
801+
parUpsample(alpha, alpha);
802+
}
803+
804+
parallel_for_(Range(0, hOriginal), ApplyTransform_ParBody(*this, alpha, beta));
753805
if (dDepth != CV_32F)
754806
{
755807
for (int i = 0; i < srcCnNum; i++)
@@ -782,15 +834,15 @@ void GuidedFilterImpl::computeCovGuideAndSrc(vector<Mat>& srcCn, vector<Mat>& sr
782834
//////////////////////////////////////////////////////////////////////////
783835

784836
CV_EXPORTS_W
785-
Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps)
837+
Ptr<GuidedFilter> createGuidedFilter(InputArray guide, int radius, double eps, double scale)
786838
{
787-
return Ptr<GuidedFilter>(GuidedFilterImpl::create(guide, radius, eps));
839+
return Ptr<GuidedFilter>(GuidedFilterImpl::create(guide, radius, eps, scale));
788840
}
789841

790842
CV_EXPORTS_W
791-
void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth)
843+
void guidedFilter(InputArray guide, InputArray src, OutputArray dst, int radius, double eps, int dDepth, double scale)
792844
{
793-
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps);
845+
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps, scale);
794846
gf->filter(src, dst, dDepth);
795847
}
796848

modules/ximgproc/test/test_guided_filter.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,26 +327,28 @@ TEST_P(GuidedFilterTest, accuracy)
327327
int nThreads = cv::getNumThreads();
328328
if (nThreads == 1)
329329
throw SkipTestException("Single thread environment");
330-
for (int iter = 0; iter < 2; iter++)
330+
for (int scale = 1; scale <= 4; scale++)
331331
{
332332
int radius = rng.uniform(0, 50);
333333
double eps = rng.uniform(0.0, SQR(255.0));
334334

335335
cv::setNumThreads(nThreads);
336336
Mat res;
337-
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps);
337+
Ptr<GuidedFilter> gf = createGuidedFilter(guide, radius, eps, scale);
338338
gf->filter(src, res);
339339

340340
cv::setNumThreads(1);
341341
Mat resRef;
342342
Ptr<GuidedFilter> gfRef(new GuidedFilterRefImpl(guide, radius, eps));
343343
gfRef->filter(src, resRef);
344344

345-
double normInf = cv::norm(res, resRef, NORM_INF);
346-
double normL2 = cv::norm(res, resRef, NORM_L2) / guide.total();
345+
if (scale == 1) {
346+
double normInf = cv::norm(res, resRef, NORM_INF);
347+
EXPECT_LE(normInf, 1.0);
348+
}
347349

348-
EXPECT_LE(normInf, 1.0);
349-
EXPECT_LE(normL2, 1.0/64.0);
350+
double normL2 = cv::norm(res, resRef, NORM_L2) / guide.total();
351+
EXPECT_LE(normL2, scale*scale/64.0);
350352
}
351353
}
352354

0 commit comments

Comments
 (0)