Commit 1a0282df authored by Oded Green's avatar Oded Green

CUDA median filtering using histograms

parent d2e16992
......@@ -314,6 +314,18 @@ CV_EXPORTS Ptr<Filter> createColumnSumFilter(int srcType, int dstType, int ksize
//! @}
///////////////////////////// Median Filtering //////////////////////////////
/** @brief Performs median filtering for each point of the source image.
@param srcType type of of source image. Only CV_8UC1 images are supported for now.
@param windowSize Size of the kernerl used for the filtering. Uses a (windowSize x windowSize) filter.
@param partition Specifies the parallel granularity of the workload. This parameter should be used GPU experts when optimizing performance.
Outputs an image that has been filtered using median-filtering formulation.
*/
CV_EXPORTS Ptr<Filter> createMedianFilter(int srcType, int windowSize, int partition=128);
}} // namespace cv { namespace cuda {
#endif /* __OPENCV_CUDAFILTERS_HPP__ */
......@@ -375,3 +375,43 @@ PERF_TEST_P(Sz_Type_Op, MorphologyEx, Combine(CUDA_TYPICAL_MAT_SIZES, Values(CV_
CPU_SANITY_CHECK(dst);
}
}
//////////////////////////////////////////////////////////////////////
// MedianFilter
//////////////////////////////////////////////////////////////////////
// Median
DEF_PARAM_TEST(Sz_KernelSz, cv::Size, int);
//PERF_TEST_P(Sz_Type_KernelSz, Median, Combine(CUDA_TYPICAL_MAT_SIZES, Values(CV_8UC1,CV_8UC1), Values(3, 5, 7, 9, 11, 13, 15)))
PERF_TEST_P(Sz_KernelSz, Median, Combine(CUDA_TYPICAL_MAT_SIZES, Values(3, 5, 7, 9, 11, 13, 15)))
{
declare.time(20.0);
const cv::Size size = GET_PARAM(0);
// const int type = GET_PARAM(1);
const int type = CV_8UC1;
const int kernel = GET_PARAM(1);
cv::Mat src(size, type);
declare.in(src, WARMUP_RNG);
if (PERF_RUN_CUDA())
{
const cv::cuda::GpuMat d_src(src);
cv::cuda::GpuMat dst;
cv::Ptr<cv::cuda::Filter> median = cv::cuda::createMedianFilter(d_src.type(), kernel);
TEST_CYCLE() median->apply(d_src, dst);
SANITY_CHECK_NOTHING();
}
else
{
cv::Mat dst;
TEST_CYCLE() cv::medianBlur(src,dst,kernel);
SANITY_CHECK_NOTHING();
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -69,6 +69,8 @@ Ptr<Filter> cv::cuda::createBoxMinFilter(int, Size, Point, int, Scalar) { throw_
Ptr<Filter> cv::cuda::createRowSumFilter(int, int, int, int, int, Scalar) { throw_no_cuda(); return Ptr<Filter>(); }
Ptr<Filter> cv::cuda::createColumnSumFilter(int, int, int, int, int, Scalar) { throw_no_cuda(); return Ptr<Filter>(); }
Ptr<Filter> cv::cuda::createMedianFilter(int srcType, int _windowSize, int _partitions){ throw_no_cuda(); return Ptr<Filter>();}
#else
namespace
......@@ -995,4 +997,74 @@ Ptr<Filter> cv::cuda::createColumnSumFilter(int srcType, int dstType, int ksize,
return makePtr<NppColumnSumFilter>(srcType, dstType, ksize, anchor, borderMode, borderVal);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Median Filter
namespace cv { namespace cuda { namespace device
{
void medianFiltering_gpu(const PtrStepSzb src, PtrStepSzb dst, PtrStepSzi devHist,
PtrStepSzi devCoarseHist,int kernel, int partitions, cudaStream_t stream);
}}}
namespace
{
class MedianFilter : public Filter
{
public:
MedianFilter(int srcType, int _windowSize, int _partitions=128);
void apply(InputArray src, OutputArray dst, Stream& stream = Stream::Null());
private:
int windowSize;
int partitions;
};
MedianFilter::MedianFilter(int srcType, int _windowSize, int _partitions) :
windowSize(_windowSize),partitions(_partitions)
{
CV_Assert( srcType == CV_8UC1 );
CV_Assert(windowSize>=3);
CV_Assert(_partitions>=1);
}
void MedianFilter::apply(InputArray _src, OutputArray _dst, Stream& _stream)
{
using namespace cv::cuda::device;
GpuMat src = _src.getGpuMat();
_dst.create(src.rows, src.cols, src.type());
GpuMat dst = _dst.getGpuMat();
if (partitions>src.rows)
partitions=src.rows/2;
// Kernel needs to be half window size
int kernel=windowSize/2;
CV_Assert(kernel < src.rows);
CV_Assert(kernel < src.cols);
// Note - these are hardcoded in the actual GPU kernel. Do not change these values.
int histSize=256, histCoarseSize=8;
BufferPool pool(_stream);
GpuMat devHist = pool.getBuffer(1, src.cols*histSize*partitions,CV_32SC1);
GpuMat devCoarseHist = pool.getBuffer(1,src.cols*histCoarseSize*partitions,CV_32SC1);
devHist.setTo(0, _stream);
devCoarseHist.setTo(0, _stream);
medianFiltering_gpu(src,dst,devHist, devCoarseHist,kernel,partitions,StreamAccessor::getStream(_stream));
}
}
Ptr<Filter> cv::cuda::createMedianFilter(int srcType, int _windowSize, int _partitions)
{
return makePtr<MedianFilter>(srcType, _windowSize,_partitions);
}
#endif
......@@ -53,6 +53,7 @@ namespace
IMPLEMENT_PARAM_CLASS(Deriv_X, int)
IMPLEMENT_PARAM_CLASS(Deriv_Y, int)
IMPLEMENT_PARAM_CLASS(Iterations, int)
IMPLEMENT_PARAM_CLASS(KernelSize, int)
cv::Mat getInnerROI(cv::InputArray m_, cv::Size ksize)
{
......@@ -647,4 +648,64 @@ INSTANTIATE_TEST_CASE_P(CUDA_Filters, MorphEx, testing::Combine(
testing::Values(Iterations(1), Iterations(2), Iterations(3)),
WHOLE_SUBMAT));
/////////////////////////////////////////////////////////////////////////////////////////////////
// Median
PARAM_TEST_CASE(Median, cv::cuda::DeviceInfo, cv::Size, MatDepth, KernelSize, UseRoi)
{
cv::cuda::DeviceInfo devInfo;
cv::Size size;
int type;
int kernel;
bool useRoi;
virtual void SetUp()
{
devInfo = GET_PARAM(0);
size = GET_PARAM(1);
type = GET_PARAM(2);
kernel = GET_PARAM(3);
useRoi = GET_PARAM(4);
cv::cuda::setDevice(devInfo.deviceID());
}
};
CUDA_TEST_P(Median, Accuracy)
{
cv::Mat src = randomMat(size, type);
cv::Ptr<cv::cuda::Filter> median = cv::cuda::createMedianFilter(src.type(), kernel);
cv::cuda::GpuMat dst = createMat(size, type, useRoi);
median->apply(loadMat(src, useRoi), dst);
cv::Mat dst_gold;
cv::medianBlur(src,dst_gold,kernel);
cv::Rect rect(kernel+1,0,src.cols-(2*kernel+1),src.rows);
cv::Mat dst_gold_no_border = dst_gold(rect);
cv::cuda::GpuMat dst_no_border = cv::cuda::GpuMat(dst, rect);
EXPECT_MAT_NEAR(dst_gold_no_border, dst_no_border, 1);
}
INSTANTIATE_TEST_CASE_P(CUDA_Filters, Median, testing::Combine(
ALL_DEVICES,
DIFFERENT_SIZES,
testing::Values(MatDepth(CV_8U)),
testing::Values(KernelSize(3),
KernelSize(5),
KernelSize(7),
KernelSize(9),
KernelSize(11),
KernelSize(13),
KernelSize(15)),
WHOLE_SUBMAT)
);
#endif // HAVE_CUDA
......@@ -588,6 +588,7 @@ CV_EXPORTS Ptr<CornersDetector> createGoodFeaturesToTrackDetector(int srcType, i
//! @} cudaimgproc_feature
///////////////////////////// Mean Shift //////////////////////////////
/** @brief Performs mean-shift filtering for each point of the source image.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment