Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oiiotool --metamerge gives output merged metdata of all inputs #2311

Merged
merged 1 commit into from
Aug 9, 2019
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
13 changes: 11 additions & 2 deletions src/doc/oiiotool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ output each one to a different file, with names `sub0001.tif`,
it vertically to preserve the original aspect ratio, and then print a
message to the console revealing the resolution of the resulting image.

.. option:: =-metamatch <regex>, --no-metamatch <regex>
.. option:: --metamatch <regex>, --no-metamatch <regex>

Regular expressions to restrict which metadata are output when using
`oiiotool --info -v`. The `--metamatch` expression causes only metadata
Expand Down Expand Up @@ -1223,7 +1223,7 @@ Writing images

.. option:: --dither

Turns on *diether* when outputting to 8-bit image files (does not affect
Turns on *dither* when outputting to 8-bit image files (does not affect
other data types). This adds just a bit of noise that reduces visible
banding artifacts.

Expand Down Expand Up @@ -1263,6 +1263,15 @@ Writing images
pixels. In other words, trim off any all-black border rows and columns
before writing the file.

.. option:: --metamerge

When this flag is used, most image operations will try to merge the
metadata found in all of their source input images into the output.
The default (if this is not used) is that image oprations with multiple
input images will just take metadata from the first source image.

(This was added for OpenImageIO 2.1.)



:program:`oiiotool` commands that change the current image metadata
Expand Down
1 change: 1 addition & 0 deletions src/include/OpenImageIO/imagebufalgo_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ enum IBAprep_flags {
IBAprep_DST_FLOAT_PIXELS = 1<<13, // If dst is uninit, make it float
IBAprep_MINIMIZE_NCHANNELS = 1<<14, // Multi-inputs get min(nchannels)
IBAprep_REQUIRE_MATCHING_CHANNELS = 1<<15, // Channel names must match
IBAprep_MERGE_METADATA = 1 << 16, // Merge all inputs' metadata
};


Expand Down
19 changes: 15 additions & 4 deletions src/include/OpenImageIO/paramlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,23 @@ class OIIO_API ParamValueList : public std::vector<ParamValue> {
bool getattribute(string_view name, std::string& value,
bool casesensitive = false) const;

// Sort alphabetically, optionally case-insensitively, locale-
// independently, and with all the "un-namespaced" items appearing
// first, followed by items with "prefixed namespaces" (e.g. "z" comes
// before "foo:a").
/// Sort alphabetically, optionally case-insensitively, locale-
/// independently, and with all the "un-namespaced" items appearing
/// first, followed by items with "prefixed namespaces" (e.g. "z" comes
/// before "foo:a").
void sort(bool casesensitive = true);

/// Merge items from PVL `other` into `*this`. Note how this differs
/// from `operator=` : assignment completely replaces the list with
/// the contents of another. But merge() adds the other items without
/// erasing any items already in this list.
///
/// @param override
/// If true, `other` attributes will replace any identically-named
/// attributes already in this list. If false, only attributes whose
/// names are not already in this list will be appended.
void merge(const ParamValueList& other, bool override = false);

/// Even more radical than clear, free ALL memory associated with the
/// list itself.
void free()
Expand Down
20 changes: 20 additions & 0 deletions src/libOpenImageIO/imagebufalgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ ImageBufAlgo::IBAprep(ROI& roi, ImageBuf* dst, const ImageBuf* A,
// to fully read it into allocated memory so that we're able
// to write to it subsequently.
dst->make_writeable(true);

// Merge source metadata into destination if requested.
if (prepflags & IBAprep_MERGE_METADATA) {
if (A && A->initialized())
dst->specmod().extra_attribs.merge(A->spec().extra_attribs);
if (B && B->initialized())
dst->specmod().extra_attribs.merge(B->spec().extra_attribs);
if (C && C->initialized())
dst->specmod().extra_attribs.merge(C->spec().extra_attribs);
}
} else {
// Not an initialized destination image!
ASSERT((A || roi.defined())
Expand Down Expand Up @@ -216,6 +226,16 @@ ImageBufAlgo::IBAprep(ROI& roi, ImageBuf* dst, const ImageBuf* A,
else
set_roi_full(spec, roi);

// Merge source metadata into destination if requested.
if (prepflags & IBAprep_MERGE_METADATA) {
if (A && A->initialized())
spec.extra_attribs.merge(A->spec().extra_attribs);
if (B && B->initialized())
spec.extra_attribs.merge(B->spec().extra_attribs);
if (C && C->initialized())
spec.extra_attribs.merge(C->spec().extra_attribs);
}

if (prepflags & IBAprep_NO_COPY_METADATA)
spec.extra_attribs.clear();
else if (!(prepflags & IBAprep_COPY_ALL_METADATA)) {
Expand Down
11 changes: 11 additions & 0 deletions src/libutil/paramlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,4 +699,15 @@ ParamValueList::sort(bool casesensitive)
});
}



void
ParamValueList::merge(const ParamValueList& other, bool override)
{
for (const auto& attr : other) {
if (override || !contains(attr.name()))
add_or_replace(attr);
}
}

OIIO_NAMESPACE_END
33 changes: 33 additions & 0 deletions src/libutil/paramlist_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,39 @@ test_paramlist()
pl.remove("foo");
OIIO_CHECK_ASSERT(!pl.contains("foo"));
OIIO_CHECK_ASSERT(pl.contains("bar"));

{
// Check merge
ParamValueList list1, list2;
list1.emplace_back("b", 2);
list1.emplace_back("c", 3);
list1.emplace_back("a", 1);
list2.emplace_back("d", 11);
list2.emplace_back("c", 10);
list1.merge(list2, /*override=*/false);
OIIO_CHECK_EQUAL(list1.size(), 4);
OIIO_CHECK_EQUAL(list1.get_int("a"), 1);
OIIO_CHECK_EQUAL(list1.get_int("b"), 2);
OIIO_CHECK_EQUAL(list1.get_int("c"), 3);
OIIO_CHECK_EQUAL(list1.get_int("d"), 11);
list1.merge(list2, /*override=*/true);
OIIO_CHECK_EQUAL(list1.size(), 4);
OIIO_CHECK_EQUAL(list1.get_int("a"), 1);
OIIO_CHECK_EQUAL(list1.get_int("b"), 2);
OIIO_CHECK_EQUAL(list1.get_int("c"), 10);
OIIO_CHECK_EQUAL(list1.get_int("d"), 11);

// Check sort
OIIO_CHECK_EQUAL(list1[0].name(), "b");
OIIO_CHECK_EQUAL(list1[1].name(), "c");
OIIO_CHECK_EQUAL(list1[2].name(), "a");
OIIO_CHECK_EQUAL(list1[3].name(), "d");
list1.sort();
OIIO_CHECK_EQUAL(list1[0].name(), "a");
OIIO_CHECK_EQUAL(list1[1].name(), "b");
OIIO_CHECK_EQUAL(list1[2].name(), "c");
OIIO_CHECK_EQUAL(list1[3].name(), "d");
}
}


Expand Down
15 changes: 15 additions & 0 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Oiiotool::clear_options()
autocc = false;
autopremult = true;
nativeread = false;
metamerge = false;
cachesize = 4096;
autotile = 0; // was: 4096
// FIXME: Turned off autotile by default Jan 2018 after thinking that
Expand Down Expand Up @@ -2370,6 +2371,13 @@ action_chappend(int argc, const char* argv[])
(*B)(s, m));
if (!ok)
ot.error(command, (*R)(s, m).geterror());
if (ot.metamerge) {
(*R)(s, m).specmod().extra_attribs.merge(
A->spec(s, m)->extra_attribs);
(*R)(s, m).specmod().extra_attribs.merge(
B->spec(s, m)->extra_attribs);
}

// Tricky subtlety: IBA::channels changed the underlying IB,
// we may need to update the IRR's copy of the spec.
R->update_spec_from_imagebuf(s, m);
Expand Down Expand Up @@ -5519,6 +5527,11 @@ print_help_end(const ArgParse& ap, std::ostream& out)
// same area is this executable, otherwise just point to the copy on
// GitHub corresponding to our version of the softare.
out << "Full OIIO documentation can be found at\n";
out << " https://openimageio.readthedocs.io\n";
#if 0
FIXME -- when we have multiple versions online, return to this and
customize the version we have them look up.

std::string path = Sysutil::this_program_path();
path = Filesystem::parent_path(path);
path = Filesystem::parent_path(path);
Expand All @@ -5537,6 +5550,7 @@ print_help_end(const ArgParse& ap, std::ostream& out)
branch);
out << " " << docsurl << "\n";
}
#endif
}


Expand Down Expand Up @@ -5610,6 +5624,7 @@ getargs(int argc, char* argv[])
"--native %@", set_native, &ot.nativeread, "Keep native pixel data type (bypass cache if necessary)",
"--cache %@ %d", set_cachesize, &ot.cachesize, "ImageCache size (in MB: default=4096)",
"--autotile %@ %d", set_autotile, &ot.autotile, "Autotile enable for cached images (the argument is the tile size, default 0 means no autotile)",
"--metamerge", &ot.metamerge, "Always merge metadata of all inputs into output",
"--crash %@", crash_me, nullptr, "", // hidden option
"<SEPARATOR>", "Commands that read images:",
"-i %@ %s", input_file, NULL, "Input file (argument: filename) (options: now=, printinfo=, autocc=, type=, ch=)",
Expand Down
8 changes: 8 additions & 0 deletions src/oiiotool/oiiotool.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Oiiotool {
bool autopremult; // auto premult unassociated alpha input
bool nativeread; // force native data type reads
bool printinfo_verbose;
bool metamerge; // Merge source input metadata into output
int cachesize;
int autotile;
int frame_padding;
Expand Down Expand Up @@ -658,6 +659,13 @@ class OiiotoolOp {
bool ok = impl(nimages() ? &img[0] : NULL);
if (!ok)
ot.errorf(opname(), "%s", img[0]->geterror());

// Merge metadata if called for
if (ot.metamerge)
for (int i = 1; i < nimages(); ++i)
img[0]->specmod().extra_attribs.merge(
img[i]->spec().extra_attribs);

ir[0]->update_spec_from_imagebuf(s);
}

Expand Down
1 change: 1 addition & 0 deletions src/python/py_paramvalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ declare_paramvalue(py::module& m)
},
"value"_a, "casesensitive"_a = true)
.def("sort", &ParamValueList::sort, "casesensitive"_a = true)
.def("merge", &ParamValueList::merge, "other"_a, "override"_a = false)
.def("attribute",
[](ParamValueList& self, const std::string& name, float val) {
self.attribute(name, TypeFloat, &val);
Expand Down
21 changes: 21 additions & 0 deletions testsuite/oiiotool-copy/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,24 @@ tmp.tif : 128 x 128, 3 channel, uint8 tiff
combining images result:
tmp.tif : 128 x 128, 3 channel, uint8 tiff
tile size: 16 x 16
Reading nometamerge.exr
nometamerge.exr : 64 x 64, 6 channel, float openexr
SHA-1: 9F13A523321C66208E90D45F87FA0CD9B370E111
channel list: R, G, B, A, Z, channel3
a: 3
compression: "zip"
PixelAspectRatio: 1
screenWindowCenter: 0, 0
screenWindowWidth: 1
oiio:ColorSpace: "Linear"
Reading metamerge.exr
metamerge.exr : 64 x 64, 6 channel, float openexr
SHA-1: 9F13A523321C66208E90D45F87FA0CD9B370E111
channel list: R, G, B, A, Z, channel3
a: 3
b: 1
compression: "zip"
PixelAspectRatio: 1
screenWindowCenter: 0, 0
screenWindowWidth: 1
oiio:ColorSpace: "Linear"
7 changes: 7 additions & 0 deletions testsuite/oiiotool-copy/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
"-echo '\ncombining images result: ' -metamatch \"width|tile\" -i:info=2 tmp.tif")


# test --metamerge, using chappend as an example
command += oiiotool ("--create 64x64 3 -chnames R,G,B -attrib a 3.0 -o aimg.exr")
command += oiiotool ("--create 64x64 3 -chnames A,Z -attrib b 1.0 -o bimg.exr")
command += oiiotool ("aimg.exr bimg.exr --chappend -o nometamerge.exr")
command += oiiotool ("--metamerge aimg.exr bimg.exr --chappend -o metamerge.exr")
command += info_command ("nometamerge.exr", safematch=True)
command += info_command ("metamerge.exr", safematch=True)



Expand Down
13 changes: 13 additions & 0 deletions testsuite/python-paramlist/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,23 @@ pl['e'] = 2.71828
pl['pi'] = 3.14159
pl['foo'] = bar
after removing 'e', len= 5 pl.contains('e')= False
pl2 =
item a string aval
item m int 1
After merge, pl =
item i int 1
item s string Bob
item j int 42
item foo string bar
item pi float 3.14159
item a string aval
item m int 1
after sorting:
item a string aval
item foo string bar
item i int 1
item j int 42
item m int 1
item pi float 3.14159
item s string Bob
Done.
21 changes: 16 additions & 5 deletions testsuite/python-paramlist/src/test_paramlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
import OpenImageIO as oiio


def print_param_list(pl) :
for p in pl :
if type(p.value) == float :
print (" item {} {} {:.6}".format(p.name, p.type, p.value))
else :
print (" item {} {} {}".format(p.name, p.type, p.value))



Expand Down Expand Up @@ -36,13 +42,18 @@
pl.remove('e')
print ("after removing 'e', len=", len(pl), "pl.contains('e')=", pl.contains('e'))

pl2 = oiio.ParamValueList()
pl2.attribute ("a", "aval")
pl2.attribute ("m", 1)
print ("pl2 =")
print_param_list(pl2)
pl.merge(pl2)
print ("After merge, pl =")
print_param_list(pl)

pl.sort()
print ("after sorting:")
for p in pl :
if type(p.value) == float :
print (" item {} {} {:.6}".format(p.name, p.type, p.value))
else :
print (" item {} {} {}".format(p.name, p.type, p.value))
print_param_list(pl)

print ("Done.")
except Exception as detail:
Expand Down