Skip to content

Commit 24cd5e2

Browse files
clunietpalalek
authored andcommitted
Merge pull request #2113 from clunietp:quality-refactor
* Simplified quality API * Compilation fixes * test fixes * Test updates * Test fixes * Fixed brisque data file location for install, test * Increase error epsilon to account for multiple architectures
1 parent 5eaa25c commit 24cd5e2

21 files changed

+268
-491
lines changed

modules/quality/CMakeLists.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
set(the_description "Image Quality Analysis API")
22
ocv_define_module(quality opencv_core opencv_imgproc opencv_ml WRAP python)
3-
ocv_add_testdata(samples/ contrib/quality
4-
FILES_MATCHING PATTERN "*.yml"
5-
)
3+
4+
# add test data from samples dir to contrib/quality
5+
ocv_add_testdata(samples/ contrib/quality FILES_MATCHING PATTERN "*.yml")
6+
7+
# add brisque model, range files to installation
8+
file(GLOB QUALITY_MODEL_DATA samples/*.yml)
9+
install(FILES ${QUALITY_MODEL_DATA} DESTINATION ${OPENCV_OTHER_INSTALL_PATH}/quality COMPONENT libs)

modules/quality/README.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ Quick Start/Usage
4646

4747
```cpp
4848
#include <opencv2/quality.hpp>
49-
cv::Mat img1, img2; /* your cv::Mat images */
50-
std::vector<cv::Mat> quality_maps; /* output quality map(s) (optional) */
49+
cv::Mat img1, img2; /* your cv::Mat images to compare */
50+
cv::Mat quality_map; /* output quality map (optional) */
5151
/* compute MSE via static method */
52-
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_maps); /* or cv::noArray() if not interested in output quality maps */
52+
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map); /* or cv::noArray() if not interested in output quality maps */
5353
/* alternatively, compute MSE via instance */
5454
cv::Ptr<quality::QualityBase> ptr = quality::QualityMSE::create(img1);
5555
cv::Scalar result = ptr->compute( img2 ); /* compute MSE, compare img1 vs img2 */
56-
ptr->getQualityMaps(quality_maps); /* optionally, access output quality maps */
56+
ptr->getQualityMap(quality_map); /* optionally, access output quality maps */
5757
```
5858
5959
**For No Reference IQA Algorithm (BRISQUE)**
@@ -81,11 +81,11 @@ model_path, range_path);
8181
img1 = cv2.imread(img1, 1) # specify img1
8282
img2 = cv2.imread(img2_path, 1) # specify img2_path
8383
# compute MSE score and quality maps via static method
84-
result_static, quality_maps = cv2.quality.QualityMSE_compute(img1, img2)
84+
result_static, quality_map = cv2.quality.QualityMSE_compute(img1, img2)
8585
# compute MSE score and quality maps via Instance
8686
obj = cv2.quality.QualityMSE_create(img1)
8787
result = obj.compute(img2)
88-
quality_maps = obj.getQualityMaps()
88+
quality_map = obj.getQualityMap()
8989
```
9090

9191
**For No Reference IQA Algorithm (BRISQUE)**
@@ -94,28 +94,26 @@ model_path, range_path);
9494
import cv2
9595
# read image
9696
img = cv2.imread(img_path, 1) # mention img_path
97-
# make a list of image to be passed
98-
img_list = [img]
9997
# compute brisque quality score via static method
100-
score = cv2.quality.QualityBRISQUE_compute(img_list, model_path,
98+
score = cv2.quality.QualityBRISQUE_compute(img, model_path,
10199
range_path) # specify model_path and range_path
102100
# compute brisque quality score via instance
103101
# specify model_path and range_path
104102
obj = cv2.quality.QualityBRISQUE_create(model_path, range_path)
105-
score = obj.compute(img_list)
103+
score = obj.compute(img)
106104
```
107105

108106
Library Design
109107
-----------------------------------------
110108
Each implemented algorithm shall:
111109
- Inherit from `QualityBase`, and properly implement/override `compute`, `empty` and `clear` instance methods, along with a static `compute` method.
112-
- Accept one or more `cv::Mat` or `cv::UMat` via `InputArrayOfArrays` for computation. Each input `cv::Mat` or `cv::UMat` may contain one or more channels. If the algorithm does not support multiple channels or multiple inputs, it should be documented and an appropriate assertion should be in place.
113-
- Return a `cv::Scalar` with per-channel computed value. If multiple input images are provided, the resulting scalar should return the average result per channel.
110+
- Accept one `cv::Mat` or `cv::UMat` via `InputArray` for computation. Each input `cv::Mat` or `cv::UMat` may contain one or more channels. If the algorithm does not support multiple channels, it should be documented and an appropriate assertion should be in place.
111+
- Return a `cv::Scalar` with per-channel computed value
114112
- Compute result via a single, static method named `compute` and via an overridden instance method (see `compute` in `qualitybase.hpp`).
115-
- Perform any setup and/or pre-processing of reference images in the constructor, allowing for efficient computation when comparing the reference image(s) versus multiple comparison image(s). No-reference algorithms should accept images for evaluation in the `compute` method.
116-
- Optionally compute resulting quality maps. Instance `compute` method should store them in `QualityBase::_qualityMaps` as the mat type defined by `QualityBase::_quality_map_type`, or override `QualityBase::getQualityMaps`. Static `compute` method should return them in an `OutputArrayOfArrays` parameter.
117-
- Document algorithm in this readme and in its respective header. Documentation should include interpretation for the results of `compute` as well as the format of the output quality maps (if supported), along with any other notable usage information.
118-
- Implement tests of static `compute` method and instance methods using single- and multi-channel images, multi-frame images, and OpenCL enabled and disabled
113+
- Perform any setup and/or pre-processing of reference images in the constructor, allowing for efficient computation when comparing the reference image versus multiple comparison image(s). No-reference algorithms should accept images for evaluation in the `compute` method.
114+
- Optionally compute resulting quality map. Instance `compute` method should store them in `QualityBase::_qualityMap` as the mat type defined by `QualityBase::_mat_type`, or override `QualityBase::getQualityMap`. Static `compute` method should return the quality map in an `OutputArray` parameter.
115+
- Document algorithm in this readme and in its respective header. Documentation should include interpretation for the results of `compute` as well as the format of the output quality map (if supported), along with any other notable usage information.
116+
- Implement tests of static `compute` method and instance methods using single- and multi-channel images and OpenCL enabled and disabled
119117

120118
To Do
121119
-----------------------------------------

modules/quality/include/opencv2/quality/quality_utils.hpp

Lines changed: 13 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#ifndef OPENCV_QUALITY_QUALITY_UTILS_HPP
66
#define OPENCV_QUALITY_QUALITY_UTILS_HPP
77

8-
#include <limits> // numeric_limits
98
#include "qualitybase.hpp"
109

1110
namespace cv
@@ -18,94 +17,44 @@ namespace quality_utils
1817
// default type of matrix to expand to
1918
static CV_CONSTEXPR const int EXPANDED_MAT_DEFAULT_TYPE = CV_32F;
2019

21-
// convert input array to vector of specified mat types. set type == -1 to preserve existing type
20+
// convert inputarray to specified mat type. set type == -1 to preserve existing type
2221
template <typename R>
23-
inline std::vector<R> extract_mats( InputArrayOfArrays arr, const int type = -1 )
22+
inline R extract_mat(InputArray in, const int type = -1)
2423
{
25-
std::vector<R> result = {};
26-
std::vector<UMat> umats = {};
27-
std::vector<Mat> mats = {};
28-
29-
if (arr.isUMatVector())
30-
arr.getUMatVector(umats);
31-
else if (arr.isUMat())
32-
umats.emplace_back(arr.getUMat());
33-
else if (arr.isMatVector())
34-
arr.getMatVector(mats);
35-
else if (arr.isMat())
36-
mats.emplace_back(arr.getMat());
24+
R result = {};
25+
if ( in.isMat() )
26+
in.getMat().convertTo( result, (type != -1) ? type : in.getMat().type());
27+
else if ( in.isUMat() )
28+
in.getUMat().convertTo( result, (type != -1) ? type : in.getUMat().type());
3729
else
3830
CV_Error(Error::StsNotImplemented, "Unsupported input type");
3931

40-
// convert umats, mats to desired type
41-
for (auto& umat : umats)
42-
{
43-
result.emplace_back(R{});
44-
umat.convertTo(result.back(), ( type != -1 ) ? type : umat.type() );
45-
}
46-
47-
for (auto& mat : mats)
48-
{
49-
result.emplace_back(R{});
50-
mat.convertTo(result.back(), (type != -1) ? type : mat.type() );
51-
}
52-
5332
return result;
5433
}
5534

56-
// expand matrix to target type
57-
template <typename OutT, typename InT>
58-
inline OutT expand_mat(const InT& src, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
35+
// extract and expand matrix to target type
36+
template <typename R>
37+
inline R expand_mat( InputArray src, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
5938
{
60-
OutT result = {};
39+
auto result = extract_mat<R>(src, -1);
6140

6241
// by default, expand to 32F unless we already have >= 32 bits, then go to 64
6342
// if/when we can detect OpenCL CV_16F support, opt for that when input depth == 8
6443
// note that this may impact the precision of the algorithms and would need testing
6544
int type = TYPE_DEFAULT;
6645

67-
switch (src.depth())
46+
switch (result.depth())
6847
{
6948
case CV_32F:
7049
case CV_32S:
7150
case CV_64F:
7251
type = CV_64F;
7352
}; // switch
7453

75-
src.convertTo(result, type);
54+
result.convertTo(result, type);
7655
return result;
7756
}
7857

79-
// convert input array to vector of expanded mat types
80-
template <typename R>
81-
inline std::vector<R> expand_mats(InputArrayOfArrays arr, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
82-
{
83-
std::vector<R> result = {};
84-
85-
auto mats = extract_mats<R>(arr, -1);
86-
for (auto& mat : mats)
87-
result.emplace_back(expand_mat<R>(mat, TYPE_DEFAULT));
88-
89-
return result;
90-
}
91-
92-
// convert mse to psnr
93-
inline double mse_to_psnr(double mse, double max_pixel_value)
94-
{
95-
return (mse == 0.)
96-
? std::numeric_limits<double>::infinity()
97-
: 10. * std::log10((max_pixel_value * max_pixel_value) / mse)
98-
;
99-
}
100-
101-
// convert scalar of mses to psnrs
102-
inline cv::Scalar mse_to_psnr(cv::Scalar mse, double max_pixel_value)
103-
{
104-
for (int i = 0; i < mse.rows; ++i)
105-
mse(i) = mse_to_psnr(mse(i), max_pixel_value);
106-
return mse;
107-
}
108-
10958
// return mat of observed min/max pair per column
11059
// row 0: min per column
11160
// row 1: max per column

modules/quality/include/opencv2/quality/qualitybase.hpp

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#ifndef OPENCV_QUALITYBASE_HPP
66
#define OPENCV_QUALITYBASE_HPP
77

8-
#include <vector>
98
#include <opencv2/core.hpp>
109

1110
/**
@@ -21,7 +20,6 @@ namespace quality
2120
//! @{
2221

2322
/************************************ Quality Base Class ************************************/
24-
2523
class CV_EXPORTS_W QualityBase
2624
: public virtual Algorithm
2725
{
@@ -32,34 +30,31 @@ class CV_EXPORTS_W QualityBase
3230

3331
/**
3432
@brief Compute quality score per channel with the per-channel score in each element of the resulting cv::Scalar. See specific algorithm for interpreting result scores
35-
@param cmpImgs comparison image(s), or image(s) to evalute for no-reference quality algorithms
33+
@param img comparison image, or image to evalute for no-reference quality algorithms
3634
*/
37-
virtual CV_WRAP cv::Scalar compute( InputArrayOfArrays cmpImgs ) = 0;
35+
virtual CV_WRAP cv::Scalar compute( InputArray img ) = 0;
3836

39-
/** @brief Returns output quality map images that were generated during computation, if supported by the algorithm */
40-
virtual CV_WRAP void getQualityMaps(OutputArrayOfArrays dst) const
37+
/** @brief Returns output quality map that was generated during computation, if supported by the algorithm */
38+
virtual CV_WRAP void getQualityMap(OutputArray dst) const
4139
{
42-
if (!dst.needed() || _qualityMaps.empty() )
40+
if (!dst.needed() || _qualityMap.empty() )
4341
return;
44-
45-
auto qMaps = InputArray(_qualityMaps);
46-
dst.create(qMaps.size(), qMaps.type());
47-
dst.assign(_qualityMaps);
42+
dst.assign(_qualityMap);
4843
}
4944

5045
/** @brief Implements Algorithm::clear() */
51-
CV_WRAP void clear() CV_OVERRIDE { _qualityMaps.clear(); Algorithm::clear(); }
46+
CV_WRAP void clear() CV_OVERRIDE { _qualityMap = _mat_type(); Algorithm::clear(); }
5247

5348
/** @brief Implements Algorithm::empty() */
54-
CV_WRAP bool empty() const CV_OVERRIDE { return _qualityMaps.empty(); }
49+
CV_WRAP bool empty() const CV_OVERRIDE { return _qualityMap.empty(); }
5550

5651
protected:
5752

58-
/** @brief internal quality map type default */
59-
using _quality_map_type = cv::UMat;
53+
/** @brief internal mat type default */
54+
using _mat_type = cv::UMat;
6055

6156
/** @brief Output quality maps if generated by algorithm */
62-
std::vector<_quality_map_type> _qualityMaps;
57+
_mat_type _qualityMap;
6358

6459
}; // QualityBase
6560
//! @}

modules/quality/include/opencv2/quality/qualitybrisque.hpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,18 @@ C++ code for the BRISQUE LIVE-R2 trainer and TID2008 evaluator are also provided
2626
class CV_EXPORTS_W QualityBRISQUE : public QualityBase {
2727
public:
2828

29-
/** @brief Computes BRISQUE quality score for input images
30-
@param imgs Images for which to compute quality (should be passed as a vector<Mat> in C++ and list of images in Python)
31-
@returns Score (averaged over individual scores of all images) ranging from 0 to 100
32-
(0 denotes the best quality and 100 denotes the worst quality). The format of the score is: {score, 0., 0., 0.}
29+
/** @brief Computes BRISQUE quality score for input image
30+
@param img Image for which to compute quality
31+
@returns cv::Scalar with the score in the first element. The score ranges from 0 (best quality) to 100 (worst quality)
3332
*/
34-
CV_WRAP cv::Scalar compute( InputArrayOfArrays imgs ) CV_OVERRIDE;
33+
CV_WRAP cv::Scalar compute( InputArray img ) CV_OVERRIDE;
3534

3635
/**
3736
@brief Create an object which calculates quality
38-
@param model_file_path cv::String which contains a path to the BRISQUE model data. If empty, attempts to load from ${OPENCV_DIR}/testdata/contrib/quality/brisque_model_live.yml
39-
@param range_file_path cv::String which contains a path to the BRISQUE range data. If empty, attempts to load from ${OPENCV_DIR}/testdata/contrib/quality/brisque_range_live.yml
37+
@param model_file_path cv::String which contains a path to the BRISQUE model data, eg. /path/to/brisque_model_live.yml
38+
@param range_file_path cv::String which contains a path to the BRISQUE range data, eg. /path/to/brisque_range_live.yml
4039
*/
41-
CV_WRAP static Ptr<QualityBRISQUE> create( const cv::String& model_file_path = "", const cv::String& range_file_path = "" );
40+
CV_WRAP static Ptr<QualityBRISQUE> create( const cv::String& model_file_path, const cv::String& range_file_path );
4241

4342
/**
4443
@brief Create an object which calculates quality
@@ -49,12 +48,12 @@ class CV_EXPORTS_W QualityBRISQUE : public QualityBase {
4948

5049
/**
5150
@brief static method for computing quality
52-
@param imgs image(s) for which to compute quality (passed as Mat or vector<Mat> in C++ and as list of images in Python)
53-
@param model_file_path cv::String which contains a path to the BRISQUE model data. If empty, attempts to load from ${OPENCV_DIR}/testdata/contrib/quality/brisque_model_live.yml
54-
@param range_file_path cv::String which contains a path to the BRISQUE range data. If empty, attempts to load from ${OPENCV_DIR}/testdata/contrib/quality/brisque_range_live.yml
55-
@returns cv::Scalar result of format {std::double score, 0., 0., 0.}. Score ranges from 0 to 100 (100 means worst and 0 means best)
51+
@param img image for which to compute quality
52+
@param model_file_path cv::String which contains a path to the BRISQUE model data, eg. /path/to/brisque_model_live.yml
53+
@param range_file_path cv::String which contains a path to the BRISQUE range data, eg. /path/to/brisque_range_live.yml
54+
@returns cv::Scalar with the score in the first element. The score ranges from 0 (best quality) to 100 (worst quality)
5655
*/
57-
CV_WRAP static cv::Scalar compute( InputArrayOfArrays imgs, const cv::String& model_file_path, const cv::String& range_file_path );
56+
CV_WRAP static cv::Scalar compute( InputArray img, const cv::String& model_file_path, const cv::String& range_file_path );
5857

5958
/**
6059
@brief static method for computing image features used by the BRISQUE algorithm

modules/quality/include/opencv2/quality/qualitygmsd.hpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,65 +22,67 @@ class CV_EXPORTS_W QualityGMSD
2222

2323
/**
2424
@brief Compute GMSD
25-
@param cmpImgs Comparison images
26-
@returns Per-channel GMSD
25+
@param cmp comparison image
26+
@returns cv::Scalar with per-channel quality value. Values range from 0 (worst) to 1 (best)
2727
*/
28-
CV_WRAP cv::Scalar compute(InputArrayOfArrays cmpImgs) CV_OVERRIDE;
28+
CV_WRAP cv::Scalar compute( InputArray cmp ) CV_OVERRIDE;
2929

3030
/** @brief Implements Algorithm::empty() */
3131
CV_WRAP bool empty() const CV_OVERRIDE { return _refImgData.empty() && QualityBase::empty(); }
3232

3333
/** @brief Implements Algorithm::clear() */
34-
CV_WRAP void clear() CV_OVERRIDE { _refImgData.clear(); QualityBase::clear(); }
34+
CV_WRAP void clear() CV_OVERRIDE { _refImgData = _mat_data(); QualityBase::clear(); }
3535

3636
/**
3737
@brief Create an object which calculates image quality
38-
@param refImgs input image(s) to use as the source for comparison
38+
@param ref reference image
3939
*/
40-
CV_WRAP static Ptr<QualityGMSD> create(InputArrayOfArrays refImgs);
40+
CV_WRAP static Ptr<QualityGMSD> create( InputArray ref );
4141

4242
/**
4343
@brief static method for computing quality
44-
@param refImgs reference image(s)
45-
@param cmpImgs comparison image(s)
46-
@param qualityMaps output quality map(s), or cv::noArray()
44+
@param ref reference image
45+
@param cmp comparison image
46+
@param qualityMap output quality map, or cv::noArray()
4747
@returns cv::Scalar with per-channel quality value. Values range from 0 (worst) to 1 (best)
4848
*/
49-
CV_WRAP static cv::Scalar compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps);
49+
CV_WRAP static cv::Scalar compute( InputArray ref, InputArray cmp, OutputArray qualityMap );
5050

5151
protected:
5252

53-
// holds computed values for an input mat
53+
// holds computed values for a mat
5454
struct _mat_data
5555
{
56-
using mat_type = QualityBase::_quality_map_type;
56+
// internal mat type
57+
using mat_type = QualityBase::_mat_type;
5758

5859
mat_type
5960
gradient_map
6061
, gradient_map_squared
6162
;
6263

64+
// allow default construction
65+
_mat_data() = default;
66+
67+
// construct from mat_type
6368
_mat_data(const mat_type&);
6469

65-
// converts mat/umat to vector of mat_data
66-
static std::vector<_mat_data> create(InputArrayOfArrays arr);
70+
// construct from inputarray
71+
_mat_data(InputArray);
72+
73+
// returns flag if empty
74+
bool empty() const { return this->gradient_map.empty() && this->gradient_map_squared.empty(); }
6775

6876
// compute for a single frame
6977
static std::pair<cv::Scalar, mat_type> compute(const _mat_data& lhs, const _mat_data& rhs);
7078

71-
// compute for vector of inputs
72-
static cv::Scalar compute(const std::vector<_mat_data>& lhs, const std::vector<_mat_data>& rhs, OutputArrayOfArrays qualityMaps);
73-
7479
}; // mat_data
7580

7681
/** @brief Reference image data */
77-
std::vector<_mat_data> _refImgData;
82+
_mat_data _refImgData;
7883

79-
/**
80-
@brief Constructor
81-
@param refImgData vector of reference images, converted to internal type
82-
*/
83-
QualityGMSD(std::vector<_mat_data> refImgData)
84+
// internal constructor
85+
QualityGMSD(_mat_data refImgData)
8486
: _refImgData(std::move(refImgData))
8587
{}
8688

0 commit comments

Comments
 (0)