Skip to content

Commit f5e493b

Browse files
committed
Merge pull request #2263 from kqwyf:fix
2 parents b32180b + 334b5c0 commit f5e493b

File tree

2 files changed

+267
-72
lines changed

2 files changed

+267
-72
lines changed

modules/xphoto/src/simple_color_balance.cpp

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939

4040
#include <algorithm>
4141
#include <iostream>
42-
#include <iterator>
4342
#include <vector>
4443

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

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

67-
int nElements = int(pow((float)bins, (float)depth));
68-
// number of elements in histogram tree
63+
float minValue0 = inputMin;
64+
float maxValue0 = inputMax;
6965

70-
for (size_t i = 0; i < src.size(); ++i)
66+
// deal with cv::calcHist (exclusive upper bound)
67+
if (src[0].depth() == CV_32F || src[0].depth() == CV_64F) // floating
7168
{
72-
std::vector<int> hist(nElements, 0);
73-
74-
typename Mat_<T>::iterator beginIt = src[i].begin();
75-
typename Mat_<T>::iterator endIt = src[i].end();
76-
77-
for (typename Mat_<T>::iterator it = beginIt; it != endIt; ++it)
78-
// histogram filling
79-
{
80-
int pos = 0;
81-
float minValue = inputMin - 0.5f;
82-
float maxValue = inputMax + 0.5f;
83-
T val = *it;
84-
85-
float interval = float(maxValue - minValue) / bins;
69+
maxValue0 += MIN((inputMax - inputMin) / (nElements - 1), 1);
70+
if (inputMax == inputMin) // single value
71+
maxValue0 += 1;
72+
}
73+
else // integer
74+
{
75+
maxValue0 += 1;
76+
}
8677

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

92-
pos = (pos + currentBin) * bins;
80+
for (size_t i = 0; i < src.size(); ++i)
81+
{
82+
float minValue = minValue0;
83+
float maxValue = maxValue0;
9384

94-
minValue = minValue + currentBin * interval;
95-
maxValue = minValue + interval;
85+
Mat img = src[i].reshape(1);
86+
Mat hist;
87+
int channels[] = {0};
88+
int histSize[] = {nElements};
89+
float inputRange[] = {minValue, maxValue};
90+
const float *ranges[] = {inputRange};
9691

97-
interval /= bins;
98-
}
99-
}
92+
calcHist(&img, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
10093

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

103-
int p1 = 0, p2 = bins - 1;
96+
int p1 = 0, p2 = nElements - 1;
10497
int n1 = 0, n2 = total;
10598

106-
float minValue = inputMin - 0.5f;
107-
float maxValue = inputMax + 0.5f;
108-
109-
float interval = (maxValue - minValue) / float(bins);
110-
111-
for (int j = 0; j < depth; ++j)
11299
// searching for s1 and s2
100+
while (n1 + hist.at<float>(p1) < s1 * total / 100.0f)
101+
{
102+
n1 += saturate_cast<int>(hist.at<float>(p1++));
103+
minValue += interval;
104+
}
105+
106+
while (n2 - hist.at<float>(p2) > (100.0f - s2) * total / 100.0f)
113107
{
114-
while (n1 + hist[p1] < s1 * total / 100.0f)
115-
{
116-
n1 += hist[p1++];
117-
minValue += interval;
118-
}
119-
p1 *= bins;
120-
121-
while (n2 - hist[p2] > (100.0f - s2) * total / 100.0f)
122-
{
123-
n2 -= hist[p2--];
124-
maxValue -= interval;
125-
}
126-
p2 = (p2 + 1) * bins - 1;
127-
128-
interval /= bins;
108+
n2 -= saturate_cast<int>(hist.at<float>(p2--));
109+
maxValue -= interval;
129110
}
130111

131112
src[i] = (outputMax - outputMin) * (src[i] - minValue) / (maxValue - minValue) + outputMin;

modules/xphoto/test/simple_color_balance.cpp

Lines changed: 232 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,140 @@
55

66
namespace opencv_test { namespace {
77

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

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

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

23-
cv::Mat currentResult;
24-
wb->balanceWhite(src, currentResult);
23+
wb->balanceWhite(test, test);
2524

26-
double psnr = cv::PSNR(currentResult, previousResult);
25+
double minDst, maxDst;
26+
cv::minMaxIdx(test, &minDst, &maxDst);
2727

28-
EXPECT_GE( psnr, 30 );
29-
}
28+
ASSERT_NEAR(maxDst, newMax, 1e-4);
3029
}
3130

32-
TEST(xphoto_simplecolorbalance, max_value)
31+
TEST(xphoto_simplecolorbalance, uchar_min_value)
3332
{
34-
const float oldMax = 24000., newMax = 65536.;
33+
const uchar oldMin = 120, newMin = 0;
34+
35+
Mat test = Mat::zeros(1,3,CV_8UC1);
36+
test.at<uchar>(0, 0) = oldMin;
37+
test.at<uchar>(0, 1) = (256 + oldMin) / 2;
38+
test.at<uchar>(0, 2) = 255;
39+
40+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
41+
wb->setInputMin(oldMin);
42+
wb->setInputMax(255);
43+
wb->setOutputMin(newMin);
44+
wb->setOutputMax(255);
45+
46+
wb->balanceWhite(test, test);
47+
48+
double minDst, maxDst;
49+
cv::minMaxIdx(test, &minDst, &maxDst);
50+
51+
ASSERT_NEAR(minDst, newMin, 1e-4);
52+
}
53+
54+
TEST(xphoto_simplecolorbalance, uchar_equal_range)
55+
{
56+
const int N = 4;
57+
uchar data[N] = {0, 1, 16, 255};
58+
Mat test = Mat(1, N, CV_8UC1, data);
59+
Mat result = Mat(1, N, CV_8UC1, data);
60+
61+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
62+
wb->setInputMin(0);
63+
wb->setInputMax(255);
64+
wb->setOutputMin(0);
65+
wb->setOutputMax(255);
66+
67+
wb->balanceWhite(test, test);
68+
69+
double err;
70+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
71+
ASSERT_LE(err, 1e-4);
72+
}
73+
74+
TEST(xphoto_simplecolorbalance, uchar_single_value)
75+
{
76+
const int N = 4;
77+
uchar data0[N] = {51, 51, 51, 51};
78+
uchar data1[N] = {33, 33, 33, 33};
79+
Mat test = Mat(1, N, CV_8UC1, data0);
80+
Mat result = Mat(1, N, CV_8UC1, data1);
81+
82+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
83+
wb->setInputMin(51);
84+
wb->setInputMax(51);
85+
wb->setOutputMin(33);
86+
wb->setOutputMax(200);
87+
88+
wb->balanceWhite(test, test);
89+
90+
double err;
91+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
92+
ASSERT_LE(err, 1e-4);
93+
}
94+
95+
TEST(xphoto_simplecolorbalance, uchar_p)
96+
{
97+
const int N = 5;
98+
uchar data0[N] = {10, 55, 102, 188, 233};
99+
uchar data1[N] = {0, 1, 90, 254, 255};
100+
Mat test = Mat(1, N, CV_8UC1, data0);
101+
Mat result = Mat(1, N, CV_8UC1, data1);
102+
103+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
104+
wb->setInputMin(10);
105+
wb->setInputMax(233);
106+
wb->setOutputMin(0);
107+
wb->setOutputMax(255);
108+
wb->setP(21);
109+
110+
wb->balanceWhite(test, test);
111+
112+
double err;
113+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
114+
ASSERT_LE(err, 1e-4);
115+
}
116+
117+
TEST(xphoto_simplecolorbalance, uchar_c3)
118+
{
119+
const int N = 15;
120+
uchar data0[N] = {10, 55, 102, 55, 102, 188, 102, 188, 233, 188, 233, 10, 233, 10, 55};
121+
uchar data1[N] = {0, 1, 90, 1, 90, 254, 90, 254, 255, 254, 255, 0, 255, 0, 1};
122+
Mat test = Mat(1, N / 3, CV_8UC3, data0);
123+
Mat result = Mat(1, N / 3, CV_8UC3, data1);
124+
125+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
126+
wb->setInputMin(10);
127+
wb->setInputMax(233);
128+
wb->setOutputMin(0);
129+
wb->setOutputMax(255);
130+
wb->setP(21);
131+
132+
wb->balanceWhite(test, test);
133+
134+
double err;
135+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
136+
ASSERT_LE(err, 1e-4);
137+
}
138+
139+
TEST(xphoto_simplecolorbalance, float_max_value)
140+
{
141+
const float oldMax = 24000.f, newMax = 65536.f;
35142

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

165+
TEST(xphoto_simplecolorbalance, float_min_value)
166+
{
167+
const float oldMin = 24000.f, newMin = 0.f;
168+
169+
Mat test = Mat::zeros(1,3,CV_32FC1);
170+
test.at<float>(0, 0) = oldMin;
171+
test.at<float>(0, 1) = (65536.f + oldMin) / 2;
172+
test.at<float>(0, 2) = 65536.f;
173+
174+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
175+
wb->setInputMin(oldMin);
176+
wb->setInputMax(65536.f);
177+
wb->setOutputMin(newMin);
178+
wb->setOutputMax(65536.f);
179+
180+
wb->balanceWhite(test, test);
181+
182+
double minDst, maxDst;
183+
cv::minMaxIdx(test, &minDst, &maxDst);
184+
185+
ASSERT_NEAR(minDst, newMin, 65536*1e-4);
186+
}
187+
188+
TEST(xphoto_simplecolorbalance, float_equal_range)
189+
{
190+
const int N = 5;
191+
float data[N] = {0.f, 1.f, 16.2f, 256.3f, 4096.f};
192+
Mat test = Mat(1, N, CV_32FC1, data);
193+
Mat result = Mat(1, N, CV_32FC1, data);
194+
195+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
196+
wb->setInputMin(0);
197+
wb->setInputMax(4096);
198+
wb->setOutputMin(0);
199+
wb->setOutputMax(4096);
200+
201+
wb->balanceWhite(test, test);
202+
203+
double err;
204+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
205+
ASSERT_LE(err, 1e-4);
206+
}
207+
208+
TEST(xphoto_simplecolorbalance, float_single_value)
209+
{
210+
const int N = 4;
211+
float data0[N] = {24000.5f, 24000.5f, 24000.5f, 24000.5f};
212+
float data1[N] = {52000.25f, 52000.25f, 52000.25f, 52000.25f};
213+
Mat test = Mat(1, N, CV_32FC1, data0);
214+
Mat result = Mat(1, N, CV_32FC1, data1);
215+
216+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
217+
wb->setInputMin(24000.5f);
218+
wb->setInputMax(24000.5f);
219+
wb->setOutputMin(52000.25f);
220+
wb->setOutputMax(65536.f);
221+
222+
wb->balanceWhite(test, test);
223+
224+
double err;
225+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
226+
ASSERT_LE(err, 65536*1e-4);
227+
}
228+
229+
TEST(xphoto_simplecolorbalance, float_p)
230+
{
231+
const int N = 5;
232+
float data0[N] = {16000.f, 20000.5f, 24000.f, 36000.5f, 48000.f};
233+
float data1[N] = {-16381.952f, 0.f, 16381.952f, 65536.f, 114685.952f};
234+
Mat test = Mat(1, N, CV_32FC1, data0);
235+
Mat result = Mat(1, N, CV_32FC1, data1);
236+
237+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
238+
wb->setInputMin(16000.f);
239+
wb->setInputMax(48000.f);
240+
wb->setOutputMin(0.f);
241+
wb->setOutputMax(65536.f);
242+
wb->setP(21);
243+
244+
wb->balanceWhite(test, test);
245+
246+
double err;
247+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
248+
ASSERT_LE(err, 65536*1e-4);
249+
}
250+
251+
TEST(xphoto_simplecolorbalance, float_c3)
252+
{
253+
const int N = 15;
254+
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};
255+
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};
256+
Mat test = Mat(1, N / 3, CV_32FC3, data0);
257+
Mat result = Mat(1, N / 3, CV_32FC3, data1);
258+
259+
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
260+
wb->setInputMin(16000.f);
261+
wb->setInputMax(48000.f);
262+
wb->setOutputMin(0.f);
263+
wb->setOutputMax(65536.f);
264+
wb->setP(21);
265+
266+
wb->balanceWhite(test, test);
267+
268+
double err;
269+
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
270+
ASSERT_LE(err, 65536*1e-4);
271+
}
58272

59273
}} // namespace

0 commit comments

Comments
 (0)