diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2018-11-15 15:49:02 -0800 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2018-11-15 16:07:24 -0800 |
commit | 09f199a694ef5b956cabc368e40ab5ca11c64044 (patch) | |
tree | 456d184a3817c8b6524a90fc2da60893a2fc1895 /includes | |
parent | 2d25fc4f6a7e7453f877958a2f3f59b6cc588ca4 (diff) | |
download | dynamic_depth-09f199a694ef5b956cabc368e40ab5ca11c64044.tar.gz |
Initial commit of libdynamic_depth
Dynamic depth is a standard for embedding depth maps and
other similar extensions into standard image files like JPEG.
Test: m libdynamic_depth
Bug: 109735087
Bug: 119211681
Change-Id: I0103b7d47e60dc8e3a3b277456903d76f727926f
Diffstat (limited to 'includes')
28 files changed, 1794 insertions, 0 deletions
diff --git a/includes/dynamic_depth/app_info.h b/includes/dynamic_depth/app_info.h new file mode 100644 index 0000000..4a519b4 --- /dev/null +++ b/includes/dynamic_depth/app_info.h @@ -0,0 +1,81 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/item.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +/** + * A AppInfo element for a Dynamic Depth device. + */ +class AppInfo : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("AppInfo", "http://ns.google.com/photos/dd/1.0/appinfo/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates an AppInfo from the given fields. Returns null if the version + // field is empty and [item_uri is empty and items is null]. + // Params' descriptions: + // application is the name of the application that created the content. + // version is the application's version for the content. + // data is the optional payload associated with the given app. If this field + // is not empty but item_uri is empty or items is null, then the data will + // not be serialized. + // item_uri is the Container URI of the file that contains the content. + // application, and at least one of version or item_uri, must not be + // empty. + // items is the list of items where the serialized data is stored. It is the + // caller's responsibility to use items to construct a Container, and + // ensure that it is serialized along with this Image element. Data will + // not be serialized if this field is null. + static std::unique_ptr<AppInfo> FromData( + const string& application, const string& version, const string& data, + const string& item_uri, std::vector<std::unique_ptr<Item>>* items); + + // Returns the deserialized AppInfo; null if parsing fails. + static std::unique_ptr<AppInfo> FromDeserializer( + const xml::Deserializer& parent_deserializer, + const string& namespace_str); + + // Getters. + const string& GetApplication() const; + const string& GetVersion() const; + const string& GetItemUri() const; + + // Disallow copying. + AppInfo(const AppInfo&) = delete; + void operator=(const AppInfo&) = delete; + + private: + AppInfo(); + + bool ParseFields(const xml::Deserializer& deserializer); + + // Required. + string application_; + + // At least one of version or item_uri must be present. + string version_; + string item_uri_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_APP_INFO_H_ // NOLINT diff --git a/includes/dynamic_depth/camera.h b/includes/dynamic_depth/camera.h new file mode 100644 index 0000000..9c9c001 --- /dev/null +++ b/includes/dynamic_depth/camera.h @@ -0,0 +1,126 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> +#include <utility> + +#include "dynamic_depth/app_info.h" +#include "dynamic_depth/depth_map.h" +#include "dynamic_depth/element.h" +#include "dynamic_depth/image.h" +#include "dynamic_depth/imaging_model.h" +#include "dynamic_depth/light_estimate.h" +#include "dynamic_depth/point_cloud.h" +#include "dynamic_depth/pose.h" +#include "dynamic_depth/vendor_info.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// The camera trait is serialized only if it is one of PHYSICAL or LOGICAL. +// NONE signifies an undefined trait. +enum CameraTrait { NONE = 0, PHYSICAL = 1, LOGICAL = 2 }; + +struct CameraParams { + // The Image must be present. + std::unique_ptr<Image> image; + + // Optional elements. + std::unique_ptr<DepthMap> depth_map; + std::unique_ptr<LightEstimate> light_estimate; + std::unique_ptr<Pose> pose; + std::unique_ptr<ImagingModel> imaging_model; + std::unique_ptr<PointCloud> point_cloud; + std::unique_ptr<VendorInfo> vendor_info; + std::unique_ptr<AppInfo> app_info; + CameraTrait trait; + + explicit CameraParams(std::unique_ptr<Image> img) + : depth_map(nullptr), + light_estimate(nullptr), + pose(nullptr), + imaging_model(nullptr), + point_cloud(nullptr), + vendor_info(nullptr), + app_info(nullptr), + trait(CameraTrait::PHYSICAL) { + image = std::move(img); + } + + inline bool operator==(const CameraParams& other) const { + return image.get() == other.image.get() && + light_estimate.get() == other.light_estimate.get() && + pose.get() == other.pose.get() && + depth_map.get() == other.depth_map.get() && + imaging_model.get() == other.imaging_model.get() && + point_cloud.get() == other.point_cloud.get() && + vendor_info.get() == other.vendor_info.get() && + app_info.get() == other.app_info.get(); + } + + inline bool operator!=(const CameraParams& other) const { + return !(*this == other); + } +}; + +// Implements the Camera element from the Dynamic Depth specification, with +// serialization and deserialization. +class Camera : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a Camera from the given objects in params. + // Aside from the Image element, all other elements are optional and can be + // null. + static std::unique_ptr<Camera> FromData(std::unique_ptr<CameraParams> params); + + // Same as above, but allows the Image element to be null. This should be used + // only for Camera 0, since the lack of an Image element indicates that it + // refers to the JPEG container image. An Item element is generated for the + // container image, and populated into items. + // Currently supports only image/jpeg. + // The items parameter may be null if params->image is not null. + // TODO(miraleung): Add other mime types as args when needed. + static std::unique_ptr<Camera> FromDataForCamera0( + std::unique_ptr<CameraParams> params, + std::vector<std::unique_ptr<Item>>* items); + + // Returns the deserialized Camera object, null if parsing fails. + // Not sensitive to case when parsing a camera's trait. + static std::unique_ptr<Camera> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Getters. Except for Imaeg (which should never be null), these will return + // null if the corresponding fields are not present. + // which should never be null. + const Image* GetImage() const; + const LightEstimate* GetLightEstimate() const; + const Pose* GetPose() const; + const DepthMap* GetDepthMap() const; + const ImagingModel* GetImagingModel() const; + const PointCloud* GetPointCloud() const; + const VendorInfo* GetVendorInfo() const; + const AppInfo* GetAppInfo() const; + CameraTrait GetTrait() const; + + // Disallow copying. + Camera(const Camera&) = delete; + void operator=(const Camera&) = delete; + + private: + explicit Camera(std::unique_ptr<CameraParams> params); + + std::unique_ptr<CameraParams> params_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERA_H_ // NOLINT diff --git a/includes/dynamic_depth/cameras.h b/includes/dynamic_depth/cameras.h new file mode 100644 index 0000000..5537e03 --- /dev/null +++ b/includes/dynamic_depth/cameras.h @@ -0,0 +1,53 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/camera.h" +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Implements the Device:Cameras field from the Dynamic Depth specification, +// with serialization and deserialization for its child Camera elements. +class Cameras : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates this object from the given cameras. Returns null if the list is + // empty. + // If creation succeeds, ownership of the Camera objects are transferred to + // the resulting Cameras object. The vector of Camera objects will be cleared. + static std::unique_ptr<Cameras> FromCameraArray( + std::vector<std::unique_ptr<Camera>>* camera_list); + + // Returns the deserialized cameras in a Cameras object, null if parsing + // failed for all the cameras. + static std::unique_ptr<Cameras> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Returns the list of cameras. + const std::vector<const Camera*> GetCameras() const; + + // Disallow copying. + Cameras(const Cameras&) = delete; + void operator=(const Cameras&) = delete; + + private: + Cameras(); + + std::vector<std::unique_ptr<Camera>> camera_list_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CAMERAS_H_ // NOLINT diff --git a/includes/dynamic_depth/container.h b/includes/dynamic_depth/container.h new file mode 100644 index 0000000..a8a437d --- /dev/null +++ b/includes/dynamic_depth/container.h @@ -0,0 +1,52 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/item.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// A Container that holds a directory / array of file Item elementss. Files +// at the end of the image appear in the same sequential order as the Item +// elements held in an instance of this object. +class Container : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates this object from the given items. Returns null if the list is + // empty. If creation succeeds, ownership of the Item objects are transferred + // to the resulting Container object. The vector of Item objects will be + // cleared. + static std::unique_ptr<Container> FromItems( + std::vector<std::unique_ptr<Item>>* items); + + // Returns the deserialized item elements, null if parsing failed for all + // items. + static std::unique_ptr<Container> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Returns the list of cameras. + const std::vector<const Item*> GetItems() const; + + // Disallow copying. + Container(const Container&) = delete; + void operator=(const Container&) = delete; + + private: + Container(); + + std::vector<std::unique_ptr<Item>> items_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_CONTAINER_H_ // NOLINT diff --git a/includes/dynamic_depth/depth_map.h b/includes/dynamic_depth/depth_map.h new file mode 100644 index 0000000..836ff23 --- /dev/null +++ b/includes/dynamic_depth/depth_map.h @@ -0,0 +1,128 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> +#include <utility> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/item.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// The depth conversion format. Please see the Depth Map element in the +// Dynamnic Depth specification for more details. +enum class DepthFormat { kFormatNone = 0, kRangeInverse = 1, kRangeLinear = 2 }; + +// The Units of the depth map. Please see the Depth Map element in the Dynamic +// Depth specification. +enum class DepthUnits { kUnitsNone = 0, kMeters = 1, kDiopters = 2 }; + +// The type of depth measurement. Please see the Depth Map element in the +// Dynamic Depth specification. +enum class DepthMeasureType { kOpticalAxis = 1, kOpticRay = 2 }; + +// The semantics of this depth map. +enum class DepthItemSemantic { kDepth = 1, kSegmentation = 2 }; + +struct DepthMapParams { + // Mandatory values. + DepthFormat format; + float near; + float far; + DepthUnits units; + string depth_uri; + string mime; + DepthItemSemantic item_semantic = DepthItemSemantic::kDepth; + + // The bytes of the depth image. Must be non-empty at write-time (i.e. + // programmatic construction). + string depth_image_data = ""; + + // Optional values. + DepthMeasureType measure_type = DepthMeasureType::kOpticalAxis; + string confidence_uri = ""; + // The bytes of the confidence map. If confidence_uri is not empty, the + // confidence data must be non-empty at write-time (i.e. programmatic + // construction). + string confidence_data = ""; + string software = ""; + + // A list of (distance, radius) pairs. This should generally have a short + // length, so copying is expected to be inexpensive. + std::vector<float> focal_table; + + explicit DepthMapParams(DepthFormat in_format, float in_near, float in_far, + DepthUnits in_units, string in_depth_uri) + : format(in_format), + near(in_near), + far(in_far), + units(in_units), + depth_uri(in_depth_uri) {} + + inline bool operator==(const DepthMapParams& other) const { + return format == other.format && near == other.near && far == other.far && + units == other.units && depth_uri == other.depth_uri && + depth_image_data == other.depth_image_data && + measure_type == other.measure_type && + confidence_uri == other.confidence_uri && + confidence_data == other.confidence_data && + software == other.software && focal_table == other.focal_table; + } + + inline bool operator!=(const DepthMapParams& other) const { + return !(*this == other); + } +}; + +// Implements the Depth Map element from the Dynamic Depth specification, with +// serialization and deserialization. +class DepthMap : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a DepthMap from the given objects in params. + static std::unique_ptr<DepthMap> FromData( + const DepthMapParams& params, std::vector<std::unique_ptr<Item>>* items); + + // Returns the deserialized DepthMap object, null if parsing fails. + // Not sensitive to case when parsing the Format, Units, or MeasureType + // fields. + static std::unique_ptr<DepthMap> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + DepthFormat GetFormat() const; + float GetNear() const; + float GetFar() const; + DepthUnits GetUnits() const; + const string GetDepthUri() const; + DepthItemSemantic GetItemSemantic() const; + const string GetConfidenceUri() const; + DepthMeasureType GetMeasureType() const; + const string GetSoftware() const; + const std::vector<float>& GetFocalTable() const; + size_t GetFocalTableEntryCount() const; + + // Disallow copying + DepthMap(const DepthMap&) = delete; + void operator=(const DepthMap&) = delete; + + private: + explicit DepthMap(const DepthMapParams& params); + static std::unique_ptr<DepthMap> ParseFields( + const xml::Deserializer& deserializer); + + DepthMapParams params_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEPTH_MAP_H_ // NOLINT diff --git a/includes/dynamic_depth/device.h b/includes/dynamic_depth/device.h new file mode 100644 index 0000000..77f1665 --- /dev/null +++ b/includes/dynamic_depth/device.h @@ -0,0 +1,131 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xmlmemory.h> +#include <libxml/xpath.h> + +#include <memory> +#include <string> +#include <unordered_map> +#include <utility> + +#include "dynamic_depth/app_info.h" +#include "dynamic_depth/cameras.h" +#include "dynamic_depth/container.h" +#include "dynamic_depth/earth_pose.h" +#include "dynamic_depth/planes.h" +#include "dynamic_depth/pose.h" +#include "dynamic_depth/profiles.h" +#include "dynamic_depth/vendor_info.h" +#include "xmpmeta/xmp_data.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +struct DeviceParams { + // Cameras must be present (i.e. contain at least one camera). + std::unique_ptr<Cameras> cameras; + + // GContainer. Optional, depending on element presence or user choice. + std::unique_ptr<Container> container; + + // Optional elements. + std::unique_ptr<Profiles> profiles; + std::unique_ptr<Planes> planes; + std::unique_ptr<EarthPose> earth_pose; + std::unique_ptr<Pose> pose; + std::unique_ptr<VendorInfo> vendor_info; + std::unique_ptr<AppInfo> app_info; + + explicit DeviceParams(std::unique_ptr<Cameras> new_cameras) + : container(nullptr), + profiles(nullptr), + planes(nullptr), + earth_pose(nullptr), + pose(nullptr), + vendor_info(nullptr), + app_info(nullptr) { + cameras = std::move(new_cameras); + } + + inline bool operator==(const DeviceParams& other) const { + return cameras.get() == other.cameras.get() && + container.get() == other.container.get() && + profiles.get() == other.profiles.get() && + planes.get() == other.planes.get() && + earth_pose.get() == other.earth_pose.get() && + pose.get() == other.pose.get() && + vendor_info.get() == other.vendor_info.get() && + app_info.get() == other.app_info.get(); + } + + inline bool operator!=(const DeviceParams& other) const { + return !(*this == other); + } +}; + +// Implements a Device from the Dynamic Depth specification, with serialization +// and deserialization. +// Does not implement the Element interface because Device is at the top level +// in the tree. +class Device { + public: + // Creates a Device from the given elements. + static std::unique_ptr<Device> FromData(std::unique_ptr<DeviceParams> params); + + // Creates a Device from pre-extracted XMP metadata. Returns null if + // parsing fails. Both the standard and extended XMP sections are required. + static std::unique_ptr<Device> FromXmp(const XmpData& xmp); + + // Creates a Device by extracting XMP metadata from a JPEG and parsing it. + // If using XMP for other things as well, FromXmp() should be used instead to + // prevent redundant extraction of XMP from the JPEG. + static std::unique_ptr<Device> FromJpegFile(const string& filename); + + // Creates a Device by parsing XML file containing the metadata. + static std::unique_ptr<Device> FromXmlFile(const string& filename); + + // Getters. + // May return null values for optional fields. + const Cameras* GetCameras() const; + const Container* GetContainer() const; + const EarthPose* GetEarthPose() const; + const Pose* GetPose() const; + const Planes* GetPlanes() const; + const Profiles* GetProfiles() const; + const VendorInfo* GetVendorInfo() const; + const AppInfo* GetAppInfo() const; + + // Not const for XML memory management reasons. More info in source comments. + bool SerializeToXmp(XmpData* xmp); + + // Saves Device metadata to a .xml file. + bool SerializeToXmlFile(const char* filename); + + // Disallow copying. + Device(const Device&) = delete; + void operator=(const Device&) = delete; + + private: + explicit Device(std::unique_ptr<DeviceParams> params); + + // Retrieves the namespaces of all child elements. + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) const; + // Gathers all the XML namespaces of child elements. + void PopulateNamespaces(); + bool Serialize(xmlDocPtr* xmlDoc); + + // Keep a reference to the XML namespaces, so that they are created only once + // when Device is constructed. + std::unordered_map<string, xmlNsPtr> namespaces_; + + std::unique_ptr<DeviceParams> params_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DEVICE_H_ // NOLINT diff --git a/includes/dynamic_depth/dynamic_depth.h b/includes/dynamic_depth/dynamic_depth.h new file mode 100644 index 0000000..40a0db7 --- /dev/null +++ b/includes/dynamic_depth/dynamic_depth.h @@ -0,0 +1,34 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT + +#include "dynamic_depth/device.h" +#include "xmpmeta/xmp_writer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Serialize a JPEG image, its Dynamic Depth metadata, and GContainer files +// if applicable. +bool WriteImageAndMetadataAndContainer(const string& out_filename, + const uint8_t* primary_image_bytes, + size_t primary_image_bytes_count, + Device* device); + +// Retrieves the contents of a Container:Item's associated file. The contents +// are populated into out_payload. +// As per the Dynamic Depth spec, file contents are base64-encoded if they're +// of an image/ mime type, and not if they're of a text/ mime type. Dynamic +// Depth elements that use Container:Item will haev handled this appropriately +// on item construction. +bool GetItemPayload(const string& input_image_filename, + const Container* container, const string& item_uri, + string* out_payload); + +// Convenience method for the aboove. +bool GetItemPayload(const string& input_image_filename, const Device* device, + const string& item_uri, string* out_payload); + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_DYNAMIC_DEPTH_H_ // NOLINT diff --git a/includes/dynamic_depth/earth_pose.h b/includes/dynamic_depth/earth_pose.h new file mode 100644 index 0000000..0487cdb --- /dev/null +++ b/includes/dynamic_depth/earth_pose.h @@ -0,0 +1,94 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Implements the EarthPose element in the Dynamic Depth specification, with +// serialization and deserialization. +class EarthPose : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("EarthPose", "http://ns.google.com/photos/dd/1.0/earthpose/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. Returns true on success. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a EarthPose from the given data. + // The order of values in position is latitude, longitude, altitude.. + // The order of values in orientation is the quaternion x, y, z, w fields. + // rotation angle in quaternion format. + // Position and orientation are in raw coordinates, and will be stored as + // normalied values. Please refer to the Dynamic Depth specification for the + // Realm coordinate system. + // At least one valid position or orientation must be provided. These + // arguments will be ignored if the vector is of the wrong size. + static std::unique_ptr<EarthPose> FromData( + const std::vector<double>& position, + const std::vector<float>& orientation, const int64 timestamp = -1); + + // Returns the deserialized XdmAudio; null if parsing fails. + // The returned pointer is owned by the caller. + static std::unique_ptr<EarthPose> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Returns true if the device's position is provided. + bool HasPosition() const; + + // Returns true if the device's orientation is provided. + bool HasOrientation() const; + + // Returns the device's position fields, or an empty vector if they are + // not present. + const std::vector<double>& GetPosition() const; + + // Returns the device's orientation fields, or an empty vector if they are + // not present. + const std::vector<float>& GetOrientation() const; + + // Timestamp. + int64 GetTimestamp() const; + + // Disallow copying. + EarthPose(const EarthPose&) = delete; + void operator=(const EarthPose&) = delete; + + private: + EarthPose(); + + // Extracts device pose fields. + bool ParseEarthPoseFields(const xml::Deserializer& deserializer); + + // Position variables, in meters relative to camera 0. + // If providing position data, all three fields must be set. + // Stored in normalized form. + // TODO(miraleung): Cleanup: consider std::optional for this and orientation_. + std::vector<double> position_; + + // Orientation variables. + // If providing orientation data, all four fields must be set. + // Stored in normalized form. + std::vector<float> orientation_; + + // Timestamp is Epoch time in milliseconds. + int64 timestamp_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_EARTH_POSE_H_ // NOLINT diff --git a/includes/dynamic_depth/image.h b/includes/dynamic_depth/image.h new file mode 100644 index 0000000..b944337 --- /dev/null +++ b/includes/dynamic_depth/image.h @@ -0,0 +1,89 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/item.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// The ItemSemantic of this Image. +enum class ImageItemSemantic { kPrimary = 1, kOriginal = 2 }; + +/** + * An Image element for a Dynamic Depth device. + */ +class Image : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("Image", "http://ns.google.com/photos/dd/1.0/image/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates an original (non-primary) Image from the given fields. Returns null + // if one of the following is true: + // - Mime field is empty. + // - Data field is null or empty. + // - Uri field is empty. + // - The items field is null. + // Param description: + // - Data is NOT base64-encoded. This method takes care of encoding when it + // is passed to the generated Item element. + // - Mime is the mimetype of the image data. + // - Uri is the case-sensitive Item:DataUri of this Image, and must be + // unique amongst all Dynamic Depth elements. This will be the only way + // that metadata parsers will be able to retrieve the image. + // Both data and mime are used to construct a new Container:Item, which will + // be the last one appended to items. + // It is the caller's responsibility to use items to construct a Container, + // and ensure that it is serialized along with this Image element. + static std::unique_ptr<Image> FromData( + const string& data, const string& mime, const string& item_uri, + std::vector<std::unique_ptr<Item>>* items); + + // Same as above, but more performant because it avoids an extra string copy. + static std::unique_ptr<Image> FromData( + const uint8_t* data, size_t data_size, const string& mime, + const string& item_uri, std::vector<std::unique_ptr<Item>>* items); + + // Image instantiator for the primary (container) image. + static std::unique_ptr<Image> FromDataForPrimaryImage( + const string& mime, std::vector<std::unique_ptr<Item>>* items); + + // Returns the deserialized Image; null if parsing fails. + static std::unique_ptr<Image> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + const string& GetItemUri() const; + ImageItemSemantic GetItemSemantic() const; + + // Disallow copying. + Image(const Image&) = delete; + void operator=(const Image&) = delete; + + private: + Image(); + + // Extracts image fields. + bool ParseImageFields(const xml::Deserializer& deserializer); + + string item_uri_; + ImageItemSemantic item_semantic_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGE_H_ // NOLINT diff --git a/includes/dynamic_depth/imaging_model.h b/includes/dynamic_depth/imaging_model.h new file mode 100644 index 0000000..1175a66 --- /dev/null +++ b/includes/dynamic_depth/imaging_model.h @@ -0,0 +1,90 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT + +#include <memory> +#include <unordered_map> + +#include "dynamic_depth/dimension.h" +#include "dynamic_depth/element.h" +#include "dynamic_depth/point.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { +struct ImagingModelParams { + // Required. The order of numbers is (x, y), in pixels. + Point<double> focal_length; + + // Required. The order of numbers is (width, height), in pixels. + Dimension image_size; + + // Optional. Set to (0.5, 0.5) if not present. + // The order of numbers is (x, y). + Point<double> principal_point; + + // Optional. + std::vector<float> distortion; // Distortion parameters. + double skew; + double pixel_aspect_ratio; + + ImagingModelParams(const Point<double>& focal_len, + const Dimension& image_size) + : focal_length(focal_len), + image_size(image_size), + principal_point(Point<double>(0.5, 0.5)), + distortion(std::vector<float>()), + skew(0), + pixel_aspect_ratio(1) {} + + inline bool operator==(const ImagingModelParams& other) const { + return focal_length == other.focal_length && + image_size == other.image_size && + principal_point == other.principal_point && + distortion == other.distortion && skew == other.skew && + pixel_aspect_ratio == other.pixel_aspect_ratio; + } + + inline bool operator!=(const ImagingModelParams& other) const { + return !(*this == other); + } +}; + +class ImagingModel : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates an ImagingModel from the given params. + static std::unique_ptr<ImagingModel> FromData( + const ImagingModelParams& params); + + // Returns the deserialized equirect model, null if parsing fails. + static std::unique_ptr<ImagingModel> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Getters. + Point<double> GetFocalLength() const; + Point<double> GetPrincipalPoint() const; + Dimension GetImageSize() const; + double GetSkew() const; + double GetPixelAspectRatio() const; + const std::vector<float>& GetDistortion() const; + int GetDistortionCount() const; + + // Disallow copying. + ImagingModel(const ImagingModel&) = delete; + void operator=(const ImagingModel&) = delete; + + private: + ImagingModel(const ImagingModelParams& params); + + ImagingModelParams params_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_IMAGING_MODEL_H_ // NOLINT diff --git a/includes/dynamic_depth/item.h b/includes/dynamic_depth/item.h new file mode 100644 index 0000000..d1fcd36 --- /dev/null +++ b/includes/dynamic_depth/item.h @@ -0,0 +1,89 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/dimension.h" +#include "dynamic_depth/element.h" +#include "dynamic_depth/point.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { +struct ItemParams { + // Required fields. + string mime; // Must not be empty. + unsigned int length; // Must not be zero. + + // Optional. + unsigned int padding; + string data_uri; + + // Only for final file serialization - not used in XMP metadata I/O. + // IMPORTANT: Callers should enforce that this file exists. + // TODO(miraleung): This could be stored as char* to optimize performance. + string payload_to_serialize; + + ItemParams(const string& in_mime, unsigned int len) + : mime(in_mime), + length(len), + padding(0), + data_uri(""), + payload_to_serialize("") {} + ItemParams(const string& in_mime, unsigned int len, const string& uri) + : mime(in_mime), + length(len), + padding(0), + data_uri(uri), + payload_to_serialize("") {} + + inline bool operator==(const ItemParams& other) const { + return mime == other.mime && length == other.length && + padding == other.padding && data_uri == other.data_uri && + payload_to_serialize == other.payload_to_serialize; + } + + inline bool operator!=(const ItemParams& other) const { + return !(*this == other); + } +}; + +class Item : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + static std::unique_ptr<Item> FromData(const ItemParams& params); + + // Returns the deserialized item elements, null if parsing failed for all + // items. + static std::unique_ptr<Item> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + const string& GetMime() const; + unsigned int GetLength() const; + const string& GetDataUri() const; + unsigned int GetPadding() const; + const string& GetPayloadToSerialize() const; + + // Disallow copying. + Item(const Item&) = delete; + void operator=(const Item&) = delete; + + private: + Item(const ItemParams& params); + static std::unique_ptr<Item> FromDataInternal(const ItemParams& params, + bool check_filepath); + + ItemParams params_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_ITEM_H_ // NOLINT diff --git a/includes/dynamic_depth/light_estimate.h b/includes/dynamic_depth/light_estimate.h new file mode 100644 index 0000000..51cbde2 --- /dev/null +++ b/includes/dynamic_depth/light_estimate.h @@ -0,0 +1,68 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Light estimation parameters for a camera. +// This is stored as a sibling element to LightEstimate because it may apply to +// the container image, so the decoupling is required. +class LightEstimate : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("LightEstimate", "http://ns.google.com/photos/dd/1.0/image/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates an LightEstimate from the given field. + static std::unique_ptr<LightEstimate> FromData(float pixel_intensity); + + // Takes the first three values from color_correction if the vector length is + // greater than 3. + // Color correction values should be between 0 and 1 (plus or minus 0.2). + static std::unique_ptr<LightEstimate> FromData( + float pixel_intensity, const std::vector<float>& color_correction); + + // Returns the deserialized LightEstimate; null if parsing fails. + static std::unique_ptr<LightEstimate> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Returns the average pixel internsity. + float GetPixelIntensity() const; + const std::vector<float>& GetColorCorrection() const; + + // Disallow copying. + LightEstimate(const LightEstimate&) = delete; + void operator=(const LightEstimate&) = delete; + + private: + LightEstimate(); + + float pixel_intensity_ = 1.0f; + + // Optional, either all three together or none at all. + // A size-3 vector of color correction values, in the order R, G, B. + // Values can be approximately between 0 and 1 (plus or minus 0.2). + // On reading back image metadata, if only one or two of the values are + // present, then the defaults below are used. + std::vector<float> color_correction_ = {1.0f, 1.0f, 1.0f}; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_LIGHT_ESTIMATE_H_ // NOLINT diff --git a/includes/dynamic_depth/plane.h b/includes/dynamic_depth/plane.h new file mode 100644 index 0000000..bec2289 --- /dev/null +++ b/includes/dynamic_depth/plane.h @@ -0,0 +1,93 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/pose.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// A Plane element for a Dynamic Depth device. +// Only horizontal planes are currently supported. +class Plane : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("Plane", "http://ns.google.com/photos/dd/1.0/plane/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. + bool Serialize(xml::Serializer* serializer) const override; + + // Returns the deserialized Plane; null if parsing fails. + static std::unique_ptr<Plane> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Creates a Plane from the given fields. + // The Pose must be present. + // boundary contains the bounding vertices: [x0, z0, x1, z1, ..., xn, zn]. + // extent_x is the length of the plane on the X axis. + // extent_z is the length of the plane on the Z axis. + static std::unique_ptr<Plane> FromData(std::unique_ptr<Pose> pose, + const std::vector<float>& boundary, + const double extent_x, + const double extent_z); + + // Getters. + const Pose* GetPose() const; + + // Returns the plane's bounding vertices on the XZ plane. + const std::vector<float>& GetBoundary() const; + + // Returns the number of vertices in the boundary polygon. + int GetBoundaryVertexCount() const; + + // Returns the plane's length along the X axis. + const double GetExtentX() const; + + // Returns the plane's length along the Z axis. + const double GetExtentZ() const; + + // Disallow copying. + Plane(const Plane&) = delete; + void operator=(const Plane&) = delete; + + private: + Plane(); + + // Extracts plane fields. + bool ParsePlaneFields(const xml::Deserializer& deserializer); + + // The pose of the center of this plane. + std::unique_ptr<Pose> pose_; + + // Contains the plane's bounding vertices: [x0, z0, x1, z1, ..., xn, zn]. + // Optional field, set to an empty vector if not present. + std::vector<float> boundary_; + + // Required field if "Boundary" is present. Optional otherwise and + // expected to be zero; set to 0 if not present. + int boundary_vertex_count_; + + // The length of the plane on the X axis. A value of -1 represents infinity. + // Optional field, set to -1 (infinity) if not present. + double extent_x_; + + // The length of the plane on the Z axis. A value of -1 represents infinity. + // Optional field, set to -1 (infinity) if not present. + double extent_z_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANE_H_ // NOLINT diff --git a/includes/dynamic_depth/planes.h b/includes/dynamic_depth/planes.h new file mode 100644 index 0000000..c38aad4 --- /dev/null +++ b/includes/dynamic_depth/planes.h @@ -0,0 +1,61 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/plane.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// THe list of planes in a Dynamic Depth Device t ype. +class Planes : public Element { + public: + // Creates this object from the given planes. Returns null if the list is + // empty, null, or contains null elements. + // If creation succeeds, ownership of the Plane objects are transferred to + // the resulting Planes object. The pointer to the vector of Plane objects + // will be replaced with the plane_list_, whose contents are not necessarily + // defined. + static std::unique_ptr<Planes> FromPlaneArray( + std::vector<std::unique_ptr<Plane>>* plane_list); + + // Returns the deserialized cameras in a Planes object, null if parsing + // failed for all the planes, one of the planes is null, or the list of + // planes was empty. + static std::unique_ptr<Planes> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Disallow copying. + Planes(const Planes&) = delete; + Plane& operator=(const Planes&) = delete; + + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Returns false if the list of planes is empty, or serialization fails. + bool Serialize(xml::Serializer* serializer) const override; + + // Returns the number of plane elements in this Plane object. + int GetPlaneCount() const; + + // Returns the ith plane, which is guaranteed not to be null because + // FromPlaneArray or FromDeserializer would have failed to construct a valid + // Planes object. + const Plane* GetPlaneAt(int i) const; + + private: + Planes(); + + std::vector<std::unique_ptr<Plane>> plane_list_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PLANES_H_ // NOLINT diff --git a/includes/dynamic_depth/point_cloud.h b/includes/dynamic_depth/point_cloud.h new file mode 100644 index 0000000..3e53744 --- /dev/null +++ b/includes/dynamic_depth/point_cloud.h @@ -0,0 +1,61 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +// Implements the Point Cloud element from the Dynamic Depth specification, with +// serialization and deserialization. +namespace photos_editing_formats { +namespace dynamic_depth { + +class PointCloud : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a Point Cloud from the given fields. Returns null if position is + // empty or points.size() is not divisible by 3. + // The first two arguments are required fields, the rest are optional. + // points is a list of (x, y, z) tuples, so it must have a size that is + // evenly divisible by 3. + static std::unique_ptr<PointCloud> FromData(const std::vector<float>& points, + bool metric); + + // Returns the deserialized PointCloud; null if parsing fails. + // The returned pointer is owned by the caller. + static std::unique_ptr<PointCloud> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Getters. + // Returns the number of (x, y, z) tuples, *not* the length of points_. + int GetPointCount() const; + const std::vector<float>& GetPoints() const; + bool GetMetric() const; + + PointCloud(const PointCloud&) = delete; + void operator=(const PointCloud&) = delete; + + private: + PointCloud(); + + bool ParseFields(const xml::Deserializer& deserializer); + + // Required fields. + std::vector<float> points_; + + // Optional fields. + bool metric_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POINT_CLOUD_H_ // NOLINT diff --git a/includes/dynamic_depth/pose.h b/includes/dynamic_depth/pose.h new file mode 100644 index 0000000..9bee030 --- /dev/null +++ b/includes/dynamic_depth/pose.h @@ -0,0 +1,98 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +/** + * Implements the Pose element in the Dynamic Depth specification, with + * serialization and deserialization. + * See xdm.org. + */ +class Pose : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("Pose", "http://ns.google.com/photos/dd/1.0/pose/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. Returns true on success. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a Pose from the given data. + // The order of values in position is x, y, z. + // The order of values in orientation is the quaternion x, y, z, w fields. + // This is assusmed to be un-normalized. + // Position coordinates are relative to Camera 0, and must be 0 for Camera 0. + // Position and orientation are in raw coordinates, and will be stored as + // normalied values. + // At least one valid position or orientation must be provided. These + // arguments will be ignored if the vector is of the wrong size. + static std::unique_ptr<Pose> FromData(const std::vector<float>& position, + const std::vector<float>& orientation, + const int64 timestamp = -1); + + // Returns the deserialized XdmAudio; null if parsing fails. + // The returned pointer is owned by the caller. + static std::unique_ptr<Pose> FromDeserializer( + const xml::Deserializer& parent_deserializer, + const char* parent_namespace); + + // Returns true if the device's position is provided. + bool HasPosition() const; + + // Returns true if the device's orientation is provided. + bool HasOrientation() const; + + // Returns the device's position fields, or an empty vector if they are + // not present. + const std::vector<float>& GetPosition() const; + + // Returns the device's orientation fields, or an empty vector if they are + // not present. + const std::vector<float>& GetOrientation() const; + + // Timestamp. + int64 GetTimestamp() const; + + // Disallow copying. + Pose(const Pose&) = delete; + void operator=(const Pose&) = delete; + + private: + Pose(); + + // Extracts camera pose fields. + bool ParsePoseFields(const xml::Deserializer& deserializer); + + // Position variables, in meters relative to camera 0. + // If providing position data, all three fields must be set. + // Stored in normalized form. + // TODO(miraleung): Cleanup: consider std::optional for this and orientation_. + std::vector<float> position_; + + // Orientation variables. + // If providing orientation data, all four fields must be set. + // Stored in normalized form. + std::vector<float> orientation_; + + // Timestamp is Epoch time in milliseconds. + int64 timestamp_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_POSE_H_ // NOLINT diff --git a/includes/dynamic_depth/profile.h b/includes/dynamic_depth/profile.h new file mode 100644 index 0000000..b1a311c --- /dev/null +++ b/includes/dynamic_depth/profile.h @@ -0,0 +1,56 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Implements the Profile element in the Dynamic Depth specification, with +// serialization and deserialization. +class Profile : public Element { + public: + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Creates a Profile element from the given fields. Returns null if + // the type is empty, or if the camera_indices are shorter than the + // specified minimum length for types supported in the Dynamic Depth + // specification. Type is case-sensitive. If wrong, this will be treated as an + // unsupported type. + static std::unique_ptr<Profile> FromData( + const string& type, const std::vector<int>& camera_indices); + + // Returns the deserialized Profile, null if parsing fails. + static std::unique_ptr<Profile> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Returns the Profile type. + const string& GetType() const; + + // Returns the camera indices. + const std::vector<int>& GetCameraIndices() const; + + // Disallow copying. + Profile(const Profile&) = delete; + void operator=(const Profile&) = delete; + + private: + Profile(const string& type, const std::vector<int>& camera_indices); + + string type_; + std::vector<int> camera_indices_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILE_H_ // NOLINT diff --git a/includes/dynamic_depth/profiles.h b/includes/dynamic_depth/profiles.h new file mode 100644 index 0000000..abcf6c1 --- /dev/null +++ b/includes/dynamic_depth/profiles.h @@ -0,0 +1,56 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "dynamic_depth/profile.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +// Implements the Device:Profiles field from the Dynamic Depth specification, +// with serialization and deserialization for its child Profile elements. +class Profiles : public Element { + public: + // Interface methods. + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + bool Serialize(xml::Serializer* serializer) const override; + + // Static methods. + + // Creates this object from the given profiles. If the list is empty, returns + // a unique_ptr owning nothing. + static std::unique_ptr<Profiles> FromProfileArray( + std::vector<std::unique_ptr<Profile>>* profile_list); + + // Returns the deserialized profiles in a Profiles object, a unique_ptr owning + // nothing if parsing failed for all the profiles. + static std::unique_ptr<Profiles> FromDeserializer( + const xml::Deserializer& parent_deserializer); + + // Non-static methods. + + // Returns the list of cameras. + const std::vector<const Profile*> GetProfiles() const; + + // Disallow copying + Profiles(const Profiles&) = delete; + void operator=(const Profiles&) = delete; + + private: + Profiles() = default; + + std::vector<std::unique_ptr<Profile>> profile_list_; +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_PROFILES_H_ // NOLINT diff --git a/includes/dynamic_depth/vendor_info.h b/includes/dynamic_depth/vendor_info.h new file mode 100644 index 0000000..ba8ca48 --- /dev/null +++ b/includes/dynamic_depth/vendor_info.h @@ -0,0 +1,74 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT + +#include <memory> +#include <string> +#include <unordered_map> + +#include "dynamic_depth/element.h" +#include "xmpmeta/xml/deserializer.h" +#include "xmpmeta/xml/serializer.h" + +namespace photos_editing_formats { +namespace dynamic_depth { + +/** + * A VendorInfo element for a Dynamic Depth device. + */ +class VendorInfo : public Element { + public: + // Appends child elements' namespaces' and their respective hrefs to the + // given collection, and any parent nodes' names to prefix_names. + // Key: Name of the namespace. + // Value: Full namespace URL. + // Example: ("VendorInfo", "http://ns.google.com/photos/dd/1.0/vendorinfo/") + void GetNamespaces( + std::unordered_map<string, string>* ns_name_href_map) override; + + // Serializes this object. + bool Serialize(xml::Serializer* serializer) const override; + + // Creates an VendorInfo from the given fields. Returns null if + // any of the required fields is empty (see fields below). + // Manufacturer is the manufacturer of the element that created the content. + // Model is the model of the element that created the content. + // Notes is general comments. + static std::unique_ptr<VendorInfo> FromData(const string& manufacturer, + const string& model, + const string& notes); + + // Returns the deserialized VendorInfo; null if parsing fails. + static std::unique_ptr<VendorInfo> FromDeserializer( + const xml::Deserializer& parent_deserializer, + const string& namespace_str); + + // Returns the Manufacturer. + const string& GetManufacturer() const; + + // Returns the Model. + const string& GetModel() const; + + // Returns the Notes. + const string& GetNotes() const; + + // Disallow copying. + VendorInfo(const VendorInfo&) = delete; + void operator=(const VendorInfo&) = delete; + + private: + VendorInfo(); + + bool ParseFields(const xml::Deserializer& deserializer); + + // Required field. + string manufacturer_; // The manufacturer. + + // Optional fields. + string model_; // The model. + string notes_; // The notes. +}; + +} // namespace dynamic_depth +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VENDOR_INFO_H_ // NOLINT diff --git a/includes/dynamic_depth/version.h b/includes/dynamic_depth/version.h new file mode 100644 index 0000000..6193b1a --- /dev/null +++ b/includes/dynamic_depth/version.h @@ -0,0 +1,20 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT + +#define DYNAMIC_DEPTH_VERSION_MAJOR 1 +#define DYNAMIC_DEPTH_VERSION_MINOR 0 +#define DYNAMIC_DEPTH_VERSION_REVISION 0 + +// Classic CPP stringifcation; the extra level of indirection allows the +// preprocessor to expand the macro before being converted to a string. +#define DYNAMIC_DEPTH_TO_STRING_HELPER(x) #x +#define DYNAMIC_DEPTH_TO_STRING(x) DYNAMIC_DEPTH_TO_STRING_HELPER(x) + +// The xdmlib version as a string; for example "1.9.0". +#define DYNAMIC_DEPTH_VERSION_STRING \ + DYNAMIC_DEPTH_TO_STRING(DYNAMIC_DEPTH_VERSION_MAJOR) \ + "." DYNAMIC_DEPTH_TO_STRING( \ + DYNAMIC_DEPTH_VERSION_MINOR) "." \ + DYNAMIC_DEPTH_TO_STRING(DYNAMIC_DEPTH_VERSION_REVISION) + +#endif // DYNAMIC_DEPTH_INCLUDES_DYNAMIC_DEPTH_VERSION_H_ // NOLINT diff --git a/includes/xmpmeta/jpeg_io.h b/includes/xmpmeta/jpeg_io.h new file mode 100644 index 0000000..aba2b5a --- /dev/null +++ b/includes/xmpmeta/jpeg_io.h @@ -0,0 +1,53 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT + +#include <string> +#include <vector> + +#include "base/port.h" + +namespace photos_editing_formats { + +// Contains the data for a section in a JPEG file. +// A JPEG file contains many sections in addition to image data. +struct Section { + // Constructors. + Section() = default; + explicit Section(const string& buffer); + + // Returns true if the section's marker matches an APP1 marker. + bool IsMarkerApp1(); + + int marker; + bool is_image_section; + string data; +}; + +struct ParseOptions { + // If set to true, keeps only the EXIF and XMP sections (with + // marker kApp1) and ignores others. Otherwise, keeps everything including + // image data. + bool read_meta_only = false; + + // If section_header is set, this boolean controls whether only the 1st + // section matching the section_header will be returned. If not set + // (the default), all the sections that math the section header will be + // returned. + bool section_header_return_first = false; + + // A filter that keeps all the sections whose data starts with the + // given string. Ignored if empty. + string section_header; +}; + +// Parses the JPEG image file. +std::vector<Section> Parse(const ParseOptions& options, + std::istream* input_stream); + +// Writes JPEG data sections to a file. +void WriteSections(const std::vector<Section>& sections, + std::ostream* output_stream); + +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_JPEG_IO_H_ // NOLINT diff --git a/includes/xmpmeta/md5.h b/includes/xmpmeta/md5.h new file mode 100644 index 0000000..a3e8a7f --- /dev/null +++ b/includes/xmpmeta/md5.h @@ -0,0 +1,16 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT + +#include <string> + +#include "base/port.h" + +namespace photos_editing_formats { + +// Returns the MD5 hash of to_hash as a 32-character hex string. +// Wrapper around OpenSSL to avoid Gyp dependency problems. +string MD5Hash(const string& to_hash); + +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_MD5_H_ // NOLINT diff --git a/includes/xmpmeta/version.h b/includes/xmpmeta/version.h new file mode 100644 index 0000000..91198c4 --- /dev/null +++ b/includes/xmpmeta/version.h @@ -0,0 +1,26 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT + +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MAJOR 1 // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MINOR 0 // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_REVISION 0 // NOLINT + +// Classic CPP stringifcation; the extra level of indirection allows the +// preprocessor to expand the macro before being converted to a string. +#define \ + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING_HELPER( \ + x) \ +#x +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING(x) \ // NOLINT + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING_HELPER(x) + +// The Dynamic Depth version as a string; for example "1.9.0". +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_STRING \ // NOLINT + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \ + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MAJOR) \ + "." PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \ + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_MINOR) \ + "." PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_TO_STRING( \ + PHOTOS_EDITING_FORMATS_DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_REVISION) + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_VERSION_H_ // NOLINT diff --git a/includes/xmpmeta/xmp_const.h b/includes/xmpmeta/xmp_const.h new file mode 100644 index 0000000..c32ecd9 --- /dev/null +++ b/includes/xmpmeta/xmp_const.h @@ -0,0 +1,30 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT + +namespace photos_editing_formats { + +// Constants used in writing XMP metadata. +struct XmpConst { + // XMP namespaces. + static const char* Namespace(); + static const char* NamespacePrefix(); + static const char* NodeName(); + static const char* AdobePropName(); + static const char* AdobePropValue(); + static const char* NoteNamespace(); + + // XMP headers. + static const char* Header(); + static const char* ExtensionHeader(); + static const char* HasExtensionPrefix(); + static const char* HasExtension(); + + // Sizes. + static const int ExtensionHeaderOffset(); + static const int MaxBufferSize(); + static const int ExtendedMaxBufferSize(); +}; + +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_CONST_H_ // NOLINT diff --git a/includes/xmpmeta/xmp_data.h b/includes/xmpmeta/xmp_data.h new file mode 100644 index 0000000..1e59b1d --- /dev/null +++ b/includes/xmpmeta/xmp_data.h @@ -0,0 +1,34 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT + +#include <libxml/tree.h> + +namespace photos_editing_formats { + +// XmpData contains the standard, and optionally extended, XMP metadata from a +// JPEG file. See xmp_parser for reading XmpData from a JPEG or reading +// attributes from XmpData. +class XmpData { + public: + XmpData(); + ~XmpData(); + + // Frees any allocated resources and resets the xmlDocPtrs to null. + void Reset(); + + // The standard XMP section. + const xmlDocPtr StandardSection() const; + xmlDocPtr* MutableStandardSection(); + + // The extended XMP section. + const xmlDocPtr ExtendedSection() const; + xmlDocPtr* MutableExtendedSection(); + + private: + xmlDocPtr xmp_; + xmlDocPtr xmp_extended_; +}; + +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_DATA_H_ // NOLINT diff --git a/includes/xmpmeta/xmp_parser.h b/includes/xmpmeta/xmp_parser.h new file mode 100644 index 0000000..e2781ec --- /dev/null +++ b/includes/xmpmeta/xmp_parser.h @@ -0,0 +1,27 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT + +#include <fstream> +#include <string> + +#include "base/port.h" +#include "xmpmeta/xmp_data.h" + +namespace photos_editing_formats { + +// Populates a XmpData from the header of the JPEG file. +bool ReadXmpHeader(const string& filename, bool skip_extended, + XmpData* xmp_data); + +// Populates a XmpData from the header of JPEG file that has already been read +// into memory. +bool ReadXmpFromMemory(const string& jpeg_contents, bool skip_extended, + XmpData* xmp_data); + +// Populates a XmpData from the header of the given stream (stream data is +// in JPEG format). +bool ReadXmpHeader(std::istream* input_stream, bool skip_extended, + XmpData* xmp_data); +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_PARSER_H_ // NOLINT diff --git a/includes/xmpmeta/xmp_writer.h b/includes/xmpmeta/xmp_writer.h new file mode 100644 index 0000000..e2ea8f7 --- /dev/null +++ b/includes/xmpmeta/xmp_writer.h @@ -0,0 +1,43 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT + +#include <iostream> +#include <memory> +#include <string> + +#include "base/port.h" +#include "xmpmeta/xmp_data.h" + +namespace photos_editing_formats { + +// Creates a new XmpData object and initializes the boilerplate for the +// standard XMP section. +// The extended section is initialized only if create_extended is true. +std::unique_ptr<XmpData> CreateXmpData(bool create_extended); + +// Writes XMP data to an existing JPEG image file. +// This is equivalent to writeXMPMeta in geo/lightfield/metadata/XmpUtil.java. +// If the extended section is not null, this will modify the given XmpData by +// setting a property in the standard section that links it with the +// extended section. +bool WriteLeftEyeAndXmpMeta(const string& left_data, const string& filename, + const XmpData& xmp_data); + +// Same as above, but allows the caller to manage their own istream and ostream. +// filename is written to only if metadata serialization is successful. +// Assumes the caller will take care of opening and closing the +// output_jpeg_stream, as well as initialization of the input_jpeg_stream. +bool WriteLeftEyeAndXmpMeta(const string& filename, const XmpData& xmp_data, + std::istringstream* input_jpeg_stream, + std::ofstream* output_jpeg_stream); + +// Updates a JPEG input stream with new XMP data and writes it to an +// output stream. +// This is equivalent to writeXMPMeta in geo/lightfield/metadata/XmpUtil.java. +bool AddXmpMetaToJpegStream(std::istream* input_jpeg_stream, + const XmpData& xmp_data, + std::ostream* output_jpeg_stream); + +} // namespace photos_editing_formats + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMP_WRITER_H_ // NOLINT diff --git a/includes/xmpmeta/xmpmeta.h b/includes/xmpmeta/xmpmeta.h new file mode 100644 index 0000000..d27a854 --- /dev/null +++ b/includes/xmpmeta/xmpmeta.h @@ -0,0 +1,11 @@ +#ifndef DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT +#define DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT + +#include "xmpmeta/base64.h" +#include "xmpmeta/md5.h" +#include "xmpmeta/xmp_const.h" +#include "xmpmeta/xmp_data.h" +#include "xmpmeta/xmp_parser.h" +#include "xmpmeta/xmp_writer.h" + +#endif // DYNAMIC_DEPTH_INCLUDES_XMPMETA_XMPMETA_H_ // NOLINT |