# -*- 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()
评论