diff --git a/colour_hdri/graph/GraphRawProcessingCameraSensitivities.png b/colour_hdri/graph/GraphRawProcessingCameraSensitivities.png new file mode 100644 index 0000000..9fa4de3 Binary files /dev/null and b/colour_hdri/graph/GraphRawProcessingCameraSensitivities.png differ diff --git a/colour_hdri/graph/RawProcessingGraphDNG.png b/colour_hdri/graph/GraphRawProcessingDNG.png similarity index 100% rename from colour_hdri/graph/RawProcessingGraphDNG.png rename to colour_hdri/graph/GraphRawProcessingDNG.png diff --git a/colour_hdri/graph/RawProcessingGraphSensitivities.png b/colour_hdri/graph/RawProcessingGraphSensitivities.png deleted file mode 100644 index 9320832..0000000 Binary files a/colour_hdri/graph/RawProcessingGraphSensitivities.png and /dev/null differ diff --git a/colour_hdri/graph/conversion.py b/colour_hdri/graph/conversion.py index bec40d2..f71b768 100644 --- a/colour_hdri/graph/conversion.py +++ b/colour_hdri/graph/conversion.py @@ -3,6 +3,8 @@ import json import logging import os +import sys +import time from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Set @@ -121,12 +123,7 @@ def __init__( @property def node(self) -> Node: """ - Getter and setter property for the port node. - - Parameters - ---------- - value - Value to set the port node with. + Getter property for the port node. Returns ------- @@ -663,7 +660,53 @@ def process(self) -> None: self.dirty = False -class NodeReadDngFileMetadata(Node): +class NodeProcessingMetadata(Node): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.add_input_port("input_metadata") + self.add_input_port("namespace", "colour-science/colour-hdri/processing") + self.add_input_port("input_transform", InputTransform()) + self.add_input_port("orientation") + self.add_input_port("type", "sdr") + self.add_input_port("sources") + self.add_output_port("output_metadata") + + @property + def description(cls) -> str: + return "Add processing metadata to the input metadata." + + def process(self) -> None: + input_metadata = self.get_input("input_metadata") + if input_metadata is None: + input_metadata = {} + + sources = self.get_input("sources") + if isinstance(sources, str): + sources = [sources] + + processing_metadata = { + self.get_input("namespace"): { + "type": self.get_input("type"), + "sources": sources, + "input_transform": { + "M": self.get_input("input_transform").M, + "RGB_w": self.get_input("input_transform").RGB_w, + }, + "orientation": self.get_input("orientation"), + "script": sys.argv[0] if len(sys.argv) else __file__, + "time": time.strftime("%Y%m%d-%H%M%S", time.gmtime()), + } + } + + output_metadata = dict(**input_metadata, **processing_metadata) + + self.set_output("output_metadata", output_metadata) + + self.dirty = False + + +class NodeReadFileMetadataDNG(Node): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -1087,17 +1130,51 @@ def process(self) -> None: self.dirty = False -class RawProcessingGraphDNG: +class MixinGraph: + @property + def graph(self) -> Node: + """ + Getter property for the graph. + + Returns + ------- + :class:`Graph` + Graph. + """ + + return self._graph + + @property + def nodes(self) -> List[Node]: + """ + Getter property for the nodes. + + Returns + ------- + :class:`list` + Nodes. + """ + + return self._nodes + + def visualise(self): + return self._graph.visualise() + + +class GraphRawProcessingDNG(MixinGraph): def __init__(self): + super().__init__() + self._graph = Graph() self._nodes = { "convert_raw_file_to_dng_file": NodeConvertRawFileToDNGFile(), - "read_dng_file_metadata": NodeReadDngFileMetadata(), + "read_file_metadata_dng": NodeReadFileMetadataDNG(), "compute_input_transform_dng": NodeComputeInputTransformDNG(), "process_raw_file": NodeProcessRawFile(), "downsample": NodeDownsample(), "apply_input_transform_dng": NodeApplyInputTransformDNG(), + "processing_metadata": NodeProcessingMetadata(), "write_image": NodeWriteImage(), } @@ -1107,14 +1184,14 @@ def __init__(self): for connection in [ ( ("convert_raw_file_to_dng_file", "dng_file_path"), - ("read_dng_file_metadata", "dng_file_path"), + ("read_file_metadata_dng", "dng_file_path"), ), ( - ("read_dng_file_metadata", "metadata"), + ("read_file_metadata_dng", "metadata"), ("compute_input_transform_dng", "metadata"), ), ( - ("read_dng_file_metadata", "metadata"), + ("read_file_metadata_dng", "metadata"), ("compute_input_transform_dng", "metadata"), ), ( @@ -1137,12 +1214,20 @@ def __init__(self): ("compute_input_transform_dng", "input_transform"), ("apply_input_transform_dng", "input_transform"), ), + ( + ("read_file_metadata_dng", "metadata"), + ("processing_metadata", "input_metadata"), + ), + ( + ("convert_raw_file_to_dng_file", "dng_file_path"), + ("processing_metadata", "sources"), + ), ( ("apply_input_transform_dng", "output_image"), ("write_image", "image"), ), ( - ("read_dng_file_metadata", "metadata"), + ("processing_metadata", "output_metadata"), ("write_image", "metadata"), ), ]: @@ -1156,9 +1241,7 @@ def __init__(self): def process(self, raw_file_path, output_file_path): if ( - self._nodes["convert_raw_file_to_dng_file"] - .input_ports["raw_file_path"] - .value + self._nodes["convert_raw_file_to_dng_file"].get_input("raw_file_path") != raw_file_path ): self._nodes["convert_raw_file_to_dng_file"].set_input( @@ -1166,35 +1249,33 @@ def process(self, raw_file_path, output_file_path): ) if ( - self._nodes["convert_raw_file_to_dng_file"] - .input_ports["output_directory"] - .value + self._nodes["convert_raw_file_to_dng_file"].get_input("output_directory") != output_directory ): self._nodes["convert_raw_file_to_dng_file"].set_input( "output_directory", output_directory ) - if self._nodes["write_image"].input_ports["path"].value != output_file_path: + if self._nodes["write_image"].get_input("path") != output_file_path: self._nodes["write_image"].set_input("path", output_file_path) self._graph.process() - def visualise(self): - return self._graph.visualise() - -class RawProcessingGraphCameraSensitivities: +class GraphRawProcessingCameraSensitivities(MixinGraph): def __init__(self): + super().__init__() + self._graph = Graph() self._nodes = { "convert_raw_file_to_dng_file": NodeConvertRawFileToDNGFile(), - "read_dng_file_metadata": NodeReadDngFileMetadata(), + "read_file_metadata_dng": NodeReadFileMetadataDNG(), "compute_input_transform_camera_sensitivities": NodeComputeInputTransformCameraSensitivities(), "process_raw_file": NodeProcessRawFile(), "downsample": NodeDownsample(), "apply_input_transform_camera_sensitivities": NodeApplyInputTransformCameraSensitivities(), + "processing_metadata": NodeProcessingMetadata(), "write_image": NodeWriteImage(), } @@ -1204,14 +1285,14 @@ def __init__(self): for connection in [ ( ("convert_raw_file_to_dng_file", "dng_file_path"), - ("read_dng_file_metadata", "dng_file_path"), + ("read_file_metadata_dng", "dng_file_path"), ), ( - ("read_dng_file_metadata", "metadata"), + ("read_file_metadata_dng", "metadata"), ("compute_input_transform_camera_sensitivities", "metadata"), ), ( - ("read_dng_file_metadata", "metadata"), + ("read_file_metadata_dng", "metadata"), ("compute_input_transform_camera_sensitivities", "metadata"), ), ( @@ -1234,12 +1315,16 @@ def __init__(self): ("compute_input_transform_camera_sensitivities", "input_transform"), ("apply_input_transform_camera_sensitivities", "input_transform"), ), + ( + ("read_file_metadata_dng", "metadata"), + ("processing_metadata", "input_metadata"), + ), ( ("apply_input_transform_camera_sensitivities", "output_image"), ("write_image", "image"), ), ( - ("read_dng_file_metadata", "metadata"), + ("processing_metadata", "output_metadata"), ("write_image", "metadata"), ), ]: @@ -1253,9 +1338,7 @@ def __init__(self): def process(self, raw_file_path, output_file_path, camera_sensitivities): if ( - self._nodes["convert_raw_file_to_dng_file"] - .input_ports["raw_file_path"] - .value + self._nodes["convert_raw_file_to_dng_file"].get_input("raw_file_path") != raw_file_path ): self._nodes["convert_raw_file_to_dng_file"].set_input( @@ -1263,9 +1346,7 @@ def process(self, raw_file_path, output_file_path, camera_sensitivities): ) if ( - self._nodes["convert_raw_file_to_dng_file"] - .input_ports["output_directory"] - .value + self._nodes["convert_raw_file_to_dng_file"].get_input("output_directory") != output_directory ): self._nodes["convert_raw_file_to_dng_file"].set_input( @@ -1273,22 +1354,82 @@ def process(self, raw_file_path, output_file_path, camera_sensitivities): ) if ( - self._nodes["compute_input_transform_camera_sensitivities"] - .input_ports["camera_sensitivities"] - .value + self._nodes["compute_input_transform_camera_sensitivities"].get_input( + "camera_sensitivities" + ) != camera_sensitivities ): self._nodes["compute_input_transform_camera_sensitivities"].set_input( "camera_sensitivities", camera_sensitivities ) - if self._nodes["write_image"].input_ports["path"].value != output_file_path: + if self._nodes["write_image"].get_input("path") != output_file_path: self._nodes["write_image"].set_input("path", output_file_path) self._graph.process() - def visualise(self): - return self._graph.visualise() + +class GraphMergeHDR(MixinGraph): + def __init__(self): + super().__init__() + + self._graph = Graph() + + self._nodes = { + "create_image_stack": NodeCreateImageStack(), + "node_merge_image_stack": NodeMergeImageStack(), + "downsample": NodeDownsample(), + "processing_metadata": NodeProcessingMetadata(), + "write_image": NodeWriteImage(), + } + + for node in self._nodes.values(): + self._graph.add_node(node) + + for connection in [ + ( + ("create_image_stack", "image_stack"), + ("node_merge_image_stack", "image_stack"), + ), + ( + ("node_merge_image_stack", "image"), + ("downsample", "input_image"), + ), + ( + ("processing_metadata", "output_metadata"), + ("write_image", "metadata"), + ), + ( + ("downsample", "output_image"), + ("write_image", "image"), + ), + ]: + (input_node, input_port), (output_node, output_port) = connection + self._graph.connect( + self._nodes[input_node], + input_port, + self._nodes[output_node], + output_port, + ) + + def process(self, exr_file_paths, output_file_path, metadata): + if self._nodes["create_image_stack"].get_input("paths") != exr_file_paths: + self._nodes["create_image_stack"].set_input("paths", exr_file_paths) + + if self._nodes["processing_metadata"].get_input("input_metadata") != dict( + metadata + ): + self._nodes["processing_metadata"].set_input("input_metadata", metadata) + + if self._nodes["processing_metadata"].get_input("sources") != exr_file_paths: + self._nodes["processing_metadata"].set_input("sources", exr_file_paths) + + self._nodes["processing_metadata"].set_input("type", "hdr") + + if self._nodes["write_image"].get_input("path") != output_file_path: + self._nodes["write_image"].set_input("path", output_file_path) + + self._graph.process() # TODO: @@ -1331,8 +1472,8 @@ def visualise(self): if test: LOGGER.info("*" * 79) LOGGER.info("1") - graph = RawProcessingGraphDNG() - graph.visualise().draw("RawProcessingGraphDNG.png", prog="dot") + graph = GraphRawProcessingDNG() + graph.visualise().draw("GraphRawProcessingDNG.png", prog="dot") raw_file = os.path.join(output_directory, "IMG_2599.CR2") output_path = os.path.join(output_directory, "IMG_2599_DNG.exr") graph.process(raw_file, output_path) @@ -1347,48 +1488,51 @@ def visualise(self): output_path = os.path.join(output_directory, "IMG_2601_DNG.exr") graph.process(raw_file, output_path) - LOGGER.info("*" * 79) - LOGGER.info("4") - import colour_datasets + LOGGER.info("*" * 79) + LOGGER.info("4") + import colour_datasets - camera_sensitivities = CanonicalMapping( - { - slugify(key.replace("_", " ")): value - for key, value in colour_datasets.load("6590768").items() - } - ) - - graph = RawProcessingGraphCameraSensitivities() + camera_sensitivities = CanonicalMapping( + { + slugify(key.replace("_", " ")): value + for key, value in colour_datasets.load("6590768").items() + } + ) - raw_file_paths = filter_files(output_directory, ("CR2",)) - graph.visualise().draw("RawProcessingGraphSensitivities.png", prog="dot") - exr_file_paths = [] - for raw_file_path in raw_file_paths: - exr_file_path = raw_file_path.replace(".CR2", ".exr") - exr_file_paths.append(exr_file_path) - graph.process( - raw_file_path, exr_file_path, dict(camera_sensitivities.items()) + graph = GraphRawProcessingCameraSensitivities() + graph.visualise().draw("GraphRawProcessingCameraSensitivities.png", prog="dot") + raw_file_paths = filter_files(output_directory, ("CR2",)) + exr_file_paths, metadata, input_transforms = [], [], [] + for raw_file_path in raw_file_paths: + exr_file_path = raw_file_path.replace(".CR2", ".exr") + exr_file_paths.append(exr_file_path) + graph.process(raw_file_path, exr_file_path, dict(camera_sensitivities.items())) + metadata.append(graph.nodes["read_file_metadata_dng"].get_output("metadata")) + input_transforms.append( + graph.nodes["compute_input_transform_camera_sensitivities"].get_output( + "input_transform" ) + ) - exr_file_paths = filter_files(output_directory, ("exr",)) - exr_file_paths = [ - exr_file_path - for exr_file_path in exr_file_paths - if not exr_file_path.endswith("_MRF.exr") - ] - node_create_image_stack = NodeCreateImageStack() - node_create_image_stack.set_input("paths", exr_file_paths) - node_create_image_stack.process() - - node_merge_image_stack = NodeMergeImageStack() - node_merge_image_stack.set_input( - "image_stack", node_create_image_stack.get_output("image_stack") - ) - node_merge_image_stack.process() - - node_write_image = NodeWriteImage() - node_write_image.set_input("image", node_merge_image_stack.get_output("image")) - node_write_image.set_input( - "path", os.path.join(output_directory, "IMG_2598_5_MRF.exr") + # Metadata: + # - script + # - time + # - orientation + # - idt + # - type + # - sources + + # exr_file_paths = filter_files(output_directory, ("exr",)) + # exr_file_paths = [ + # exr_file_path + # for exr_file_path in exr_file_paths + # if not exr_file_path.endswith("_MRF.exr") + # ] + # metadata = [{"EXIF": {"foo": "bar"}}] + graph = GraphMergeHDR() + graph.visualise().draw("GraphMergeHDR.png", prog="dot") + graph.process( + exr_file_paths, + os.path.join(output_directory, "IMG_2598_5_MRF.exr"), + next(iter(metadata)), ) - node_write_image.process()