mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #5938 from ynput/enhancement/OP-6659_thumbnail-color-managed
General: Use colorspace data when creating thumbnail
This commit is contained in:
commit
ced3e1ecc2
4 changed files with 182 additions and 73 deletions
|
|
@ -536,7 +536,7 @@ def convert_for_ffmpeg(
|
|||
input_frame_end=None,
|
||||
logger=None
|
||||
):
|
||||
"""Contert source file to format supported in ffmpeg.
|
||||
"""Convert source file to format supported in ffmpeg.
|
||||
|
||||
Currently can convert only exrs.
|
||||
|
||||
|
|
@ -592,29 +592,7 @@ def convert_for_ffmpeg(
|
|||
oiio_cmd.extend(["--compression", compression])
|
||||
|
||||
# Collect channels to export
|
||||
channel_names = input_info["channelnames"]
|
||||
review_channels = get_convert_rgb_channels(channel_names)
|
||||
if review_channels is None:
|
||||
raise ValueError(
|
||||
"Couldn't find channels that can be used for conversion."
|
||||
)
|
||||
|
||||
red, green, blue, alpha = review_channels
|
||||
input_channels = [red, green, blue]
|
||||
channels_arg = "R={},G={},B={}".format(red, green, blue)
|
||||
if alpha is not None:
|
||||
channels_arg += ",A={}".format(alpha)
|
||||
input_channels.append(alpha)
|
||||
input_channels_str = ",".join(input_channels)
|
||||
|
||||
subimages = input_info.get("subimages")
|
||||
input_arg = "-i"
|
||||
if subimages is None or subimages == 1:
|
||||
# Tell oiiotool which channels should be loaded
|
||||
# - other channels are not loaded to memory so helps to avoid memory
|
||||
# leak issues
|
||||
# - this option is crashing if used on multipart/subimages exrs
|
||||
input_arg += ":ch={}".format(input_channels_str)
|
||||
input_arg, channels_arg = get_oiio_input_and_channel_args(input_info)
|
||||
|
||||
oiio_cmd.extend([
|
||||
input_arg, first_input_path,
|
||||
|
|
@ -635,7 +613,7 @@ def convert_for_ffmpeg(
|
|||
continue
|
||||
|
||||
# Remove attributes that have string value longer than allowed length
|
||||
# for ffmpeg or when contain unallowed symbols
|
||||
# for ffmpeg or when contain prohibited symbols
|
||||
erase_reason = "Missing reason"
|
||||
erase_attribute = False
|
||||
if len(attr_value) > MAX_FFMPEG_STRING_LEN:
|
||||
|
|
@ -677,6 +655,47 @@ def convert_for_ffmpeg(
|
|||
run_subprocess(oiio_cmd, logger=logger)
|
||||
|
||||
|
||||
def get_oiio_input_and_channel_args(oiio_input_info):
|
||||
"""Get input and channel arguments for oiiotool.
|
||||
|
||||
Args:
|
||||
oiio_input_info (dict): Information about input from oiio tool.
|
||||
Should be output of function `get_oiio_info_for_input`.
|
||||
|
||||
Returns:
|
||||
tuple[str, str]: Tuple of input and channel arguments.
|
||||
"""
|
||||
channel_names = oiio_input_info["channelnames"]
|
||||
review_channels = get_convert_rgb_channels(channel_names)
|
||||
|
||||
if review_channels is None:
|
||||
raise ValueError(
|
||||
"Couldn't find channels that can be used for conversion."
|
||||
)
|
||||
|
||||
red, green, blue, alpha = review_channels
|
||||
input_channels = [red, green, blue]
|
||||
|
||||
# TODO find subimage where rgba is available for multipart exrs
|
||||
channels_arg = "R={},G={},B={}".format(red, green, blue)
|
||||
if alpha is not None:
|
||||
channels_arg += ",A={}".format(alpha)
|
||||
input_channels.append(alpha)
|
||||
|
||||
input_channels_str = ",".join(input_channels)
|
||||
|
||||
subimages = oiio_input_info.get("subimages")
|
||||
input_arg = "-i"
|
||||
if subimages is None or subimages == 1:
|
||||
# Tell oiiotool which channels should be loaded
|
||||
# - other channels are not loaded to memory so helps to avoid memory
|
||||
# leak issues
|
||||
# - this option is crashing if used on multipart exrs
|
||||
input_arg += ":ch={}".format(input_channels_str)
|
||||
|
||||
return input_arg, channels_arg
|
||||
|
||||
|
||||
def convert_input_paths_for_ffmpeg(
|
||||
input_paths,
|
||||
output_dir,
|
||||
|
|
@ -695,7 +714,7 @@ def convert_input_paths_for_ffmpeg(
|
|||
|
||||
Args:
|
||||
input_paths (str): Paths that should be converted. It is expected that
|
||||
contains single file or image sequence of samy type.
|
||||
contains single file or image sequence of same type.
|
||||
output_dir (str): Path to directory where output will be rendered.
|
||||
Must not be same as input's directory.
|
||||
logger (logging.Logger): Logger used for logging.
|
||||
|
|
@ -709,6 +728,7 @@ def convert_input_paths_for_ffmpeg(
|
|||
|
||||
first_input_path = input_paths[0]
|
||||
ext = os.path.splitext(first_input_path)[1].lower()
|
||||
|
||||
if ext != ".exr":
|
||||
raise ValueError((
|
||||
"Function 'convert_for_ffmpeg' currently support only"
|
||||
|
|
@ -724,30 +744,7 @@ def convert_input_paths_for_ffmpeg(
|
|||
compression = "none"
|
||||
|
||||
# Collect channels to export
|
||||
channel_names = input_info["channelnames"]
|
||||
review_channels = get_convert_rgb_channels(channel_names)
|
||||
if review_channels is None:
|
||||
raise ValueError(
|
||||
"Couldn't find channels that can be used for conversion."
|
||||
)
|
||||
|
||||
red, green, blue, alpha = review_channels
|
||||
input_channels = [red, green, blue]
|
||||
# TODO find subimage inder where rgba is available for multipart exrs
|
||||
channels_arg = "R={},G={},B={}".format(red, green, blue)
|
||||
if alpha is not None:
|
||||
channels_arg += ",A={}".format(alpha)
|
||||
input_channels.append(alpha)
|
||||
input_channels_str = ",".join(input_channels)
|
||||
|
||||
subimages = input_info.get("subimages")
|
||||
input_arg = "-i"
|
||||
if subimages is None or subimages == 1:
|
||||
# Tell oiiotool which channels should be loaded
|
||||
# - other channels are not loaded to memory so helps to avoid memory
|
||||
# leak issues
|
||||
# - this option is crashing if used on multipart exrs
|
||||
input_arg += ":ch={}".format(input_channels_str)
|
||||
input_arg, channels_arg = get_oiio_input_and_channel_args(input_info)
|
||||
|
||||
for input_path in input_paths:
|
||||
# Prepare subprocess arguments
|
||||
|
|
@ -774,7 +771,7 @@ def convert_input_paths_for_ffmpeg(
|
|||
continue
|
||||
|
||||
# Remove attributes that have string value longer than allowed
|
||||
# length for ffmpeg or when containing unallowed symbols
|
||||
# length for ffmpeg or when containing prohibited symbols
|
||||
erase_reason = "Missing reason"
|
||||
erase_attribute = False
|
||||
if len(attr_value) > MAX_FFMPEG_STRING_LEN:
|
||||
|
|
@ -1021,9 +1018,7 @@ def _ffmpeg_h264_codec_args(stream_data, source_ffmpeg_cmd):
|
|||
if pix_fmt:
|
||||
output.extend(["-pix_fmt", pix_fmt])
|
||||
|
||||
output.extend(["-intra"])
|
||||
output.extend(["-g", "1"])
|
||||
|
||||
output.extend(["-intra", "-g", "1"])
|
||||
return output
|
||||
|
||||
|
||||
|
|
@ -1150,7 +1145,7 @@ def convert_colorspace(
|
|||
view=None,
|
||||
display=None,
|
||||
additional_command_args=None,
|
||||
logger=None
|
||||
logger=None,
|
||||
):
|
||||
"""Convert source file from one color space to another.
|
||||
|
||||
|
|
@ -1169,6 +1164,7 @@ def convert_colorspace(
|
|||
view (str): name for viewer space (ocio valid)
|
||||
both 'view' and 'display' must be filled (if 'target_colorspace')
|
||||
display (str): name for display-referred reference space (ocio valid)
|
||||
both 'view' and 'display' must be filled (if 'target_colorspace')
|
||||
additional_command_args (list): arguments for oiiotool (like binary
|
||||
depth for .dpx)
|
||||
logger (logging.Logger): Logger used for logging.
|
||||
|
|
@ -1178,14 +1174,28 @@ def convert_colorspace(
|
|||
if logger is None:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
input_info = get_oiio_info_for_input(input_path, logger=logger)
|
||||
|
||||
# Collect channels to export
|
||||
input_arg, channels_arg = get_oiio_input_and_channel_args(input_info)
|
||||
|
||||
# Prepare subprocess arguments
|
||||
oiio_cmd = get_oiio_tool_args(
|
||||
"oiiotool",
|
||||
input_path,
|
||||
# Don't add any additional attributes
|
||||
"--nosoftwareattrib",
|
||||
"--colorconfig", config_path
|
||||
)
|
||||
|
||||
oiio_cmd.extend([
|
||||
input_arg, input_path,
|
||||
# Tell oiiotool which channels should be put to top stack
|
||||
# (and output)
|
||||
"--ch", channels_arg,
|
||||
# Use first subimage
|
||||
"--subimage", "0"
|
||||
])
|
||||
|
||||
if all([target_colorspace, view, display]):
|
||||
raise ValueError("Colorspace and both screen and display"
|
||||
" cannot be set together."
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import tempfile
|
|||
import pyblish.api
|
||||
from openpype.lib import (
|
||||
get_ffmpeg_tool_args,
|
||||
get_oiio_tool_args,
|
||||
is_oiio_supported,
|
||||
|
||||
run_subprocess,
|
||||
path_to_subprocess_arg,
|
||||
)
|
||||
from openpype.lib.transcoding import convert_colorspace
|
||||
|
||||
|
||||
class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||
|
|
@ -25,7 +25,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
|||
hosts = ["shell", "fusion", "resolve", "traypublisher", "substancepainter"]
|
||||
enabled = False
|
||||
|
||||
# presetable attribute
|
||||
# attribute presets from settings
|
||||
oiiotool_defaults = None
|
||||
ffmpeg_args = None
|
||||
|
||||
def process(self, instance):
|
||||
|
|
@ -94,17 +95,26 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
|||
filename = os.path.splitext(input_file)[0]
|
||||
jpeg_file = filename + "_thumb.jpg"
|
||||
full_output_path = os.path.join(dst_staging, jpeg_file)
|
||||
colorspace_data = repre.get("colorspaceData")
|
||||
|
||||
if oiio_supported:
|
||||
self.log.debug("Trying to convert with OIIO")
|
||||
# only use OIIO if it is supported and representation has
|
||||
# colorspace data
|
||||
if oiio_supported and colorspace_data:
|
||||
self.log.debug(
|
||||
"Trying to convert with OIIO "
|
||||
"with colorspace data: {}".format(colorspace_data)
|
||||
)
|
||||
# If the input can read by OIIO then use OIIO method for
|
||||
# conversion otherwise use ffmpeg
|
||||
thumbnail_created = self.create_thumbnail_oiio(
|
||||
full_input_path, full_output_path
|
||||
full_input_path,
|
||||
full_output_path,
|
||||
colorspace_data
|
||||
)
|
||||
|
||||
# Try to use FFMPEG if OIIO is not supported or for cases when
|
||||
# oiiotool isn't available
|
||||
# oiiotool isn't available or representation is not having
|
||||
# colorspace data
|
||||
if not thumbnail_created:
|
||||
if oiio_supported:
|
||||
self.log.debug(
|
||||
|
|
@ -138,7 +148,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
|||
break
|
||||
|
||||
if not thumbnail_created:
|
||||
self.log.warning("Thumbanil has not been created.")
|
||||
self.log.warning("Thumbnail has not been created.")
|
||||
|
||||
def _is_review_instance(self, instance):
|
||||
# TODO: We should probably handle "not creating" of thumbnail
|
||||
|
|
@ -173,17 +183,66 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
|||
filtered_repres.append(repre)
|
||||
return filtered_repres
|
||||
|
||||
def create_thumbnail_oiio(self, src_path, dst_path):
|
||||
self.log.debug("Extracting thumbnail with OIIO: {}".format(dst_path))
|
||||
oiio_cmd = get_oiio_tool_args(
|
||||
"oiiotool",
|
||||
"-a", src_path,
|
||||
"-o", dst_path
|
||||
)
|
||||
self.log.debug("running: {}".format(" ".join(oiio_cmd)))
|
||||
def create_thumbnail_oiio(
|
||||
self,
|
||||
src_path,
|
||||
dst_path,
|
||||
colorspace_data,
|
||||
):
|
||||
"""Create thumbnail using OIIO tool oiiotool
|
||||
|
||||
Args:
|
||||
src_path (str): path to source file
|
||||
dst_path (str): path to destination file
|
||||
colorspace_data (dict): colorspace data from representation
|
||||
keys:
|
||||
colorspace (str)
|
||||
config (dict)
|
||||
display (Optional[str])
|
||||
view (Optional[str])
|
||||
|
||||
Returns:
|
||||
str: path to created thumbnail
|
||||
"""
|
||||
self.log.info("Extracting thumbnail {}".format(dst_path))
|
||||
|
||||
repre_display = colorspace_data.get("display")
|
||||
repre_view = colorspace_data.get("view")
|
||||
oiio_default_type = None
|
||||
oiio_default_display = None
|
||||
oiio_default_view = None
|
||||
oiio_default_colorspace = None
|
||||
# first look into representation colorspaceData, perhaps it has
|
||||
# display and view
|
||||
if all([repre_display, repre_view]):
|
||||
self.log.info(
|
||||
"Using Display & View from "
|
||||
"representation: '{} ({})'".format(
|
||||
repre_view,
|
||||
repre_display
|
||||
)
|
||||
)
|
||||
# if representation doesn't have display and view then use
|
||||
# oiiotool_defaults
|
||||
elif self.oiiotool_defaults:
|
||||
oiio_default_type = self.oiiotool_defaults["type"]
|
||||
if "colorspace" in oiio_default_type:
|
||||
oiio_default_colorspace = self.oiiotool_defaults["colorspace"]
|
||||
else:
|
||||
oiio_default_display = self.oiiotool_defaults["display"]
|
||||
oiio_default_view = self.oiiotool_defaults["view"]
|
||||
|
||||
try:
|
||||
run_subprocess(oiio_cmd, logger=self.log)
|
||||
return True
|
||||
convert_colorspace(
|
||||
src_path,
|
||||
dst_path,
|
||||
colorspace_data["config"]["path"],
|
||||
colorspace_data["colorspace"],
|
||||
display=repre_display or oiio_default_display,
|
||||
view=repre_view or oiio_default_view,
|
||||
target_colorspace=oiio_default_colorspace,
|
||||
logger=self.log,
|
||||
)
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
"Failed to create thumbnail using oiiotool",
|
||||
|
|
@ -191,6 +250,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
|||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def create_thumbnail_ffmpeg(self, src_path, dst_path):
|
||||
self.log.debug("Extracting thumbnail with FFMPEG: {}".format(dst_path))
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,12 @@
|
|||
},
|
||||
"ExtractThumbnail": {
|
||||
"enabled": true,
|
||||
"oiiotool_defaults": {
|
||||
"type": "colorspace",
|
||||
"colorspace": "color_picking",
|
||||
"view": "sRGB",
|
||||
"display": "default"
|
||||
},
|
||||
"ffmpeg_args": {
|
||||
"input": [
|
||||
"-apply_trc gamma22"
|
||||
|
|
|
|||
|
|
@ -202,6 +202,38 @@
|
|||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "oiiotool_defaults",
|
||||
"label": "OIIOtool defaults",
|
||||
"children": [
|
||||
{
|
||||
"type": "enum",
|
||||
"key": "type",
|
||||
"label": "Target type",
|
||||
"enum_items": [
|
||||
{ "colorspace": "Colorspace" },
|
||||
{ "display_and_view": "Display & View" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "colorspace",
|
||||
"label": "Colorspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "view",
|
||||
"label": "View"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "display",
|
||||
"label": "Display"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "ffmpeg_args",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue