vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
set(the_description "Intensity transformations")
|
||||
ocv_define_module(intensity_transform opencv_core opencv_imgproc WRAP python)
|
||||
@@ -0,0 +1,4 @@
|
||||
Intensity Transformations
|
||||
========================
|
||||
|
||||
This module contains some of the intensity transformation methods used to adjust the constrast of an image. The methods in the module include autoscaling, gamma correction, log transformations, and contrast stretching.
|
||||
@@ -0,0 +1,28 @@
|
||||
@book{Gonzalez2018,
|
||||
title = {Digital Image Processing 4th Edition},
|
||||
author = {Rafael C. Gonzalez, Richard E. Woods},
|
||||
year = {2018},
|
||||
publisher = {Pearson}
|
||||
}
|
||||
@misc{lcs435lab,
|
||||
title = {CS425 Lab: Intensity Transformations and Spatial Filtering},
|
||||
url = {http://www.cs.uregina.ca/Links/class-info/425/Lab3/}
|
||||
}
|
||||
@misc{theailearner,
|
||||
title = {Contrast Stretching},
|
||||
url = {https://theailearner.com/2019/01/30/contrast-stretching/}
|
||||
}
|
||||
@article{ying2017bio,
|
||||
title = {A Bio-Inspired Multi-Exposure Fusion Framework for Low-light Image Enhancement},
|
||||
author = {Ying, Zhenqiang and Li, Ge and Gao, Wen},
|
||||
journal = {arXiv preprint arXiv:1711.00591},
|
||||
year = {2017}
|
||||
}
|
||||
@inproceedings{ying2017new,
|
||||
title = {A New Image Contrast Enhancement Algorithm Using Exposure Fusion Framework},
|
||||
author = {Ying, Zhenqiang and Li, Ge and Ren, Yurui and Wang, Ronggang and Wang, Wenmin},
|
||||
booktitle = {International Conference on Computer Analysis of Images and Patterns},
|
||||
pages = {36--46},
|
||||
year = {2017},
|
||||
organization = {Springer}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// 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.
|
||||
|
||||
#ifndef OPENCV_INTENSITY_TRANSFORM_H
|
||||
#define OPENCV_INTENSITY_TRANSFORM_H
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
/**
|
||||
* @defgroup intensity_transform The module brings implementations of intensity transformation algorithms to adjust image contrast.
|
||||
*
|
||||
* Namespace for all functions is `cv::intensity_transform`.
|
||||
*
|
||||
* ### Supported Algorithms
|
||||
* - Autoscaling
|
||||
* - Log Transformations
|
||||
* - Power-Law (Gamma) Transformations
|
||||
* - Contrast Stretching
|
||||
* - BIMEF, A Bio-Inspired Multi-Exposure Fusion Framework for Low-light Image Enhancement @cite ying2017bio @cite ying2017new
|
||||
*
|
||||
* References from following book and websites:
|
||||
* - Digital Image Processing 4th Edition Chapter 3 [Rafael C. Gonzalez, Richard E. Woods] @cite Gonzalez2018
|
||||
* - http://www.cs.uregina.ca/Links/class-info/425/Lab3/ @cite lcs435lab
|
||||
* - https://theailearner.com/2019/01/30/contrast-stretching/ @cite theailearner
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace intensity_transform {
|
||||
|
||||
//! @addtogroup intensity_transform
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Given an input bgr or grayscale image and constant c, apply log transformation to the image
|
||||
* on domain [0, 255] and return the resulting image.
|
||||
*
|
||||
* @param input input bgr or grayscale image.
|
||||
* @param output resulting image of log transformations.
|
||||
*/
|
||||
CV_EXPORTS_W void logTransform(const Mat input, Mat& output);
|
||||
|
||||
/**
|
||||
* @brief Given an input bgr or grayscale image and constant gamma, apply power-law transformation,
|
||||
* a.k.a. gamma correction to the image on domain [0, 255] and return the resulting image.
|
||||
*
|
||||
* @param input input bgr or grayscale image.
|
||||
* @param output resulting image of gamma corrections.
|
||||
* @param gamma constant in c*r^gamma where r is pixel value.
|
||||
*/
|
||||
CV_EXPORTS_W void gammaCorrection(const Mat input, Mat& output, const float gamma);
|
||||
|
||||
/**
|
||||
* @brief Given an input bgr or grayscale image, apply autoscaling on domain [0, 255] to increase
|
||||
* the contrast of the input image and return the resulting image.
|
||||
*
|
||||
* @param input input bgr or grayscale image.
|
||||
* @param output resulting image of autoscaling.
|
||||
*/
|
||||
CV_EXPORTS_W void autoscaling(const Mat input, Mat& output);
|
||||
|
||||
/**
|
||||
* @brief Given an input bgr or grayscale image, apply linear contrast stretching on domain [0, 255]
|
||||
* and return the resulting image.
|
||||
*
|
||||
* @param input input bgr or grayscale image.
|
||||
* @param output resulting image of contrast stretching.
|
||||
* @param r1 x coordinate of first point (r1, s1) in the transformation function.
|
||||
* @param s1 y coordinate of first point (r1, s1) in the transformation function.
|
||||
* @param r2 x coordinate of second point (r2, s2) in the transformation function.
|
||||
* @param s2 y coordinate of second point (r2, s2) in the transformation function.
|
||||
*/
|
||||
CV_EXPORTS_W void contrastStretching(const Mat input, Mat& output, const int r1, const int s1, const int r2, const int s2);
|
||||
|
||||
/**
|
||||
* @brief Given an input color image, enhance low-light images using the BIMEF method (@cite ying2017bio @cite ying2017new).
|
||||
*
|
||||
* @param input input color image.
|
||||
* @param output resulting image.
|
||||
* @param mu enhancement ratio.
|
||||
* @param a a-parameter in the Camera Response Function (CRF).
|
||||
* @param b b-parameter in the Camera Response Function (CRF).
|
||||
*
|
||||
* @warning This is a C++ implementation of the [original MATLAB algorithm](https://github.com/baidut/BIMEF).
|
||||
* Compared to the original code, this implementation is a little bit slower and does not provide the same results.
|
||||
* In particular, quality of the image enhancement is degraded for the bright areas in certain conditions.
|
||||
*/
|
||||
CV_EXPORTS_W void BIMEF(InputArray input, OutputArray output, float mu=0.5f, float a=-0.3293f, float b=1.1258f);
|
||||
|
||||
/**
|
||||
* @brief Given an input color image, enhance low-light images using the BIMEF method (@cite ying2017bio @cite ying2017new).
|
||||
*
|
||||
* This is an overloaded function with the exposure ratio given as parameter.
|
||||
*
|
||||
* @param input input color image.
|
||||
* @param output resulting image.
|
||||
* @param k exposure ratio.
|
||||
* @param mu enhancement ratio.
|
||||
* @param a a-parameter in the Camera Response Function (CRF).
|
||||
* @param b b-parameter in the Camera Response Function (CRF).
|
||||
*
|
||||
* @warning This is a C++ implementation of the [original MATLAB algorithm](https://github.com/baidut/BIMEF).
|
||||
* Compared to the original code, this implementation is a little bit slower and does not provide the same results.
|
||||
* In particular, quality of the image enhancement is degraded for the bright areas in certain conditions.
|
||||
*/
|
||||
CV_EXPORTS_AS(BIMEF2) void BIMEF(InputArray input, OutputArray output, float k, float mu, float a, float b);
|
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::intensity_transform::
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class intensity_transform_test(NewOpenCVTests):
|
||||
def setUp(self):
|
||||
super(intensity_transform_test, self).setUp()
|
||||
try:
|
||||
result_ = cv.intensity_transform.BIMEF(None)
|
||||
except cv.error as e:
|
||||
if e.code == cv.Error.StsNotImplemented:
|
||||
self.skipTest('BIMEF is not implemented (missing Eigen dependency)')
|
||||
|
||||
@unittest.skipIf('OPENCV_TEST_DATA_PATH' not in os.environ,
|
||||
"OPENCV_TEST_DATA_PATH is not defined")
|
||||
def test_BIMEF(self):
|
||||
filenames = ['P1000205_resize', 'P1010676_resize', 'P1010815_resize']
|
||||
|
||||
for f in filenames:
|
||||
img = self.get_sample('cv/intensity_transform/BIMEF/{}.png'.format(f))
|
||||
self.assertTrue(img.size > 0)
|
||||
|
||||
img_ref = self.get_sample('cv/intensity_transform/BIMEF/{}_ref.png'.format(f))
|
||||
self.assertTrue(img_ref.size > 0)
|
||||
|
||||
img_BIMEF = cv.intensity_transform.BIMEF(img)
|
||||
self.assertTrue(img_BIMEF.size > 0)
|
||||
self.assertTrue(img_BIMEF.shape == img_ref.shape)
|
||||
self.assertTrue(img_BIMEF.dtype == img_ref.dtype)
|
||||
|
||||
RMSE = np.sqrt(cv.norm(img_BIMEF, img_ref, cv.NORM_L2SQR) / (img_ref.shape[0]*img_ref.shape[1]*img_ref.shape[2]))
|
||||
max_RMSE_threshold = 9.0
|
||||
self.assertLessEqual(RMSE, max_RMSE_threshold)
|
||||
print('BIMEF RMSE:', RMSE)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/intensity_transform.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::intensity_transform;
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string keys =
|
||||
"{ help h | | Print help message. }"
|
||||
"{ input i | | Path to the input image. }";
|
||||
|
||||
// global variables
|
||||
Mat g_image;
|
||||
|
||||
int g_gamma = 40;
|
||||
const int g_gammaMax = 500;
|
||||
Mat g_imgGamma;
|
||||
const std::string g_gammaWinName = "Gamma Correction";
|
||||
|
||||
Mat g_contrastStretch;
|
||||
int g_r1 = 70;
|
||||
int g_s1 = 15;
|
||||
int g_r2 = 120;
|
||||
int g_s2 = 240;
|
||||
const std::string g_contrastWinName = "Contrast Stretching";
|
||||
|
||||
Mat g_imgBIMEF;
|
||||
int g_mu = 50;
|
||||
const int g_muMax = 100;
|
||||
const std::string g_BIMEFWinName = "BIMEF";
|
||||
|
||||
static void onTrackbarGamma(int, void*)
|
||||
{
|
||||
float gamma = g_gamma / 100.0f;
|
||||
gammaCorrection(g_image, g_imgGamma, gamma);
|
||||
imshow(g_gammaWinName, g_imgGamma);
|
||||
}
|
||||
|
||||
static void onTrackbarContrastR1(int, void*)
|
||||
{
|
||||
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
|
||||
imshow("Contrast Stretching", g_contrastStretch);
|
||||
}
|
||||
|
||||
static void onTrackbarContrastS1(int, void*)
|
||||
{
|
||||
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
|
||||
imshow("Contrast Stretching", g_contrastStretch);
|
||||
}
|
||||
|
||||
static void onTrackbarContrastR2(int, void*)
|
||||
{
|
||||
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
|
||||
imshow("Contrast Stretching", g_contrastStretch);
|
||||
}
|
||||
|
||||
static void onTrackbarContrastS2(int, void*)
|
||||
{
|
||||
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
|
||||
imshow("Contrast Stretching", g_contrastStretch);
|
||||
}
|
||||
|
||||
static void onTrackbarBIMEF(int, void*)
|
||||
{
|
||||
float mu = g_mu / 100.0f;
|
||||
BIMEF(g_image, g_imgBIMEF, mu);
|
||||
imshow(g_BIMEFWinName, g_imgBIMEF);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
|
||||
const std::string inputFilename = parser.get<String>("input");
|
||||
parser.about("Use this script to apply intensity transformation on an input image.");
|
||||
if (parser.has("help") || inputFilename.empty())
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read input image
|
||||
g_image = imread(inputFilename);
|
||||
|
||||
// Create trackbars
|
||||
namedWindow(g_gammaWinName);
|
||||
createTrackbar("Gamma value", g_gammaWinName, &g_gamma, g_gammaMax, onTrackbarGamma);
|
||||
|
||||
namedWindow(g_contrastWinName);
|
||||
createTrackbar("Contrast R1", g_contrastWinName, &g_r1, 256, onTrackbarContrastR1);
|
||||
createTrackbar("Contrast S1", g_contrastWinName, &g_s1, 256, onTrackbarContrastS1);
|
||||
createTrackbar("Contrast R2", g_contrastWinName, &g_r2, 256, onTrackbarContrastR2);
|
||||
createTrackbar("Contrast S2", g_contrastWinName, &g_s2, 256, onTrackbarContrastS2);
|
||||
|
||||
namedWindow(g_BIMEFWinName);
|
||||
createTrackbar("Enhancement ratio mu", g_BIMEFWinName, &g_mu, g_muMax, onTrackbarBIMEF);
|
||||
|
||||
// Apply intensity transformations
|
||||
Mat imgAutoscaled, imgLog;
|
||||
autoscaling(g_image, imgAutoscaled);
|
||||
gammaCorrection(g_image, g_imgGamma, g_gamma/100.0f);
|
||||
logTransform(g_image, imgLog);
|
||||
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
|
||||
BIMEF(g_image, g_imgBIMEF, g_mu / 100.0f);
|
||||
|
||||
// Display intensity transformation results
|
||||
imshow("Original Image", g_image);
|
||||
imshow("Autoscale", imgAutoscaled);
|
||||
imshow(g_gammaWinName, g_imgGamma);
|
||||
imshow("Log Transformation", imgLog);
|
||||
imshow(g_contrastWinName, g_contrastStretch);
|
||||
imshow(g_BIMEFWinName, g_imgBIMEF);
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,577 @@
|
||||
// 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.
|
||||
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Zhenqiang.Ying
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#ifdef HAVE_EIGEN
|
||||
#include <Eigen/Sparse>
|
||||
#include <opencv2/core/eigen.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
namespace intensity_transform {
|
||||
|
||||
#ifdef HAVE_EIGEN
|
||||
static void diff(const Mat_<float>& src, Mat_<float>& srcVDiff, Mat_<float>& srcHDiff)
|
||||
{
|
||||
srcVDiff = Mat_<float>(src.size());
|
||||
for (int i = 0; i < src.rows; i++)
|
||||
{
|
||||
if (i < src.rows-1)
|
||||
{
|
||||
for (int j = 0; j < src.cols; j++)
|
||||
{
|
||||
srcVDiff(i,j) = src(i+1,j) - src(i,j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < src.cols; j++)
|
||||
{
|
||||
srcVDiff(i,j) = src(0,j) - src(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcHDiff = Mat_<float>(src.size());
|
||||
for (int j = 0; j < src.cols-1; j++)
|
||||
{
|
||||
for (int i = 0; i < src.rows; i++)
|
||||
{
|
||||
srcHDiff(i,j) = src(i,j+1) - src(i,j);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < src.rows; i++)
|
||||
{
|
||||
srcHDiff(i,src.cols-1) = src(i,0) - src(i,src.cols-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void computeTextureWeights(const Mat_<float>& x, float sigma, float sharpness, Mat_<float>& W_h, Mat_<float>& W_v)
|
||||
{
|
||||
Mat_<float> dt0_v, dt0_h;
|
||||
diff(x, dt0_v, dt0_h);
|
||||
|
||||
Mat_<float> gauker_h;
|
||||
Mat_<float> kernel_h = Mat_<float>::ones(1, static_cast<int>(sigma));
|
||||
filter2D(dt0_h, gauker_h, -1, kernel_h, Point(-1,-1), 0, BORDER_CONSTANT);
|
||||
|
||||
Mat_<float> gauker_v;
|
||||
Mat_<float> kernel_v = Mat_<float>::ones(static_cast<int>(sigma), 1);
|
||||
filter2D(dt0_v, gauker_v, -1, kernel_v, Point(-1,-1), 0, BORDER_CONSTANT);
|
||||
|
||||
W_h = Mat_<float>(gauker_h.size());
|
||||
W_v = Mat_<float>(gauker_v.size());
|
||||
|
||||
for (int i = 0; i < gauker_h.rows; i++)
|
||||
{
|
||||
for (int j = 0; j < gauker_h.cols; j++)
|
||||
{
|
||||
W_h(i,j) = 1 / (std::abs(gauker_h(i,j)) * std::abs(dt0_h(i,j)) + sharpness);
|
||||
W_v(i,j) = 1 / (std::abs(gauker_v(i,j)) * std::abs(dt0_v(i,j)) + sharpness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class numeric_t>
|
||||
static Eigen::SparseMatrix<numeric_t> spdiags(const Eigen::Matrix<numeric_t,-1,-1> &B,
|
||||
const Eigen::VectorXi &d, int m, int n) {
|
||||
typedef Eigen::Triplet<numeric_t> triplet_t;
|
||||
std::vector<triplet_t> triplets;
|
||||
triplets.reserve(static_cast<size_t>(std::min(m,n)*d.size()));
|
||||
|
||||
for (int k = 0; k < d.size(); ++k) {
|
||||
int diag = d(k); // get diagonal
|
||||
int i_start = std::max(-diag, 0); // get row of 1st element
|
||||
int i_end = std::min(m, m-diag-(m-n)); // get row of last element
|
||||
int j = -std::min(0, -diag); // get col of 1st element
|
||||
int B_i; // start index i in matrix B
|
||||
if (m < n) {
|
||||
B_i = std::max(-diag,0); // m < n
|
||||
} else {
|
||||
B_i = std::max(0,diag); // m >= n
|
||||
}
|
||||
for (int i = i_start; i < i_end; ++i, ++j, ++B_i) {
|
||||
triplets.push_back( {i, j, B(B_i,k)} );
|
||||
}
|
||||
}
|
||||
Eigen::SparseMatrix<numeric_t> A(m,n);
|
||||
A.setFromTriplets(triplets.begin(), triplets.end());
|
||||
return A;
|
||||
}
|
||||
|
||||
|
||||
static Mat solveLinearEquation(const Mat_<float>& img, Mat_<float>& W_h_, Mat_<float>& W_v_, float lambda)
|
||||
{
|
||||
Eigen::MatrixXf W_h;
|
||||
cv2eigen(W_h_, W_h);
|
||||
Eigen::MatrixXf tempx(W_h.rows(), W_h.cols());
|
||||
tempx.block(0, 1, tempx.rows(), tempx.cols()-1) = W_h.block(0, 0, W_h.rows(), W_h.cols()-1);
|
||||
for (Eigen::Index i = 0; i < tempx.rows(); i++)
|
||||
{
|
||||
tempx(i,0) = W_h(i, W_h.cols()-1);
|
||||
}
|
||||
|
||||
Eigen::MatrixXf W_v;
|
||||
cv2eigen(W_v_, W_v);
|
||||
Eigen::MatrixXf tempy(W_v.rows(), W_v.cols());
|
||||
tempy.block(1, 0, tempx.rows()-1, tempx.cols()) = W_v.block(0, 0, W_v.rows()-1, W_v.cols());
|
||||
for (Eigen::Index j = 0; j < tempy.cols(); j++)
|
||||
{
|
||||
tempy(0,j) = W_v(W_v.rows()-1, j);
|
||||
}
|
||||
|
||||
|
||||
Eigen::VectorXf dx(W_h.rows()*W_h.cols());
|
||||
Eigen::VectorXf dy(W_v.rows()*W_v.cols());
|
||||
|
||||
Eigen::VectorXf dxa(tempx.rows()*tempx.cols());
|
||||
Eigen::VectorXf dya(tempy.rows()*tempy.cols());
|
||||
|
||||
//Flatten in a col-major order
|
||||
for (Eigen::Index j = 0; j < W_h.cols(); j++)
|
||||
{
|
||||
for (Eigen::Index i = 0; i < W_h.rows(); i++)
|
||||
{
|
||||
dx(j*W_h.rows() + i) = -lambda*W_h(i,j);
|
||||
dy(j*W_h.rows() + i) = -lambda*W_v(i,j);
|
||||
|
||||
dxa(j*W_h.rows() + i) = -lambda*tempx(i,j);
|
||||
dya(j*W_h.rows() + i) = -lambda*tempy(i,j);
|
||||
}
|
||||
}
|
||||
|
||||
tempx.setZero();
|
||||
tempx.col(0) = W_h.col(W_h.cols()-1);
|
||||
|
||||
tempy.setZero();
|
||||
tempy.row(0) = W_v.row(W_v.rows()-1);
|
||||
|
||||
W_h.col(W_h.cols()-1).setZero();
|
||||
W_v.row(W_v.rows()-1).setZero();
|
||||
|
||||
Eigen::VectorXf dxd1(tempx.rows()*tempx.cols());
|
||||
Eigen::VectorXf dyd1(tempy.rows()*tempy.cols());
|
||||
Eigen::VectorXf dxd2(W_h.rows()*W_h.cols());
|
||||
Eigen::VectorXf dyd2(W_v.rows()*W_v.cols());
|
||||
|
||||
//Flatten in a col-major order
|
||||
for (Eigen::Index j = 0; j < tempx.cols(); j++)
|
||||
{
|
||||
for (Eigen::Index i = 0; i < tempx.rows(); i++)
|
||||
{
|
||||
dxd1(j*tempx.rows() + i) = -lambda*tempx(i,j);
|
||||
dyd1(j*tempx.rows() + i) = -lambda*tempy(i,j);
|
||||
|
||||
dxd2(j*tempx.rows() + i) = -lambda*W_h(i,j);
|
||||
dyd2(j*tempx.rows() + i) = -lambda*W_v(i,j);
|
||||
}
|
||||
}
|
||||
|
||||
Eigen::MatrixXf dxd(dxd1.rows(), dxd1.cols()+dxd2.cols());
|
||||
dxd << dxd1, dxd2;
|
||||
|
||||
Eigen::MatrixXf dyd(dyd1.rows(), dyd1.cols()+dyd2.cols());
|
||||
dyd << dyd1, dyd2;
|
||||
|
||||
const int k = img.rows*img.cols;
|
||||
const int r = img.rows;
|
||||
Eigen::Matrix<int, 2, 1> diagx_idx;
|
||||
diagx_idx << -k+r, -r;
|
||||
Eigen::SparseMatrix<float> Ax = spdiags(dxd, diagx_idx, k, k);
|
||||
|
||||
Eigen::Matrix<int, 2, 1> diagy_idx;
|
||||
diagy_idx << -r+1, -1;
|
||||
Eigen::SparseMatrix<float> Ay = spdiags(dyd, diagy_idx, k, k);
|
||||
|
||||
Eigen::MatrixXf D = (dx + dy + dxa + dya);
|
||||
D = Eigen::MatrixXf::Ones(D.rows(), D.cols()) - D;
|
||||
|
||||
Eigen::Matrix<int, 1, 1> diag_idx_zero;
|
||||
diag_idx_zero << 0;
|
||||
Eigen::SparseMatrix<float> A = (Ax + Ay) + Eigen::SparseMatrix<float>((Ax + Ay).transpose()) + spdiags(D, diag_idx_zero, k, k);
|
||||
|
||||
//CG solver of Eigen
|
||||
Eigen::ConjugateGradient<Eigen::SparseMatrix<float>, Eigen::Lower|Eigen::Upper, Eigen::IncompleteCholesky<float> > cg;
|
||||
cg.setTolerance(0.1f);
|
||||
cg.setMaxIterations(50);
|
||||
cg.compute(A);
|
||||
Mat_<float> img_t = img.t();
|
||||
Eigen::Map<const Eigen::VectorXf> tin(img_t.ptr<float>(), img_t.rows*img_t.cols);
|
||||
Eigen::VectorXf x = cg.solve(tin);
|
||||
|
||||
Mat tout(img.rows, img.cols, CV_32FC1);
|
||||
tout.forEach<float>(
|
||||
[&](float &pixel, const int * position) -> void
|
||||
{
|
||||
pixel = x(position[1]*img.rows + position[0]);
|
||||
}
|
||||
);
|
||||
|
||||
return tout;
|
||||
}
|
||||
|
||||
static Mat_<float> tsmooth(const Mat_<float>& src, float lambda=0.01f, float sigma=3.0f, float sharpness=0.001f)
|
||||
{
|
||||
Mat_<float> W_h, W_v;
|
||||
computeTextureWeights(src, sigma, sharpness, W_h, W_v);
|
||||
|
||||
Mat_<float> S = solveLinearEquation(src, W_h, W_v, lambda);
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
static Mat_<float> rgb2gm(const Mat_<Vec3f>& I)
|
||||
{
|
||||
Mat_<float> gm(I.rows, I.cols);
|
||||
gm.forEach(
|
||||
[&](float &pixel, const int * position) -> void
|
||||
{
|
||||
pixel = std::pow(I(position[0], position[1])[0]*I(position[0], position[1])[1]*I(position[0], position[1])[2], 1/3.0f);
|
||||
}
|
||||
);
|
||||
|
||||
return gm;
|
||||
}
|
||||
|
||||
static Mat_<float> applyK(const Mat_<float>& I, float k, float a=-0.3293f, float b=1.1258f) {
|
||||
float beta = std::exp((1 - std::pow(k, a)) * b);
|
||||
float gamma = std::pow(k, a);
|
||||
|
||||
Mat_<float> J(I.size());
|
||||
CV_Assert(I.isContinuous());
|
||||
size_t i, npix = I.total();
|
||||
const float* Iptr = I.ptr<float>();
|
||||
float* Jptr = J.ptr<float>();
|
||||
for (i = 0; i < npix; i++) {
|
||||
Jptr[i] = pow(Iptr[i], gamma)*beta;
|
||||
}
|
||||
return J;
|
||||
}
|
||||
|
||||
static Mat_<Vec3f> applyK(const Mat_<Vec3f>& I, float k, float a=-0.3293f, float b=1.1258f, float offset=0) {
|
||||
float beta = std::exp((1 - std::pow(k, a)) * b);
|
||||
float gamma = std::pow(k, a);
|
||||
|
||||
Mat_<Vec3f> J(I.size());
|
||||
pow(I, gamma, J);
|
||||
|
||||
return J * beta + Scalar::all(offset);
|
||||
}
|
||||
|
||||
static float entropy(const Mat_<float>& I)
|
||||
{
|
||||
Mat_<uchar> I_uchar;
|
||||
I.convertTo(I_uchar, CV_8U, 255);
|
||||
|
||||
std::vector<Mat> planes;
|
||||
planes.push_back(I_uchar);
|
||||
Mat_<float> hist;
|
||||
const int histSize = 256;
|
||||
float range[] = { 0, 256 };
|
||||
const float* histRange = { range };
|
||||
calcHist(&I_uchar, 1, NULL, Mat(), hist, 1, &histSize, &histRange);
|
||||
double histsum = cv::sum(hist)[0];
|
||||
|
||||
Mat_<float> hist_norm = hist / histsum;
|
||||
int i, nbins = (int)hist_norm.total();
|
||||
|
||||
float E = 0;
|
||||
for (i = 0; i < nbins; i++)
|
||||
{
|
||||
float v = hist_norm(i);
|
||||
if (v > 0)
|
||||
{
|
||||
E += v * std::log2(v);
|
||||
}
|
||||
}
|
||||
|
||||
return -E;
|
||||
}
|
||||
|
||||
template <typename T> static int sgn(T val)
|
||||
{
|
||||
return (T(0) < val) - (val < T(0));
|
||||
}
|
||||
|
||||
static double minimize_scalar_bounded(const Mat_<float>& I, double begin, double end,
|
||||
double xatol=1e-4, int maxiter=500)
|
||||
{
|
||||
// From scipy: https://github.com/scipy/scipy/blob/v1.4.1/scipy/optimize/optimize.py#L1753-L1894
|
||||
// """
|
||||
// Options
|
||||
// -------
|
||||
// maxiter : int
|
||||
// Maximum number of iterations to perform.
|
||||
// disp: int, optional
|
||||
// If non-zero, print messages.
|
||||
// 0 : no message printing.
|
||||
// 1 : non-convergence notification messages only.
|
||||
// 2 : print a message on convergence too.
|
||||
// 3 : print iteration results.
|
||||
// xatol : float
|
||||
// Absolute error in solution `xopt` acceptable for convergence.
|
||||
// """
|
||||
double x1 = begin, x2 = end;
|
||||
|
||||
if (x1 > x2) {
|
||||
throw std::runtime_error("The lower bound exceeds the upper bound.");
|
||||
}
|
||||
|
||||
double sqrt_eps = std::sqrt(2.2e-16);
|
||||
double golden_mean = 0.5 * (3.0 - std::sqrt(5.0));
|
||||
double a = x1, b = x2;
|
||||
double fulc = a + golden_mean * (b - a);
|
||||
double nfc = fulc, xf = fulc;
|
||||
double rat = 0.0, e = 0.0;
|
||||
double x = xf;
|
||||
double fx = -entropy(applyK(I, static_cast<float>(x)));
|
||||
double fu = std::numeric_limits<double>::infinity();
|
||||
|
||||
double ffulc = fx, fnfc = fx;
|
||||
double xm = 0.5 * (a + b);
|
||||
double tol1 = sqrt_eps * std::abs(xf) + xatol / 3.0;
|
||||
double tol2 = 2.0 * tol1;
|
||||
|
||||
for (int iter = 0; iter < maxiter && std::abs(xf - xm) > (tol2 - 0.5 * (b - a)); iter++)
|
||||
{
|
||||
int golden = 1;
|
||||
// Check for parabolic fit
|
||||
if (std::abs(e) > tol1) {
|
||||
golden = 0;
|
||||
double r = (xf - nfc) * (-entropy(applyK(I, static_cast<float>(x))) - ffulc);
|
||||
double q = (xf - fulc) * (-entropy(applyK(I, static_cast<float>(x))) - fnfc);
|
||||
double p = (xf - fulc) * q - (xf - nfc) * r;
|
||||
q = 2.0 * (q - r);
|
||||
|
||||
if (q > 0.0) {
|
||||
p = -p;
|
||||
}
|
||||
q = std::abs(q);
|
||||
r = e;
|
||||
e = rat;
|
||||
|
||||
// Check for acceptability of parabola
|
||||
if (((std::abs(p) < std::abs(0.5*q*r)) && (p > q*(a - xf)) &
|
||||
(p < q * (b - xf)))) {
|
||||
rat = (p + 0.0) / q;
|
||||
x = xf + rat;
|
||||
|
||||
if (((x - a) < tol2) || ((b - x) < tol2)) {
|
||||
double si = sgn(xm - xf) + ((xm - xf) == 0);
|
||||
rat = tol1 * si;
|
||||
}
|
||||
} else { // do a golden-section step
|
||||
golden = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (golden) { // do a golden-section step
|
||||
if (xf >= xm) {
|
||||
e = a - xf;
|
||||
} else {
|
||||
e = b - xf;
|
||||
}
|
||||
rat = golden_mean*e;
|
||||
}
|
||||
|
||||
double si = sgn(rat) + (rat == 0);
|
||||
x = xf + si * std::max(std::abs(rat), tol1);
|
||||
fu = -entropy(applyK(I, static_cast<float>(x)));
|
||||
|
||||
if (fu <= fx) {
|
||||
if (x >= xf) {
|
||||
a = xf;
|
||||
} else {
|
||||
b = xf;
|
||||
}
|
||||
|
||||
fulc = nfc;
|
||||
ffulc = fnfc;
|
||||
nfc = xf;
|
||||
fnfc = fx;
|
||||
xf = x;
|
||||
fx = fu;
|
||||
} else {
|
||||
if (x < xf) {
|
||||
a = x;
|
||||
} else {
|
||||
b = x;
|
||||
}
|
||||
|
||||
if ((fu <= fnfc) || (nfc == xf)) {
|
||||
fulc = nfc;
|
||||
ffulc = fnfc;
|
||||
nfc = x;
|
||||
fnfc = fu;
|
||||
} else if ((fu <= ffulc) || (fulc == xf) || (fulc == nfc)) {
|
||||
fulc = x;
|
||||
ffulc = fu;
|
||||
}
|
||||
}
|
||||
|
||||
xm = 0.5 * (a + b);
|
||||
tol1 = sqrt_eps * std::abs(xf) + xatol / 3.0;
|
||||
tol2 = 2.0 * tol1;
|
||||
}
|
||||
|
||||
return xf;
|
||||
}
|
||||
|
||||
static Mat_<Vec3f> maxEntropyEnhance(const Mat_<Vec3f>& I, const Mat_<uchar>& isBad, float a, float b)
|
||||
{
|
||||
Mat_<Vec3f> input;
|
||||
resize(I, input, Size(50,50));
|
||||
|
||||
Mat_<float> Y = rgb2gm(input);
|
||||
|
||||
Mat_<uchar> isBad_resize;
|
||||
resize(isBad, isBad_resize, Size(50,50));
|
||||
|
||||
std::vector<float> Y_vec;
|
||||
for (int i = 0; i < isBad_resize.rows; i++)
|
||||
{
|
||||
for (int j = 0; j < isBad_resize.cols; j++)
|
||||
{
|
||||
if (isBad_resize(i,j) >= 0.5)
|
||||
{
|
||||
Y_vec.push_back(Y(i,j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Y_vec.empty())
|
||||
{
|
||||
return I;
|
||||
}
|
||||
|
||||
Mat_<float> Y_mat(static_cast<int>(Y_vec.size()), 1, Y_vec.data());
|
||||
float opt_k = static_cast<float>(minimize_scalar_bounded(Y_mat, 1, 7));
|
||||
|
||||
return applyK(I, opt_k, a, b, -0.01f);
|
||||
}
|
||||
|
||||
static void BIMEF_impl(InputArray input_, OutputArray output_, float mu, float *k, float a, float b)
|
||||
{
|
||||
CV_INSTRUMENT_REGION()
|
||||
|
||||
Mat input = input_.getMat();
|
||||
if (input.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
CV_CheckTypeEQ(input.type(), CV_8UC3, "Input image must be 8-bits color image (CV_8UC3).");
|
||||
|
||||
Mat_<Vec3f> imgDouble;
|
||||
input.convertTo(imgDouble, CV_32F, 1/255.0);
|
||||
|
||||
// t: scene illumination map
|
||||
Mat_<float> t_b(imgDouble.size());
|
||||
t_b.forEach(
|
||||
[&](float &pixel, const int* position) -> void
|
||||
{
|
||||
pixel = std::max(std::max(imgDouble(position[0], position[1])[0],
|
||||
imgDouble(position[0], position[1])[1]),
|
||||
imgDouble(position[0], position[1])[2]);
|
||||
}
|
||||
);
|
||||
|
||||
const float lambda = 0.5;
|
||||
const float sigma = 5;
|
||||
|
||||
Mat_<float> t_b_resize;
|
||||
resize(t_b, t_b_resize, Size(), 0.5, 0.5);
|
||||
|
||||
Mat_<float> t_our = tsmooth(t_b_resize, lambda, sigma);
|
||||
resize(t_our, t_our, t_b.size());
|
||||
|
||||
// k: exposure ratio
|
||||
Mat_<Vec3f> J;
|
||||
if (k == NULL)
|
||||
{
|
||||
Mat_<uchar> isBad(t_our.size());
|
||||
isBad.forEach(
|
||||
[&](uchar &pixel, const int * position) -> void
|
||||
{
|
||||
pixel = t_our(position[0], position[1]) < 0.5 ? 1 : 0;
|
||||
}
|
||||
);
|
||||
|
||||
J = maxEntropyEnhance(imgDouble, isBad, a, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
J = applyK(imgDouble, *k, a, b);
|
||||
|
||||
// fix overflow
|
||||
J.forEach(
|
||||
[](Vec3f &pixel, const int * /*position*/) -> void
|
||||
{
|
||||
pixel(0) = std::min(1.0f, pixel(0));
|
||||
pixel(1) = std::min(1.0f, pixel(1));
|
||||
pixel(2) = std::min(1.0f, pixel(2));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// W: Weight Matrix
|
||||
Mat_<float> W(t_our.size());
|
||||
pow(t_our, mu, W);
|
||||
|
||||
|
||||
output_.create(input.size(), CV_8UC3);
|
||||
Mat output = output_.getMat();
|
||||
output.forEach<Vec3b>(
|
||||
[&](Vec3b &pixel, const int * position) -> void
|
||||
{
|
||||
float w = W(position[0], position[1]);
|
||||
pixel(0) = saturate_cast<uchar>((imgDouble(position[0], position[1])[0] * w + J(position[0], position[1])[0] * (1 - w)) * 255);
|
||||
pixel(1) = saturate_cast<uchar>((imgDouble(position[0], position[1])[1] * w + J(position[0], position[1])[1] * (1 - w)) * 255);
|
||||
pixel(2) = saturate_cast<uchar>((imgDouble(position[0], position[1])[2] * w + J(position[0], position[1])[2] * (1 - w)) * 255);
|
||||
}
|
||||
);
|
||||
}
|
||||
#else
|
||||
static void BIMEF_impl(InputArray, OutputArray, float, float *, float, float)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "This algorithm requires OpenCV built with the Eigen library.");
|
||||
}
|
||||
#endif
|
||||
|
||||
void BIMEF(InputArray input, OutputArray output, float mu, float a, float b)
|
||||
{
|
||||
BIMEF_impl(input, output, mu, NULL, a, b);
|
||||
}
|
||||
|
||||
void BIMEF(InputArray input, OutputArray output, float k, float mu, float a, float b)
|
||||
{
|
||||
BIMEF_impl(input, output, mu, &k, a, b);
|
||||
}
|
||||
|
||||
}} // cv::intensity_transform::
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
namespace cv {
|
||||
namespace intensity_transform {
|
||||
|
||||
void logTransform(const Mat input, Mat& output)
|
||||
{
|
||||
double maxVal;
|
||||
minMaxLoc(input, NULL, &maxVal, NULL, NULL);
|
||||
const double c = 255 / log(1 + maxVal);
|
||||
Mat add_one_64f;
|
||||
input.convertTo(add_one_64f, CV_64F, 1, 1.0f);
|
||||
Mat log_64f;
|
||||
cv::log(add_one_64f, log_64f);
|
||||
log_64f.convertTo(output, CV_8UC3, c, 0.0f);
|
||||
}
|
||||
|
||||
void gammaCorrection(const Mat input, Mat& output, const float gamma)
|
||||
{
|
||||
std::array<uchar, 256> table;
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
table[i] = saturate_cast<uchar>(pow((i / 255.0), gamma) * 255.0);
|
||||
}
|
||||
|
||||
LUT(input, table, output);
|
||||
}
|
||||
|
||||
void autoscaling(const Mat input, Mat& output)
|
||||
{
|
||||
double minVal, maxVal;
|
||||
minMaxLoc(input, &minVal, &maxVal, NULL, NULL);
|
||||
output = 255 * (input - minVal) / (maxVal - minVal);
|
||||
}
|
||||
|
||||
void contrastStretching(const Mat input, Mat& output, const int r1, const int s1, const int r2, const int s2)
|
||||
{
|
||||
std::array<uchar, 256> table;
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (i <= r1)
|
||||
{
|
||||
table[i] = saturate_cast<uchar>(((float)s1 / (float)r1) * i);
|
||||
}
|
||||
else if (r1 < i && i <= r2)
|
||||
{
|
||||
table[i] = saturate_cast<uchar>(((float)(s2 - s1)/(float)(r2 - r1)) * (i - r1) + s1);
|
||||
}
|
||||
else // (r2 < i)
|
||||
{
|
||||
table[i] = saturate_cast<uchar>(((float)(255 - s2)/(float)(255 - r2)) * (i - r2) + s2);
|
||||
}
|
||||
}
|
||||
|
||||
LUT(input, table, output);
|
||||
}
|
||||
|
||||
}} // cv::intensity_transform::
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
#ifndef OPENCV_INTENSITY_TRANSFORM_PRECOMP_H
|
||||
#define OPENCV_INTENSITY_TRANSFORM_PRECOMP_H
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/intensity_transform.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include <array>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,239 @@
|
||||
// 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 "test_precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
TEST(intensity_transform_logTransform, accuracy)
|
||||
{
|
||||
uchar image_data[] = {
|
||||
51, 211, 212, 38, 48, 25, 189, 16, 64, 197,
|
||||
104, 137, 60, 10, 78, 234, 186, 149, 37, 236,
|
||||
128, 80, 6, 53, 7, 65, 233, 15, 216, 42,
|
||||
108, 132, 136, 194, 117, 128, 214, 46, 220, 119,
|
||||
101, 126, 148, 22, 86, 206, 91, 125, 234, 24,
|
||||
162, 136, 46, 247, 245, 81, 157, 126, 73, 173,
|
||||
120, 230, 117, 111, 145, 168, 169, 187, 23, 109,
|
||||
0, 184, 23, 43, 108, 201, 13, 170, 249, 228,
|
||||
107, 59, 73, 254, 116, 156, 209, 155, 149, 95,
|
||||
24, 245, 136, 107, 192, 114, 69, 80, 199, 8
|
||||
};
|
||||
|
||||
Mat_<uchar> image(10, 10, image_data);
|
||||
|
||||
Mat res;
|
||||
cv::intensity_transform::logTransform(image, res);
|
||||
|
||||
uchar expectedRes_data[] = {
|
||||
182, 247, 247, 169, 179, 150, 241, 130, 192, 243,
|
||||
214, 227, 189, 110, 201, 251, 241, 231, 167, 252,
|
||||
224, 202, 90, 184, 96, 193, 251, 128, 248, 173,
|
||||
216, 225, 226, 243, 220, 224, 247, 177, 248, 220,
|
||||
213, 223, 230, 144, 206, 245, 208, 223, 251, 148,
|
||||
234, 226, 177, 254, 253, 203, 233, 223, 198, 237,
|
||||
221, 250, 220, 217, 229, 236, 236, 241, 146, 216,
|
||||
0, 240, 146, 174, 216, 244, 121, 237, 254, 250,
|
||||
215, 188, 198, 255, 219, 233, 246, 232, 231, 210,
|
||||
148, 253, 226, 215, 242, 218, 196, 202, 244, 101
|
||||
};
|
||||
|
||||
Mat_<uchar> expectedRes(10, 10, expectedRes_data);
|
||||
|
||||
EXPECT_LE(cvtest::norm(res, expectedRes, NORM_INF), 1);
|
||||
}
|
||||
|
||||
TEST(intensity_transform_gammaCorrection, accuracy1)
|
||||
{
|
||||
uchar image_data[] = {
|
||||
51, 211, 212, 38, 48, 25, 189, 16, 64, 197,
|
||||
104, 137, 60, 10, 78, 234, 186, 149, 37, 236,
|
||||
128, 80, 6, 53, 7, 65, 233, 15, 216, 42,
|
||||
108, 132, 136, 194, 117, 128, 214, 46, 220, 119,
|
||||
101, 126, 148, 22, 86, 206, 91, 125, 234, 24,
|
||||
162, 136, 46, 247, 245, 81, 157, 126, 73, 173,
|
||||
120, 230, 117, 111, 145, 168, 169, 187, 23, 109,
|
||||
0, 184, 23, 43, 108, 201, 13, 170, 249, 228,
|
||||
107, 59, 73, 254, 116, 156, 209, 155, 149, 95,
|
||||
24, 245, 136, 107, 192, 114, 69, 80, 199, 8
|
||||
};
|
||||
|
||||
Mat_<uchar> image(10, 10, image_data);
|
||||
|
||||
Mat res;
|
||||
cv::intensity_transform::gammaCorrection(image, res, 1.0);
|
||||
|
||||
uchar expectedRes_data[] = {
|
||||
51, 211, 212, 38, 48, 25, 189, 16, 64, 197,
|
||||
104, 137, 60, 10, 78, 234, 186, 149, 37, 236,
|
||||
128, 80, 6, 53, 7, 65, 233, 15, 216, 42,
|
||||
108, 132, 136, 194, 117, 128, 214, 46, 220, 119,
|
||||
101, 126, 148, 22, 86, 206, 91, 125, 234, 24,
|
||||
162, 136, 46, 247, 245, 81, 157, 126, 73, 173,
|
||||
120, 230, 117, 111, 145, 168, 169, 187, 23, 109,
|
||||
0, 184, 23, 43, 108, 201, 13, 170, 249, 228,
|
||||
107, 59, 73, 254, 116, 156, 209, 155, 149, 95,
|
||||
24, 245, 136, 107, 192, 114, 69, 80, 199, 8
|
||||
};
|
||||
|
||||
Mat_<uchar> expectedRes(10, 10, expectedRes_data);
|
||||
|
||||
EXPECT_LE(cvtest::norm(res, expectedRes, NORM_INF), 1);
|
||||
}
|
||||
|
||||
TEST(intensity_transform_gammaCorrection, accuracy2)
|
||||
{
|
||||
uchar image_data[] = {
|
||||
51, 211, 212, 38, 48, 25, 189, 16, 64, 197,
|
||||
104, 137, 60, 10, 78, 234, 186, 149, 37, 236,
|
||||
128, 80, 6, 53, 7, 65, 233, 15, 216, 42,
|
||||
108, 132, 136, 194, 117, 128, 214, 46, 220, 119,
|
||||
101, 126, 148, 22, 86, 206, 91, 125, 234, 24,
|
||||
162, 136, 46, 247, 245, 81, 157, 126, 73, 173,
|
||||
120, 230, 117, 111, 145, 168, 169, 187, 23, 109,
|
||||
0, 184, 23, 43, 108, 201, 13, 170, 249, 228,
|
||||
107, 59, 73, 254, 116, 156, 209, 155, 149, 95,
|
||||
24, 245, 136, 107, 192, 114, 69, 80, 199, 8
|
||||
};
|
||||
|
||||
Mat_<uchar> image(10, 10, image_data);
|
||||
|
||||
Mat res;
|
||||
cv::intensity_transform::gammaCorrection(image, res, (float)(0.4));
|
||||
|
||||
uchar expectedRes_data[] = {
|
||||
133, 236, 236, 119, 130, 100, 226, 84, 146, 229,
|
||||
178, 198, 142, 69, 158, 246, 224, 205, 117, 247,
|
||||
193, 160, 56, 136, 60, 147, 245, 82, 238, 123,
|
||||
180, 195, 198, 228, 186, 193, 237, 128, 240, 187,
|
||||
176, 192, 205, 95, 165, 234, 168, 191, 246, 99,
|
||||
212, 198, 128, 251, 250, 161, 210, 192, 154, 218,
|
||||
188, 244, 186, 182, 203, 215, 216, 225, 97, 181,
|
||||
0, 223, 97, 125, 180, 231, 77, 216, 252, 243,
|
||||
180, 141, 154, 254, 186, 209, 235, 208, 205, 171,
|
||||
99, 250, 198, 180, 227, 184, 151, 160, 230, 63
|
||||
};
|
||||
|
||||
Mat_<uchar> expectedRes(10, 10, expectedRes_data);
|
||||
|
||||
EXPECT_LE(cvtest::norm(res, expectedRes, NORM_INF), 1);
|
||||
}
|
||||
|
||||
TEST(intensity_transform_autoscaling, accuracy)
|
||||
{
|
||||
uchar image_data[] = {
|
||||
32, 59, 164, 127, 151, 107, 167, 62, 195, 143,
|
||||
54, 166, 104, 27, 152, 20, 35, 135, 12, 198,
|
||||
107, 63, 90, 169, 67, 135, 136, 14, 94, 115,
|
||||
34, 150, 169, 171, 130, 39, 190, 108, 103, 32,
|
||||
57, 83, 146, 37, 81, 143, 144, 47, 87, 49,
|
||||
32, 108, 17, 165, 127, 137, 108, 35, 179, 175,
|
||||
40, 148, 174, 79, 146, 119, 103, 168, 167, 160,
|
||||
66, 107, 164, 19, 85, 126, 58, 95, 15, 131,
|
||||
88, 58, 162, 90, 147, 125, 61, 157, 60, 104,
|
||||
128, 193, 69, 104, 94, 196, 11, 66, 18, 179
|
||||
};
|
||||
|
||||
Mat_<uchar> image(10, 10, image_data);
|
||||
|
||||
Mat res;
|
||||
cv::intensity_transform::autoscaling(image, res);
|
||||
|
||||
uchar expectedRes_data[] = {
|
||||
29, 65, 209, 158, 191, 131, 213, 70, 251, 180,
|
||||
59, 211, 127, 22, 192, 12, 33, 169, 1, 255,
|
||||
131, 71, 108, 215, 76, 169, 170, 4, 113, 142,
|
||||
31, 190, 215, 218, 162, 38, 244, 132, 125, 29,
|
||||
63, 98, 184, 35, 95, 180, 181, 49, 104, 52,
|
||||
29, 132, 8, 210, 158, 172, 132, 33, 229, 224,
|
||||
40, 187, 222, 93, 184, 147, 125, 214, 213, 203,
|
||||
75, 131, 209, 11, 101, 157, 64, 115, 5, 164,
|
||||
105, 64, 206, 108, 185, 155, 68, 199, 67, 127,
|
||||
160, 248, 79, 127, 113, 252, 0, 75, 10, 229
|
||||
};
|
||||
|
||||
Mat_<uchar> expectedRes(10, 10, expectedRes_data);
|
||||
|
||||
EXPECT_LE(cvtest::norm(res, expectedRes, NORM_INF), 1);
|
||||
}
|
||||
|
||||
TEST(intensity_transform_contrastStretching, accuracy)
|
||||
{
|
||||
uchar image_data[] = {
|
||||
32, 59, 164, 127, 151, 107, 167, 62, 195, 143,
|
||||
54, 166, 104, 27, 152, 20, 35, 135, 12, 198,
|
||||
107, 63, 90, 169, 67, 135, 136, 14, 94, 115,
|
||||
34, 150, 169, 171, 130, 39, 190, 108, 103, 32,
|
||||
57, 83, 146, 37, 81, 143, 144, 47, 87, 49,
|
||||
32, 108, 17, 165, 127, 137, 108, 35, 179, 175,
|
||||
40, 148, 174, 79, 146, 119, 103, 168, 167, 160,
|
||||
66, 107, 164, 19, 85, 126, 58, 95, 15, 131,
|
||||
88, 58, 162, 90, 147, 125, 61, 157, 60, 104,
|
||||
128, 193, 69, 104, 94, 196, 11, 66, 18, 179
|
||||
};
|
||||
|
||||
Mat_<uchar> image(10, 10, image_data);
|
||||
|
||||
Mat res;
|
||||
cv::intensity_transform::contrastStretching(image, res, 70, 15, 120, 240);
|
||||
|
||||
uchar expectedRes_data[] = {
|
||||
6, 12, 244, 240, 243, 181, 245, 13, 248, 242,
|
||||
11, 245, 168, 5, 243, 4, 7, 241, 2, 248,
|
||||
181, 13, 105, 245, 14, 241, 241, 3, 123, 217,
|
||||
7, 243, 245, 245, 241, 8, 247, 186, 163, 6,
|
||||
12, 73, 242, 7, 64, 242, 242, 10, 91, 10,
|
||||
6, 186, 3, 245, 240, 241, 186, 7, 246, 246,
|
||||
8, 243, 246, 55, 242, 235, 163, 245, 245, 244,
|
||||
14, 181, 244, 4, 82, 240, 12, 127, 3, 241,
|
||||
96, 12, 244, 105, 243, 240, 13, 244, 12, 168,
|
||||
240, 248, 14, 168, 123, 248, 2, 14, 3, 246
|
||||
};
|
||||
|
||||
Mat_<uchar> expectedRes(10, 10, expectedRes_data);
|
||||
|
||||
EXPECT_LE(cvtest::norm(res, expectedRes, NORM_INF), 1);
|
||||
}
|
||||
|
||||
typedef testing::TestWithParam<std::string> intensity_transform_BIMEF;
|
||||
|
||||
TEST_P(intensity_transform_BIMEF, accuracy)
|
||||
{
|
||||
#ifdef HAVE_EIGEN
|
||||
const std::string directory = "intensity_transform/BIMEF/";
|
||||
std::string filename = GetParam();
|
||||
|
||||
const std::string inputFilename = cvtest::findDataFile(directory + filename + ".png");
|
||||
Mat img = imread(inputFilename);
|
||||
EXPECT_TRUE(!img.empty());
|
||||
Mat imgBIMEF;
|
||||
BIMEF(img, imgBIMEF);
|
||||
|
||||
const std::string referenceFilename = cvtest::findDataFile(directory + filename + "_ref.png");
|
||||
Mat imgRef = imread(referenceFilename);
|
||||
EXPECT_TRUE(!imgRef.empty());
|
||||
|
||||
EXPECT_EQ(imgBIMEF.rows, imgRef.rows);
|
||||
EXPECT_EQ(imgBIMEF.cols, imgRef.cols);
|
||||
EXPECT_EQ(imgBIMEF.type(), imgRef.type());
|
||||
double rmse = sqrt(cv::norm(imgBIMEF, imgRef, NORM_L2SQR) / (imgRef.total()*imgRef.channels()));
|
||||
std::cout << "BIMEF, RMSE for " << filename << ": " << rmse << std::endl;
|
||||
const float max_rmse = 9;
|
||||
EXPECT_LE(rmse, max_rmse);
|
||||
#endif
|
||||
}
|
||||
|
||||
const string BIMEF_accuracy_cases[] = {
|
||||
"P1000205_resize",
|
||||
"P1010676_resize",
|
||||
"P1010815_resize"
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, intensity_transform_BIMEF,
|
||||
testing::ValuesIn(BIMEF_accuracy_cases)
|
||||
);
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,7 @@
|
||||
// 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 "test_precomp.hpp"
|
||||
|
||||
CV_TEST_MAIN("cv")
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/intensity_transform.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace cv::intensity_transform;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user