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:
Petr Kalis 2022-02-25 12:04:46 +01:00 committed by GitHub
commit 5a4cd11932
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 42 deletions

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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": []
} }
} }

View file

@ -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,