mirror of
https://github.com/opencv/opencv.git
synced 2026-11-09 18:04:29 -06:00
imgproc: parallelize calcHist using stripe-based parallel_for_ (#30585)
* imgproc: parallelize calcHist using stripe-based parallel_for_ * backport calcHist parallelization from 5.x
This commit is contained in:
@@ -806,6 +806,83 @@ namespace cv
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
template<typename CalcFunc>
|
||||
static void calcHistRunParallel( std::vector<uchar*>& ptrs, const std::vector<int>& deltas,
|
||||
Size imsize, Mat& hist, int dims, const float** ranges,
|
||||
const double* uniranges, bool uniform, int esz1, CalcFunc calcFunc )
|
||||
{
|
||||
const int64 N = (int64)imsize.width * imsize.height;
|
||||
const size_t M = hist.total();
|
||||
|
||||
if( N < (int64)256 * 1024 )
|
||||
{
|
||||
calcFunc( ptrs, deltas, imsize, hist, dims, ranges, uniranges, uniform );
|
||||
return;
|
||||
}
|
||||
|
||||
int nstripes = M > 0 ? std::max(1, std::min(8, cvRound((double)N * 1e-3 / (double)M))) : 1;
|
||||
nstripes = std::min( nstripes, getNumThreads() );
|
||||
if( nstripes <= 1 )
|
||||
{
|
||||
calcFunc( ptrs, deltas, imsize, hist, dims, ranges, uniranges, uniform );
|
||||
return;
|
||||
}
|
||||
|
||||
const bool splitByRows = imsize.height > 1;
|
||||
const int splitDim = splitByRows ? imsize.height : imsize.width;
|
||||
nstripes = std::min( nstripes, splitDim );
|
||||
|
||||
std::vector<Mat> local_hists( nstripes );
|
||||
|
||||
parallel_for_( Range( 0, nstripes ), [&]( const Range& r )
|
||||
{
|
||||
for( int s = r.start; s < r.end; s++ )
|
||||
{
|
||||
const int start = (int)((int64)s * splitDim / nstripes);
|
||||
const int end = (int)((int64)(s + 1) * splitDim / nstripes);
|
||||
if( start >= end )
|
||||
continue;
|
||||
|
||||
std::vector<uchar*> local_ptrs = ptrs;
|
||||
Size local_size;
|
||||
|
||||
if( splitByRows )
|
||||
{
|
||||
for( int i = 0; i < dims; i++ )
|
||||
{
|
||||
size_t row_step = ((size_t)imsize.width * deltas[i*2] + deltas[i*2 + 1]) * esz1;
|
||||
local_ptrs[i] = (uchar*)((const uchar*)local_ptrs[i] + (size_t)start * row_step);
|
||||
}
|
||||
if( local_ptrs[dims] )
|
||||
local_ptrs[dims] = (uchar*)((const uchar*)local_ptrs[dims] + (size_t)start * deltas[dims*2 + 1]);
|
||||
local_size = Size( imsize.width, end - start );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < dims; i++ )
|
||||
local_ptrs[i] = (uchar*)((const uchar*)local_ptrs[i] + (size_t)start * deltas[i*2] * esz1);
|
||||
if( local_ptrs[dims] )
|
||||
local_ptrs[dims] = (uchar*)((const uchar*)local_ptrs[dims] + (size_t)start * deltas[dims*2]);
|
||||
local_size = Size( end - start, 1 );
|
||||
}
|
||||
|
||||
local_hists[s] = Mat::zeros( hist.dims, hist.size.p, hist.type() );
|
||||
calcFunc( local_ptrs, deltas, local_size, local_hists[s], dims, ranges, uniranges, uniform );
|
||||
}
|
||||
});
|
||||
|
||||
for( int s = 0; s < nstripes; s++ )
|
||||
{
|
||||
if( !local_hists[s].empty() )
|
||||
hist += local_hists[s];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
|
||||
void cv::calcHist( const Mat* images, int nimages, const int* channels,
|
||||
InputArray _mask, OutputArray _hist, int dims, const int* histSize,
|
||||
const float** ranges, bool uniform, bool accumulate )
|
||||
@@ -857,13 +934,14 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels,
|
||||
const double* _uniranges = uniform ? &uniranges[0] : 0;
|
||||
|
||||
int depth = images[0].depth();
|
||||
int esz1 = (int)images[0].elemSize1();
|
||||
|
||||
if( depth == CV_8U )
|
||||
calcHist_8u(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
|
||||
calcHistRunParallel( ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform, esz1, &calcHist_8u );
|
||||
else if( depth == CV_16U )
|
||||
calcHist_<ushort>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
|
||||
calcHistRunParallel( ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform, esz1, &calcHist_<ushort> );
|
||||
else if( depth == CV_32F )
|
||||
calcHist_<float>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
|
||||
calcHistRunParallel( ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform, esz1, &calcHist_<float> );
|
||||
else
|
||||
CV_Error(cv::Error::StsUnsupportedFormat, "");
|
||||
|
||||
|
||||
@@ -2032,6 +2032,73 @@ TEST(Imgproc_Hist_Calc, IPP_ranges_with_nonequal_exponent_21595)
|
||||
ASSERT_EQ(histogram_u.at<float>(2), 4.f) << "1 not counts correctly, res: " << histogram_u.at<float>(2);
|
||||
}
|
||||
|
||||
typedef testing::TestWithParam< testing::tuple<int, int, Size> > Imgproc_Hist_Calc_Parallel;
|
||||
|
||||
TEST_P(Imgproc_Hist_Calc_Parallel, consistency)
|
||||
{
|
||||
int type = testing::get<0>(GetParam());
|
||||
int dims = testing::get<1>(GetParam());
|
||||
Size sz = testing::get<2>(GetParam());
|
||||
|
||||
int cn = dims;
|
||||
|
||||
Mat buf(Size(sz.width + 1, sz.height), CV_MAKETYPE(type, cn));
|
||||
randu(buf, Scalar::all(0), Scalar::all(256));
|
||||
Mat src = buf(Rect(0, 0, sz.width, sz.height));
|
||||
|
||||
Mat mask;
|
||||
if( dims == 1 )
|
||||
{
|
||||
mask.create(sz, CV_8UC1);
|
||||
randu(mask, Scalar::all(0), Scalar::all(2));
|
||||
}
|
||||
|
||||
std::vector<int> ch(dims), hs(dims);
|
||||
std::vector<std::vector<float> > rv(dims);
|
||||
std::vector<const float*> rp(dims);
|
||||
for( int i = 0; i < dims; i++ )
|
||||
{
|
||||
ch[i] = i;
|
||||
hs[i] = (dims == 1) ? 64 : (dims == 2) ? 8 : 4;
|
||||
rv[i].assign(2, 0.f);
|
||||
rv[i][1] = 256.f;
|
||||
rp[i] = &rv[i][0];
|
||||
}
|
||||
|
||||
// Row-by-row: every call stays below the parallel threshold.
|
||||
Mat baseline;
|
||||
for( int y = 0; y < src.rows; y++ )
|
||||
{
|
||||
Mat row = src.row(y);
|
||||
Mat mask_row = (dims == 1) ? mask.row(y) : Mat();
|
||||
bool acc = (y > 0);
|
||||
calcHist(&row, 1, ch.data(), mask_row, baseline, dims, hs.data(), rp.data(), true, acc);
|
||||
}
|
||||
|
||||
setNumThreads(8);
|
||||
Mat parallel_result;
|
||||
calcHist(&src, 1, ch.data(), mask, parallel_result, dims, hs.data(), rp.data(), true, false);
|
||||
|
||||
// Flattened to one row: column-split path.
|
||||
Mat contiguous = src.clone();
|
||||
Mat contiguous_result;
|
||||
calcHist(&contiguous, 1, ch.data(), mask, contiguous_result, dims, hs.data(), rp.data(), true, false);
|
||||
setNumThreads(-1);
|
||||
|
||||
EXPECT_EQ(0.0, cv::norm(baseline, parallel_result, NORM_INF))
|
||||
<< "row-split, type=" << type << " dims=" << dims << " size=" << sz;
|
||||
EXPECT_EQ(0.0, cv::norm(baseline, contiguous_result, NORM_INF))
|
||||
<< "column-split, type=" << type << " dims=" << dims << " size=" << sz;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgproc_Hist_Calc_Parallel,
|
||||
testing::Combine(
|
||||
testing::Values(CV_8U, CV_16U, CV_32F),
|
||||
testing::Values(1, 2, 3),
|
||||
testing::Values(Size(640, 480), Size(1920, 1080), Size(7, 13))
|
||||
)
|
||||
);
|
||||
|
||||
////////////////////////////////////////// equalizeHist() /////////////////////////////////////////
|
||||
|
||||
void equalizeHistReference(const Mat& src, Mat& dst)
|
||||
|
||||
Reference in New Issue
Block a user