Viewport Screenshot
17887 19 7- erikcarlson
- Member
- 24 posts
- Joined: Dec. 2015
- Offline
- Alexey Vanzhula
- Member
- 538 posts
- Joined: Dec. 2006
- Offline
Create shelf tool with script:
from time import gmtime, strftime
# GET VIEWPORT CAMERA PATH
cur_desktop = hou.ui.curDesktop()
desktop = cur_desktop.name()
panetab = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).name()
persp = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).curViewport().name()
camera_path = desktop + “.” + panetab + “.” + “world” “.” + persp
# BUILD DEFAULT FILE NAME FROM CURRENT TIME
default_filename = strftime(“screenshot_%d_%b_%Y_%H_%M_%S.jpg”, gmtime())
# SELECT FILE
filename = hou.ui.selectFile( title='Select Screenshot File', default_value=default_filename, file_type=hou.fileType.Image )
# WRITE TO FILE
if filename is not None:
frame = hou.frame()
hou.hscript( “viewwrite -f %d %d %s ‘%s’” % (frame, frame, camera_path, filename) )
from time import gmtime, strftime
# GET VIEWPORT CAMERA PATH
cur_desktop = hou.ui.curDesktop()
desktop = cur_desktop.name()
panetab = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).name()
persp = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).curViewport().name()
camera_path = desktop + “.” + panetab + “.” + “world” “.” + persp
# BUILD DEFAULT FILE NAME FROM CURRENT TIME
default_filename = strftime(“screenshot_%d_%b_%Y_%H_%M_%S.jpg”, gmtime())
# SELECT FILE
filename = hou.ui.selectFile( title='Select Screenshot File', default_value=default_filename, file_type=hou.fileType.Image )
# WRITE TO FILE
if filename is not None:
frame = hou.frame()
hou.hscript( “viewwrite -f %d %d %s ‘%s’” % (frame, frame, camera_path, filename) )
- erikcarlson
- Member
- 24 posts
- Joined: Dec. 2015
- Offline
- Mohanpugaz
- Member
- 146 posts
- Joined: June 2016
- Offline
That is cool trick to know.. thanks for sharing Vux!
Mohan Pugaz
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
- goldfarb
- Staff
- 3459 posts
- Joined: July 2005
- Offline
- cb
- Staff
- 635 posts
- Joined: July 2005
- Offline
elveatles
Is it possible to save what is currently in the active viewport using Python? Like flipbook for just one frame. I cannot find a way to do this and would really appreciate help.
There is also something called an “OpenGL ROP” in Houdini. I don't think it's quite what you want in this particular case, but it may serve you well in other instances when you just need a quick render of your geometry.
Cristin
- Mohanpugaz
- Member
- 146 posts
- Joined: June 2016
- Offline
vux
Create shelf tool with script:
from time import gmtime, strftime
# GET VIEWPORT CAMERA PATH
cur_desktop = hou.ui.curDesktop()
desktop = cur_desktop.name()
panetab = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).name()
persp = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer).curViewport().name()
camera_path = desktop + “.” + panetab + “.” + “world” “.” + persp
# BUILD DEFAULT FILE NAME FROM CURRENT TIME
default_filename = strftime(“screenshot_%d_%b_%Y_%H_%M_%S.jpg”, gmtime())
# SELECT FILE
filename = hou.ui.selectFile( title='Select Screenshot File', default_value=default_filename, file_type=hou.fileType.Image )
# WRITE TO FILE
if filename is not None:
frame = hou.frame()
hou.hscript( “viewwrite -f %d %d %s ‘%s’” % (frame, frame, camera_path, filename) )
For all of you who try to use VUX's script.
I copy pasted this script and it doesnt work at first.
Because there is something wrong with the double quotes,
I just deleted the quotes and retyped them that works perfectly
Thanks a lot to VUX for this script its really useful for me
Mohan Pugaz
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
- malexander
- Staff
- 5201 posts
- Joined: July 2005
- Offline
Note that H16.0 added HOM support for flipbooking directly, so that you could do the entire script in HOM now without the hscript(“viewwrite”) command.
scene = cur_desktop.paneTabOfType(hou.paneTabType.SceneViewer) flip_options = scene.flipbookSettings().stash() flip_options.frameRange( (frame, frame) ) flip_options.output(filename) scene.flipbook(scene.curViewport(), flip_options)
- Mohanpugaz
- Member
- 146 posts
- Joined: June 2016
- Offline
Really cool to know , thanks for the useful info and code
Mohan Pugaz
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
movfx
https://www.instagram.com/movfx/ [www.instagram.com]
https://www.youtube.com/channel/@_movfx
- animatrix_
- Member
- 4677 posts
- Joined: Feb. 2012
- Offline
Can we now both launch the flipbook window and also save the result on disk in H16? Before it wasn't possible. For example auto save flipbook on disk while also showing it interactively flipbooking using mplay?
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]
youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]
youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
- malexander
- Staff
- 5201 posts
- Joined: July 2005
- Offline
- animatrix_
- Member
- 4677 posts
- Joined: Feb. 2012
- Offline
Thanks Mark that's what I wanted to do. I was told before it couldn't be done.
ID #72062
I thought once I launch flipbook, I can't control mplay anymore. Any idea how would this look like in pseudo code?
ID #72062
I thought once I launch flipbook, I can't control mplay anymore. Any idea how would this look like in pseudo code?
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]
youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]
youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
- landyvfx
- Member
- 8 posts
- Joined: Sept. 2017
- Offline
- Andr
- Member
- 899 posts
- Joined: Feb. 2016
- Offline
Hello, I have to bump this old discussion.
I tried the code, which works super fine, but it popups the save dialog.
I need it to automatically write the viewport.jpg to disk, as fast as possible, as my trying to do a batch export of the crude geometry in the viewport. No need fancy stuff like AA, or shadows. Headlight only.
Ideally it should save the viewport_$F.jpg just after houdini cooks the frame. (so no save sequence)
I had a look at the hou.FlipbookSettings class [www.sidefx.com] , but couldn't find any method to automatically skip the save dialog, and write the file to disk.
thanks for any advice!
I tried the code, which works super fine, but it popups the save dialog.
I need it to automatically write the viewport.jpg to disk, as fast as possible, as my trying to do a batch export of the crude geometry in the viewport. No need fancy stuff like AA, or shadows. Headlight only.
Ideally it should save the viewport_$F.jpg just after houdini cooks the frame. (so no save sequence)
I had a look at the hou.FlipbookSettings class [www.sidefx.com] , but couldn't find any method to automatically skip the save dialog, and write the file to disk.
thanks for any advice!
- Andr
- Member
- 899 posts
- Joined: Feb. 2016
- Offline
Should I go this way maybe?
https://pypi.org/project/pyscreenshot/ [pypi.org]
External module to take screenshots and save them to disk.
*Edit*
I guess I should use the openGL render node.
https://pypi.org/project/pyscreenshot/ [pypi.org]
External module to take screenshots and save them to disk.
*Edit*
I guess I should use the openGL render node.
Edited by Andr - Nov. 13, 2018 16:21:33
- jsmack
- Member
- 8034 posts
- Joined: Sept. 2011
- Online
- Andr
- Member
- 899 posts
- Joined: Feb. 2016
- Offline
- sdugaro
- Member
- 380 posts
- Joined: July 2005
- Offline
This was a tad annoying for me in H19.0.455 as the MPlay window would launch by default whenever I invoked the render -- until I discovered that outputToMplay() is True by default.
import traceback from os.path import dirname,basename,splitext def getSceneViewer(): """ return an instance of a visible viewport. There may be many, some could be closed, any visible are current """ panetabs = hou.ui.paneTabs() panetabs = [x for x in panetabs if x.type() == hou.paneTabType.SceneViewer] panetabs = sorted(panetabs, key=lambda x: x.isCurrentTab()) if panetabs: return panetabs[-1] else: print("No SceneViewers detected.") return None def viewwrite(): """ Get the current sceneviewer (may be more than one or hidden) and screengrab the perspective viewport to a file in the publish location to be picked up with the publish. Note that .png output will render pooly, so use .jpg """ try: node = hou.node(".") path = node.parm("outputpath").eval() file,ext = splitext(basename(path)) filename = f'{dirname(path)}/{file}.jpg' frame = hou.frame() desktop = hou.ui.curDesktop() sceneview = getSceneViewer() viewport = sceneview.curViewport() # this will open an mplay window to show the result fbs = sceneview.flipbookSettings().stash() fbs.frameRange( (frame, frame) ) fbs.output(filename) fbs.outputToMPlay(False) sceneview.flipbook(viewport, fbs) except Exception as e: print(f'ERROR: Failed to Capture Viewport\n{traceback.format_exc(2)}')
- AnimGraphLab
- Member
- 66 posts
- Joined: May 2019
- Offline
This can be also done with 10 lines of Python.
There's a
At this location: C:\Program Files\Side Effects Software\Houdini 20.5.365\houdini\python3.11libs\husd
The whole code (without force cooking Python SOP):
There's a
saveThumbnailFromViewer()
function that ships with Houdini in husd
assetutils
At this location: C:\Program Files\Side Effects Software\Houdini 20.5.365\houdini\python3.11libs\husd
The whole code (without force cooking Python SOP):
from husd import assetutils viewer = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer) assetutils.saveThumbnailFromViewer( sceneviewer = viewer, frame=1, res=(512,512), output="$HIP/textures/thumbnail.png" )
Generalist. Transforming still images to 3D animation 🔮
Socials: https://linktr.ee/AnimGraphLab [linktr.ee]
Socials: https://linktr.ee/AnimGraphLab [linktr.ee]
- vinyvince
- Member
- 275 posts
- Joined: Sept. 2012
- Offline
I built a Null pipeline helper
One function it could do is to make you capture your object and place that preview next to the null , hooked, in your network...
Doing similar thing in an HDA panel is more difficult, i should give another try...
Here is the python code :
One function it could do is to make you capture your object and place that preview next to the null , hooked, in your network...
Doing similar thing in an HDA panel is more difficult, i should give another try...
Here is the python code :
import hou import os import subprocess import time from time import gmtime, strftime from pathlib import Path from sys import platform widthRatio = 4 # Change to make the screenshot bigger or smaller, this is ~x4 node width def takeScreenShot(savePath): ''' Take screenshot with a region select tool for different platforms ''' Path(os.path.dirname(Path(savePath))).mkdir(parents=True, exist_ok=True) if platform == "linux" or platform == "linux2": savepath = os.path.dirname(Path(savePath)) screenshotPNG = subprocess.check_output('mv "$(xfce4-screenshooter -ro ls)" -v ' + savepath, shell=True) screenshotPNG = screenshotPNG.decode().strip().split('\n')[0].split(' -> ')[1].replace("'", "") os.chdir(savepath) os.rename(os.path.basename(screenshotPNG), os.path.basename(savePath)) elif platform == "darwin": pass # Add Mac-specific screenshot logic here if needed elif platform == "win32": subprocess.check_call([r"C:\Users\vincent\Downloads\MiniCap\MiniCap.exe", "-captureregselect", "-save", savePath, "-exit"]) # Ensure the screenshot has been saved and the file exists if not os.path.exists(savePath): raise FileNotFoundError(f"Screenshot not saved at: {savePath}") def wait_for_image(path, timeout=10): ''' Waits until the screenshot image is available ''' elapsed = 0 while not os.path.exists(path) and elapsed < timeout: time.sleep(0.5) # Wait 0.5 seconds before checking again elapsed += 0.5 if not os.path.exists(path): raise FileNotFoundError(f"Image not found after waiting {timeout} seconds: {path}") def removeBackgroundImage(**kwargs): ''' Removes background image from the network editor ''' deletingNode = [x[1] for x in kwargs.items()][0] image = deletingNode.userData('houdinipath') editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor) backgroundImagesDic = editor.backgroundImages() backgroundImagesDic = tuple(x for x in backgroundImagesDic if x.path() != image) editor.setBackgroundImages(backgroundImagesDic) def changeBackgroundImageBrightness(event_type, **kwargs): ''' Changes brightness/visibility if template or bypass flags are checked ''' nullNode = [x[1] for x in kwargs.items()][0] image = nullNode.userData('houdinipath') brightness = 1.0 if nullNode.isBypassed(): brightness = 0.0 elif nullNode.isTemplateFlagSet(): brightness = 0.5 editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor) backgroundImagesDic = editor.backgroundImages() for i, item in enumerate(backgroundImagesDic): if item.path() == image: backgroundImagesDic[i].setBrightness(brightness) break editor.setBackgroundImages(backgroundImagesDic) # Generate unique path for screenshot timestamp = strftime('%Y%m%d_%H%M%S', gmtime()) hipname = str(hou.getenv('HIPNAME')) hippath = str(hou.getenv('HIP')) + '/screenshots' screenshotName = hipname + '.' + timestamp + '.png' systempath = os.path.join(hippath, screenshotName) houdinipath = f'$HIP/screenshots/{screenshotName}' # Take screenshot and wait for the file to be created takeScreenShot(systempath) wait_for_image(systempath) # Set up background image plane editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor) image = hou.NetworkImage() image.setPath(houdinipath) # Create or select null node sel = hou.selectedNodes() nullNode = '' if sel: lastSel = sel[-1] nullNode = lastSel.parent().createNode('null', 'screenshot') if lastSel.outputConnections(): nullNode.setInput(0, lastSel) else: nullNode = editor.pwd().createNode('null', 'screenshot') nullNode.moveToGoodPosition() lastSel = nullNode # Configure image plane placement nullNode.setUserData('nodeshape', 'task') nullNode.setPosition(lastSel.position()) nullNode.setColor(hou.Color(0.3, 0.3, 0.3)) # Adjusted node movement to reduce offset nullNode.move([lastSel.size()[0] * 1.5, -lastSel.size()[1] * 1.5]) # Calculate image resolution and placement rez = hou.imageResolution(systempath) ratio = 1.0 * rez[1] / rez[0] rect = hou.BoundingRect(0, -lastSel.size()[1] * 1.05, widthRatio, -widthRatio * ratio - lastSel.size()[1] * 1.05) image.setRelativeToPath(nullNode.path()) image.setRect(rect) # Store the image path as user data instead of a parameter nullNode.setUserData('houdinipath', houdinipath) # Attach a function that deletes the background image plane if the corresponding node is deleted nullNode.addEventCallback((hou.nodeEventType.BeingDeleted,), removeBackgroundImage) # Attach a function to change visibility or opacity if corresponding node flags are changed nullNode.addEventCallback((hou.nodeEventType.FlagChanged,), changeBackgroundImageBrightness) # Add image to network background backgroundImagesDic = editor.backgroundImages() backgroundImagesDic = backgroundImagesDic + (image,) editor.setBackgroundImages(backgroundImagesDic)
Edited by vinyvince - Oct. 2, 2024 10:20:48
Vincent Thomas (VFX and Art since 1998)
Senior Env and Lighting artist & Houdini generalist & Creative Concepts
http://fr.linkedin.com/in/vincentthomas [fr.linkedin.com]
Senior Env and Lighting artist & Houdini generalist & Creative Concepts
http://fr.linkedin.com/in/vincentthomas [fr.linkedin.com]
-
- Quick Links