Skip to content

Python internals: support int8[] metadata and ICCProfile, refactor #3556

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

Merged
merged 2 commits into from
Sep 19, 2022
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
8 changes: 5 additions & 3 deletions src/libOpenImageIO/exif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,11 @@ version4uint8_handler(const TagInfo& taginfo, const TIFFDirEntry& dir,
bool /*swapendian*/ = false, int offset_adjustment = 0)
{
const char* data = (const char*)dataptr(dir, buf, offset_adjustment);
if (tiff_data_size(dir) == 4 && data != nullptr) // sanity check
spec.attribute(taginfo.name, TypeDesc(TypeDesc::UINT8, 4),
(const char*)data);
if (tiff_data_size(dir) == 4 && data != nullptr) { // sanity check
int val[4] = { (unsigned char)data[0], (unsigned char)data[1],
(unsigned char)data[2], (unsigned char)data[3] };
spec.attribute(taginfo.name, TypeDesc(TypeDesc::INT, 4), val);
}
}


Expand Down
23 changes: 3 additions & 20 deletions src/python/py_imagespec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,7 @@ ImageSpec_getattribute_typed(const ImageSpec& spec, const std::string& name,
const ParamValue* p = spec.find_attribute(name, tmpparam, type);
if (!p)
return py::none();
type = p->type();
if (type.basetype == TypeDesc::INT)
return C_to_val_or_tuple((const int*)p->data(), type);
if (type.basetype == TypeDesc::UINT)
return C_to_val_or_tuple((const unsigned int*)p->data(), type);
if (type.basetype == TypeDesc::INT16)
return C_to_val_or_tuple((const short*)p->data(), type);
if (type.basetype == TypeDesc::UINT16)
return C_to_val_or_tuple((const unsigned short*)p->data(), type);
if (type.basetype == TypeDesc::FLOAT)
return C_to_val_or_tuple((const float*)p->data(), type);
if (type.basetype == TypeDesc::DOUBLE)
return C_to_val_or_tuple((const double*)p->data(), type);
if (type.basetype == TypeDesc::HALF)
return C_to_val_or_tuple((const half*)p->data(), type);
if (type.basetype == TypeDesc::STRING)
return C_to_val_or_tuple((const char**)p->data(), type);
return py::none();
return make_pyobject(p->data(), p->type(), p->nvalues());
}


Expand Down Expand Up @@ -238,7 +221,7 @@ declare_imagespec(py::module& m)
[](const ImageSpec& self, const std::string& key, py::object def) {
ParamValue tmpparam;
auto p = self.find_attribute(key, tmpparam);
return p ? ParamValue_getitem(*p, false, def) : def;
return p ? make_pyobject(p->data(), p->type(), 1, def) : def;
},
"key"_a, "default"_a = py::none())
.def(
Expand Down Expand Up @@ -285,7 +268,7 @@ declare_imagespec(py::module& m)
auto p = self.find_attribute(key, tmpparam);
if (p == nullptr)
throw py::key_error("key '" + key + "' does not exist");
return ParamValue_getitem(*p);
return make_pyobject(p->data(), p->type());
})
// __setitem__ is the dict-like `ImageSpec[key] = value` assignment
.def("__setitem__",
Expand Down
49 changes: 42 additions & 7 deletions src/python/py_oiio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,47 @@ oiio_attribute_typed(const std::string& name, TypeDesc type,



py::object
make_pyobject(const void* data, TypeDesc type, int nvalues,
py::object defaultvalue)
{
if (type.basetype == TypeDesc::INT32)
return C_to_val_or_tuple((const int*)data, type, nvalues);
if (type.basetype == TypeDesc::FLOAT)
return C_to_val_or_tuple((const float*)data, type, nvalues);
if (type.basetype == TypeDesc::STRING)
return C_to_val_or_tuple((const char**)data, type, nvalues);
if (type.basetype == TypeDesc::UINT32)
return C_to_val_or_tuple((const unsigned int*)data, type, nvalues);
if (type.basetype == TypeDesc::INT16)
return C_to_val_or_tuple((const short*)data, type, nvalues);
if (type.basetype == TypeDesc::UINT16)
return C_to_val_or_tuple((const unsigned short*)data, type, nvalues);
if (type.basetype == TypeDesc::INT64)
return C_to_val_or_tuple((const int64_t*)data, type, nvalues);
if (type.basetype == TypeDesc::UINT64)
return C_to_val_or_tuple((const uint64_t*)data, type, nvalues);
if (type.basetype == TypeDesc::DOUBLE)
return C_to_val_or_tuple((const double*)data, type, nvalues);
if (type.basetype == TypeDesc::HALF)
return C_to_val_or_tuple((const half*)data, type, nvalues);
if (type.basetype == TypeDesc::UINT8 && type.arraylen > 0) {
// Array of uint8 bytes
// Have to make a copy of the data, because make_numpy_array will
// take possession of it.
uint8_t* ucdata(new uint8_t[type.arraylen * nvalues]);
std::memcpy(ucdata, data, type.arraylen * nvalues);
return make_numpy_array(ucdata, 1, 1, size_t(type.arraylen),
size_t(nvalues));
}
if (type.basetype == TypeDesc::UINT8)
return C_to_val_or_tuple((const unsigned char*)data, type, nvalues);
debugfmt("Don't know how to handle type {}\n", type);
return defaultvalue;
}



static py::object
oiio_getattribute_typed(const std::string& name, TypeDesc type = TypeUnknown)
{
Expand All @@ -227,13 +268,7 @@ oiio_getattribute_typed(const std::string& name, TypeDesc type = TypeUnknown)
char* data = OIIO_ALLOCA(char, type.size());
if (!OIIO::getattribute(name, type, data))
return py::none();
if (type.basetype == TypeDesc::INT)
return C_to_val_or_tuple((const int*)data, type);
if (type.basetype == TypeDesc::FLOAT)
return C_to_val_or_tuple((const float*)data, type);
if (type.basetype == TypeDesc::STRING)
return C_to_val_or_tuple((const char**)data, type);
return py::none();
return make_pyobject(data, type);
}


Expand Down
66 changes: 9 additions & 57 deletions src/python/py_oiio.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,14 @@ attribute_typed(T& myobj, string_view name, TypeDesc type, const POBJ& dataobj)



// `data` points to values of `type`. Make a python object that represents
// them.
py::object
make_pyobject(const void* data, TypeDesc type, int nvalues = 1,
py::object defaultvalue = py::none());



template<typename T>
py::object
getattribute_typed(const T& obj, const std::string& name,
Expand All @@ -519,29 +527,7 @@ getattribute_typed(const T& obj, const std::string& name,
bool ok = obj.getattribute(name, type, data);
if (!ok)
return py::none(); // None
if (type.basetype == TypeDesc::INT)
return C_to_val_or_tuple((const int*)data, type);
if (type.basetype == TypeDesc::UINT)
return C_to_val_or_tuple((const unsigned int*)data, type);
if (type.basetype == TypeDesc::INT16)
return C_to_val_or_tuple((const short*)data, type);
if (type.basetype == TypeDesc::UINT16)
return C_to_val_or_tuple((const unsigned short*)data, type);
if (type.basetype == TypeDesc::UINT8)
return C_to_val_or_tuple((const unsigned char*)data, type);
if (type.basetype == TypeDesc::FLOAT)
return C_to_val_or_tuple((const float*)data, type);
if (type.basetype == TypeDesc::DOUBLE)
return C_to_val_or_tuple((const double*)data, type);
if (type.basetype == TypeDesc::HALF)
return C_to_val_or_tuple((const half*)data, type);
if (type.basetype == TypeDesc::STRING)
return C_to_val_or_tuple((const char**)data, type);
if (type.basetype == TypeDesc::INT64)
return C_to_val_or_tuple((const int64_t*)data, type);
if (type.basetype == TypeDesc::UINT64)
return C_to_val_or_tuple((const uint64_t*)data, type);
return py::none();
return make_pyobject(data, type);
}


Expand Down Expand Up @@ -620,40 +606,6 @@ make_numpy_array(TypeDesc format, void* data, int dims, size_t chans,



inline py::object
ParamValue_getitem(const ParamValue& self, bool allitems = false,
py::object defaultvalue = py::none())
{
TypeDesc t = self.type();
int nvals = allitems ? self.nvalues() : 1;

#define ParamValue_convert_dispatch(TYPE) \
case TypeDesc::TYPE: \
return C_to_val_or_tuple((CType<TypeDesc::TYPE>::type*)self.data(), t, \
nvals)

switch (t.basetype) {
ParamValue_convert_dispatch(UCHAR);
// ParamValue_convert_dispatch(CHAR);
ParamValue_convert_dispatch(USHORT);
ParamValue_convert_dispatch(SHORT);
ParamValue_convert_dispatch(UINT);
ParamValue_convert_dispatch(INT);
// ParamValue_convert_dispatch(ULONGLONG);
// ParamValue_convert_dispatch(LONGLONG);
ParamValue_convert_dispatch(HALF);
ParamValue_convert_dispatch(FLOAT);
ParamValue_convert_dispatch(DOUBLE);
case TypeDesc::STRING:
return C_to_val_or_tuple((const char**)self.data(), t, nvals);
default: return defaultvalue;
}

#undef ParamValue_convert_dispatch
}



template<typename C>
inline void
delegate_setitem(C& self, const std::string& key, py::object obj)
Expand Down
28 changes: 26 additions & 2 deletions src/python/py_paramvalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,28 @@ ParamValue_from_pyobject(string_view name, TypeDesc type, int nvalues,
pv.init(name, type, nvalues, interp, &u[0]);
return pv;
}
} else if (type.basetype == TypeDesc::UINT8 && type.arraylen
&& py::isinstance<py::bytes>(obj)) {
// Special case: converting a "bytes" object to a byte array
std::string s = obj.cast<py::bytes>();
if (type.arraylen < 0) // convert un-specified length to real length
type.arraylen = int(s.size()) / nvalues;
if (type.arraylen * nvalues == int(s.size())) {
std::vector<uint8_t> vals((const uint8_t*)s.data(),
(const uint8_t*)s.data() + s.size());
pv.init(name, type, nvalues, interp, vals.data());
return pv;
}
} else if (type.basetype == TypeDesc::UINT8) {
std::vector<uint8_t> vals;
py_to_stdvector(vals, obj);
if (vals.size() >= expected_size) {
pv.init(name, type, nvalues, interp, vals.data());
return pv;
}
} else {
Strutil::print("ParamValue_from_pyobject not sure how to handle {} {}\n",
name, type);
}

// I think this is what we should do here when not enough data is
Expand Down Expand Up @@ -143,7 +165,9 @@ declare_paramvalue(py::module& m)
#endif
.def_property_readonly("value",
[](const ParamValue& self) {
return ParamValue_getitem(self, true);
return make_pyobject(self.data(),
self.type(),
self.nvalues());
})
.def_property_readonly("__len__", &ParamValue::nvalues)
.def(py::init<const std::string&, int>())
Expand Down Expand Up @@ -180,7 +204,7 @@ declare_paramvalue(py::module& m)
auto p = self.find(key);
if (p == self.end())
throw py::key_error("key '" + key + "' does not exist");
return ParamValue_getitem(*p);
return make_pyobject(p->data(), p->type());
},
py::return_value_policy::reference_internal)
// __setitem__ is the dict-like `pvl[key] = value` assignment
Expand Down
Loading