max hud拍屏


# -*- coding: utf-8 -*-
from cv2 import imread
import getpass
import json
import time
import unicodedata

try:
    import _winreg as winreg
except ModuleNotFoundError:
    import winreg
import os
import tempfile

import shutil
import subprocess
st = subprocess.STARTUPINFO
st.dwFlags = subprocess.STARTF_USESHOWWINDOW
st.wShowWindow = subprocess.SW_HIDE

from Qt import QtWidgets, QtCore
from pymxs import runtime as rt

import pipe
import pipe.log
from .ui import playblast_ui
from max_pipe.tools.gen import utils

LOGGER = pipe.log.setup_default_logger('hud_playblast',
                                       'wuzu/max_pipe/ani/tools/hud_display')

DEFAULT_BORDER_COLOR = [0, 0, 0, 0.5]
DEFAULT_TEXT_COLOR = [0, 1, 0, 1]

CONTROL_DATAS = [
    {'control_type': 'colorSliderGrp', 'name': 'data_color_slider',
     'label': u'字体颜色', 'attr': 'fontColor'},
    {'control_type': 'floatSliderButtonGrp', 'name': 'data_alpha_slider',
     'label': u'字体透明度', 'attr': 'fontAlpha',
     'min': 0, 'max': 1},
    {'control_type': 'colorSliderGrp', 'name': 'border_color_slider',
     'label': u'遮罩颜色', 'attr': 'borderColor'},
    {'control_type': 'floatSliderButtonGrp', 'name': 'border_alpha_slider',
     'label': u'遮罩透明度', 'attr': 'borderAlpha',
     'min': 0, 'max': 1},
    {'control_type': 'floatSliderButtonGrp', 'name': 'text_padding_slider',
     'label': u'侧边距', 'attr': 'textPadding',
     'min': 0.1, 'max': 50},
    {'control_type': 'floatSliderButtonGrp', 'name': 'font_scale_slider',
     'label': u'字体大小', 'attr': 'fontScale',
     'min': 0.1, 'max': 2},
    {'control_type': 'floatSliderButtonGrp', 'name': 'border_scale_slider',
     'label': u'遮罩大小', 'attr': 'borderScale',
     'min': 0.5, 'max': 2}]

DATA_ITEMS = [
    u'无',
    u'摄像机名称',
    u'焦距',
    u'日期',
    u'任务环节',
    u'帧范围',
    u'文件名',
    u'当前帧',
    u'用户名']

def desktop_path():
    key = winreg.OpenKey(
        winreg.HKEY_CURRENT_USER,
        r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
    return winreg.QueryValueEx(key, "Desktop")[0]


class PlayBlastWindow(QtWidgets.QMainWindow,
                      playblast_ui.Ui_hud_playblast_window):
    def __init__(self, parent=None):
        super(PlayBlastWindow, self).__init__(parent=parent)
        LOGGER.info(u'Max_HUD工具调用')
        self.data = self.load_default_data()
        self.setupUi(self)
        self.setup_ui_user()
        self.connect_signal()

    def setup_ui_user(self):
        self.set_output_path(default=True)
        self.set_default_data()
        self.load_camera()
        self.set_default_data()

    def load_default_data(self):
        """
        读取默认配置

        """
        config = pipe.config.init_nconfig('hud_display')
        data = config.get(key='max_hud_default')
        return data

    def connect_signal(self):
        self.playblast_but.clicked.connect(self.playblast)
        self.load_but.clicked.connect(self.set_output_path)
        self.refresh_but.clicked.connect(self.load_camera)
        self.color_change_but.clicked.connect(self.text_color_changed)
        self.size_slider.valueChanged.connect(self.size_slider_changed)
        self.size_box.valueChanged.connect(self.size_box_changed)

    def load_camera(self):
        self.camera_combo.clear()
        all_cams = []
        for node_type in ['Freecamera', 'Targetcamera', 'Physical']:
            all_cams.extend(utils.get_node_by_type(node_type))

        for camera in all_cams:
            self.camera_combo.addItem(camera.name)

    def text_color_changed(self):
        color = QtWidgets.QColorDialog.getColor(QtCore.Qt.green).getRgb()[0:3]
        qss = "background-color:rgb{}".format(color)
        self.text_color_frame.setStyleSheet(qss)
        self.data['fontColor'] = color

    def size_box_changed(self):
        box_value = self.size_box.value()
        slider_value = self.size_slider.value()
        if box_value != slider_value:
            self.size_slider.setValue(box_value)
        self.data['fontScale'] = box_value

    def size_slider_changed(self):
        box_value = self.size_box.value()
        slider_value = self.size_slider.value()
        if box_value != slider_value:
            self.size_box.setValue(slider_value)
        self.data['fontScale'] = slider_value

    def set_default_data(self):
        """
        UI读取预设配置
        """
        self.task_type_line.setText(self.data['taskType'])
        for hud_name, hud_data in self.data['hud_items'].items():
            combo = self.findChild(QtWidgets.QComboBox,
                                   '{}_combo'.format(hud_name))
            line = self.findChild(QtWidgets.QLineEdit,
                                  '{}_line'.format(hud_name))
            if combo:
                items = set()
                for i in range(len(DATA_ITEMS)):
                    items.add(combo.itemText(i))

                for text in DATA_ITEMS:
                    if text in items:
                        continue
                    combo.addItem(text)
                    combo.setCurrentIndex(hud_data)
            elif line:
                line.setText(hud_data)
        self.size_box.setValue(self.data['fontScale'])
        self.size_slider.setValue(self.data['fontScale'])

    def set_output_path(self, default=False):
        work_space = rt.maxFilePath if rt.maxFilePath else desktop_path()
        filename = rt.maxFileName[:-4] if rt.maxFileName else 'untitled'
        if default:
            path = os.path.join(work_space, filename) + '.mov'

        else:
            path = QtWidgets.QFileDialog.getSaveFileName(self,
                                                         'getSaveFileName',
                                                         work_space,
                                                         'MOV(*.mov)')
        self.mov_path_line.setText(path)

    def playblast(self):
        output_path = self.mov_path_line.text()
        temp_dir = tempfile.mkdtemp(prefix="hud_render_")
        camera_name = self.camera_combo.currentText()
        camera = rt.getnodebyname(camera_name)
        rt.viewport.setCamera(camera)
        start = rt.animationRange.start
        end = rt.animationRange.end
        json_data = {}

        config = pipe.config.init_nconfig('hud_display')
        w, h = config.get(key='max_pixel_size')
        rt.renderWidth = w
        rt.renderHeight = h
        rt.createPreview(percentSize=100, start=start, end=end,
                         dspFrameNums=True, dspGrid=False, dspCameras=False,
                         dspBones=False, dspHelper=False, vfb=False)
        avi_path = os.path.join(os.getenv('USERPROFILE'),
                                'Documents/3dsMax/previews/_scene.avi')
        command_cmd = r'"ffmpeg" -y  -i {} {}'.format(
            avi_path, temp_dir + '/render_%04d.png')

        error = subprocess.check_output(command_cmd)
        if error:
            self.logger.error("Failed to ffmpeg compose video")

        img_num = 1
        for frame in range(start, end + 1):
            rt.sliderTime = frame
            json_data[img_num] = self.get_hud()
            img_num += 1
        json_path = temp_dir + '/info.json'
        with open(json_path, 'w') as f:
            json.dump(json_data, f)

        img_num = 1
        for frame in range(start, end + 1):
            temp_path = temp_dir + '/render_{:0>4}.png'.format(img_num)
            text_path = temp_dir + '/render_t_{:0>4}.png'.format(img_num)
            color = '0x'
            for i in self.data['fontColor']:
                color += '{:0>2}'.format(hex(i)[2:])
            draw_hud(img_num, self.data['fontScale'],  color,
                     json_path, temp_path, text_path)
            img_num += 1

        command_cmd = r'"ffmpeg" -y  -i {}/render_t_%4d.png {}'.format(
            temp_dir, output_path)
        error = subprocess.check_output(command_cmd)
        if error:
            self.logger.error("Failed to ffmpeg compose video")
        else:
            shutil.rmtree(temp_dir)
        os.startfile(output_path)

    def get_hud(self):
        info = {}
        get_data_fun = {
            u'无': lambda: '',
            u'摄像机名称': lambda: self.camera_combo.currentText(),
            u'焦距': lambda: round(rt.gw.getFocalDist(), 3),
            u'日期': lambda: time.strftime("%Y/%m/%d", time.localtime()),
            u'任务环节': lambda: self.task_type_line.text(),
            u'帧范围': lambda: '{}/{}'.format(int(rt.animationRange.start),
                                           int(rt.animationRange.end)),
            u'文件名': lambda: rt.maxFileName[:-4] if rt.maxFileName else ' ',
            u'当前帧': lambda: int(rt.currentTime),
            u'用户名': lambda: getpass.getuser()}

        for postion in ["topLeft", "topCenter",
                        "topRight", "bottomLeft",
                        "bottomCenter", "bottomRight"]:
            combo = self.findChild(QtWidgets.QComboBox,
                                   '{}Data_combo'.format(postion))
            line = self.findChild(QtWidgets.QLineEdit,
                                  '{}Label_line'.format(postion))
            label = line.text()
            text = get_data_fun[combo.currentText()]()

            info[postion] = '{}{}'.format(label, text)
        return info


def draw_hud(frame, size, color, json_path, img_path, output_path):
    with open(json_path, 'r') as f:
        data = json.load(f)[str(frame)]
    img_h, img_w, _ = imread(img_path).shape
    item = ["topLeft", "topCenter",
            "topRight", "bottomLeft",
            "bottomCenter", "bottomRight"]
    dip = {"topLeft": ('30', '30'),
           "topCenter": ('w/2- tw/2', '30'),
           "topRight": ('w-tw-30', '30'),
           "bottomLeft": ('30', 'h-th-30'),
           "bottomCenter": ('w/2-tw/2', 'h-th-30'),
           "bottomRight": ('w-tw-30', 'h-th-30')}
    for postion in item:
        text = data[postion]
        command_str = ('ffmpeg -y -i {img_path} -vf "drawtext=fontcolor={color}:' 
                       'fontsize={size}:text=\'{text}\':x={w}:y={h}:' 
                       'shadowy=2" {output_path}').format(
            img_path=img_path,
            color=color,
            size=size,
            text=text,
            w=dip[postion][0],
            h=dip[postion][1],
            output_path=output_path)

        error = subprocess.check_output(command_str)
        if error:
            return "Failed to ffmpeg compose video"
        shutil.copy(output_path, img_path)


def main():
    playblast_mainwindows = PlayBlastWindow()
    return playblast_mainwindows


if __name__ == '__main__':
    main()

评论
  目录