Merge pull request #3699 from AleksandrPanov:mcc_add_perf_tests_improve_performance

Mcc add perf tests improve performance #3699

Added perf tests to mcc module.
Also these optimizations have been added:

- added `parallel_for_` to `performThreshold()`
- removed `toL`/`fromL` and added `dst` to avoid copy data
- added `parallel_for_` to `elementWise()` ("batch" optimization improves performance of Windows version, Linux without changes).

Configuration:
Ryzen 5950X, 2x16 GB 3000 MHz DDR4
OS: Windows 10, Ubuntu 20.04.5 LTS

Performance results in milliseconds:

| OS and alg version   | process, ms | infer, ms |
| -------------------- | ----- | ------ |
| win_default          | 63.09 | 457.57 |
| win_optimized_without_batch       | 48.69 | 111.78 |
| win_optimized_batch  | 48.42 | 47.28  |
| linux_default        | 50.88 | 300.7  |
| linux_optimized_batch| 36.06 | 41.62  |

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
pull/3705/head
Alexander Panov 1 year ago committed by GitHub
parent 5300337197
commit 5e592c2d96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      modules/mcc/perf/perf_main.cpp
  2. 51
      modules/mcc/perf/perf_mcc.cpp
  3. 14
      modules/mcc/perf/perf_precomp.hpp
  4. 7
      modules/mcc/src/ccm.cpp
  5. 23
      modules/mcc/src/checker_detector.cpp
  6. 37
      modules/mcc/src/colorspace.cpp
  7. 21
      modules/mcc/src/colorspace.hpp
  8. 6
      modules/mcc/src/utils.cpp
  9. 23
      modules/mcc/src/utils.hpp

@ -0,0 +1,3 @@
#include "perf_precomp.hpp"
CV_PERF_TEST_MAIN(mcc)

@ -0,0 +1,51 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test
{
namespace
{
using namespace std;
PERF_TEST(CV_mcc_perf, detect) {
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.jpg");
Mat img = imread(path, IMREAD_COLOR);
Ptr<CCheckerDetector> detector = CCheckerDetector::create();
// detect MCC24 board
TEST_CYCLE() {
ASSERT_TRUE(detector->process(img, MCC24, 1, false));
}
SANITY_CHECK_NOTHING();
}
PERF_TEST(CV_mcc_perf, infer) {
// read gold chartsRGB
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.yml");
FileStorage fs(path, FileStorage::READ);
Mat chartsRGB;
FileNode node = fs["chartsRGB"];
node >> chartsRGB;
fs.release();
// compute CCM
ColorCorrectionModel model(chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255., COLORCHECKER_Macbeth);
model.run();
Mat img(1000, 4000, CV_8UC3);
randu(img, 0, 255);
img.convertTo(img, CV_64F, 1. / 255.);
TEST_CYCLE() {
model.infer(img);
}
SANITY_CHECK_NOTHING();
}
} // namespace
} // namespace opencv_test

@ -0,0 +1,14 @@
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/mcc.hpp"
namespace opencv_test
{
using namespace cv::mcc;
using namespace cv::ccm;
using namespace perf;
}
#endif

@ -289,14 +289,13 @@ Mat ColorCorrectionModel::infer(const Mat& img, bool islinear)
CV_Error(Error::StsBadArg, "No CCM values!" );
}
Mat img_lin = (p->linear)->linearize(img);
Mat img_ccm(img_lin.size(), img_lin.type());
Mat ccm_ = p->ccm.reshape(0, p->shape / 3);
img_ccm = multiple(p->prepare(img_lin), ccm_);
Mat ccm = p->ccm.reshape(0, p->shape / 3);
Mat img_ccm = multiple(p->prepare(img_lin), ccm);
if (islinear == true)
{
return img_ccm;
}
return p->cs.fromL(img_ccm);
return p->cs.fromLFunc(img_ccm, img_lin);
}
void ColorCorrectionModel::Impl::getColor(CONST_COLOR constcolor)

@ -539,17 +539,18 @@ void CCheckerDetectorImpl::
// number of window sizes (scales) to apply adaptive thresholding
int nScales = (params->adaptiveThreshWinSizeMax - params->adaptiveThreshWinSizeMin) / params->adaptiveThreshWinSizeStep + 1;
thresholdImgs.create(nScales, 1, CV_8U);
std::vector<cv::Mat> _thresholdImgs;
for (int i = 0; i < nScales; i++)
{
int currScale = params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep;
cv::Mat tempThresholdImg;
cv::adaptiveThreshold(grayscaleImg, tempThresholdImg, 255, cv::ADAPTIVE_THRESH_MEAN_C,
cv::THRESH_BINARY_INV, currScale, params->adaptiveThreshConstant);
_thresholdImgs.push_back(tempThresholdImg);
}
std::vector<cv::Mat> _thresholdImgs(nScales);
parallel_for_(Range(0, nScales),[&](const Range& range) {
const int start = range.start;
const int end = range.end;
for (int i = start; i < end; i++) {
int currScale = params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep;
cv::Mat tempThresholdImg;
cv::adaptiveThreshold(grayscaleImg, tempThresholdImg, 255, ADAPTIVE_THRESH_MEAN_C,
THRESH_BINARY_INV, currScale, params->adaptiveThreshConstant);
_thresholdImgs[i] = tempThresholdImg;
}
});
thresholdImgs.assign(_thresholdImgs);
}

@ -83,9 +83,9 @@ Operations RGBBase_::relation(const ColorSpace& other) const
}
if (linear)
{
return Operations({ Operation(fromL) });
return Operations({ Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
}
return Operations({ Operation(toL) });
return Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); })});
}
/* @brief Initial operations.
@ -134,12 +134,6 @@ void RGBBase_::calM()
*/
void RGBBase_::calOperations()
{
// rgb -> rgbl
toL = [this](Mat rgb) -> Mat { return toLFunc(rgb); };
// rgbl -> rgb
fromL = [this](Mat rgbl) -> Mat { return fromLFunc(rgbl); };
if (linear)
{
to = Operations({ Operation(M_to.t()) });
@ -147,23 +141,25 @@ void RGBBase_::calOperations()
}
else
{
to = Operations({ Operation(toL), Operation(M_to.t()) });
from = Operations({ Operation(M_from.t()), Operation(fromL) });
// rgb -> rgbl
to = Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); }), Operation(M_to.t()) });
// rgbl -> rgb
from = Operations({ Operation(M_from.t()), Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
}
}
Mat RGBBase_::toLFunc(Mat& /*rgb*/) { return Mat(); }
Mat RGBBase_::toLFunc(Mat& /*rgb*/) const { return Mat(); }
Mat RGBBase_::fromLFunc(Mat& /*rgbl*/) { return Mat(); }
Mat RGBBase_::fromLFunc(Mat& /*rgbl*/, Mat dst) const { return dst; }
/* @brief Base of Adobe RGB color space;
*/
Mat AdobeRGBBase_::toLFunc(Mat& rgb) { return gammaCorrection(rgb, gamma); }
Mat AdobeRGBBase_::toLFunc(Mat& rgb) const { return gammaCorrection(rgb, gamma); }
Mat AdobeRGBBase_::fromLFunc(Mat& rgbl)
Mat AdobeRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
{
return gammaCorrection(rgbl, 1. / gamma);
return gammaCorrection(rgbl, 1. / gamma, dst);
}
/* @brief Base of sRGB color space;
@ -179,7 +175,7 @@ void sRGBBase_::calLinear()
/* @brief Used by toLFunc.
*/
double sRGBBase_::toLFuncEW(double& x)
double sRGBBase_::toLFuncEW(double& x) const
{
if (x > K0)
{
@ -199,7 +195,7 @@ double sRGBBase_::toLFuncEW(double& x)
* @param rgb the input array, type of cv::Mat.
* @return the output array, type of cv::Mat.
*/
Mat sRGBBase_::toLFunc(Mat& rgb)
Mat sRGBBase_::toLFunc(Mat& rgb) const
{
return elementWise(rgb,
[this](double a_) -> double { return toLFuncEW(a_); });
@ -207,7 +203,7 @@ Mat sRGBBase_::toLFunc(Mat& rgb)
/* @brief Used by fromLFunc.
*/
double sRGBBase_::fromLFuncEW(double& x)
double sRGBBase_::fromLFuncEW(const double& x) const
{
if (x > beta)
{
@ -227,10 +223,9 @@ double sRGBBase_::fromLFuncEW(double& x)
* @param rgbl the input array, type of cv::Mat.
* @return the output array, type of cv::Mat.
*/
Mat sRGBBase_::fromLFunc(Mat& rgbl)
Mat sRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
{
return elementWise(rgbl,
[this](double a_) -> double { return fromLFuncEW(a_); });
return elementWise(rgbl, [this](double a_) -> double { return fromLFuncEW(a_); }, dst);
}
/* @brief sRGB color space.

@ -83,8 +83,6 @@ public:
double yg;
double xb;
double yb;
MatFunc toL;
MatFunc fromL;
Mat M_to;
Mat M_from;
@ -108,6 +106,9 @@ public:
*/
void bind(RGBBase_& rgbl);
virtual Mat toLFunc(Mat& /*rgb*/) const;
virtual Mat fromLFunc(Mat& /*rgbl*/, Mat dst=Mat()) const;
private:
virtual void setParameter() {};
@ -120,10 +121,6 @@ private:
virtual void calOperations();
virtual void calLinear() {};
virtual Mat toLFunc(Mat& /*rgb*/);
virtual Mat fromLFunc(Mat& /*rgbl*/);
};
/** @brief Base of Adobe RGB color space;
@ -136,8 +133,8 @@ public:
double gamma;
private:
Mat toLFunc(Mat& rgb) CV_OVERRIDE;
Mat fromLFunc(Mat& rgbl) CV_OVERRIDE;
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
};
/** @brief Base of sRGB color space;
@ -160,23 +157,23 @@ private:
virtual void calLinear() CV_OVERRIDE;
/** @brief Used by toLFunc.
*/
double toLFuncEW(double& x);
double toLFuncEW(double& x) const;
/** @brief Linearization.
@param rgb the input array, type of cv::Mat.
@return the output array, type of cv::Mat.
*/
Mat toLFunc(Mat& rgb) CV_OVERRIDE;
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
/** @brief Used by fromLFunc.
*/
double fromLFuncEW(double& x);
double fromLFuncEW(const double& x) const;
/** @brief Delinearization.
@param rgbl the input array, type of cv::Mat.
@return the output array, type of cv::Mat.
*/
Mat fromLFunc(Mat& rgbl) CV_OVERRIDE;
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
};
/** @brief sRGB color space.

@ -30,14 +30,14 @@
namespace cv {
namespace ccm {
double gammaCorrection_(const double& element, const double& gamma)
inline double gammaCorrection_(const double& element, const double& gamma)
{
return (element >= 0 ? pow(element, gamma) : -pow((-element), gamma));
}
Mat gammaCorrection(const Mat& src, const double& gamma)
Mat gammaCorrection(const Mat& src, const double& gamma, Mat dst)
{
return elementWise(src, [gamma](double element) -> double { return gammaCorrection_(element, gamma); });
return elementWise(src, [gamma](double element) -> double { return gammaCorrection_(element, gamma); }, dst);
}
Mat maskCopyTo(const Mat& src, const Mat& mask)

@ -42,8 +42,9 @@ double gammaCorrection_(const double& element, const double& gamma);
\f]
@param src the input array,type of Mat.
@param gamma a constant for gamma correction.
@param dst the output array, type of Mat.
*/
Mat gammaCorrection(const Mat& src, const double& gamma);
Mat gammaCorrection(const Mat& src, const double& gamma, Mat dst=Mat());
/** @brief maskCopyTo a function to delete unsatisfied elementwise.
@param src the input array, type of Mat.
@ -77,10 +78,26 @@ Mat rgb2gray(const Mat& rgb);
@param lambda a for operation
*/
template <typename F>
Mat elementWise(const Mat& src, F&& lambda)
Mat elementWise(const Mat& src, F&& lambda, Mat dst=Mat())
{
Mat dst = src.clone();
if (dst.empty() || !dst.isContinuous() || dst.total() != src.total() || dst.type() != src.type())
dst = Mat(src.rows, src.cols, src.type());
const int channel = src.channels();
if (src.isContinuous()) {
const int num_elements = (int)src.total()*channel;
const double *psrc = (double*)src.data;
double *pdst = (double*)dst.data;
const int batch = getNumThreads() > 1 ? 128 : num_elements;
const int N = (num_elements / batch) + ((num_elements % batch) > 0);
parallel_for_(Range(0, N),[&](const Range& range) {
const int start = range.start * batch;
const int end = std::min(range.end*batch, num_elements);
for (int i = start; i < end; i++) {
pdst[i] = lambda(psrc[i]);
}
});
return dst;
}
switch (channel)
{
case 1:

Loading…
Cancel
Save