Skip to content

Commit db6afdf

Browse files
antond-wetalgritz
authored andcommitted
feat(IBA): IBA:demosaic add white balancing (#4499)
Adds white balancing functionality to IBA:demosaic Signed-off-by: Anton Dukhovnikov <[email protected]>
1 parent c8f9e87 commit db6afdf

File tree

19 files changed

+332
-139
lines changed

19 files changed

+332
-139
lines changed

src/cmake/testing.cmake

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,16 @@ macro (oiio_add_all_tests)
136136
iinfo igrep
137137
nonwhole-tiles
138138
oiiotool
139-
oiiotool-composite oiiotool-control oiiotool-copy
139+
oiiotool-composite
140+
oiiotool-control
141+
oiiotool-copy
142+
oiiotool-demosaic
140143
oiiotool-fixnan
141144
oiiotool-pattern
142145
oiiotool-readerror
143-
oiiotool-subimage oiiotool-text oiiotool-xform
144-
oiiotool-demosaic
146+
oiiotool-subimage
147+
oiiotool-text
148+
oiiotool-xform
145149
diff
146150
dither dup-channels
147151
jpeg-corrupt jpeg-metadata

src/doc/imagebufalgo.rst

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,22 +1163,27 @@ Shuffling channels
11631163

11641164
.. code-tab:: c++
11651165

1166-
ImageSpec hint;
1167-
hint["raw:Demosaic"] = "none";
1168-
ImageBuf Src ("test.cr3", 0, 0, nullptr, &hint);
1169-
ParamValue options[] = { { "layout", "GRBG" } };
1170-
ImageBuf Dst = ImageBufAlgo::demosaic (Src, options);
1166+
.. tab:: C++
1167+
1168+
.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebufalgo.cpp
1169+
:language: c++
1170+
:start-after: BEGIN-imagebufalgo-demosaic
1171+
:end-before: END-imagebufalgo-demosaic
1172+
:dedent: 4
11711173

11721174
.. code-tab:: py
11731175

1174-
hint = ImageSpec()
1175-
hint["raw:Demosaic"] = "none"
1176-
Src = ImageBuf("test.cr3", 0, 0, hint)
1177-
Dst = OpenImageIO.ImageBufAlgo.demosaic(Src, layout="GRBG")
1176+
.. tab:: Python
1177+
1178+
.. literalinclude:: ../../testsuite/docs-examples-python/src/docs-examples-imagebufalgo.py
1179+
:language: py
1180+
:start-after: BEGIN-imagebufalgo-demosaic
1181+
:end-before: END-imagebufalgo-demosaic
1182+
:dedent: 4
11781183

11791184
.. code-tab:: bash oiiotool
11801185

1181-
oiiotool -iconfig raw:Demosaic none -i test.cr3 --demosaic:layout=GRBG -o out.exr
1186+
oiiotool -iconfig raw:Demosaic none -i test.cr3 --demosaic:layout=GRBG:white_balance=2.0,0.8,1.2,1.5 -o out.exr
11821187

11831188
|
11841189

src/doc/oiiotool.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4184,6 +4184,36 @@ current top image.
41844184

41854185

41864186

4187+
.. option:: --demosaic
4188+
4189+
Demosaic a raw digital camera image.
4190+
4191+
Optional appended modifiers include:
4192+
4193+
`pattern=` *name*
4194+
sensor pattern. Currently supported patterns: "bayer"
4195+
`layout=` *name*
4196+
photosite order of the specified pattern. For layouts of the Bayer
4197+
pattern supported: "RGGB", "GRBG", "GBRG", "BGGR".
4198+
`algorithm=` *name*
4199+
the name of the algorithm to use: "linear"(simple bilinear demosaicing),
4200+
"MHC"(Malvar-He-Cutler algorithm)
4201+
`white-balance=` *v1,v2,v3...*
4202+
optional white balance weights, can contain either three (R,G,B) or four
4203+
(R,G1,B,G2) values. The order of the white balance multipliers is as
4204+
specified, it does not depend on the matrix layout.
4205+
4206+
Examples::
4207+
4208+
oiiotool --iconfig raw:Demosaic none --input test.cr3 --demosaic \
4209+
--output out.exr
4210+
4211+
oiiotool --iconfig raw:Demosaic none --input test.cr3 \
4212+
--demosaic:pattern=bayer:layout=GRBG:algorithm=MHC:white_balance=2.0,0.8,1.2,1.5 \
4213+
--output out.exr
4214+
4215+
4216+
41874217
:program:`oiiotool` commands for color management
41884218
=================================================
41894219

src/doc/pythonbindings.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3680,6 +3680,27 @@ Color manipulation
36803680
ImageBufAlgo.unpremult (A, A)
36813681
36823682
3683+
.. py:method:: ImageBuf ImageBufAlgo.demosaic (src, pattern="", algorithm="", layout="", white_balance=py::none(), roi=ROI.All, nthreads=0)
3684+
bool ImageBufAlgo.demosaic (dst, src, pattern="", algorithm="", layout="", white_balance=py::none(), roi=ROI.All, nthreads=0)
3685+
Demosaic a raw digital camera image.
3686+
3687+
`demosaic` can currently process Bayer pattern images (pattern="bayer")
3688+
using two algorithms: "linear" (simple bilinear demosaicing), and "MHC"
3689+
(Malvar-He-Cutler algorithm). The optional white_balance parameter can take
3690+
a tuple of three (R,G,B), or four (R,G1,B,G2) values. The order of the
3691+
white balance multipliers is as specified, it does not depend on the matrix
3692+
layout.
3693+
3694+
Example:
3695+
3696+
.. code-block:: python
3697+
3698+
Src = ImageBuf("test.cr3", 0, 0, hint)
3699+
WB_RGBG = (2.0, 0.8, 1.5, 1.2)
3700+
Dst = OpenImageIO.ImageBufAlgo.demosaic(Src, layout="GRBG",
3701+
white_balance = WB_RGBG)
3702+
3703+
36833704
36843705
36853706
.. _sec-iba-py-importexport:

src/include/OpenImageIO/imagebufalgo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,10 @@ bool OIIO_API repremult (ImageBuf &dst, const ImageBuf &src,
21632163
///
21642164
/// The order the color filter array elements are arranged in, pattern-specific.
21652165
///
2166+
/// - "white-balance" : float[3] or float[4] (default: {1.0, 1.0, 1.0, 1.0})
2167+
///
2168+
/// Optional white-balancing weights. Can contain either three (R,G,B), or four (R,G1,B,G2) values.
2169+
/// The order of the white balance multipliers does not depend on the matrix layout.
21662170

21672171
ImageBuf OIIO_API demosaic (const ImageBuf& src, KWArgs options = {},
21682172
ROI roi = {}, int nthreads = 0);

src/libOpenImageIO/imagebufalgo_demosaic.cpp

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace {
1818
static const ustring pattern_us("pattern");
1919
static const ustring algorithm_us("algorithm");
2020
static const ustring layout_us("layout");
21+
static const ustring white_balance_us("white_balance");
2122

2223
} // namespace
2324

@@ -31,7 +32,17 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
3132
/// the source iterator.
3233
struct Row {
3334
ImageBuf::ConstIterator<Atype> iterator;
35+
float white_balance[2];
36+
int x_offset;
3437
float data[size];
38+
39+
float fetch()
40+
{
41+
float result = iterator[0] * white_balance[x_offset];
42+
iterator++;
43+
x_offset = 1 - x_offset;
44+
return result;
45+
}
3546
};
3647

3748
std::vector<Row> rows;
@@ -46,7 +57,8 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
4657
int src_yend;
4758
int x;
4859

49-
Window(int y, int xbegin, const ImageBuf& src)
60+
Window(int y, int xbegin, const ImageBuf& src, int x_offset,
61+
int y_offset, const float white_balance[4])
5062
{
5163
assert(size >= 3);
5264
assert(size % 2 == 1);
@@ -78,12 +90,15 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
7890
ystart = src_yend - 1 - (ystart - src_yend + 1) % 2;
7991
}
8092

81-
Row row
82-
= { ImageBuf::ConstIterator<Atype>(src, xstart, ystart) };
93+
int x_off = (xstart + x_offset) % 2;
94+
int y_off = ((ystart + y_offset) % 2) * 2;
95+
96+
Row row = { ImageBuf::ConstIterator<Atype>(src, xstart, ystart),
97+
{ white_balance[y_off], white_balance[y_off + 1] },
98+
x_off };
8399

84100
for (int j = skip; j < size; j++) {
85-
row.data[j] = row.iterator[0];
86-
row.iterator++;
101+
row.data[j] = row.fetch();
87102
}
88103

89104
for (int j = 0; j < skip; j++) {
@@ -113,8 +128,7 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
113128
if (x + size / 2 < src_xend) {
114129
for (int i = 0; i < size; i++) {
115130
Row& row = rows[i];
116-
row.data[curr] = row.iterator[0];
117-
row.iterator++;
131+
row.data[curr] = row.fetch();
118132
}
119133
} else {
120134
int src = column_mapping[size - 3];
@@ -141,7 +155,7 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
141155

142156
public:
143157
bool process(ImageBuf& dst, const ImageBuf& src, const std::string& layout,
144-
ROI roi, int nthreads)
158+
const float white_balance[4], ROI roi, int nthreads)
145159
{
146160
int x_offset, y_offset;
147161

@@ -167,7 +181,7 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
167181

168182
for (int y = roi.ybegin; y < roi.yend; y++) {
169183
typename BayerDemosaicing<Rtype, Atype, size>::Window window(
170-
y, roi.xbegin, src);
184+
y, roi.xbegin, src, x_offset, y_offset, white_balance);
171185

172186
int r = (y_offset + y) % 2;
173187
Decoder calc_0 = decoders[r][(x_offset + roi.xbegin + 0) % 2];
@@ -360,20 +374,21 @@ class MHCBayerDemosaicing : public BayerDemosaicing<Rtype, Atype, size> {
360374
template<class Rtype, class Atype>
361375
static bool
362376
bayer_demosaic_linear_impl(ImageBuf& dst, const ImageBuf& src,
363-
const std::string& bayer_pattern, ROI roi,
364-
int nthreads)
377+
const std::string& bayer_pattern,
378+
const float white_balance[4], ROI roi, int nthreads)
365379
{
366380
LinearBayerDemosaicing<Rtype, Atype> obj;
367-
return obj.process(dst, src, bayer_pattern, roi, nthreads);
381+
return obj.process(dst, src, bayer_pattern, white_balance, roi, nthreads);
368382
}
369383

370384
template<class Rtype, class Atype>
371385
static bool
372386
bayer_demosaic_MHC_impl(ImageBuf& dst, const ImageBuf& src,
373-
const std::string& bayer_pattern, ROI roi, int nthreads)
387+
const std::string& bayer_pattern,
388+
const float white_balance[4], ROI roi, int nthreads)
374389
{
375390
MHCBayerDemosaicing<Rtype, Atype> obj;
376-
return obj.process(dst, src, bayer_pattern, roi, nthreads);
391+
return obj.process(dst, src, bayer_pattern, white_balance, roi, nthreads);
377392

378393
return true;
379394
}
@@ -389,6 +404,7 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
389404
std::string pattern;
390405
std::string algorithm;
391406
std::string layout;
407+
float white_balance_RGGB[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
392408

393409
for (auto&& pv : options) {
394410
if (pv.name() == pattern_us) {
@@ -409,6 +425,25 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
409425
} else {
410426
dst.errorfmt("ImageBufAlgo::demosaic() invalid layout");
411427
}
428+
} else if (pv.name() == white_balance_us) {
429+
if (pv.type() == TypeFloat && pv.nvalues() == 4) {
430+
// The order in the options is always (R,G1,B,G2)
431+
white_balance_RGGB[0] = pv.get_float_indexed(0);
432+
white_balance_RGGB[1] = pv.get_float_indexed(1);
433+
white_balance_RGGB[2] = pv.get_float_indexed(3);
434+
white_balance_RGGB[3] = pv.get_float_indexed(2);
435+
436+
if (white_balance_RGGB[2] == 0)
437+
white_balance_RGGB[2] = white_balance_RGGB[1];
438+
} else if (pv.type() == TypeFloat && pv.nvalues() == 3) {
439+
// The order in the options is always (R,G,B)
440+
white_balance_RGGB[0] = pv.get_float_indexed(0);
441+
white_balance_RGGB[1] = pv.get_float_indexed(1);
442+
white_balance_RGGB[2] = white_balance_RGGB[1];
443+
white_balance_RGGB[3] = pv.get_float_indexed(2);
444+
} else {
445+
dst.errorfmt("ImageBufAlgo::demosaic() invalid white balance");
446+
}
412447
} else {
413448
dst.errorfmt("ImageBufAlgo::demosaic() unknown parameter {}",
414449
pv.name());
@@ -418,7 +453,7 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
418453

419454
ROI dst_roi = roi;
420455
if (!dst_roi.defined()) {
421-
dst_roi = src.roi_full();
456+
dst_roi = src.roi();
422457
}
423458

424459
dst_roi.chbegin = 0;
@@ -449,12 +484,14 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
449484
OIIO_DISPATCH_COMMON_TYPES2(ok, "bayer_demosaic_linear",
450485
bayer_demosaic_linear_impl,
451486
dst.spec().format, src.spec().format,
452-
dst, src, layout, dst_roi, nthreads);
487+
dst, src, layout, white_balance_RGGB,
488+
dst_roi, nthreads);
453489
} else if (algorithm == "MHC") {
454490
OIIO_DISPATCH_COMMON_TYPES2(ok, "bayer_demosaic_MHC",
455491
bayer_demosaic_MHC_impl,
456492
dst.spec().format, src.spec().format,
457-
dst, src, layout, dst_roi, nthreads);
493+
dst, src, layout, white_balance_RGGB,
494+
dst_roi, nthreads);
458495
} else {
459496
dst.errorfmt("ImageBufAlgo::demosaic() invalid algorithm");
460497
}

src/oiiotool/oiiotool.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3470,14 +3470,28 @@ OIIOTOOL_OP(warp, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
34703470

34713471
// --demosaic
34723472
OIIOTOOL_OP(demosaic, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
3473-
std::string pattern = op.options().get_string("pattern");
3474-
std::string algorithm = op.options().get_string("algorithm");
3475-
std::string layout = op.options().get_string("layout");
3476-
3477-
return ImageBufAlgo::demosaic(*img[0], *img[1],
3478-
{ { "pattern", pattern },
3479-
{ "algorithm", algorithm },
3480-
{ "layout", layout } });
3473+
ParamValueList list;
3474+
const std::vector<std::string> keys = { "pattern", "algorithm", "layout" };
3475+
for (const auto& key : keys) {
3476+
auto iter = op.options().find(key);
3477+
if (iter != op.options().cend()) {
3478+
list.push_back(iter[0]);
3479+
}
3480+
}
3481+
3482+
std::string wb = op.options()["white_balance"];
3483+
string_view str(wb);
3484+
float f3[3];
3485+
float f4[4];
3486+
if (Strutil::parse_values(str, "", f4, ",") && str.empty()) {
3487+
ParamValue pv("white_balance", TypeFloat, 4, f4);
3488+
list.push_back(pv);
3489+
} else if (Strutil::parse_values(str, "", f3, ",") && str.empty()) {
3490+
ParamValue pv("white_balance", TypeFloat, 3, f3);
3491+
list.push_back(pv);
3492+
}
3493+
3494+
return ImageBufAlgo::demosaic(*img[0], *img[1], KWArgs(list));
34813495
});
34823496

34833497

@@ -6850,7 +6864,7 @@ Oiiotool::getargs(int argc, char* argv[])
68506864
.help("Render text into the current image (options: x=, y=, size=, color=)")
68516865
.OTACTION(action_text);
68526866
ap.arg("--demosaic")
6853-
.help("Demosaic (options: pattern=%s, algorithm=%s, layout=%s)")
6867+
.help("Demosaic (options: pattern=%s, algorithm=%s, layout=%s, white_balance=%f:R,G1,G2,B)")
68546868
.OTACTION(action_demosaic);
68556869

68566870
ap.separator("Manipulating channels or subimages:");

0 commit comments

Comments
 (0)