mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge pull request #2569 from Ellipsanime/add-option-to-create-image-sequemce-with-photoshop-review
Photoshop: add sequence image option in review plugin
This commit is contained in:
commit
5a4cd11932
5 changed files with 144 additions and 42 deletions
|
|
@ -344,6 +344,28 @@ class PhotoshopServerStub:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def hide_all_others_layers(self, layers):
|
||||||
|
"""hides all layers that are not part of the list or that are not
|
||||||
|
children of this list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
layers (list): list of PSItem - highest hierarchy
|
||||||
|
"""
|
||||||
|
extract_ids = set([ll.id for ll in self.get_layers_in_layers(layers)])
|
||||||
|
|
||||||
|
self.hide_all_others_layers_ids(extract_ids)
|
||||||
|
|
||||||
|
def hide_all_others_layers_ids(self, extract_ids):
|
||||||
|
"""hides all layers that are not part of the list or that are not
|
||||||
|
children of this list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
extract_ids (list): list of integer that should be visible
|
||||||
|
"""
|
||||||
|
for layer in self.get_layers():
|
||||||
|
if layer.visible and layer.id not in extract_ids:
|
||||||
|
self.set_visible(layer.id, False)
|
||||||
|
|
||||||
def get_layers_metadata(self):
|
def get_layers_metadata(self):
|
||||||
"""Reads layers metadata from Headline from active document in PS.
|
"""Reads layers metadata from Headline from active document in PS.
|
||||||
(Headline accessible by File > File Info)
|
(Headline accessible by File > File Info)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ class ExtractImage(openpype.api.Extractor):
|
||||||
with photoshop.maintained_selection():
|
with photoshop.maintained_selection():
|
||||||
self.log.info("Extracting %s" % str(list(instance)))
|
self.log.info("Extracting %s" % str(list(instance)))
|
||||||
with photoshop.maintained_visibility():
|
with photoshop.maintained_visibility():
|
||||||
# Hide all other layers.
|
|
||||||
layer = instance.data.get("layer")
|
layer = instance.data.get("layer")
|
||||||
ids = set([layer.id])
|
ids = set([layer.id])
|
||||||
add_ids = instance.data.pop("ids", None)
|
add_ids = instance.data.pop("ids", None)
|
||||||
|
|
@ -34,11 +33,7 @@ class ExtractImage(openpype.api.Extractor):
|
||||||
ids.update(set(add_ids))
|
ids.update(set(add_ids))
|
||||||
extract_ids = set([ll.id for ll in stub.
|
extract_ids = set([ll.id for ll in stub.
|
||||||
get_layers_in_layers_ids(ids)])
|
get_layers_in_layers_ids(ids)])
|
||||||
|
stub.hide_all_others_layers_ids(extract_ids)
|
||||||
for layer in stub.get_layers():
|
|
||||||
# limit unnecessary calls to client
|
|
||||||
if layer.visible and layer.id not in extract_ids:
|
|
||||||
stub.set_visible(layer.id, False)
|
|
||||||
|
|
||||||
file_basename = os.path.splitext(
|
file_basename = os.path.splitext(
|
||||||
stub.get_active_document_name()
|
stub.get_active_document_name()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
import openpype.api
|
import openpype.api
|
||||||
import openpype.lib
|
import openpype.lib
|
||||||
|
|
@ -7,7 +8,7 @@ from openpype.hosts.photoshop import api as photoshop
|
||||||
|
|
||||||
class ExtractReview(openpype.api.Extractor):
|
class ExtractReview(openpype.api.Extractor):
|
||||||
"""
|
"""
|
||||||
Produce a flattened image file from all 'image' instances.
|
Produce a flattened or sequence image file from all 'image' instances.
|
||||||
|
|
||||||
If no 'image' instance is created, it produces flattened image from
|
If no 'image' instance is created, it produces flattened image from
|
||||||
all visible layers.
|
all visible layers.
|
||||||
|
|
@ -20,54 +21,58 @@ class ExtractReview(openpype.api.Extractor):
|
||||||
# Extract Options
|
# Extract Options
|
||||||
jpg_options = None
|
jpg_options = None
|
||||||
mov_options = None
|
mov_options = None
|
||||||
|
make_image_sequence = None
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
staging_dir = self.staging_dir(instance)
|
staging_dir = self.staging_dir(instance)
|
||||||
self.log.info("Outputting image to {}".format(staging_dir))
|
self.log.info("Outputting image to {}".format(staging_dir))
|
||||||
|
|
||||||
|
fps = instance.data.get("fps", 25)
|
||||||
stub = photoshop.stub()
|
stub = photoshop.stub()
|
||||||
|
self.output_seq_filename = os.path.splitext(
|
||||||
|
stub.get_active_document_name())[0] + ".%04d.jpg"
|
||||||
|
|
||||||
layers = []
|
layers = self._get_layers_from_image_instances(instance)
|
||||||
for image_instance in instance.context:
|
self.log.info("Layers image instance found: {}".format(layers))
|
||||||
if image_instance.data["family"] != "image":
|
|
||||||
continue
|
|
||||||
layers.append(image_instance.data.get("layer"))
|
|
||||||
|
|
||||||
# Perform extraction
|
if self.make_image_sequence and len(layers) > 1:
|
||||||
output_image = "{}.jpg".format(
|
self.log.info("Extract layers to image sequence.")
|
||||||
os.path.splitext(stub.get_active_document_name())[0]
|
img_list = self._saves_sequences_layers(staging_dir, layers)
|
||||||
)
|
|
||||||
output_image_path = os.path.join(staging_dir, output_image)
|
|
||||||
with photoshop.maintained_visibility():
|
|
||||||
if layers:
|
|
||||||
# Hide all other layers.
|
|
||||||
extract_ids = set([ll.id for ll in stub.
|
|
||||||
get_layers_in_layers(layers)])
|
|
||||||
self.log.debug("extract_ids {}".format(extract_ids))
|
|
||||||
for layer in stub.get_layers():
|
|
||||||
# limit unnecessary calls to client
|
|
||||||
if layer.visible and layer.id not in extract_ids:
|
|
||||||
stub.set_visible(layer.id, False)
|
|
||||||
|
|
||||||
stub.saveAs(output_image_path, 'jpg', True)
|
instance.data["representations"].append({
|
||||||
|
"name": "jpg",
|
||||||
|
"ext": "jpg",
|
||||||
|
"files": img_list,
|
||||||
|
"frameStart": 0,
|
||||||
|
"frameEnd": len(img_list),
|
||||||
|
"fps": fps,
|
||||||
|
"stagingDir": staging_dir,
|
||||||
|
"tags": self.jpg_options['tags'],
|
||||||
|
})
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.log.info("Extract layers to flatten image.")
|
||||||
|
img_list = self._saves_flattened_layers(staging_dir, layers)
|
||||||
|
|
||||||
|
instance.data["representations"].append({
|
||||||
|
"name": "jpg",
|
||||||
|
"ext": "jpg",
|
||||||
|
"files": img_list,
|
||||||
|
"stagingDir": staging_dir,
|
||||||
|
"tags": self.jpg_options['tags']
|
||||||
|
})
|
||||||
|
|
||||||
ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
|
ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
|
||||||
|
|
||||||
instance.data["representations"].append({
|
|
||||||
"name": "jpg",
|
|
||||||
"ext": "jpg",
|
|
||||||
"files": output_image,
|
|
||||||
"stagingDir": staging_dir,
|
|
||||||
"tags": self.jpg_options['tags']
|
|
||||||
})
|
|
||||||
instance.data["stagingDir"] = staging_dir
|
instance.data["stagingDir"] = staging_dir
|
||||||
|
|
||||||
# Generate thumbnail.
|
# Generate thumbnail.
|
||||||
thumbnail_path = os.path.join(staging_dir, "thumbnail.jpg")
|
thumbnail_path = os.path.join(staging_dir, "thumbnail.jpg")
|
||||||
|
self.log.info(f"Generate thumbnail {thumbnail_path}")
|
||||||
args = [
|
args = [
|
||||||
ffmpeg_path,
|
ffmpeg_path,
|
||||||
"-y",
|
"-y",
|
||||||
"-i", output_image_path,
|
"-i", os.path.join(staging_dir, self.output_seq_filename),
|
||||||
"-vf", "scale=300:-1",
|
"-vf", "scale=300:-1",
|
||||||
"-vframes", "1",
|
"-vframes", "1",
|
||||||
thumbnail_path
|
thumbnail_path
|
||||||
|
|
@ -81,14 +86,17 @@ class ExtractReview(openpype.api.Extractor):
|
||||||
"stagingDir": staging_dir,
|
"stagingDir": staging_dir,
|
||||||
"tags": ["thumbnail"]
|
"tags": ["thumbnail"]
|
||||||
})
|
})
|
||||||
|
|
||||||
# Generate mov.
|
# Generate mov.
|
||||||
mov_path = os.path.join(staging_dir, "review.mov")
|
mov_path = os.path.join(staging_dir, "review.mov")
|
||||||
|
self.log.info(f"Generate mov review: {mov_path}")
|
||||||
|
img_number = len(img_list)
|
||||||
args = [
|
args = [
|
||||||
ffmpeg_path,
|
ffmpeg_path,
|
||||||
"-y",
|
"-y",
|
||||||
"-i", output_image_path,
|
"-i", os.path.join(staging_dir, self.output_seq_filename),
|
||||||
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
|
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
|
||||||
"-vframes", "1",
|
"-vframes", str(img_number),
|
||||||
mov_path
|
mov_path
|
||||||
]
|
]
|
||||||
output = openpype.lib.run_subprocess(args)
|
output = openpype.lib.run_subprocess(args)
|
||||||
|
|
@ -99,15 +107,86 @@ class ExtractReview(openpype.api.Extractor):
|
||||||
"files": os.path.basename(mov_path),
|
"files": os.path.basename(mov_path),
|
||||||
"stagingDir": staging_dir,
|
"stagingDir": staging_dir,
|
||||||
"frameStart": 1,
|
"frameStart": 1,
|
||||||
"frameEnd": 1,
|
"frameEnd": img_number,
|
||||||
"fps": 25,
|
"fps": fps,
|
||||||
"preview": True,
|
"preview": True,
|
||||||
"tags": self.mov_options['tags']
|
"tags": self.mov_options['tags']
|
||||||
})
|
})
|
||||||
|
|
||||||
# Required for extract_review plugin (L222 onwards).
|
# Required for extract_review plugin (L222 onwards).
|
||||||
instance.data["frameStart"] = 1
|
instance.data["frameStart"] = 1
|
||||||
instance.data["frameEnd"] = 1
|
instance.data["frameEnd"] = img_number
|
||||||
instance.data["fps"] = 25
|
instance.data["fps"] = 25
|
||||||
|
|
||||||
self.log.info(f"Extracted {instance} to {staging_dir}")
|
self.log.info(f"Extracted {instance} to {staging_dir}")
|
||||||
|
|
||||||
|
def _get_image_path_from_instances(self, instance):
|
||||||
|
img_list = []
|
||||||
|
|
||||||
|
for instance in sorted(instance.context):
|
||||||
|
if instance.data["family"] != "image":
|
||||||
|
continue
|
||||||
|
|
||||||
|
for rep in instance.data["representations"]:
|
||||||
|
img_path = os.path.join(
|
||||||
|
rep["stagingDir"],
|
||||||
|
rep["files"]
|
||||||
|
)
|
||||||
|
img_list.append(img_path)
|
||||||
|
|
||||||
|
return img_list
|
||||||
|
|
||||||
|
def _copy_image_to_staging_dir(self, staging_dir, img_list):
|
||||||
|
copy_files = []
|
||||||
|
for i, img_src in enumerate(img_list):
|
||||||
|
img_filename = self.output_seq_filename % i
|
||||||
|
img_dst = os.path.join(staging_dir, img_filename)
|
||||||
|
|
||||||
|
self.log.debug(
|
||||||
|
"Copying file .. {} -> {}".format(img_src, img_dst)
|
||||||
|
)
|
||||||
|
shutil.copy(img_src, img_dst)
|
||||||
|
copy_files.append(img_filename)
|
||||||
|
|
||||||
|
return copy_files
|
||||||
|
|
||||||
|
def _get_layers_from_image_instances(self, instance):
|
||||||
|
layers = []
|
||||||
|
for image_instance in instance.context:
|
||||||
|
if image_instance.data["family"] != "image":
|
||||||
|
continue
|
||||||
|
layers.append(image_instance.data.get("layer"))
|
||||||
|
|
||||||
|
return sorted(layers)
|
||||||
|
|
||||||
|
def _saves_flattened_layers(self, staging_dir, layers):
|
||||||
|
img_filename = self.output_seq_filename % 0
|
||||||
|
output_image_path = os.path.join(staging_dir, img_filename)
|
||||||
|
stub = photoshop.stub()
|
||||||
|
|
||||||
|
with photoshop.maintained_visibility():
|
||||||
|
self.log.info("Extracting {}".format(layers))
|
||||||
|
if layers:
|
||||||
|
stub.hide_all_others_layers(layers)
|
||||||
|
|
||||||
|
stub.saveAs(output_image_path, 'jpg', True)
|
||||||
|
|
||||||
|
return img_filename
|
||||||
|
|
||||||
|
def _saves_sequences_layers(self, staging_dir, layers):
|
||||||
|
stub = photoshop.stub()
|
||||||
|
|
||||||
|
list_img_filename = []
|
||||||
|
with photoshop.maintained_visibility():
|
||||||
|
for i, layer in enumerate(layers):
|
||||||
|
self.log.info("Extracting {}".format(layer))
|
||||||
|
|
||||||
|
img_filename = self.output_seq_filename % i
|
||||||
|
output_image_path = os.path.join(staging_dir, img_filename)
|
||||||
|
list_img_filename.append(img_filename)
|
||||||
|
|
||||||
|
with photoshop.maintained_visibility():
|
||||||
|
stub.hide_all_others_layers([layer])
|
||||||
|
stub.saveAs(output_image_path, 'jpg', True)
|
||||||
|
|
||||||
|
return list_img_filename
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ExtractReview": {
|
"ExtractReview": {
|
||||||
|
"make_image_sequence": false,
|
||||||
"jpg_options": {
|
"jpg_options": {
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
|
|
@ -43,4 +44,4 @@
|
||||||
"create_first_version": false,
|
"create_first_version": false,
|
||||||
"custom_templates": []
|
"custom_templates": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,11 @@
|
||||||
"key": "ExtractReview",
|
"key": "ExtractReview",
|
||||||
"label": "Extract Review",
|
"label": "Extract Review",
|
||||||
"children": [
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"key": "make_image_sequence",
|
||||||
|
"label": "Makes an image sequence instead of a flatten image"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "dict",
|
"type": "dict",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue