Python

From cgwiki

Bits of pymel, bits of houdini, bits of general file manipulation.

Renaming sequence of files

Have sequence starting at 5098034.jpg, needs to be 4 digit pad starting at 1.

Yes I'm sure its faster in bash/tcsh, whatevs.

import os
def doit():
    files = os.listdir(os.getcwd())
    # trim list to numbered jpgs, just in case
    files = [f for f in files if f[0].isdigit() and f.endswith(".jpg")]
    files.sort()
    fmt = '%04d.jpg'
    newnames = list(enumerate(files,1))
    newnames = [ (fmt % i[0], i[1]) for i in newnames]
 
    for f in newnames:
       os.rename(f[1], f[0])
       print '%s -> %s' % (f[1], f[0])
 
'''
# from python prompt:
import renum
import os
os.chdir('/path/to/images'); renum.doit()
'''

Fillframes, copy nearest frames in an image sequence to missing frames

Happens all the time; you have a broken render, or a long running render, tools downstream require a full image sequence to generate quicktimes or mp4s, but manually patching the missing frames is a chore. This is a largely untested, untrustworthy script that if given a directory, will find the missing exr images, and copy the nearest frame. Only works with exr, and assumes for prefix.####.exr.

#!/usr/bin/python
 
import os
import sys
import shutil
 
def fillframes():
    '''
    Given a directory, it will scan for a sequence of exrs, find missing images, 
    and copy the nearest frame to the missing ones to patch the holes.
    Assumes it'll find a bunch of exrs in the specified folder, named somethingsomething.####.exr.
 
    Error checking, saftey etc is almost non-existent. May contain traces of peanuts.
 
    Use thusly:
 
    fillframes.py /path/to/the/directory/of/exrs/
    '''
 
 
    try:
        dir = sys.argv[1]
    except IndexError:
	print 'usage: fillframes.py /path/to/folder'
	print '2nd argument not found, exiting'
	sys.exit()
    if not os.path.isdir(dir):
	print 'usage: fillframes.py /path/to/folder'
	print '2nd argument not a directory, exiting'
	sys.exit()
    print '\nscanning ', dir
 
    files = [x for x in os.listdir(dir) if x.endswith('.exr')]
    if not files:
	print 'no exr images found, exiting'
	sys.exit()
    prefix = files[0].split('.')[0]
    suffix = files[0].split('.')[2]
 
    actualframes = [int(f.split('.')[1]) for f in files]
    actualframes.sort()
 
    firstframe = actualframes[0]
    lastframe = actualframes[-1]
 
    idealrange = range(firstframe,lastframe+1)
 
    print 'found ' + str(len(actualframes)) + ' images'
    print 'startframe: ', firstframe
    print 'lastframe: ', lastframe
 
    if len(idealrange) == len(actualframes):
	    print 'no missing frames, all good!'
    else:
        for idealframe in idealrange:
        	if idealframe not in actualframes:
		    print idealframe, ' missing' 
		    nearestframe = min(actualframes, key=lambda x:abs(x-idealframe))
		    sourcename = [prefix, str(nearestframe), suffix]
		    sourcename = '.'.join(sourcename)
	    	    targetname = [prefix, str(idealframe), suffix]
	    	    targetname = '.'.join(targetname)
		    print 'copy ', sourcename,' -> ', targetname
		    sourcename = dir+sourcename
		    targetname = dir+targetname
		    shutil.copyfile(sourcename, targetname)
 
fillframes()

Write to a file, read it back

#source
build = hou.node('/obj/box').asCode(brief=False, recurse=True )
f = open('/tmp/houbuild.py','w'); f.write(build); f.close()
 
#target
del(hou_parent)    # if you've run this before
f = open('/tmp/houbuild.py','r'); build=f.read(); f.close(); exec(build)

Clone a nurbs curve to many curves

Like cloning poly meshes with outmesh to inmesh, you can connect mycurveshape.local to othercurve.create to have many curves replicate the shape of one curve. The connection editor is sucky at the best of times, and this seemed like a good excuse to write a clean little pymel tidbit. Master curve shape is called 'master_curve', and I've selected all the other curve shapes that'll be clones.

from pymel.core import *
 
for c in selected():
    SCENE.master_curve.local >> c.create
 
# same, but as a list comprehension
[ SCENE.master_curve.local >> c.create for c in selected() ]


Simple! 2 handy tricks here:

  • You can refer to anything in the scene by name with the SCENE. prefix
  • connecting attributes is as easy as using '>>'

clone curves with animated timeoffset

Similar to the previous example, except this time I have a soup timeoffset node which I've keyframed, I want that also duplicated and driving each curve shape I have selected. Boring repetitive work becomes very easy with pymel.

'But Matt!' you cry, 'Why don't you just duplicate with history?'. Because there's a massive history behind the original curve, and that'll remain identical for all the curves, the only thing I want different for each curve is the timeoffset, which I'll fiddle by hand. This creates me a simple setup ready for me to play with.

from pymel.core import *
 
driver = SCENE.hero_curve_shape
timeoffset = SCENE.timeOffset1
animcurve = SCENE.timeOffset1_time
 
for c in selected():  # assume this is a nurbs curve shape for now
    to = duplicate(timeoffset)[0]
    ac = duplicate(animcurve)[0]
    driver.local >> to.inGeometry
    to.outGeometry >> c.create
    ac.output >> to.time


Random scale/rotate of lots of trees

I'd instance-duplicated out a bunch of vray proxy trees, needed a little randomising. This set of 3 list comprehensions gives a random rotation in y between 0 and 360, a random y scale between 0.8 and 1.2, and shuffles their translation 15 units in x and z.

from pymel.core import *
import random
[ x.rotate.set( 0, random.randrange(0,360),0)   for x in ls(selection=True) ]
[ x.scale.set( 1, random.uniform(0.8,1.2) ,1)   for x in ls(selection=True) ]
[ x.translate.set( x.translate.get() + (random.randrange(-15,15),0,random.randrange(-15,15) ))   for x in ls(selection=True) ]

Set decay region on spotlights, move each light back 200 units on its own axis

Was doing a searchlights style shot, the vray volume fog went crazy at the base of the lights. figured it was cos it was crazy high intensity at a single point, so if i could use the light decay region to offset the light intensity away from its center, then offset the translation of the light to compensate, it'd help. It did. :)

from pymel.core import *
 
lgts = ls(selection=True)
for lgt in lgts:
    lgt.useDecayRegions.set(1)
 
    lgt.startDistance1.set(0)
    lgt.endDistance1.set(0)
 
    lgt.startDistance2.set(0)
    lgt.endDistance2.set(0)
 
    lgt.startDistance3.set(200)
    lgt.endDistance3.set(10000)
 
    g = group(empty=True, parent=lgt.getParent(), name='%s_offset'%lgt.name() )
    g.translate.set(0,0,200)
    parent(lgt, g, shape=True, relative=True)

Rename an image sequence

Good to do this in python, keep the skillz up etc.

import os
folder = '//path/to/bad/images/images/'
old = 'oldbrokenprefix'
new = 'newprefix'
os.chdir(folder)
 
for f in os.listdir(folder):
   os.rename(f,  f.replace(old,new)  )

Use os.walk to get all image files under the current folder

#!/usr/bin/python
 
suffixes = ['jpg', 'png','cr2']
suffixes += [x.upper() for x in suffixes]
suffixes = ['.'+x for x in suffixes]
other = []
matches = []
print suffixes
import os
for root, dirs, files in os.walk(os.curdir):
    for name in files:
        found = 0
        for suf in suffixes:
            if (suf in name):
                matches.append(os.path.join(root, name))
                found +=1
        if not found:
            other.append(os.path.join(root, name))
 
 
print other
print matches