Skip to content

Commit 40b238b

Browse files
tsenstalalek
authored andcommitted
Merge pull request #2367 from tsenst:add_robust_interpolation_of_correspondence
Add RIC method for sparse match interpolation * * add RIC * sparse_match_interpolators EdgeAwareInterpolation - enhance limitation of the maximal number of matches to be interpolated from SHORT_MAX to INT_MAX by substituting short by int types * * add RIC citation * * update EdgeAwareInterpolatorImpl * * add intermediate RIC implementation * * implementation of new paralelization classes. First test where sucessfull. * * update documentation RICInterpolatorImpl and RLOF * * update CMakeLists.txt remove highgui * add test for RIC * add ASSERTION for curr image * * add cost map interface * * fix internal cost map allocation bug * * remove white spaces * fix warnings * *fix compiler warnings * * remove double whitespaces * underscore from parameters * substitute parallel_for_() classes with lambda functions * remove complex assertion statements * remove dead code * substitute swap function with std::swap() * ocv_define_module now contains video instead of tracking module * * remove whitespace endings * * documentation update * * remove double declarations that lead to warnings * * unrole tracker.py * remove double space * use static for inner functions * update create function * modify mem init. to avoid manual memory management * * uncomment parallel_for_ for parallelization * * unrole unwanted changes * uncomment paralellization * * remove empty comment * change CHECK to CHK_GD * remove not necessary ; * *documentation remove double space
1 parent cacec38 commit 40b238b

File tree

10 files changed

+1446
-118
lines changed

10 files changed

+1446
-118
lines changed

modules/optflow/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Optical Flow Algorithms
22
=======================
33

4-
Algorithms for running and evaluating deepflow, simpleflow, sparsetodenseflow and motion templates (silhouette flow).
4+
Algorithms for running and evaluating deepflow, simpleflow, sparsetodenseflow, robust local optical flow and motion templates (silhouette flow).

modules/optflow/doc/optflow.bib

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,26 @@ @article{Senst2012
104104
number = {9},
105105
}
106106

107+
@phdthesis{Senst2019,
108+
title={Estimation and analysis of motion in video data},
109+
author={Senst, Tobias},
110+
year={2019},
111+
school={Technical Univerity Berlin},
112+
doi = {10.14279/depositonce-9085},
113+
url = {https://doi.org/10.14279/depositonce-9085}
114+
}
115+
107116
@article{Tibshirani2008,
108117
title={Fast computation of the median by successive binning},
109118
author={Tibshirani, Ryan J},
110119
journal={arXiv preprint arXiv:0806.3301},
111120
year={2008}
112121
}
122+
123+
@inproceedings{Hu2017,
124+
title={Robust interpolation of correspondences for large displacement optical flow},
125+
author={Hu, Yinlin and Li, Yunsong and Song, Rui},
126+
booktitle={IEEE Conference on Computer Vision and Pattern Recognition},
127+
pages={481--489},
128+
year={2017}
129+
}

modules/optflow/include/opencv2/optflow/rlofflow.hpp

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ enum SolverType {
3030
enum InterpolationType
3131
{
3232
INTERP_GEO = 0, /**< Fast geodesic interpolation, see @cite Geistert2016 */
33-
INTERP_EPIC = 1, /**< Edge-preserving interpolation, see @cite Revaud2015,Geistert2016. */
33+
INTERP_EPIC = 1, /**< Edge-preserving interpolation using ximgproc::EdgeAwareInterpolator, see @cite Revaud2015,Geistert2016. */
34+
INTERP_RIC = 2, /**< SLIC based robust interpolation using ximgproc::RICInterpolator, see @cite Hu2017. */
3435
};
3536

3637
/** @brief This is used store and set up the parameters of the robust local optical flow (RLOF) algoritm.
3738
*
3839
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
3940
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
40-
* proposed by @cite Bouguet00. The implementation is derived from optflow::calcOpticalFlowPyrLK().
41+
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
42+
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
4143
* This RLOF implementation can be seen as an improved pyramidal iterative Lucas-Kanade and includes
4244
* a set of improving modules. The main improvements in respect to the pyramidal iterative Lucas-Kanade
4345
* are:
@@ -200,7 +202,8 @@ class CV_EXPORTS_W RLOFOpticalFlowParameter{
200202
*
201203
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
202204
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
203-
* proposed by @cite Bouguet00. The implementation is derived from optflow::calcOpticalFlowPyrLK().
205+
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
206+
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
204207
*
205208
* The sparse-to-dense interpolation scheme allows for fast computation of dense optical flow using RLOF (see @cite Geistert2016).
206209
* For this scheme the following steps are applied:
@@ -324,6 +327,35 @@ class CV_EXPORTS_W DenseRLOFOpticalFlow : public DenseOpticalFlow
324327
* @see ximgproc::fastGlobalSmootherFilter, setUsePostProc
325328
*/
326329
CV_WRAP virtual bool getUsePostProc() const = 0;
330+
//! @brief enables VariationalRefinement
331+
/**
332+
* @see getUseVariationalRefinement
333+
*/
334+
CV_WRAP virtual void setUseVariationalRefinement(bool val) = 0;
335+
/** @copybrief setUseVariationalRefinement
336+
* @see ximgproc::fastGlobalSmootherFilter, setUsePostProc
337+
*/
338+
CV_WRAP virtual bool getUseVariationalRefinement() const = 0;
339+
//! @brief Parameter to tune the approximate size of the superpixel used for oversegmentation.
340+
/**
341+
* @see cv::ximgproc::createSuperpixelSLIC, cv::ximgproc::RICInterpolator
342+
*/
343+
CV_WRAP virtual void setRICSPSize(int val) = 0;
344+
/** @copybrief setRICSPSize
345+
* @see setRICSPSize
346+
*/
347+
CV_WRAP virtual int getRICSPSize() const = 0;
348+
/** @brief Parameter to choose superpixel algorithm variant to use:
349+
* - cv::ximgproc::SLICType SLIC segments image using a desired region_size (value: 100)
350+
* - cv::ximgproc::SLICType SLICO will optimize using adaptive compactness factor (value: 101)
351+
* - cv::ximgproc::SLICType MSLIC will optimize using manifold methods resulting in more content-sensitive superpixels (value: 102).
352+
* @see cv::ximgproc::createSuperpixelSLIC, cv::ximgproc::RICInterpolator
353+
*/
354+
CV_WRAP virtual void setRICSLICType(int val) = 0;
355+
/** @copybrief setRICSLICType
356+
* @see setRICSLICType
357+
*/
358+
CV_WRAP virtual int getRICSLICType() const = 0;
327359
//! @brief Creates instance of optflow::DenseRLOFOpticalFlow
328360
/**
329361
* @param rlofParam see optflow::RLOFOpticalFlowParameter
@@ -333,9 +365,12 @@ class CV_EXPORTS_W DenseRLOFOpticalFlow : public DenseOpticalFlow
333365
* @param epicK see setEPICK
334366
* @param epicSigma see setEPICSigma
335367
* @param epicLambda see setEPICLambda
368+
* @param ricSPSize see setRICSPSize
369+
* @param ricSLICType see setRICSLICType
336370
* @param use_post_proc see setUsePostProc
337371
* @param fgsLambda see setFgsLambda
338372
* @param fgsSigma see setFgsSigma
373+
* @param use_variational_refinement see setUseVariationalRefinement
339374
*/
340375
CV_WRAP static Ptr<DenseRLOFOpticalFlow> create(
341376
Ptr<RLOFOpticalFlowParameter> rlofParam = Ptr<RLOFOpticalFlowParameter>(),
@@ -345,16 +380,20 @@ class CV_EXPORTS_W DenseRLOFOpticalFlow : public DenseOpticalFlow
345380
int epicK = 128,
346381
float epicSigma = 0.05f,
347382
float epicLambda = 999.0f,
383+
int ricSPSize = 15,
384+
int ricSLICType = 100,
348385
bool use_post_proc = true,
349386
float fgsLambda = 500.0f,
350-
float fgsSigma = 1.5f);
387+
float fgsSigma = 1.5f,
388+
bool use_variational_refinement = false);
351389
};
352390

353391
/** @brief Class used for calculation sparse optical flow and feature tracking with robust local optical flow (RLOF) algorithms.
354392
*
355393
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
356394
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
357-
* proposed by @cite Bouguet00. The implementation is derived from optflow::calcOpticalFlowPyrLK().
395+
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
396+
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
358397
*
359398
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
360399
* Parameters have been described in @cite Senst2012, @cite Senst2013, @cite Senst2014 and @cite Senst2016.
@@ -401,7 +440,8 @@ class CV_EXPORTS_W SparseRLOFOpticalFlow : public SparseOpticalFlow
401440
*
402441
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
403442
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
404-
* proposed by @cite Bouguet00. The implementation is derived from optflow::calcOpticalFlowPyrLK().
443+
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
444+
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
405445
*
406446
* The sparse-to-dense interpolation scheme allows for fast computation of dense optical flow using RLOF (see @cite Geistert2016).
407447
* For this scheme the following steps are applied:
@@ -430,12 +470,15 @@ class CV_EXPORTS_W SparseRLOFOpticalFlow : public SparseOpticalFlow
430470
* supported:
431471
* - **INTERP_GEO** applies the fast geodesic interpolation, see @cite Geistert2016.
432472
* - **INTERP_EPIC_RESIDUAL** applies the edge-preserving interpolation, see @cite Revaud2015,Geistert2016.
433-
* @param epicK see ximgproc::EdgeAwareInterpolator() sets the respective parameter.
434-
* @param epicSigma see ximgproc::EdgeAwareInterpolator() sets the respective parameter.
435-
* @param epicLambda see ximgproc::EdgeAwareInterpolator() sets the respective parameter.
473+
* @param epicK see ximgproc::EdgeAwareInterpolator sets the respective parameter.
474+
* @param epicSigma see ximgproc::EdgeAwareInterpolator sets the respective parameter.
475+
* @param epicLambda see ximgproc::EdgeAwareInterpolator sets the respective parameter.
476+
* @param ricSPSize see ximgproc::RICInterpolator sets the respective parameter.
477+
* @param ricSLICType see ximgproc::RICInterpolator sets the respective parameter.
436478
* @param use_post_proc enables ximgproc::fastGlobalSmootherFilter() parameter.
437479
* @param fgsLambda sets the respective ximgproc::fastGlobalSmootherFilter() parameter.
438480
* @param fgsSigma sets the respective ximgproc::fastGlobalSmootherFilter() parameter.
481+
* @param use_variational_refinement enables VariationalRefinement
439482
*
440483
* Parameters have been described in @cite Senst2012, @cite Senst2013, @cite Senst2014, @cite Senst2016.
441484
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
@@ -451,14 +494,17 @@ CV_EXPORTS_W void calcOpticalFlowDenseRLOF(InputArray I0, InputArray I1, InputOu
451494
float forwardBackwardThreshold = 0, Size gridStep = Size(6, 6),
452495
InterpolationType interp_type = InterpolationType::INTERP_EPIC,
453496
int epicK = 128, float epicSigma = 0.05f, float epicLambda = 100.f,
454-
bool use_post_proc = true, float fgsLambda = 500.0f, float fgsSigma = 1.5f);
497+
int ricSPSize = 15, int ricSLICType = 100,
498+
bool use_post_proc = true, float fgsLambda = 500.0f, float fgsSigma = 1.5f,
499+
bool use_variational_refinement = false);
455500

456501
/** @brief Calculates fast optical flow for a sparse feature set using the robust local optical flow (RLOF) similar
457502
* to optflow::calcOpticalFlowPyrLK().
458503
*
459504
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
460505
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
461-
* proposed by @cite Bouguet00. The implementation is derived from optflow::calcOpticalFlowPyrLK().
506+
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
507+
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
462508
*
463509
* @param prevImg first 8-bit input image. If The cross-based RLOF is used (by selecting optflow::RLOFOpticalFlowParameter::supportRegionType
464510
* = SupportRegionType::SR_CROSS) image has to be a 8-bit 3 channel image.

modules/optflow/samples/optical_flow_evaluation.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ using namespace optflow;
1212
const String keys = "{help h usage ? | | print this message }"
1313
"{@image1 | | image1 }"
1414
"{@image2 | | image2 }"
15-
"{@algorithm | | [farneback, simpleflow, tvl1, deepflow, sparsetodenseflow, pcaflow, DISflow_ultrafast, DISflow_fast, DISflow_medium] }"
15+
"{@algorithm | | [farneback, simpleflow, tvl1, deepflow, sparsetodenseflow, RLOF_EPIC, RLOF_RIC, pcaflow, DISflow_ultrafast, DISflow_fast, DISflow_medium] }"
1616
"{@groundtruth | | path to the .flo file (optional), Middlebury format }"
1717
"{m measure |endpoint| error measure - [endpoint or angular] }"
1818
"{r region |all | region to compute stats about [all, discontinuities, untextured] }"
@@ -259,6 +259,20 @@ int main( int argc, char** argv )
259259
algorithm = createOptFlow_DeepFlow();
260260
else if ( method == "sparsetodenseflow" )
261261
algorithm = createOptFlow_SparseToDense();
262+
else if (method == "RLOF_EPIC")
263+
{
264+
algorithm = createOptFlow_DenseRLOF();
265+
Ptr<DenseRLOFOpticalFlow> rlof = algorithm.dynamicCast< DenseRLOFOpticalFlow>();
266+
rlof->setInterpolation(INTERP_EPIC);
267+
rlof->setForwardBackward(1.f);
268+
}
269+
else if (method == "RLOF_RIC")
270+
{
271+
algorithm = createOptFlow_DenseRLOF();
272+
Ptr<DenseRLOFOpticalFlow> rlof = algorithm.dynamicCast< DenseRLOFOpticalFlow>();;
273+
rlof->setInterpolation(INTERP_RIC);
274+
rlof->setForwardBackward(1.f);
275+
}
262276
else if ( method == "pcaflow" ) {
263277
if ( parser.has("prior") ) {
264278
String prior = parser.get<String>("prior");

modules/optflow/src/rlofflow.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
7070
, fgs_lambda(500.0f)
7171
, fgs_sigma(1.5f)
7272
, use_post_proc(true)
73+
, use_variational_refinement(false)
74+
, sp_size(15)
75+
, slic_type(ximgproc::SLIC)
7376

7477
{
7578
prevPyramid[0] = cv::Ptr<CImageBuffer>(new CImageBuffer);
@@ -107,6 +110,15 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
107110
virtual bool getUsePostProc() const CV_OVERRIDE { return use_post_proc; }
108111
virtual void setUsePostProc(bool val) CV_OVERRIDE { use_post_proc = val; }
109112

113+
virtual void setUseVariationalRefinement(bool val) CV_OVERRIDE { use_variational_refinement = val; }
114+
virtual bool getUseVariationalRefinement() const CV_OVERRIDE { return use_variational_refinement; }
115+
116+
virtual void setRICSPSize(int val) CV_OVERRIDE { sp_size = val; }
117+
virtual int getRICSPSize() const CV_OVERRIDE { return sp_size; }
118+
119+
virtual void setRICSLICType(int val) CV_OVERRIDE { slic_type = static_cast<ximgproc::SLICType>(val); }
120+
virtual int getRICSLICType() const CV_OVERRIDE { return slic_type; }
121+
110122
virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow) CV_OVERRIDE
111123
{
112124
CV_Assert(!I0.empty() && I0.depth() == CV_8U && (I0.channels() == 3 || I0.channels() == 1));
@@ -116,7 +128,7 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
116128
param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter());
117129
if (param->supportRegionType == SR_CROSS)
118130
CV_Assert( I0.channels() == 3 && I1.channels() == 3);
119-
CV_Assert(interp_type == InterpolationType::INTERP_EPIC || interp_type == InterpolationType::INTERP_GEO);
131+
CV_Assert(interp_type == InterpolationType::INTERP_EPIC || interp_type == InterpolationType::INTERP_GEO || interp_type == InterpolationType::INTERP_RIC);
120132
// if no parameter is used use the default parameter
121133

122134
Mat prevImage = I0.getMat();
@@ -184,9 +196,23 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
184196
gd->setK(k);
185197
gd->setSigma(sigma);
186198
gd->setLambda(lambda);
199+
gd->setFGSLambda(fgs_lambda);
200+
gd->setFGSSigma(fgs_sigma);
187201
gd->setUsePostProcessing(false);
188202
gd->interpolate(prevImage, filtered_prevPoints, currImage, filtered_currPoints, dense_flow);
189203
}
204+
else if (interp_type == InterpolationType::INTERP_RIC)
205+
{
206+
Ptr<ximgproc::RICInterpolator> gd = ximgproc::createRICInterpolator();
207+
gd->setK(k);
208+
gd->setFGSLambda(fgs_lambda);
209+
gd->setFGSSigma(fgs_sigma);
210+
gd->setSuperpixelSize(sp_size);
211+
gd->setSuperpixelMode(slic_type);
212+
gd->setUseGlobalSmootherFilter(false);
213+
gd->setUseVariationalRefinement(false);
214+
gd->interpolate(prevImage, filtered_prevPoints, currImage, filtered_currPoints, dense_flow);
215+
}
190216
else
191217
{
192218
Mat blurredPrevImage, blurredCurrImage;
@@ -200,6 +226,15 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
200226
cv::bilateralFilter(vecMats[1], vecMats2[1], 5, 2, 20);
201227
cv::merge(vecMats2, dense_flow);
202228
}
229+
if (use_variational_refinement)
230+
{
231+
Mat prevGrey, currGrey;
232+
Ptr<VariationalRefinement > variationalrefine = VariationalRefinement::create();
233+
cvtColor(prevImage, prevGrey, COLOR_BGR2GRAY);
234+
cvtColor(currImage, currGrey, COLOR_BGR2GRAY);
235+
variationalrefine->setOmega(1.9f);
236+
variationalrefine->calc(prevGrey, currGrey, flow);
237+
}
203238
if (use_post_proc)
204239
{
205240
ximgproc::fastGlobalSmootherFilter(prevImage, flow, flow, fgs_lambda, fgs_sigma);
@@ -227,6 +262,9 @@ class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
227262
float fgs_lambda;
228263
float fgs_sigma;
229264
bool use_post_proc;
265+
bool use_variational_refinement;
266+
int sp_size;
267+
ximgproc::SLICType slic_type;
230268
};
231269

232270
Ptr<DenseRLOFOpticalFlow> DenseRLOFOpticalFlow::create(
@@ -237,9 +275,12 @@ Ptr<DenseRLOFOpticalFlow> DenseRLOFOpticalFlow::create(
237275
int epicK,
238276
float epicSigma,
239277
float epicLambda,
278+
int ricSPSize,
279+
int ricSLICType,
240280
bool use_post_proc,
241281
float fgs_lambda,
242-
float fgs_sigma)
282+
float fgs_sigma,
283+
bool use_variational_refinement)
243284
{
244285
Ptr<DenseRLOFOpticalFlow> algo = makePtr<DenseOpticalFlowRLOFImpl>();
245286
algo->setRLOFOpticalFlowParameter(rlofParam);
@@ -252,6 +293,9 @@ Ptr<DenseRLOFOpticalFlow> DenseRLOFOpticalFlow::create(
252293
algo->setUsePostProc(use_post_proc);
253294
algo->setFgsLambda(fgs_lambda);
254295
algo->setFgsSigma(fgs_sigma);
296+
algo->setRICSLICType(ricSLICType);
297+
algo->setRICSPSize(ricSPSize);
298+
algo->setUseVariationalRefinement(use_variational_refinement);
255299
return algo;
256300
}
257301

@@ -381,11 +425,13 @@ void calcOpticalFlowDenseRLOF(InputArray I0, InputArray I1, InputOutputArray flo
381425
float forewardBackwardThreshold, Size gridStep,
382426
InterpolationType interp_type,
383427
int epicK, float epicSigma, float epicLambda,
384-
bool use_post_proc, float fgsLambda, float fgsSigma)
428+
int superpixelSize, int superpixelType,
429+
bool use_post_proc, float fgsLambda, float fgsSigma, bool use_variational_refinement)
385430
{
386431
Ptr<DenseRLOFOpticalFlow> algo = DenseRLOFOpticalFlow::create(
387432
rlofParam, forewardBackwardThreshold, gridStep, interp_type,
388-
epicK, epicSigma, epicLambda, use_post_proc, fgsLambda, fgsSigma);
433+
epicK, epicSigma, epicLambda, superpixelSize, superpixelType,
434+
use_post_proc, fgsLambda, fgsSigma, use_variational_refinement);
389435
algo->calc(I0, I1, flow);
390436
algo->collectGarbage();
391437
}

modules/ximgproc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
set(the_description "Extended image processing module. It includes edge-aware filters and etc.")
2-
ocv_define_module(ximgproc opencv_core opencv_imgproc opencv_calib3d opencv_imgcodecs WRAP python java)
2+
ocv_define_module(ximgproc opencv_core opencv_imgproc opencv_calib3d opencv_imgcodecs opencv_video WRAP python java)

modules/ximgproc/doc/ximgproc.bib

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,11 @@ @inproceedings{BarronPoole2016
328328
publisher={Springer International Publishing},
329329
pages={617--632},
330330
}
331+
332+
@inproceedings{Hu2017,
333+
title={Robust interpolation of correspondences for large displacement optical flow},
334+
author={Hu, Yinlin and Li, Yunsong and Song, Rui},
335+
booktitle={IEEE Conference on Computer Vision and Pattern Recognition},
336+
pages={481--489},
337+
year={2017}
338+
}

0 commit comments

Comments
 (0)