Commit 24cd5e21 authored by clunietp's avatar clunietp Committed by Alexander Alekhin

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
parent 5eaa25c9
set(the_description "Image Quality Analysis API")
ocv_define_module(quality opencv_core opencv_imgproc opencv_ml WRAP python)
ocv_add_testdata(samples/ contrib/quality
FILES_MATCHING PATTERN "*.yml"
)
\ No newline at end of file
# add test data from samples dir to contrib/quality
ocv_add_testdata(samples/ contrib/quality FILES_MATCHING PATTERN "*.yml")
# add brisque model, range files to installation
file(GLOB QUALITY_MODEL_DATA samples/*.yml)
install(FILES ${QUALITY_MODEL_DATA} DESTINATION ${OPENCV_OTHER_INSTALL_PATH}/quality COMPONENT libs)
\ No newline at end of file
......@@ -46,14 +46,14 @@ Quick Start/Usage
```cpp
#include <opencv2/quality.hpp>
cv::Mat img1, img2; /* your cv::Mat images */
std::vector<cv::Mat> quality_maps; /* output quality map(s) (optional) */
cv::Mat img1, img2; /* your cv::Mat images to compare */
cv::Mat quality_map; /* output quality map (optional) */
/* compute MSE via static method */
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_maps); /* or cv::noArray() if not interested in output quality maps */
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map); /* or cv::noArray() if not interested in output quality maps */
/* alternatively, compute MSE via instance */
cv::Ptr<quality::QualityBase> ptr = quality::QualityMSE::create(img1);
cv::Scalar result = ptr->compute( img2 ); /* compute MSE, compare img1 vs img2 */
ptr->getQualityMaps(quality_maps); /* optionally, access output quality maps */
ptr->getQualityMap(quality_map); /* optionally, access output quality maps */
```
**For No Reference IQA Algorithm (BRISQUE)**
......@@ -81,11 +81,11 @@ model_path, range_path);
img1 = cv2.imread(img1, 1) # specify img1
img2 = cv2.imread(img2_path, 1) # specify img2_path
# compute MSE score and quality maps via static method
result_static, quality_maps = cv2.quality.QualityMSE_compute(img1, img2)
result_static, quality_map = cv2.quality.QualityMSE_compute(img1, img2)
# compute MSE score and quality maps via Instance
obj = cv2.quality.QualityMSE_create(img1)
result = obj.compute(img2)
quality_maps = obj.getQualityMaps()
quality_map = obj.getQualityMap()
```
**For No Reference IQA Algorithm (BRISQUE)**
......@@ -94,28 +94,26 @@ model_path, range_path);
import cv2
# read image
img = cv2.imread(img_path, 1) # mention img_path
# make a list of image to be passed
img_list = [img]
# compute brisque quality score via static method
score = cv2.quality.QualityBRISQUE_compute(img_list, model_path,
score = cv2.quality.QualityBRISQUE_compute(img, model_path,
range_path) # specify model_path and range_path
# compute brisque quality score via instance
# specify model_path and range_path
obj = cv2.quality.QualityBRISQUE_create(model_path, range_path)
score = obj.compute(img_list)
score = obj.compute(img)
```
Library Design
-----------------------------------------
Each implemented algorithm shall:
- Inherit from `QualityBase`, and properly implement/override `compute`, `empty` and `clear` instance methods, along with a static `compute` method.
- 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.
- 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.
- 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.
- Return a `cv::Scalar` with per-channel computed value
- Compute result via a single, static method named `compute` and via an overridden instance method (see `compute` in `qualitybase.hpp`).
- 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.
- 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.
- 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.
- Implement tests of static `compute` method and instance methods using single- and multi-channel images, multi-frame images, and OpenCL enabled and disabled
- 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.
- 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.
- 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.
- Implement tests of static `compute` method and instance methods using single- and multi-channel images and OpenCL enabled and disabled
To Do
-----------------------------------------
......
......@@ -5,7 +5,6 @@
#ifndef OPENCV_QUALITY_QUALITY_UTILS_HPP
#define OPENCV_QUALITY_QUALITY_UTILS_HPP
#include <limits> // numeric_limits
#include "qualitybase.hpp"
namespace cv
......@@ -18,53 +17,33 @@ namespace quality_utils
// default type of matrix to expand to
static CV_CONSTEXPR const int EXPANDED_MAT_DEFAULT_TYPE = CV_32F;
// convert input array to vector of specified mat types. set type == -1 to preserve existing type
// convert inputarray to specified mat type. set type == -1 to preserve existing type
template <typename R>
inline std::vector<R> extract_mats( InputArrayOfArrays arr, const int type = -1 )
inline R extract_mat(InputArray in, const int type = -1)
{
std::vector<R> result = {};
std::vector<UMat> umats = {};
std::vector<Mat> mats = {};
if (arr.isUMatVector())
arr.getUMatVector(umats);
else if (arr.isUMat())
umats.emplace_back(arr.getUMat());
else if (arr.isMatVector())
arr.getMatVector(mats);
else if (arr.isMat())
mats.emplace_back(arr.getMat());
R result = {};
if ( in.isMat() )
in.getMat().convertTo( result, (type != -1) ? type : in.getMat().type());
else if ( in.isUMat() )
in.getUMat().convertTo( result, (type != -1) ? type : in.getUMat().type());
else
CV_Error(Error::StsNotImplemented, "Unsupported input type");
// convert umats, mats to desired type
for (auto& umat : umats)
{
result.emplace_back(R{});
umat.convertTo(result.back(), ( type != -1 ) ? type : umat.type() );
}
for (auto& mat : mats)
{
result.emplace_back(R{});
mat.convertTo(result.back(), (type != -1) ? type : mat.type() );
}
return result;
}
// expand matrix to target type
template <typename OutT, typename InT>
inline OutT expand_mat(const InT& src, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
// extract and expand matrix to target type
template <typename R>
inline R expand_mat( InputArray src, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
{
OutT result = {};
auto result = extract_mat<R>(src, -1);
// by default, expand to 32F unless we already have >= 32 bits, then go to 64
// if/when we can detect OpenCL CV_16F support, opt for that when input depth == 8
// note that this may impact the precision of the algorithms and would need testing
int type = TYPE_DEFAULT;
switch (src.depth())
switch (result.depth())
{
case CV_32F:
case CV_32S:
......@@ -72,40 +51,10 @@ inline OutT expand_mat(const InT& src, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_T
type = CV_64F;
}; // switch
src.convertTo(result, type);
result.convertTo(result, type);
return result;
}
// convert input array to vector of expanded mat types
template <typename R>
inline std::vector<R> expand_mats(InputArrayOfArrays arr, int TYPE_DEFAULT = EXPANDED_MAT_DEFAULT_TYPE)
{
std::vector<R> result = {};
auto mats = extract_mats<R>(arr, -1);
for (auto& mat : mats)
result.emplace_back(expand_mat<R>(mat, TYPE_DEFAULT));
return result;
}
// convert mse to psnr
inline double mse_to_psnr(double mse, double max_pixel_value)
{
return (mse == 0.)
? std::numeric_limits<double>::infinity()
: 10. * std::log10((max_pixel_value * max_pixel_value) / mse)
;
}
// convert scalar of mses to psnrs
inline cv::Scalar mse_to_psnr(cv::Scalar mse, double max_pixel_value)
{
for (int i = 0; i < mse.rows; ++i)
mse(i) = mse_to_psnr(mse(i), max_pixel_value);
return mse;
}
// return mat of observed min/max pair per column
// row 0: min per column
// row 1: max per column
......
......@@ -5,7 +5,6 @@
#ifndef OPENCV_QUALITYBASE_HPP
#define OPENCV_QUALITYBASE_HPP
#include <vector>
#include <opencv2/core.hpp>
/**
......@@ -21,7 +20,6 @@ namespace quality
//! @{
/************************************ Quality Base Class ************************************/
class CV_EXPORTS_W QualityBase
: public virtual Algorithm
{
......@@ -32,34 +30,31 @@ public:
/**
@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
@param cmpImgs comparison image(s), or image(s) to evalute for no-reference quality algorithms
@param img comparison image, or image to evalute for no-reference quality algorithms
*/
virtual CV_WRAP cv::Scalar compute( InputArrayOfArrays cmpImgs ) = 0;
virtual CV_WRAP cv::Scalar compute( InputArray img ) = 0;
/** @brief Returns output quality map images that were generated during computation, if supported by the algorithm */
virtual CV_WRAP void getQualityMaps(OutputArrayOfArrays dst) const
/** @brief Returns output quality map that was generated during computation, if supported by the algorithm */
virtual CV_WRAP void getQualityMap(OutputArray dst) const
{
if (!dst.needed() || _qualityMaps.empty() )
if (!dst.needed() || _qualityMap.empty() )
return;
auto qMaps = InputArray(_qualityMaps);
dst.create(qMaps.size(), qMaps.type());
dst.assign(_qualityMaps);
dst.assign(_qualityMap);
}
/** @brief Implements Algorithm::clear() */
CV_WRAP void clear() CV_OVERRIDE { _qualityMaps.clear(); Algorithm::clear(); }
CV_WRAP void clear() CV_OVERRIDE { _qualityMap = _mat_type(); Algorithm::clear(); }
/** @brief Implements Algorithm::empty() */
CV_WRAP bool empty() const CV_OVERRIDE { return _qualityMaps.empty(); }
CV_WRAP bool empty() const CV_OVERRIDE { return _qualityMap.empty(); }
protected:
/** @brief internal quality map type default */
using _quality_map_type = cv::UMat;
/** @brief internal mat type default */
using _mat_type = cv::UMat;
/** @brief Output quality maps if generated by algorithm */
std::vector<_quality_map_type> _qualityMaps;
_mat_type _qualityMap;
}; // QualityBase
//! @}
......
......@@ -26,19 +26,18 @@ C++ code for the BRISQUE LIVE-R2 trainer and TID2008 evaluator are also provided
class CV_EXPORTS_W QualityBRISQUE : public QualityBase {
public:
/** @brief Computes BRISQUE quality score for input images
@param imgs Images for which to compute quality (should be passed as a vector<Mat> in C++ and list of images in Python)
@returns Score (averaged over individual scores of all images) ranging from 0 to 100
(0 denotes the best quality and 100 denotes the worst quality). The format of the score is: {score, 0., 0., 0.}
/** @brief Computes BRISQUE quality score for input image
@param img Image for which to compute quality
@returns cv::Scalar with the score in the first element. The score ranges from 0 (best quality) to 100 (worst quality)
*/
CV_WRAP cv::Scalar compute( InputArrayOfArrays imgs ) CV_OVERRIDE;
CV_WRAP cv::Scalar compute( InputArray img ) CV_OVERRIDE;
/**
@brief Create an object which calculates quality
@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
@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
@param model_file_path cv::String which contains a path to the BRISQUE model data, eg. /path/to/brisque_model_live.yml
@param range_file_path cv::String which contains a path to the BRISQUE range data, eg. /path/to/brisque_range_live.yml
*/
CV_WRAP static Ptr<QualityBRISQUE> create( const cv::String& model_file_path = "", const cv::String& range_file_path = "" );
CV_WRAP static Ptr<QualityBRISQUE> create( const cv::String& model_file_path, const cv::String& range_file_path );
/**
@brief Create an object which calculates quality
......@@ -49,12 +48,12 @@ public:
/**
@brief static method for computing quality
@param imgs image(s) for which to compute quality (passed as Mat or vector<Mat> in C++ and as list of images in Python)
@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
@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
@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)
@param img image for which to compute quality
@param model_file_path cv::String which contains a path to the BRISQUE model data, eg. /path/to/brisque_model_live.yml
@param range_file_path cv::String which contains a path to the BRISQUE range data, eg. /path/to/brisque_range_live.yml
@returns cv::Scalar with the score in the first element. The score ranges from 0 (best quality) to 100 (worst quality)
*/
CV_WRAP static cv::Scalar compute( InputArrayOfArrays imgs, const cv::String& model_file_path, const cv::String& range_file_path );
CV_WRAP static cv::Scalar compute( InputArray img, const cv::String& model_file_path, const cv::String& range_file_path );
/**
@brief static method for computing image features used by the BRISQUE algorithm
......
......@@ -22,65 +22,67 @@ public:
/**
@brief Compute GMSD
@param cmpImgs Comparison images
@returns Per-channel GMSD
@param cmp comparison image
@returns cv::Scalar with per-channel quality value. Values range from 0 (worst) to 1 (best)
*/
CV_WRAP cv::Scalar compute(InputArrayOfArrays cmpImgs) CV_OVERRIDE;
CV_WRAP cv::Scalar compute( InputArray cmp ) CV_OVERRIDE;
/** @brief Implements Algorithm::empty() */
CV_WRAP bool empty() const CV_OVERRIDE { return _refImgData.empty() && QualityBase::empty(); }
/** @brief Implements Algorithm::clear() */
CV_WRAP void clear() CV_OVERRIDE { _refImgData.clear(); QualityBase::clear(); }
CV_WRAP void clear() CV_OVERRIDE { _refImgData = _mat_data(); QualityBase::clear(); }
/**
@brief Create an object which calculates image quality
@param refImgs input image(s) to use as the source for comparison
@param ref reference image
*/
CV_WRAP static Ptr<QualityGMSD> create(InputArrayOfArrays refImgs);
CV_WRAP static Ptr<QualityGMSD> create( InputArray ref );
/**
@brief static method for computing quality
@param refImgs reference image(s)
@param cmpImgs comparison image(s)
@param qualityMaps output quality map(s), or cv::noArray()
@param ref reference image
@param cmp comparison image
@param qualityMap output quality map, or cv::noArray()
@returns cv::Scalar with per-channel quality value. Values range from 0 (worst) to 1 (best)
*/
CV_WRAP static cv::Scalar compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps);
CV_WRAP static cv::Scalar compute( InputArray ref, InputArray cmp, OutputArray qualityMap );
protected:
// holds computed values for an input mat
// holds computed values for a mat
struct _mat_data
{
using mat_type = QualityBase::_quality_map_type;
// internal mat type
using mat_type = QualityBase::_mat_type;
mat_type
gradient_map
, gradient_map_squared
;
// allow default construction
_mat_data() = default;
// construct from mat_type
_mat_data(const mat_type&);
// converts mat/umat to vector of mat_data
static std::vector<_mat_data> create(InputArrayOfArrays arr);
// construct from inputarray
_mat_data(InputArray);
// returns flag if empty
bool empty() const { return this->gradient_map.empty() && this->gradient_map_squared.empty(); }
// compute for a single frame
static std::pair<cv::Scalar, mat_type> compute(const _mat_data& lhs, const _mat_data& rhs);
// compute for vector of inputs
static cv::Scalar compute(const std::vector<_mat_data>& lhs, const std::vector<_mat_data>& rhs, OutputArrayOfArrays qualityMaps);
}; // mat_data
/** @brief Reference image data */
std::vector<_mat_data> _refImgData;
_mat_data _refImgData;
/**
@brief Constructor
@param refImgData vector of reference images, converted to internal type
*/
QualityGMSD(std::vector<_mat_data> refImgData)
// internal constructor
QualityGMSD(_mat_data refImgData)
: _refImgData(std::move(refImgData))
{}
......
......@@ -25,37 +25,37 @@ public:
CV_WRAP cv::Scalar compute( InputArrayOfArrays cmpImgs ) CV_OVERRIDE;
/** @brief Implements Algorithm::empty() */
CV_WRAP bool empty() const CV_OVERRIDE { return _refImgs.empty() && QualityBase::empty(); }
CV_WRAP bool empty() const CV_OVERRIDE { return _ref.empty() && QualityBase::empty(); }
/** @brief Implements Algorithm::clear() */
CV_WRAP void clear() CV_OVERRIDE { _refImgs.clear(); QualityBase::clear(); }
CV_WRAP void clear() CV_OVERRIDE { _ref = _mat_type(); QualityBase::clear(); }
/**
@brief Create an object which calculates quality
@param refImgs input image(s) to use as the source for comparison
@param ref input image to use as the reference for comparison
*/
CV_WRAP static Ptr<QualityMSE> create(InputArrayOfArrays refImgs);
CV_WRAP static Ptr<QualityMSE> create(InputArray ref);
/**
@brief static method for computing quality
@param refImgs reference image(s)
@param cmpImgs comparison image(s)
@param qualityMaps output quality map(s), or cv::noArray()
@returns cv::Scalar with per-channel quality values. Values range from 0 (best) to potentially max float (worst)
@param ref reference image
@param cmp comparison image=
@param qualityMap output quality map, or cv::noArray()
@returns cv::Scalar with per-channel quality values. Values range from 0 (best) to max float (worst)
*/
CV_WRAP static cv::Scalar compute( InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps );
CV_WRAP static cv::Scalar compute( InputArray ref, InputArray cmp, OutputArray qualityMap );
protected:
/** @brief Reference images, converted to internal mat type */
std::vector<QualityBase::_quality_map_type> _refImgs;
/** @brief Reference image, converted to internal mat type */
QualityBase::_mat_type _ref;
/**
@brief Constructor
@param refImgs vector of reference images, converted to internal type
@param ref reference image, converted to internal type
*/
QualityMSE(std::vector<QualityBase::_quality_map_type> refImgs)
: _refImgs(std::move(refImgs))
QualityMSE(QualityBase::_mat_type ref)
: _ref(std::move(ref))
{}
}; // QualityMSE
......
......@@ -5,9 +5,9 @@
#ifndef OPENCV_QUALITY_QUALITYPSNR_HPP
#define OPENCV_QUALITY_QUALITYPSNR_HPP
#include <limits> // numeric_limits
#include "qualitybase.hpp"
#include "qualitymse.hpp"
#include "quality_utils.hpp"
namespace cv
{
......@@ -31,26 +31,25 @@ public:
#endif
/**
@brief Create an object which calculates quality via mean square error
@param refImgs input image(s) to use as the source for comparison
@brief Create an object which calculates quality
@param ref input image to use as the source for comparison
@param maxPixelValue maximum per-channel value for any individual pixel; eg 255 for uint8 image
*/
CV_WRAP static Ptr<QualityPSNR> create(InputArrayOfArrays refImgs, double maxPixelValue = QualityPSNR::MAX_PIXEL_VALUE_DEFAULT )
CV_WRAP static Ptr<QualityPSNR> create( InputArray ref, double maxPixelValue = QualityPSNR::MAX_PIXEL_VALUE_DEFAULT )
{
return Ptr<QualityPSNR>(new QualityPSNR(QualityMSE::create(refImgs), maxPixelValue));
return Ptr<QualityPSNR>(new QualityPSNR(QualityMSE::create(ref), maxPixelValue));
}
/**
@brief compute the PSNR
@param cmpImgs Comparison images
@brief Compute the PSNR
@param cmp Comparison image
@returns Per-channel PSNR value, or std::numeric_limits<double>::infinity() if the MSE between the two images == 0
The PSNR for multi-frame images is computed by calculating the average MSE of all frames and then generating the PSNR from that value
*/
CV_WRAP cv::Scalar compute(InputArrayOfArrays cmpImgs) CV_OVERRIDE
CV_WRAP cv::Scalar compute( InputArray cmp ) CV_OVERRIDE
{
auto result = _qualityMSE->compute(cmpImgs);
_qualityMSE->getQualityMaps(_qualityMaps); // copy from internal obj to this obj
return quality_utils::mse_to_psnr(
auto result = _qualityMSE->compute( cmp );
_qualityMSE->getQualityMap(_qualityMap); // copy from internal obj to this obj
return _mse_to_psnr(
result
, _maxPixelValue
);
......@@ -64,17 +63,16 @@ public:
/**
@brief static method for computing quality
@param refImgs reference image(s)
@param cmpImgs comparison image(s)
@param qualityMaps output quality map(s), or cv::noArray()
@param ref reference image
@param cmp comparison image
@param qualityMap output quality map, or cv::noArray()
@param maxPixelValue maximum per-channel value for any individual pixel; eg 255 for uint8 image
@returns PSNR value, or std::numeric_limits<double>::infinity() if the MSE between the two images == 0
The PSNR for multi-frame images is computed by calculating the average MSE of all frames and then generating the PSNR from that value
*/
CV_WRAP static cv::Scalar compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps, double maxPixelValue = QualityPSNR::MAX_PIXEL_VALUE_DEFAULT)
CV_WRAP static cv::Scalar compute( InputArray ref, InputArray cmp, OutputArray qualityMap, double maxPixelValue = QualityPSNR::MAX_PIXEL_VALUE_DEFAULT)
{
return quality_utils::mse_to_psnr(
QualityMSE::compute(refImgs, cmpImgs, qualityMaps)
return _mse_to_psnr(
QualityMSE::compute(ref, cmp, qualityMap)
, maxPixelValue
);
}
......@@ -99,6 +97,23 @@ protected:
, _maxPixelValue(maxPixelValue)
{}
// convert mse to psnr
static double _mse_to_psnr(double mse, double max_pixel_value)
{
return (mse == 0.)
? std::numeric_limits<double>::infinity()
: 10. * std::log10((max_pixel_value * max_pixel_value) / mse)
;
}
// convert scalar of mses to psnrs
static cv::Scalar _mse_to_psnr(cv::Scalar mse, double max_pixel_value)
{
for (int i = 0; i < mse.rows; ++i)
mse(i) = _mse_to_psnr(mse(i), max_pixel_value);
return mse;
}
}; // QualityPSNR
} // quality
} // cv
......
......@@ -21,38 +21,39 @@ public:
/**
@brief Computes SSIM
@param cmpImgs Comparison images
@param cmp Comparison image
@returns cv::Scalar with per-channel quality values. Values range from 0 (worst) to 1 (best)
*/
CV_WRAP cv::Scalar compute(InputArrayOfArrays cmpImgs) CV_OVERRIDE;
CV_WRAP cv::Scalar compute( InputArray cmp ) CV_OVERRIDE;
/** @brief Implements Algorithm::empty() */
CV_WRAP bool empty() const CV_OVERRIDE { return _refImgData.empty() && QualityBase::empty(); }
/** @brief Implements Algorithm::clear() */
CV_WRAP void clear() CV_OVERRIDE { _refImgData.clear(); QualityBase::clear(); }
CV_WRAP void clear() CV_OVERRIDE { _refImgData = _mat_data(); QualityBase::clear(); }
/**
@brief Create an object which calculates quality via mean square error
@param refImgs input image(s) to use as the source for comparison
@brief Create an object which calculates quality
@param ref input image to use as the reference image for comparison
*/
CV_WRAP static Ptr<QualitySSIM> create(InputArrayOfArrays refImgs);
CV_WRAP static Ptr<QualitySSIM> create( InputArray ref );
/**
@brief static method for computing quality
@param refImgs reference image(s)
@param cmpImgs comparison image(s)
@param qualityMaps output quality map(s), or cv::noArray()
@param ref reference image
@param cmp comparison image
@param qualityMap output quality map, or cv::noArray()
@returns cv::Scalar with per-channel quality values. Values range from 0 (worst) to 1 (best)
*/
CV_WRAP static cv::Scalar compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps);
CV_WRAP static cv::Scalar compute( InputArray ref, InputArray cmp, OutputArray qualityMap );
protected:
// holds computed values for a mat
struct _mat_data
{
using mat_type = QualityBase::_quality_map_type;
// internal mat type
using mat_type = QualityBase::_mat_type;
mat_type
I
......@@ -62,28 +63,32 @@ protected:
, sigma_2
;
// allow default construction
_mat_data() = default;
// construct from mat_type
_mat_data(const mat_type&);
// construct vector of _mat_data for input mats
static std::vector<_mat_data> create(InputArrayOfArrays arr);
// construct from inputarray
_mat_data(InputArray);
// return flag if this is empty
bool empty() const { return I.empty() && I_2.empty() && mu.empty() && mu_2.empty() && sigma_2.empty(); }
// computes ssim and quality map for single frame
static std::pair<cv::Scalar, mat_type> compute(const _mat_data& lhs, const _mat_data& rhs);
// computes mse and quality maps for multiple frames
static cv::Scalar compute(const std::vector<_mat_data>& lhs, const std::vector<_mat_data>& rhs, OutputArrayOfArrays qualityMaps);
}; // mat_data
/** @brief Reference image data */
std::vector<_mat_data> _refImgData;
_mat_data _refImgData;
/**
@brief Constructor
@param refImgData vector of reference images, converted to internal type
@param refImgData reference image, converted to internal type
*/
QualitySSIM(std::vector<_mat_data> refImgData)
: _refImgData(std::move(refImgData))
QualitySSIM( _mat_data refImgData )
: _refImgData( std::move(refImgData) )
{}
}; // QualitySSIM
......
......@@ -2,6 +2,7 @@
#include <iostream>
#include "opencv2/quality.hpp"
#include "opencv2/quality/quality_utils.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/ml.hpp"
......
......@@ -240,26 +240,6 @@ namespace
result[0] = computescore(model, range, img);
return result;
}
cv::Scalar compute( const cv::Ptr<cv::ml::SVM>& model, const cv::Mat& range, std::vector<brisque_mat_type>& imgs)
{
CV_DbgAssert(imgs.size() > 0);
cv::Scalar result = {};
const auto sz = imgs.size();
for (unsigned i = 0; i < sz; ++i)
{
auto cmp = compute(model, range, imgs[i]);
cv::add(result, cmp, result);
}
if (sz > 1)
result /= (cv::Scalar::value_type)sz; // average result
return result;
}
}
// static
......@@ -275,52 +255,26 @@ cv::Ptr<QualityBRISQUE> QualityBRISQUE::create(const cv::Ptr<cv::ml::SVM>& model
}
// static
cv::Scalar QualityBRISQUE::compute(InputArrayOfArrays imgs, const cv::String& model_file_path, const cv::String& range_file_path)
cv::Scalar QualityBRISQUE::compute( InputArray img, const cv::String& model_file_path, const cv::String& range_file_path)
{
auto obj = create(model_file_path, range_file_path);
return obj->compute(imgs);
return QualityBRISQUE(model_file_path, range_file_path).compute(img);
}
// QualityBRISQUE() constructor
QualityBRISQUE::QualityBRISQUE(const cv::String& model_file_path, const cv::String& range_file_path)
{
// construct data file path from OPENCV_DIR env var and quality subdir
const auto get_data_path = [](const cv::String& fname)
{
cv::String path{ std::getenv("OPENCV_DIR") };
return path.empty()
? path // empty
: path + "/testdata/contrib/quality/" + fname
;
};
const auto
modelpath = model_file_path.empty() ? get_data_path("brisque_model_live.yml") : model_file_path
, rangepath = range_file_path.empty() ? get_data_path("brisque_range_live.yml") : range_file_path
;
if (modelpath.empty())
CV_Error(cv::Error::StsObjectNotFound, "BRISQUE model data not found");
if (rangepath.empty())
CV_Error(cv::Error::StsObjectNotFound, "BRISQUE range data not found");
*this = QualityBRISQUE(
cv::ml::SVM::load(modelpath)
, cv::FileStorage(rangepath, cv::FileStorage::READ)["range"].mat()
);
}
: QualityBRISQUE(
cv::ml::SVM::load(model_file_path)
, cv::FileStorage(range_file_path, cv::FileStorage::READ)["range"].mat()
)
{}
cv::Scalar QualityBRISQUE::compute(InputArrayOfArrays imgs)
cv::Scalar QualityBRISQUE::compute( InputArray img )
{
auto vec = quality_utils::extract_mats<brisque_mat_type>(imgs); // extract input mats
CV_Assert(!vec.empty());
auto mat = quality_utils::extract_mat<brisque_mat_type>(img); // extract input mats
for (auto& mat : vec)
mat = mat_convert(mat); // convert to gs, scale to [0,1]
mat = mat_convert(mat);// convert to gs, scale to [0,1]
return ::compute(this->_model, this->_range, vec);
return ::compute(this->_model, this->_range, mat );
}
//static
......
......@@ -123,36 +123,31 @@ QualityGMSD::_mat_data::_mat_data(const QualityGMSD::_mat_data::mat_type& mat)
this->gradient_map_squared = this->gradient_map.mul(this->gradient_map);
}
QualityGMSD::_mat_data::_mat_data(InputArray arr)
: _mat_data(quality_utils::expand_mat<mat_type>(arr))//delegate
{}
// static
Ptr<QualityGMSD> QualityGMSD::create(InputArrayOfArrays refImgs)
Ptr<QualityGMSD> QualityGMSD::create( InputArray ref )
{
return Ptr<QualityGMSD>(new QualityGMSD( _mat_data::create( refImgs )));
return Ptr<QualityGMSD>(new QualityGMSD( _mat_data(ref)));
}
// static
cv::Scalar QualityGMSD::compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps)
cv::Scalar QualityGMSD::compute( InputArray ref, InputArray cmp, OutputArray qualityMap )
{
auto ref = _mat_data::create( refImgs );
auto cmp = _mat_data::create( cmpImgs );
auto result = _mat_data::compute( _mat_data(ref), _mat_data(cmp) );
return _mat_data::compute(ref, cmp, qualityMaps);
if (qualityMap.needed())
qualityMap.assign(result.second);
return result.first;
}
cv::Scalar QualityGMSD::compute(InputArrayOfArrays cmpImgs)
cv::Scalar QualityGMSD::compute( InputArray cmp )
{
auto cmp = _mat_data::create(cmpImgs);
return _mat_data::compute(this->_refImgData, cmp, this->_qualityMaps);
}
// static, converts mat/umat to vector of mat_data
std::vector<QualityGMSD::_mat_data> QualityGMSD::_mat_data::create(InputArrayOfArrays arr)
{
std::vector<_mat_data> result = {};
auto mats = quality_utils::expand_mats<_mat_type>(arr);
result.reserve(mats.size());
for (auto& mat : mats)
result.emplace_back(mat);
return result;
auto result = _mat_data::compute(this->_refImgData, _mat_data(cmp));
OutputArray(this->_qualityMap).assign(result.second);
return result.first;
}
// computes gmsd and quality map for single frame
......@@ -180,39 +175,4 @@ std::pair<cv::Scalar, _quality_map_type> QualityGMSD::_mat_data::compute(const Q
result.second = std::move(qm);
return result;
} // compute
// static, computes mse and quality maps for multiple frames
cv::Scalar QualityGMSD::_mat_data::compute(const std::vector<QualityGMSD::_mat_data>& lhs, const std::vector<QualityGMSD::_mat_data>& rhs, OutputArrayOfArrays qualityMaps)
{
CV_Assert(lhs.size() > 0);
CV_Assert(lhs.size() == rhs.size());
cv::Scalar result = {};
std::vector<_quality_map_type> quality_maps = {};
const auto sz = lhs.size();
for (unsigned i = 0; i < sz; ++i)
{
CV_Assert(!lhs.empty() && !rhs.empty());
auto cmp = compute(lhs[i], rhs[i]); // differs slightly when using umat vs mat
cv::add(result, cmp.first, result); // result += cmp.first
if (qualityMaps.needed())
quality_maps.emplace_back(std::move(cmp.second));
}
if (qualityMaps.needed())
{
auto qMaps = InputArray(quality_maps);
qualityMaps.create(qMaps.size(), qMaps.type());
qualityMaps.assign(quality_maps);
}
if (sz > 1)
result /= (cv::Scalar::value_type)sz; // average result
return result;
}
\ No newline at end of file
} // compute
\ No newline at end of file
......@@ -28,59 +28,32 @@ namespace
return result;
}
// computes mse and quality maps for multiple frames
cv::Scalar compute(const std::vector<mse_mat_type>& lhs, const std::vector<mse_mat_type>& rhs, OutputArrayOfArrays qualityMaps )
{
CV_Assert(lhs.size() > 0);
CV_Assert(lhs.size() == rhs.size());
cv::Scalar result = {};
std::vector<_quality_map_type> quality_maps = {};
const auto sz = lhs.size();
for (unsigned i = 0; i < sz; ++i)
{
CV_Assert(!lhs.empty() && !rhs.empty());
auto cmp = compute(lhs[i], rhs[i]);
cv::add(result, cmp.first, result);
if ( qualityMaps.needed() )
quality_maps.emplace_back(std::move(cmp.second));
}
if (qualityMaps.needed())
{
auto qMaps = InputArray(quality_maps);
qualityMaps.create(qMaps.size(), qMaps.type());
qualityMaps.assign(quality_maps);
}
if (sz > 1)
result /= (cv::Scalar::value_type)sz; // average result
return result;
}
}
// static
Ptr<QualityMSE> QualityMSE::create( InputArrayOfArrays refImgs )
Ptr<QualityMSE> QualityMSE::create( InputArray ref )
{
return Ptr<QualityMSE>(new QualityMSE(quality_utils::expand_mats<mse_mat_type>(refImgs)));
return Ptr<QualityMSE>(new QualityMSE(quality_utils::expand_mat<mse_mat_type>(ref)));
}
// static
cv::Scalar QualityMSE::compute( InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps )
cv::Scalar QualityMSE::compute( InputArray ref_, InputArray cmp_, OutputArray qualityMap )
{
auto ref = quality_utils::expand_mats<mse_mat_type>(refImgs);
auto cmp = quality_utils::expand_mats<mse_mat_type>(cmpImgs);
auto ref = quality_utils::expand_mat<mse_mat_type>(ref_);
auto cmp = quality_utils::expand_mat<mse_mat_type>(cmp_);
auto result = ::compute(ref, cmp);
if (qualityMap.needed())
qualityMap.assign(result.second);
return ::compute(ref, cmp, qualityMaps);
return result.first;
}
cv::Scalar QualityMSE::compute( InputArrayOfArrays cmpImgs )
cv::Scalar QualityMSE::compute( InputArray cmp_ )
{
auto cmp = quality_utils::expand_mats<mse_mat_type>(cmpImgs);
return ::compute( this->_refImgs, cmp, this->_qualityMaps);
auto cmp = quality_utils::expand_mat<mse_mat_type>(cmp_);
auto result = ::compute( this->_ref, cmp );
OutputArray(this->_qualityMap).assign(result.second);
return result.first;
}
\ No newline at end of file
......@@ -34,40 +34,40 @@ QualitySSIM::_mat_data::_mat_data( const _mat_type& mat )
cv::subtract(this->sigma_2, this->mu_2, this->sigma_2);
}
QualitySSIM::_mat_data::_mat_data(InputArray arr )
: _mat_data( quality_utils::expand_mat<mat_type>(arr) ) // delegate
{}
// static
Ptr<QualitySSIM> QualitySSIM::create(InputArrayOfArrays refImgs)
Ptr<QualitySSIM> QualitySSIM::create( InputArray ref )
{
return Ptr<QualitySSIM>(new QualitySSIM( _mat_data::create( refImgs )));
return Ptr<QualitySSIM>(new QualitySSIM( _mat_data( ref )));
}
// static
cv::Scalar QualitySSIM::compute(InputArrayOfArrays refImgs, InputArrayOfArrays cmpImgs, OutputArrayOfArrays qualityMaps)
cv::Scalar QualitySSIM::compute( InputArray ref, InputArray cmp, OutputArray qualityMap )
{
auto ref = _mat_data::create( refImgs );
auto cmp = _mat_data::create( cmpImgs );
auto result = _mat_data::compute( _mat_data(ref), _mat_data(cmp) );
return _mat_data::compute(ref, cmp, qualityMaps);
}
if (qualityMap.needed())
qualityMap.assign(result.second);
cv::Scalar QualitySSIM::compute(InputArrayOfArrays cmpImgs)
{
auto cmp = _mat_data::create(cmpImgs);
return _mat_data::compute(this->_refImgData, cmp, this->_qualityMaps);
return result.first;
}
// static. converts mat/umat to vector of mat_data
std::vector<QualitySSIM::_mat_data> QualitySSIM::_mat_data::create(InputArrayOfArrays arr)
cv::Scalar QualitySSIM::compute( InputArray cmp )
{
std::vector<QualitySSIM::_mat_data> result = {};
auto mats = quality_utils::expand_mats<_mat_type>(arr);
result.reserve(mats.size());
for (auto& mat : mats)
result.emplace_back(mat);
return result;
auto result = _mat_data::compute(
this->_refImgData
, _mat_data(cmp)
);
OutputArray(this->_qualityMap).assign(result.second);
return result.first;
}
// computes ssim and quality map for single frame
// based on https://docs.opencv.org/2.4/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html
// static. computes ssim and quality map for single frame
// based on https://docs.opencv.org/2.4/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html
std::pair<cv::Scalar, _mat_type> QualitySSIM::_mat_data::compute(const _mat_data& lhs, const _mat_data& rhs)
{
const double
......@@ -115,39 +115,4 @@ std::pair<cv::Scalar, _mat_type> QualitySSIM::_mat_data::compute(const _mat_data
cv::mean(t3)
, std::move(t3)
};
} // compute
// computes mse and quality maps for multiple frames
cv::Scalar QualitySSIM::_mat_data::compute(const std::vector<_mat_data>& lhs, const std::vector<_mat_data>& rhs, OutputArrayOfArrays qualityMaps)
{
CV_Assert(lhs.size() > 0);
CV_Assert(lhs.size() == rhs.size());
Scalar result = {};
std::vector<QualityBase::_quality_map_type> quality_maps = {};
const auto sz = lhs.size();
for (unsigned i = 0; i < sz; ++i)
{
CV_Assert(!lhs.empty() && !rhs.empty());
auto cmp = compute(lhs[i], rhs[i]); // differs slightly when using umat vs mat
cv::add(result, cmp.first, result); // result += cmp.first
if (qualityMaps.needed())
quality_maps.emplace_back(std::move(cmp.second));
}
if (qualityMaps.needed())
{
auto qMaps = InputArray(quality_maps);
qualityMaps.create(qMaps.size(), qMaps.type());
qualityMaps.assign(quality_maps);
}
if (sz > 1)
result /= (cv::Scalar::value_type)sz;// average result
return result;
}
\ No newline at end of file
} // compute
\ No newline at end of file
......@@ -18,14 +18,13 @@ const cv::Scalar
;
// default model and range file names
// opencv tests must be installed (cmake var: INSTALL_TESTS), or BRISQUE tests will be skipped
static const char* MODEL_FNAME = "brisque_model_live.yml";
static const char* RANGE_FNAME = "brisque_range_live.yml";
// instantiates a brisque object for testing
inline cv::Ptr<quality::QualityBRISQUE> create_brisque()
{
// location of BRISQUE model and range file
// place these files in ${OPENCV_TEST_DATA_PATH}/quality/, or the tests will be skipped
const auto model = cvtest::findDataFile(MODEL_FNAME, false);
const auto range = cvtest::findDataFile(RANGE_FNAME, false);
return quality::QualityBRISQUE::create(model, range);
......@@ -47,7 +46,7 @@ TEST(TEST_CASE_NAME, static_ )
// single channel, instance method, with and without opencl
TEST(TEST_CASE_NAME, single_channel )
{
auto fn = []() { quality_test(create_brisque(), get_testfile_1a(), BRISQUE_EXPECTED_1, 0, true ); };
auto fn = []() { quality_test(create_brisque(), get_testfile_1a(), BRISQUE_EXPECTED_1, false, true ); };
OCL_OFF( fn() );
OCL_ON( fn() );
}
......@@ -55,25 +54,14 @@ TEST(TEST_CASE_NAME, single_channel )
// multi-channel
TEST(TEST_CASE_NAME, multi_channel)
{
quality_test(create_brisque(), get_testfile_2a(), BRISQUE_EXPECTED_2, 0, true);
}
// multi-frame test
TEST(TEST_CASE_NAME, multi_frame)
{
// result mse == average of all frames
cv::Scalar expected;
cv::add(BRISQUE_EXPECTED_1, BRISQUE_EXPECTED_2, expected);
expected /= 2.;
quality_test(create_brisque(), get_testfile_1a2a(), expected, 0, true );
quality_test(create_brisque(), get_testfile_2a(), BRISQUE_EXPECTED_2, false, true);
}
// check brisque model/range persistence
TEST(TEST_CASE_NAME, model_persistence )
{
auto ptr = create_brisque();
auto fn = [&ptr]() { quality_test(ptr, get_testfile_1a(), BRISQUE_EXPECTED_1, 0, true); };
auto fn = [&ptr]() { quality_test(ptr, get_testfile_1a(), BRISQUE_EXPECTED_1, false, true); };
fn();
fn(); // model/range should persist with brisque ptr through multiple invocations
}
......
......@@ -11,7 +11,7 @@ namespace opencv_test
namespace quality_test
{
// gmsd per channel
// expected gmsd per channel
const cv::Scalar
GMSD_EXPECTED_1 = { .2393 }
, GMSD_EXPECTED_2 = { .0942, .1016, .0995 }
......@@ -20,9 +20,9 @@ const cv::Scalar
// static method
TEST(TEST_CASE_NAME, static_)
{
std::vector<cv::Mat> qMats = {};
quality_expect_near(quality::QualityGMSD::compute(get_testfile_1a(), get_testfile_1a(), qMats), cv::Scalar(0.)); // ref vs ref == 0.
EXPECT_EQ(qMats.size(), 1U );
cv::Mat qMat = {};
quality_expect_near(quality::QualityGMSD::compute(get_testfile_1a(), get_testfile_1a(), qMat), cv::Scalar(0.)); // ref vs ref == 0.
check_quality_map(qMat);
}
// single channel, with and without opencl
......@@ -39,17 +39,6 @@ TEST(TEST_CASE_NAME, multi_channel)
quality_test(quality::QualityGMSD::create(get_testfile_2a()), get_testfile_2b(), GMSD_EXPECTED_2);
}
// multi-frame test
TEST(TEST_CASE_NAME, multi_frame)
{
// result == average of all frames
cv::Scalar expected;
cv::add(GMSD_EXPECTED_1, GMSD_EXPECTED_2, expected);
expected /= 2.;
quality_test(quality::QualityGMSD::create(get_testfile_1a2a()), get_testfile_1b2b(), expected, 2);
}
// internal A/B test
/*
TEST(TEST_CASE_NAME, performance)
......
......@@ -4,5 +4,6 @@
#include "test_precomp.hpp"
CV_TEST_MAIN("",
cvtest::addDataSearchSubDirectory("quality")
cvtest::addDataSearchSubDirectory("contrib/quality") // for ocv_add_testdata
, cvtest::addDataSearchSubDirectory("quality") // for ${OPENCV_TEST_DATA_PATH}
)
\ No newline at end of file
......@@ -14,9 +14,9 @@ namespace quality_test
// static method
TEST(TEST_CASE_NAME, static_ )
{
std::vector<cv::Mat> qMats = {};
quality_expect_near(quality::QualityMSE::compute(get_testfile_1a(), get_testfile_1a(), qMats), cv::Scalar(0.)); // ref vs ref == 0
EXPECT_EQ(qMats.size(), 1U);
cv::Mat qMat = {};
quality_expect_near(quality::QualityMSE::compute(get_testfile_1a(), get_testfile_1a(), qMat), cv::Scalar(0.)); // ref vs ref == 0
check_quality_map(qMat);
}
// single channel, with and without opencl
......@@ -33,17 +33,6 @@ TEST(TEST_CASE_NAME, multi_channel)
quality_test(quality::QualityMSE::create(get_testfile_2a()), get_testfile_2b(), MSE_EXPECTED_2);
}
// multi-frame test
TEST(TEST_CASE_NAME, multi_frame)
{
// result mse == average mse of all frames
cv::Scalar expected;
cv::add(MSE_EXPECTED_1, MSE_EXPECTED_2, expected);
expected /= 2.;
quality_test(quality::QualityMSE::create(get_testfile_1a2a()), get_testfile_1b2b(), expected, 2 );
}
// internal a/b test
/*
TEST(TEST_CASE_NAME, performance)
......
......@@ -43,10 +43,8 @@ inline cv::Mat get_testfile_1a() { return get_testfile(testfile1a, IMREAD_GRAYSC
inline cv::Mat get_testfile_1b() { return get_testfile(testfile1b, IMREAD_GRAYSCALE); }
inline cv::Mat get_testfile_2a() { return get_testfile(testfile2a); }
inline cv::Mat get_testfile_2b() { return get_testfile(testfile2b); }
inline std::vector<cv::Mat> get_testfile_1a2a() { return { get_testfile_1a(), get_testfile_2a() }; }
inline std::vector<cv::Mat> get_testfile_1b2b() { return { get_testfile_1b(), get_testfile_2b() }; }
const double QUALITY_ERR_TOLERANCE = .001 // allowed margin of error
const double QUALITY_ERR_TOLERANCE = .002 // allowed margin of error
;
inline void quality_expect_near( const cv::Scalar& a, const cv::Scalar& b, double err_tolerance = QUALITY_ERR_TOLERANCE)
......@@ -60,14 +58,29 @@ inline void quality_expect_near( const cv::Scalar& a, const cv::Scalar& b, doubl
}
}
template <typename TMat>
inline void check_quality_map( const TMat& mat, const bool expect_empty = false )
{
EXPECT_EQ( mat.empty(), expect_empty );
if ( !expect_empty )
{
EXPECT_GT(mat.rows, 0);
EXPECT_GT(mat.cols, 0);
}
}
// execute quality test for a pair of images
template <typename TMat>
inline void quality_test(cv::Ptr<quality::QualityBase> ptr, const TMat& cmp, const Scalar& expected, const std::size_t quality_maps_expected = 1, const bool empty_expected = false )
inline void quality_test(cv::Ptr<quality::QualityBase> ptr, const TMat& cmp, const Scalar& expected, const bool quality_map_expected = true, const bool empty_expected = false )
{
std::vector<cv::Mat> qMats = {};
ptr->getQualityMaps(qMats);
EXPECT_TRUE( qMats.empty());
cv::Mat qMat = {};
cv::UMat qUMat = {};
// quality map should return empty in initial state
ptr->getQualityMap(qMat);
EXPECT_TRUE( qMat.empty() );
// compute quality, check result
quality_expect_near( expected, ptr->compute(cmp));
if (empty_expected)
......@@ -75,15 +88,15 @@ inline void quality_test(cv::Ptr<quality::QualityBase> ptr, const TMat& cmp, con
else
EXPECT_FALSE(ptr->empty());
ptr->getQualityMaps(qMats);
// getQualityMap to Mat, UMat
ptr->getQualityMap(qMat);
ptr->getQualityMap(qUMat);
EXPECT_EQ( qMats.size(), quality_maps_expected);
for (auto& qm : qMats)
{
EXPECT_GT(qm.rows, 0);
EXPECT_GT(qm.cols, 0);
}
// check them
check_quality_map(qMat, !quality_map_expected);
check_quality_map(qUMat, !quality_map_expected);
// reset algorithm, should now be empty
ptr->clear();
EXPECT_TRUE(ptr->empty());
}
......
......@@ -19,9 +19,9 @@ const cv::Scalar
// static method
TEST(TEST_CASE_NAME, static_)
{
std::vector<cv::Mat> qMats = {};
quality_expect_near(quality::QualityPSNR::compute(get_testfile_1a(), get_testfile_1a(), qMats), cv::Scalar(INFINITY,INFINITY,INFINITY,INFINITY)); // ref vs ref == inf
EXPECT_EQ(qMats.size(), 1U);
cv::Mat qMat = {};
quality_expect_near(quality::QualityPSNR::compute(get_testfile_1a(), get_testfile_1a(), qMat), cv::Scalar(INFINITY, INFINITY, INFINITY, INFINITY)); // ref vs ref == inf
check_quality_map(qMat);
}
// single channel, with/without opencl
......@@ -38,18 +38,6 @@ TEST(TEST_CASE_NAME, multi_channel)
quality_test(quality::QualityPSNR::create(get_testfile_2a()), get_testfile_2b(), PSNR_EXPECTED_2);
}
// multi-frame test
TEST(TEST_CASE_NAME, multi_frame)
{
cv::Scalar expected;
cv::add(MSE_EXPECTED_1, MSE_EXPECTED_2, expected);
expected /= 2.;
expected = quality::quality_utils::mse_to_psnr(expected, quality::QualityPSNR::MAX_PIXEL_VALUE_DEFAULT );
quality_test(quality::QualityPSNR::create(get_testfile_1a2a()), get_testfile_1b2b(), expected, 2);
}
// internal a/b test
/*
TEST(TEST_CASE_NAME, performance)
......
......@@ -11,7 +11,7 @@ namespace opencv_test
namespace quality_test
{
// ssim per channel
// expected ssim per channel
const cv::Scalar
SSIM_EXPECTED_1 = { .1501 }
, SSIM_EXPECTED_2 = { .7541, .7742, .8095 }
......@@ -20,9 +20,9 @@ const cv::Scalar
// static method
TEST(TEST_CASE_NAME, static_)
{
std::vector<cv::Mat> qMats = {};
quality_expect_near(quality::QualitySSIM::compute(get_testfile_1a(), get_testfile_1a(), qMats), cv::Scalar(1.)); // ref vs ref == 1.
EXPECT_EQ(qMats.size(), 1U );
cv::Mat qMat = {};
quality_expect_near(quality::QualitySSIM::compute(get_testfile_1a(), get_testfile_1a(), qMat), cv::Scalar(1.)); // ref vs ref == 1.
check_quality_map(qMat);
}
// single channel, with/without opencl
......@@ -39,17 +39,6 @@ TEST(TEST_CASE_NAME, multi_channel)
quality_test(quality::QualitySSIM::create(get_testfile_2a()), get_testfile_2b(), SSIM_EXPECTED_2);
}
// multi-frame test
TEST(TEST_CASE_NAME, multi_frame)
{
// result == average of all frames
cv::Scalar expected;
cv::add(SSIM_EXPECTED_1, SSIM_EXPECTED_2, expected);
expected /= 2.;
quality_test(quality::QualitySSIM::create(get_testfile_1a2a()), get_testfile_1b2b(), expected, 2U );
}
// internal a/b test
/*
TEST(TEST_CASE_NAME, performance)
......
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