Skip to content

Commit

Permalink
feat(IBA): IBA:demosaic add white balancing (#4499)
Browse files Browse the repository at this point in the history
Adds white balancing functionality to IBA:demosaic


Signed-off-by: Anton Dukhovnikov <[email protected]>
  • Loading branch information
antond-weta authored Nov 18, 2024
1 parent 519a179 commit 75e70b8
Show file tree
Hide file tree
Showing 19 changed files with 332 additions and 139 deletions.
10 changes: 7 additions & 3 deletions src/cmake/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,16 @@ macro (oiio_add_all_tests)
iinfo igrep
nonwhole-tiles
oiiotool
oiiotool-composite oiiotool-control oiiotool-copy
oiiotool-composite
oiiotool-control
oiiotool-copy
oiiotool-demosaic
oiiotool-fixnan
oiiotool-pattern
oiiotool-readerror
oiiotool-subimage oiiotool-text oiiotool-xform
oiiotool-demosaic
oiiotool-subimage
oiiotool-text
oiiotool-xform
diff
dither dup-channels
jpeg-corrupt jpeg-metadata
Expand Down
25 changes: 15 additions & 10 deletions src/doc/imagebufalgo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1163,22 +1163,27 @@ Shuffling channels

.. code-tab:: c++

ImageSpec hint;
hint["raw:Demosaic"] = "none";
ImageBuf Src ("test.cr3", 0, 0, nullptr, &hint);
ParamValue options[] = { { "layout", "GRBG" } };
ImageBuf Dst = ImageBufAlgo::demosaic (Src, options);
.. tab:: C++

.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebufalgo.cpp
:language: c++
:start-after: BEGIN-imagebufalgo-demosaic
:end-before: END-imagebufalgo-demosaic
:dedent: 4

.. code-tab:: py

hint = ImageSpec()
hint["raw:Demosaic"] = "none"
Src = ImageBuf("test.cr3", 0, 0, hint)
Dst = OpenImageIO.ImageBufAlgo.demosaic(Src, layout="GRBG")
.. tab:: Python

.. literalinclude:: ../../testsuite/docs-examples-python/src/docs-examples-imagebufalgo.py
:language: py
:start-after: BEGIN-imagebufalgo-demosaic
:end-before: END-imagebufalgo-demosaic
:dedent: 4

.. code-tab:: bash oiiotool

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

|
Expand Down
30 changes: 30 additions & 0 deletions src/doc/oiiotool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4184,6 +4184,36 @@ current top image.



.. option:: --demosaic

Demosaic a raw digital camera image.

Optional appended modifiers include:

`pattern=` *name*
sensor pattern. Currently supported patterns: "bayer"
`layout=` *name*
photosite order of the specified pattern. For layouts of the Bayer
pattern supported: "RGGB", "GRBG", "GBRG", "BGGR".
`algorithm=` *name*
the name of the algorithm to use: "linear"(simple bilinear demosaicing),
"MHC"(Malvar-He-Cutler algorithm)
`white-balance=` *v1,v2,v3...*
optional white balance weights, can contain either three (R,G,B) or four
(R,G1,B,G2) values. The order of the white balance multipliers is as
specified, it does not depend on the matrix layout.

Examples::

oiiotool --iconfig raw:Demosaic none --input test.cr3 --demosaic \
--output out.exr

oiiotool --iconfig raw:Demosaic none --input test.cr3 \
--demosaic:pattern=bayer:layout=GRBG:algorithm=MHC:white_balance=2.0,0.8,1.2,1.5 \
--output out.exr



:program:`oiiotool` commands for color management
=================================================

Expand Down
21 changes: 21 additions & 0 deletions src/doc/pythonbindings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3680,6 +3680,27 @@ Color manipulation
ImageBufAlgo.unpremult (A, A)
.. py:method:: ImageBuf ImageBufAlgo.demosaic (src, pattern="", algorithm="", layout="", white_balance=py::none(), roi=ROI.All, nthreads=0)
bool ImageBufAlgo.demosaic (dst, src, pattern="", algorithm="", layout="", white_balance=py::none(), roi=ROI.All, nthreads=0)
Demosaic a raw digital camera image.
`demosaic` can currently process Bayer pattern images (pattern="bayer")
using two algorithms: "linear" (simple bilinear demosaicing), and "MHC"
(Malvar-He-Cutler algorithm). The optional white_balance parameter can take
a tuple of three (R,G,B), or four (R,G1,B,G2) values. The order of the
white balance multipliers is as specified, it does not depend on the matrix
layout.
Example:
.. code-block:: python
Src = ImageBuf("test.cr3", 0, 0, hint)
WB_RGBG = (2.0, 0.8, 1.5, 1.2)
Dst = OpenImageIO.ImageBufAlgo.demosaic(Src, layout="GRBG",
white_balance = WB_RGBG)
.. _sec-iba-py-importexport:
Expand Down
4 changes: 4 additions & 0 deletions src/include/OpenImageIO/imagebufalgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2163,6 +2163,10 @@ bool OIIO_API repremult (ImageBuf &dst, const ImageBuf &src,
///
/// The order the color filter array elements are arranged in, pattern-specific.
///
/// - "white-balance" : float[3] or float[4] (default: {1.0, 1.0, 1.0, 1.0})
///
/// Optional white-balancing weights. Can contain either three (R,G,B), or four (R,G1,B,G2) values.
/// The order of the white balance multipliers does not depend on the matrix layout.

ImageBuf OIIO_API demosaic (const ImageBuf& src, KWArgs options = {},
ROI roi = {}, int nthreads = 0);
Expand Down
71 changes: 54 additions & 17 deletions src/libOpenImageIO/imagebufalgo_demosaic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace {
static const ustring pattern_us("pattern");
static const ustring algorithm_us("algorithm");
static const ustring layout_us("layout");
static const ustring white_balance_us("white_balance");

} // namespace

Expand All @@ -31,7 +32,17 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
/// the source iterator.
struct Row {
ImageBuf::ConstIterator<Atype> iterator;
float white_balance[2];
int x_offset;
float data[size];

float fetch()
{
float result = iterator[0] * white_balance[x_offset];
iterator++;
x_offset = 1 - x_offset;
return result;
}
};

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

Window(int y, int xbegin, const ImageBuf& src)
Window(int y, int xbegin, const ImageBuf& src, int x_offset,
int y_offset, const float white_balance[4])
{
assert(size >= 3);
assert(size % 2 == 1);
Expand Down Expand Up @@ -78,12 +90,15 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
ystart = src_yend - 1 - (ystart - src_yend + 1) % 2;
}

Row row
= { ImageBuf::ConstIterator<Atype>(src, xstart, ystart) };
int x_off = (xstart + x_offset) % 2;
int y_off = ((ystart + y_offset) % 2) * 2;

Row row = { ImageBuf::ConstIterator<Atype>(src, xstart, ystart),
{ white_balance[y_off], white_balance[y_off + 1] },
x_off };

for (int j = skip; j < size; j++) {
row.data[j] = row.iterator[0];
row.iterator++;
row.data[j] = row.fetch();
}

for (int j = 0; j < skip; j++) {
Expand Down Expand Up @@ -113,8 +128,7 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {
if (x + size / 2 < src_xend) {
for (int i = 0; i < size; i++) {
Row& row = rows[i];
row.data[curr] = row.iterator[0];
row.iterator++;
row.data[curr] = row.fetch();
}
} else {
int src = column_mapping[size - 3];
Expand All @@ -141,7 +155,7 @@ template<class Rtype, class Atype, int size> class BayerDemosaicing {

public:
bool process(ImageBuf& dst, const ImageBuf& src, const std::string& layout,
ROI roi, int nthreads)
const float white_balance[4], ROI roi, int nthreads)
{
int x_offset, y_offset;

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

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

int r = (y_offset + y) % 2;
Decoder calc_0 = decoders[r][(x_offset + roi.xbegin + 0) % 2];
Expand Down Expand Up @@ -360,20 +374,21 @@ class MHCBayerDemosaicing : public BayerDemosaicing<Rtype, Atype, size> {
template<class Rtype, class Atype>
static bool
bayer_demosaic_linear_impl(ImageBuf& dst, const ImageBuf& src,
const std::string& bayer_pattern, ROI roi,
int nthreads)
const std::string& bayer_pattern,
const float white_balance[4], ROI roi, int nthreads)
{
LinearBayerDemosaicing<Rtype, Atype> obj;
return obj.process(dst, src, bayer_pattern, roi, nthreads);
return obj.process(dst, src, bayer_pattern, white_balance, roi, nthreads);
}

template<class Rtype, class Atype>
static bool
bayer_demosaic_MHC_impl(ImageBuf& dst, const ImageBuf& src,
const std::string& bayer_pattern, ROI roi, int nthreads)
const std::string& bayer_pattern,
const float white_balance[4], ROI roi, int nthreads)
{
MHCBayerDemosaicing<Rtype, Atype> obj;
return obj.process(dst, src, bayer_pattern, roi, nthreads);
return obj.process(dst, src, bayer_pattern, white_balance, roi, nthreads);

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

for (auto&& pv : options) {
if (pv.name() == pattern_us) {
Expand All @@ -409,6 +425,25 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
} else {
dst.errorfmt("ImageBufAlgo::demosaic() invalid layout");
}
} else if (pv.name() == white_balance_us) {
if (pv.type() == TypeFloat && pv.nvalues() == 4) {
// The order in the options is always (R,G1,B,G2)
white_balance_RGGB[0] = pv.get_float_indexed(0);
white_balance_RGGB[1] = pv.get_float_indexed(1);
white_balance_RGGB[2] = pv.get_float_indexed(3);
white_balance_RGGB[3] = pv.get_float_indexed(2);

if (white_balance_RGGB[2] == 0)
white_balance_RGGB[2] = white_balance_RGGB[1];
} else if (pv.type() == TypeFloat && pv.nvalues() == 3) {
// The order in the options is always (R,G,B)
white_balance_RGGB[0] = pv.get_float_indexed(0);
white_balance_RGGB[1] = pv.get_float_indexed(1);
white_balance_RGGB[2] = white_balance_RGGB[1];
white_balance_RGGB[3] = pv.get_float_indexed(2);
} else {
dst.errorfmt("ImageBufAlgo::demosaic() invalid white balance");
}
} else {
dst.errorfmt("ImageBufAlgo::demosaic() unknown parameter {}",
pv.name());
Expand All @@ -418,7 +453,7 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,

ROI dst_roi = roi;
if (!dst_roi.defined()) {
dst_roi = src.roi_full();
dst_roi = src.roi();
}

dst_roi.chbegin = 0;
Expand Down Expand Up @@ -449,12 +484,14 @@ ImageBufAlgo::demosaic(ImageBuf& dst, const ImageBuf& src, KWArgs options,
OIIO_DISPATCH_COMMON_TYPES2(ok, "bayer_demosaic_linear",
bayer_demosaic_linear_impl,
dst.spec().format, src.spec().format,
dst, src, layout, dst_roi, nthreads);
dst, src, layout, white_balance_RGGB,
dst_roi, nthreads);
} else if (algorithm == "MHC") {
OIIO_DISPATCH_COMMON_TYPES2(ok, "bayer_demosaic_MHC",
bayer_demosaic_MHC_impl,
dst.spec().format, src.spec().format,
dst, src, layout, dst_roi, nthreads);
dst, src, layout, white_balance_RGGB,
dst_roi, nthreads);
} else {
dst.errorfmt("ImageBufAlgo::demosaic() invalid algorithm");
}
Expand Down
32 changes: 23 additions & 9 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3470,14 +3470,28 @@ OIIOTOOL_OP(warp, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {

// --demosaic
OIIOTOOL_OP(demosaic, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
std::string pattern = op.options().get_string("pattern");
std::string algorithm = op.options().get_string("algorithm");
std::string layout = op.options().get_string("layout");

return ImageBufAlgo::demosaic(*img[0], *img[1],
{ { "pattern", pattern },
{ "algorithm", algorithm },
{ "layout", layout } });
ParamValueList list;
const std::vector<std::string> keys = { "pattern", "algorithm", "layout" };
for (const auto& key : keys) {
auto iter = op.options().find(key);
if (iter != op.options().cend()) {
list.push_back(iter[0]);
}
}

std::string wb = op.options()["white_balance"];
string_view str(wb);
float f3[3];
float f4[4];
if (Strutil::parse_values(str, "", f4, ",") && str.empty()) {
ParamValue pv("white_balance", TypeFloat, 4, f4);
list.push_back(pv);
} else if (Strutil::parse_values(str, "", f3, ",") && str.empty()) {
ParamValue pv("white_balance", TypeFloat, 3, f3);
list.push_back(pv);
}

return ImageBufAlgo::demosaic(*img[0], *img[1], KWArgs(list));
});


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

ap.separator("Manipulating channels or subimages:");
Expand Down
Loading

0 comments on commit 75e70b8

Please sign in to comment.