OP-3863 - attempt to speed up ExtractImage

If workfile has a large number of layers (hundreds or thousands), ExtractImage wasn't too efficient. It was hiding/showing layers too many times.
Current logic is to hide all, show only publishable layers of instance, save to image, hide them again.
get_layers replaced by argument if possible.
This commit is contained in:
Petr Kalis 2022-09-06 13:16:01 +02:00
parent ebd8c16d27
commit 1ed5ef7dcd
3 changed files with 97 additions and 53 deletions

View file

@ -64,10 +64,15 @@ def maintained_selection():
@contextlib.contextmanager
def maintained_visibility():
"""Maintain visibility during context."""
def maintained_visibility(layers=None):
"""Maintain visibility during context.
Args:
layers (list) of PSItem (used for caching)
"""
visibility = {}
layers = stub().get_layers()
if not layers:
layers = stub().get_layers()
for layer in layers:
visibility[layer.id] = layer.visible
try:

View file

@ -229,10 +229,11 @@ class PhotoshopServerStub:
return self._get_layers_in_layers(parent_ids)
def get_layers_in_layers_ids(self, layers_ids):
def get_layers_in_layers_ids(self, layers_ids, layers=None):
"""Return all layers that belong to layers (might be groups).
Args:
layers_ids <list of Int>
layers <list of PSItem>:
Returns:
@ -240,10 +241,13 @@ class PhotoshopServerStub:
"""
parent_ids = set(layers_ids)
return self._get_layers_in_layers(parent_ids)
return self._get_layers_in_layers(parent_ids, layers)
def _get_layers_in_layers(self, parent_ids):
all_layers = self.get_layers()
def _get_layers_in_layers(self, parent_ids, layers=None):
if not layers:
layers = self.get_layers()
all_layers = layers
ret = []
for layer in all_layers:
@ -394,14 +398,17 @@ class PhotoshopServerStub:
self.hide_all_others_layers_ids(extract_ids)
def hide_all_others_layers_ids(self, extract_ids):
def hide_all_others_layers_ids(self, extract_ids, layers=None):
"""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
layers (list) of PSItem (used for caching)
"""
for layer in self.get_layers():
if not layers:
layers = self.get_layers()
for layer in layers:
if layer.visible and layer.id not in extract_ids:
self.set_visible(layer.id, False)

View file

@ -1,61 +1,93 @@
import os
import openpype.api
import pyblish.api
from openpype.pipeline import publish
from openpype.hosts.photoshop import api as photoshop
class ExtractImage(openpype.api.Extractor):
"""Produce a flattened image file from instance
This plug-in takes into account only the layers in the group.
"""
class ExtractImage(pyblish.api.ContextPlugin):
"""Save scene before extraction."""
order = publish.Extractor.order - 0.48
label = "Extract Image"
hosts = ["photoshop"]
families = ["image", "background"]
formats = ["png", "jpg"]
def process(self, instance):
staging_dir = self.staging_dir(instance)
self.log.info("Outputting image to {}".format(staging_dir))
# Perform extraction
def process(self, context):
stub = photoshop.stub()
files = {}
hidden_layer_ids = set()
all_layers = stub.get_layers()
for layer in all_layers:
if not layer.visible:
hidden_layer_ids.add(layer.id)
stub.hide_all_others_layers_ids([], layers=all_layers)
with photoshop.maintained_selection():
self.log.info("Extracting %s" % str(list(instance)))
with photoshop.maintained_visibility():
ids = set()
layer = instance.data.get("layer")
if layer:
ids.add(layer.id)
add_ids = instance.data.pop("ids", None)
if add_ids:
ids.update(set(add_ids))
extract_ids = set([ll.id for ll in stub.
get_layers_in_layers_ids(ids)])
stub.hide_all_others_layers_ids(extract_ids)
# self.log.info("Extracting %s" % str(list(instance)))
with photoshop.maintained_visibility(layers=all_layers):
for instance in context:
if instance.data["family"] not in self.families:
continue
file_basename = os.path.splitext(
stub.get_active_document_name()
)[0]
for extension in self.formats:
_filename = "{}.{}".format(file_basename, extension)
files[extension] = _filename
staging_dir = self.staging_dir(instance)
self.log.info("Outputting image to {}".format(staging_dir))
full_filename = os.path.join(staging_dir, _filename)
stub.saveAs(full_filename, extension, True)
self.log.info(f"Extracted: {extension}")
# Perform extraction
files = {}
ids = set()
layer = instance.data.get("layer")
if layer:
ids.add(layer.id)
add_ids = instance.data.pop("ids", None)
if add_ids:
ids.update(set(add_ids))
extract_ids = set([ll.id for ll in stub.
get_layers_in_layers_ids(ids, all_layers)
if ll.id not in hidden_layer_ids])
representations = []
for extension, filename in files.items():
representations.append({
"name": extension,
"ext": extension,
"files": filename,
"stagingDir": staging_dir
})
instance.data["representations"] = representations
instance.data["stagingDir"] = staging_dir
for extracted_id in extract_ids:
stub.set_visible(extracted_id, True)
self.log.info(f"Extracted {instance} to {staging_dir}")
file_basename = os.path.splitext(
stub.get_active_document_name()
)[0]
for extension in self.formats:
_filename = "{}.{}".format(file_basename,
extension)
files[extension] = _filename
full_filename = os.path.join(staging_dir,
_filename)
stub.saveAs(full_filename, extension, True)
self.log.info(f"Extracted: {extension}")
representations = []
for extension, filename in files.items():
representations.append({
"name": extension,
"ext": extension,
"files": filename,
"stagingDir": staging_dir
})
instance.data["representations"] = representations
instance.data["stagingDir"] = staging_dir
self.log.info(f"Extracted {instance} to {staging_dir}")
for extracted_id in extract_ids:
stub.set_visible(extracted_id, False)
def staging_dir(self, instance):
"""Provide a temporary directory in which to store extracted files
Upon calling this method the staging directory is stored inside
the instance.data['stagingDir']
"""
from openpype.pipeline.publish import get_instance_staging_dir
return get_instance_staging_dir(instance)