"""
Adobe DNG SDK Conversion Process
================================
Define various objects implementing raw conversion based on *Adobe DNG SDK*
and *dcraw*:
- :func:`colour_hdri.convert_raw_files_to_dng_files`
- :func:`colour_hdri.convert_dng_files_to_intermediate_files`
- :func:`colour_hdri.read_dng_files_exif_tags`
"""
from __future__ import annotations
import logging
import os
import platform
import re
import shlex
import subprocess
import typing
import numpy as np
if typing.TYPE_CHECKING:
from colour.hints import Callable, List, Mapping, Sequence, Tuple
from colour.utilities import CanonicalMapping, optional
from colour.utilities.documentation import (
DocstringText,
is_documentation_building,
)
from colour_hdri.utilities import (
EXIFTag,
parse_exif_array,
parse_exif_fraction,
parse_exif_number,
parse_exif_string,
path_exists,
read_exif_tags,
)
__author__ = "Colour Developers"
__copyright__ = "Copyright 2015 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"
__all__ = [
"RAW_CONVERTER",
"RAW_CONVERTER_ARGUMENTS_BAYER_CFA",
"RAW_CONVERTER_ARGUMENTS_DEMOSAICING",
"DNG_CONVERTER",
"DNG_CONVERTER_ARGUMENTS",
"DNG_EXIF_TAGS_BINDING",
"convert_raw_files_to_dng_files",
"convert_dng_files_to_intermediate_files",
"read_dng_files_exif_tags",
]
LOGGER = logging.getLogger(__name__)
_IS_LINUX_PLATFORM: bool = platform.system() == "Linux"
"""Whether the current platform is *Linux*."""
_IS_MACOS_PLATFORM: bool = platform.system() == "Darwin"
"""Whether the current platform is *macOS*."""
_IS_WINDOWS_PLATFORM: bool = platform.system() in ("Windows", "Microsoft")
"""Whether the current platform is *Windows*."""
RAW_CONVERTER: str = "dcraw"
if is_documentation_building(): # pragma: no cover
RAW_CONVERTER = DocstringText(RAW_CONVERTER)
RAW_CONVERTER.__doc__ = """
Command line raw conversion application, typically Dave Coffin's *dcraw*.
"""
RAW_CONVERTER_ARGUMENTS_BAYER_CFA: str = '-t 0 -D -W -4 -T "{raw_file}"'
if _IS_WINDOWS_PLATFORM:
RAW_CONVERTER_ARGUMENTS_BAYER_CFA = RAW_CONVERTER_ARGUMENTS_BAYER_CFA.replace(
'"', ""
)
if is_documentation_building(): # pragma: no cover
RAW_CONVERTER_ARGUMENTS_BAYER_CFA = DocstringText(RAW_CONVERTER_ARGUMENTS_BAYER_CFA)
RAW_CONVERTER_ARGUMENTS_BAYER_CFA.__doc__ = """
Arguments for the command line raw conversion application for non
demosaiced linear *tiff* file format output.
"""
RAW_CONVERTER_ARGUMENTS_DEMOSAICING: str = (
'-t 0 -H 1 -r 1 1 1 1 -4 -q 3 -o 0 -T "{raw_file}"'
)
if _IS_WINDOWS_PLATFORM:
RAW_CONVERTER_ARGUMENTS_DEMOSAICING = RAW_CONVERTER_ARGUMENTS_DEMOSAICING.replace(
'"', ""
)
if is_documentation_building(): # pragma: no cover
RAW_CONVERTER_ARGUMENTS_DEMOSAICING = DocstringText(
RAW_CONVERTER_ARGUMENTS_DEMOSAICING
)
RAW_CONVERTER_ARGUMENTS_DEMOSAICING.__doc__ = """
Arguments for the command line raw conversion application for demosaiced
linear *tiff* file format output.
"""
if _IS_MACOS_PLATFORM:
DNG_CONVERTER: str = (
"/Applications/Adobe DNG Converter.app/Contents/MacOS/Adobe DNG Converter"
)
elif _IS_WINDOWS_PLATFORM:
DNG_CONVERTER: str = "Adobe DNG Converter"
else:
DNG_CONVERTER: str = "dnglab"
if is_documentation_building(): # pragma: no cover
DNG_CONVERTER = DocstringText(DNG_CONVERTER)
DNG_CONVERTER.__doc__ = """
Command line *DNG* conversion application, typically *Adobe DNG Converter*.
"""
DNG_CONVERTER_ARGUMENTS: str = '-cr7.1 -l -d "{output_directory}" "{raw_file}"'
if _IS_WINDOWS_PLATFORM:
DNG_CONVERTER_ARGUMENTS = DNG_CONVERTER_ARGUMENTS.replace('"', "")
elif _IS_LINUX_PLATFORM:
DNG_CONVERTER_ARGUMENTS = 'convert "{raw_file}" "{output_directory}"'
if is_documentation_building(): # pragma: no cover
DNG_CONVERTER_ARGUMENTS = DocstringText(DNG_CONVERTER_ARGUMENTS)
DNG_CONVERTER_ARGUMENTS.__doc__ = """
Arguments for the command line *DNG* conversion application.
"""
DNG_EXIF_TAGS_BINDING: CanonicalMapping = CanonicalMapping(
{
"EXIF": CanonicalMapping(
{
"Make": (parse_exif_string, None),
"Camera Model Name": (parse_exif_string, None),
"Camera Serial Number": (parse_exif_string, None),
"Lens Model": (parse_exif_string, None),
"DNG Lens Info": (parse_exif_string, None),
"Focal Length": (parse_exif_number, None),
"Exposure Time": (parse_exif_fraction, None),
"F Number": (parse_exif_number, None),
"ISO": (parse_exif_number, None),
"CFA Pattern 2": (
lambda x: parse_exif_array(x, np.int64),
None,
),
"CFA Plane Color": (
lambda x: parse_exif_array(x, np.int64),
None,
),
"Black Level Repeat Dim": (
lambda x: parse_exif_array(x, np.int64),
None,
),
"Black Level": (lambda x: parse_exif_array(x, np.int64), None),
"White Level": (lambda x: parse_exif_array(x, np.int64), None),
"Samples Per Pixel": (
lambda x: parse_exif_number(x, np.int64),
None,
),
"Active Area": (lambda x: parse_exif_array(x, np.int64), None),
"Orientation": (lambda x: parse_exif_number(x, np.int64), None),
"Camera Calibration Sig": (parse_exif_string, None),
"Profile Calibration Sig": (parse_exif_string, None),
"Calibration Illuminant 1": (
lambda x: parse_exif_number(x, np.int64),
17,
),
"Calibration Illuminant 2": (
lambda x: parse_exif_number(x, np.int_),
21,
),
"Color Matrix 1": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Color Matrix 2": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Camera Calibration 1": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Camera Calibration 2": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Analog Balance": (
lambda x: parse_exif_array(x, np.float64),
"1 1 1",
),
"Reduction Matrix 1": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Reduction Matrix 2": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Forward Matrix 1": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"Forward Matrix 2": (
lambda x: parse_exif_array(x, np.float64, (3, 3)),
"1 0 0 0 1 0 0 0 1",
),
"As Shot Neutral": (
lambda x: parse_exif_array(x, np.float64),
"1 1 1",
),
"Baseline Exposure": (
lambda x: parse_exif_number(x, np.float64),
None,
),
"Baseline Noise": (
lambda x: parse_exif_number(x, np.float64),
None,
),
}
)
}
)
DNG_EXIF_TAGS_BINDING.__doc__ = """
Exif tags binding for a *dng* file.
"""
[docs]
def convert_raw_files_to_dng_files(
raw_files: Sequence[str],
output_directory: str,
dng_converter: str | None = None,
dng_converter_arguments: str | None = None,
) -> List[str]:
"""
Convert given raw files to *dng* files using given output directory.
Parameters
----------
raw_files
Raw files to convert to *dng* files.
output_directory
Output directory.
dng_converter
Command line *DNG* conversion application, typically
*Adobe DNG Converter*.
dng_converter_arguments
Arguments for the command line *DNG* conversion application.
Returns
-------
:class:`list`
*dng* files.
Raises
------
RuntimeError
If the *DNG* converter is not available.
"""
dng_converter = optional(dng_converter, DNG_CONVERTER)
dng_converter_arguments = optional(dng_converter_arguments, DNG_CONVERTER_ARGUMENTS)
dng_files = []
for raw_file in raw_files:
raw_file_extension = os.path.splitext(raw_file)[1]
dng_file = os.path.join(
output_directory,
os.path.basename(re.sub(f"{raw_file_extension}$", ".dng", raw_file)),
)
if path_exists(dng_file):
os.remove(dng_file)
LOGGER.info(
'Converting "%s" file to "%s" file.',
raw_file,
dng_file,
)
command = [
dng_converter,
*shlex.split(
dng_converter_arguments.format(
output_directory=output_directory, raw_file=raw_file
),
posix=not _IS_WINDOWS_PLATFORM,
),
]
subprocess.call(command, shell=_IS_WINDOWS_PLATFORM) # noqa: S603
dng_files.append(dng_file)
return dng_files