Извлечение векторного изображения или изображения с высоким разрешением из графа узлов Nuke

#python #nuke

#python #nuke

Вопрос:

Я преподаватель VFX, и в настоящее время, когда я хочу получить изображения графов узлов для использования в слайдах лекций, я должен сделать график узлов полноэкранным и сделать снимок экрана, но, как вы можете себе представить, с большими сценариями мне приходится уменьшать масштаб настолько, что иногда его невозможно распознать.

Мне кажется, что размер графа узлов Nuke изменяется при увеличении и уменьшении масштаба, это, вероятно, какое-то векторное изображение под капотом. Я ищу способ либо экспортировать это изображение, чтобы я мог получить версию всего графа узлов с более высоким разрешением. Либо как векторное, либо просто растеризованное изображение с более высоким разрешением.

Кто-нибудь знает, может ли быть способ сделать это с помощью Python? Или есть какой-то внешний скрипт, который может это сделать?

Ответ №1:

Я давно этого хотел, но ваш вопрос заставил меня разобраться в этом немного глубже.

Я не верю, что под капотом доступно реальное векторное изображение (это OpenGL под капотом), но я также не понимаю, почему не было бы способа автоматизировать скриншот в полном разрешении.

Я написал скрипт, который проверит размер вашего графа узлов, установит масштабирование на 100% и сделает скриншот каждого фрагмента DAG, а затем автоматически прошьет его. Это занимает несколько секунд, потому что, если я пытаюсь сделать это слишком быстро, все путается из-за многопоточности, поэтому мне пришлось вводить искусственные паузы между каждым снимком экрана.

Вы должны быть в состоянии выполнить приведенный ниже код. Не забудьте указать свой путь в предпоследней строке!

 from PySide2 import QtWidgets, QtOpenGL, QtGui
from math import ceil
import time

 
def get_dag():
    stack = QtWidgets.QApplication.topLevelWidgets()
    while stack:
        widget = stack.pop()
        if widget.objectName() == 'DAG.1':
            for c in widget.children():
                if isinstance(c, QtOpenGL.QGLWidget):
                    return c
        stack.extend(c for c in widget.children() if c.isWidgetType())

def grab_dag(dag, path):
    dag.updateGL()  # This does some funky back and forth but function grabs the wrong thing without it
    pix = dag.grabFrameBuffer()
    pix.save(path)
    
class DagCapture(threading.Thread):
    def __init__(self, path, margins=20, ignore_right=200):
        self.path = path
        threading.Thread.__init__(self)
        self.margins = margins
        self.ignore_right = ignore_right

    def run(self):
        # Store the current dag size and zoom
        original_zoom = nuke.zoom()
        original_center = nuke.center()
        # Calculate the total size of the DAG
        min_x = min([node.xpos() for node in nuke.allNodes()]) - self.margins
        min_y = min([node.ypos() for node in nuke.allNodes()]) - self.margins
        max_x = max([node.xpos()   node.screenWidth() for node in nuke.allNodes()])   self.margins
        max_y = max([node.ypos()   node.screenHeight() for node in nuke.allNodes()])   self.margins

        # Get the Dag Widget
        dag = get_dag()
        if not dag:
            raise RuntimeError("Couldn't get DAG widget")

        # Check the size of the current widget, excluding the right side (because of minimap)
        capture_width = dag.width() - self.ignore_right
        capture_height = dag.height()

        # Calculate the number of tiles required to coveral all
        image_width = max_x - min_x
        image_height = max_y - min_y
        horizontal_tiles = int(ceil(image_width / float(capture_width)))
        vertical_tiles = int(ceil(image_height / float(capture_height)))
        # Create a pixmap to store the results
        pixmap = QtGui.QPixmap(image_width, image_height)
        painter = QtGui.QPainter(pixmap)
        painter.setCompositionMode(painter.CompositionMode_SourceOver)
        # Move the dag so that the top left corner is in the top left corner, screenshot, paste in the pixmap, repeat
        for xtile in range(horizontal_tiles):
            left = min_x   capture_width * xtile
            for ytile in range(vertical_tiles):
                top = min_y   capture_height * ytile
                nuke.executeInMainThread(nuke.zoom, (1, (left   (capture_width   self.ignore_right) / 2, top   capture_height / 2)))
                time.sleep(.5)
                nuke.executeInMainThread(grab_dag, (dag, self.path))
                time.sleep(.5)
                screengrab = QtGui.QImage(self.path)
                painter.drawImage(capture_width * xtile, capture_height * ytile, screengrab)
        painter.end()
        pixmap.save(self.path)
        nuke.executeInMainThread(nuke.zoom, (original_zoom, original_center))
        print "Capture Complete"

t = DagCapture("C:\Users\erwan\Downloads\test.png")
t.start()
  

Я уверен, что это можно было бы улучшить, но, надеюсь, это уже сэкономит вам время!

Редактировать: теперь я немного улучшил этот код: https://github.com/herronelou/nuke_dag_capture

Комментарии:

1. Вау, это выглядит потрясающе. Я просто проверяю это сейчас. Я не получаю никаких ошибок, это доходит до печати «захват завершен», но я также не получаю вывод файла. Я изменил путь, по которому вызывается класс DagCapture, в мой локальный каталог загрузки, но ничего не генерируется. Нужно ли мне устанавливать какие-либо другие библиотеки или те, которые вы используете, являются стандартными для Nuke (12.1v2)?

2. Здравствуйте. Я заметил, что у меня нет какой-либо проверки, чтобы узнать, успешно ли выполнено сохранение на диск или нет. Я побежал по неправильному пути, и все это вело себя так, как будто это было успешно, но потом ничего не произошло. Сейчас у меня нет времени его изменять, но добавьте печать перед pix.save(), чтобы увидеть, выводит ли он False или True

3. Он возвращает False …. коррекция, он возвращает x4 False

4. Правильно, так что это будет означать, что он не сохранится. Дважды проверьте путь, если в Windows убедитесь, что вы поставили двойные обратные косые черты, а не одиночные, убедитесь, что каталог существует (это не приведет к созданию каталогов), и что вы вводите имя файла с .png в конце.

5. Я отредактировал ответ, включив ссылку на GitHub, где я улучшил код, который я изначально написал для вас, чтобы добавить пользовательский интерфейс и разрешить пользовательские уровни масштабирования, поля и т.д…