Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 35 additions & 54 deletions modules/xphoto/src/simple_color_balance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

#include "opencv2/core.hpp"
Expand All @@ -59,73 +58,55 @@ void balanceWhiteSimple(std::vector<Mat_<T> > &src, Mat &dst, const float inputM
const float s1 = p; // low quantile
const float s2 = p; // high quantile

int depth = 2; // depth of histogram tree
if (src[0].depth() != CV_8U)
++depth;
int bins = 16; // number of bins at each histogram level
int nElements = src[0].depth() == CV_8U ? 256 : 4096;

int nElements = int(pow((float)bins, (float)depth));
// number of elements in histogram tree
float minValue0 = inputMin;
float maxValue0 = inputMax;

for (size_t i = 0; i < src.size(); ++i)
// deal with cv::calcHist (exclusive upper bound)
if (src[0].depth() == CV_32F || src[0].depth() == CV_64F) // floating
{
std::vector<int> hist(nElements, 0);

typename Mat_<T>::iterator beginIt = src[i].begin();
typename Mat_<T>::iterator endIt = src[i].end();

for (typename Mat_<T>::iterator it = beginIt; it != endIt; ++it)
// histogram filling
{
int pos = 0;
float minValue = inputMin - 0.5f;
float maxValue = inputMax + 0.5f;
T val = *it;

float interval = float(maxValue - minValue) / bins;
maxValue0 += MIN((inputMax - inputMin) / (nElements - 1), 1);
if (inputMax == inputMin) // single value
maxValue0 += 1;
}
else // integer
{
maxValue0 += 1;
}

for (int j = 0; j < depth; ++j)
{
int currentBin = int((val - minValue + 1e-4f) / interval);
++hist[pos + currentBin];
float interval = (maxValue0 - minValue0) / float(nElements);

pos = (pos + currentBin) * bins;
for (size_t i = 0; i < src.size(); ++i)
{
float minValue = minValue0;
float maxValue = maxValue0;

minValue = minValue + currentBin * interval;
maxValue = minValue + interval;
Mat img = src[i].reshape(1);
Mat hist;
int channels[] = {0};
int histSize[] = {nElements};
float inputRange[] = {minValue, maxValue};
const float *ranges[] = {inputRange};

interval /= bins;
}
}
calcHist(&img, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);

int total = int(src[i].total());

int p1 = 0, p2 = bins - 1;
int p1 = 0, p2 = nElements - 1;
int n1 = 0, n2 = total;

float minValue = inputMin - 0.5f;
float maxValue = inputMax + 0.5f;

float interval = (maxValue - minValue) / float(bins);

for (int j = 0; j < depth; ++j)
// searching for s1 and s2
while (n1 + hist.at<float>(p1) < s1 * total / 100.0f)
{
n1 += saturate_cast<int>(hist.at<float>(p1++));
minValue += interval;
}

while (n2 - hist.at<float>(p2) > (100.0f - s2) * total / 100.0f)
{
while (n1 + hist[p1] < s1 * total / 100.0f)
{
n1 += hist[p1++];
minValue += interval;
}
p1 *= bins;

while (n2 - hist[p2] > (100.0f - s2) * total / 100.0f)
{
n2 -= hist[p2--];
maxValue -= interval;
}
p2 = (p2 + 1) * bins - 1;

interval /= bins;
n2 -= saturate_cast<int>(hist.at<float>(p2--));
maxValue -= interval;
}

src[i] = (outputMax - outputMin) * (src[i] - minValue) / (maxValue - minValue) + outputMin;
Expand Down
250 changes: 232 additions & 18 deletions modules/xphoto/test/simple_color_balance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,140 @@

namespace opencv_test { namespace {

TEST(xphoto_simplecolorbalance, regression)
TEST(xphoto_simplecolorbalance, uchar_max_value)
{
cv::String dir = cvtest::TS::ptr()->get_data_path() + "cv/xphoto/simple_white_balance/";
int nTests = 8;
cv::Ptr<cv::xphoto::WhiteBalancer> wb = cv::xphoto::createSimpleWB();
const uchar oldMax = 120, newMax = 255;

for (int i = 0; i < nTests; ++i)
{
cv::String srcName = dir + cv::format( "sources/%02d.png", i + 1);
cv::Mat src = cv::imread( srcName, 1 );
ASSERT_TRUE(!src.empty());
Mat test = Mat::zeros(3,3,CV_8UC1);
test.at<uchar>(0, 0) = oldMax;
test.at<uchar>(0, 1) = oldMax / 2;
test.at<uchar>(0, 2) = oldMax / 4;

cv::String previousResultName = dir + cv::format( "results/%02d.jpg", i + 1 );
cv::Mat previousResult = cv::imread( previousResultName, 1 );
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(oldMax);
wb->setOutputMin(0);
wb->setOutputMax(newMax);

cv::Mat currentResult;
wb->balanceWhite(src, currentResult);
wb->balanceWhite(test, test);

double psnr = cv::PSNR(currentResult, previousResult);
double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);

EXPECT_GE( psnr, 30 );
}
ASSERT_NEAR(maxDst, newMax, 1e-4);
}

TEST(xphoto_simplecolorbalance, max_value)
TEST(xphoto_simplecolorbalance, uchar_min_value)
{
const float oldMax = 24000., newMax = 65536.;
const uchar oldMin = 120, newMin = 0;

Mat test = Mat::zeros(1,3,CV_8UC1);
test.at<uchar>(0, 0) = oldMin;
test.at<uchar>(0, 1) = (256 + oldMin) / 2;
test.at<uchar>(0, 2) = 255;

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(oldMin);
wb->setInputMax(255);
wb->setOutputMin(newMin);
wb->setOutputMax(255);

wb->balanceWhite(test, test);

double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);

ASSERT_NEAR(minDst, newMin, 1e-4);
}

TEST(xphoto_simplecolorbalance, uchar_equal_range)
{
const int N = 4;
uchar data[N] = {0, 1, 16, 255};
Mat test = Mat(1, N, CV_8UC1, data);
Mat result = Mat(1, N, CV_8UC1, data);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(255);
wb->setOutputMin(0);
wb->setOutputMax(255);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}

TEST(xphoto_simplecolorbalance, uchar_single_value)
{
const int N = 4;
uchar data0[N] = {51, 51, 51, 51};
uchar data1[N] = {33, 33, 33, 33};
Mat test = Mat(1, N, CV_8UC1, data0);
Mat result = Mat(1, N, CV_8UC1, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(51);
wb->setInputMax(51);
wb->setOutputMin(33);
wb->setOutputMax(200);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}

TEST(xphoto_simplecolorbalance, uchar_p)
{
const int N = 5;
uchar data0[N] = {10, 55, 102, 188, 233};
uchar data1[N] = {0, 1, 90, 254, 255};
Mat test = Mat(1, N, CV_8UC1, data0);
Mat result = Mat(1, N, CV_8UC1, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(10);
wb->setInputMax(233);
wb->setOutputMin(0);
wb->setOutputMax(255);
wb->setP(21);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}

TEST(xphoto_simplecolorbalance, uchar_c3)
{
const int N = 15;
uchar data0[N] = {10, 55, 102, 55, 102, 188, 102, 188, 233, 188, 233, 10, 233, 10, 55};
uchar data1[N] = {0, 1, 90, 1, 90, 254, 90, 254, 255, 254, 255, 0, 255, 0, 1};
Mat test = Mat(1, N / 3, CV_8UC3, data0);
Mat result = Mat(1, N / 3, CV_8UC3, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(10);
wb->setInputMax(233);
wb->setOutputMin(0);
wb->setOutputMax(255);
wb->setP(21);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}

TEST(xphoto_simplecolorbalance, float_max_value)
{
const float oldMax = 24000.f, newMax = 65536.f;

Mat test = Mat::zeros(3,3,CV_32FC1);
test.at<float>(0, 0) = oldMax;
Expand All @@ -55,5 +162,112 @@ namespace opencv_test { namespace {
ASSERT_NEAR(maxDst, newMax, newMax*1e-4);
}

TEST(xphoto_simplecolorbalance, float_min_value)
{
const float oldMin = 24000.f, newMin = 0.f;

Mat test = Mat::zeros(1,3,CV_32FC1);
test.at<float>(0, 0) = oldMin;
test.at<float>(0, 1) = (65536.f + oldMin) / 2;
test.at<float>(0, 2) = 65536.f;

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(oldMin);
wb->setInputMax(65536.f);
wb->setOutputMin(newMin);
wb->setOutputMax(65536.f);

wb->balanceWhite(test, test);

double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);

ASSERT_NEAR(minDst, newMin, 65536*1e-4);
}

TEST(xphoto_simplecolorbalance, float_equal_range)
{
const int N = 5;
float data[N] = {0.f, 1.f, 16.2f, 256.3f, 4096.f};
Mat test = Mat(1, N, CV_32FC1, data);
Mat result = Mat(1, N, CV_32FC1, data);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(4096);
wb->setOutputMin(0);
wb->setOutputMax(4096);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}

TEST(xphoto_simplecolorbalance, float_single_value)
{
const int N = 4;
float data0[N] = {24000.5f, 24000.5f, 24000.5f, 24000.5f};
float data1[N] = {52000.25f, 52000.25f, 52000.25f, 52000.25f};
Mat test = Mat(1, N, CV_32FC1, data0);
Mat result = Mat(1, N, CV_32FC1, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(24000.5f);
wb->setInputMax(24000.5f);
wb->setOutputMin(52000.25f);
wb->setOutputMax(65536.f);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}

TEST(xphoto_simplecolorbalance, float_p)
{
const int N = 5;
float data0[N] = {16000.f, 20000.5f, 24000.f, 36000.5f, 48000.f};
float data1[N] = {-16381.952f, 0.f, 16381.952f, 65536.f, 114685.952f};
Mat test = Mat(1, N, CV_32FC1, data0);
Mat result = Mat(1, N, CV_32FC1, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(16000.f);
wb->setInputMax(48000.f);
wb->setOutputMin(0.f);
wb->setOutputMax(65536.f);
wb->setP(21);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}

TEST(xphoto_simplecolorbalance, float_c3)
{
const int N = 15;
float data0[N] = {16000.f, 20000.5f, 24000.f, 20000.5f, 24000.f, 36000.5f, 24000.f, 36000.5f, 48000.f, 36000.5f, 48000.f, 16000.f, 48000.f, 16000.f, 20000.5f};
float data1[N] = {-16381.952f, 0.f, 16381.952f, 0.f, 16381.952f, 65536.f, 16381.952f, 65536.f, 114685.952f, 65536.f, 114685.952f, -16381.952f, 114685.952f, -16381.952f, 0.f};
Mat test = Mat(1, N / 3, CV_32FC3, data0);
Mat result = Mat(1, N / 3, CV_32FC3, data1);

cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(16000.f);
wb->setInputMax(48000.f);
wb->setOutputMin(0.f);
wb->setOutputMax(65536.f);
wb->setP(21);

wb->balanceWhite(test, test);

double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}

}} // namespace