Skip to content

Python

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

General

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.

python
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()
'''
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.

python
#!/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()
#!/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

python
#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)
#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)

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

python
#!/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
#!/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

Maya

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.

python
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() ]
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.

python
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
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.

python
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) ]
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. 😃

python
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)
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.

python
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)  )
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)  )