mesh fabrication

fabricating other objects

material slots

animation and fcurves

incorporating python libraries


shape keys

animating curve bevel


UV layers



video sequence editor (VSE)

images and textures

analytic geometry

node trees


bl_info = {
    "name" : "Composite Scene into Matte",
    "description": "An operator that configures the compositor to substitute the 3D scene into a chroma-keyed subset of the matte image chosen by the operator",
    "author" : "Robert Forsman <>",
    "version": (0,1),
    "blender": (2,74,0),
    #"location": "????",
    "warning": "",
    #"wiki_url": "",
    "category": "Node",

import bpy
from math import *

def get_image(path):
    matches = [ img for img in if img.filepath==path]
    if len(matches)>0:
        return matches[0]
    rval =
    return rval

def matches( c1, c2, epsilon = 0.02):

    for i in range(len(c1)):
        if i<len(c2):
            b = c2[i]
        if epsilon < abs(c1[i]- b):
            return False
    return True

def find_green_rectangle(img, chroma_key):
    # hold down the left mouse button in the UV/Image editor window to get the values for chroma_key

    width = img.size[0]
    #print (img.pixels[:4])
    pix = img.pixels[:]
    min_x = None
    max_x = None
    min_y = None
    max_y = None
    for i in range(0,len(pix), 4):
        one_pixel = pix[i:(i + 4)]
        if matches(one_pixel, chroma_key):
            x = floor(i/4)% width
            y = floor(i/4/ width)
            if min_x is None or x<min_x:
                min_x = x
                print([ i,x,y , one_pixel])
            if max_x is None or x>max_x:
                max_x = x
            if min_y is None or y<min_y:
                min_y = y
            if max_y is None or y > max_y:
                max_y = y
        #if i%100000 ==0:
        #    print(i)
    return [ min_x, min_y, max_x, max_y, width, img.size[1]]

def convert_bounds_to_node_parameters(scn, x1, y1, x2, y2, W, H):
    translate_x = (x2+x1-W)/2 /W
    translate_y = (y2+y1-H)/2 /H
    scale_x = (x2-x1)/W
    scale_y = (y2-y1)/H

    #scale_nodes = [ n for n in scn.node_tree.nodes if n.type=='SCALE']
    # use the N-panel in the compositor nodes to give your scale node this name
    scale_nodes = [ n for n in scn.node_tree.nodes if'matte scale']
    translate_nodes = [ n for n in scn.node_tree.nodes if == 'matte translate']

    scale_nodes[0].inputs[1].default_value = scale_x
    scale_nodes[0].inputs[2].default_value = scale_y

    translate_nodes[0].inputs[1].default_value = translate_x
    translate_nodes[0].inputs[2].default_value = translate_y
    translate_nodes[0].use_relative = True

def wipe_and_rebuild_compositor_nodes(scn, img, wipe=True):

    scn.use_nodes = True
    while wipe and len(scn.node_tree.nodes)>0:

    nodes = scn.node_tree.nodes
    links = scn.node_tree.links

    dx = 200
    image_n ="CompositorNodeImage")
    image_n.location = (0,0)
    image_n.image = img

    #matte_n ="CompositorNodeColorMatte")
    #matte_n.location = (dx,0)[0], image_n.outputs[0])

    scale1_n ="CompositorNodeScale")
    scale1_n.location = (2*dx,0)[0], image_n.outputs[0])[0], matte_n.outputs[0])
    if True: = 'RENDER_SIZE'
        scale1_n.frame_method = 'STRETCH'
        scale1_n.inputs[1].default_value = scn.render.resolution_x / img.size[0]
        scale1_n.inputs[2].default_value = scn.render.resolution_y / img.size[1]

    layer_n ='CompositorNodeRLayers')
    layer_n.location = (0,y2)

    scale2_n ="CompositorNodeScale")
    scale2_n.location = (dx,y2) = "matte scale"[0], layer_n.outputs[0])

    translate_n ="CompositorNodeTranslate")
    translate_n.location = (2*dx, y2) = "matte translate"[0], scale2_n.outputs[0])

    mix_n ="CompositorNodeAlphaOver")
    mix_n.location = (3*dx, 0)[1], scale1_n.outputs[0])[2], translate_n.outputs[0])

    output ='CompositorNodeComposite')
    output.location = (4*dx,0)[0], mix_n.outputs[0])


class CompositeSceneIntoMatte(bpy.types.Operator):
    bl_idname = "compositor.scene_into_matte"
    bl_label = "Composite Scene Into Matte"
    bl_options = {'REGISTER', 'UNDO'}

    matte_filename = bpy.props.StringProperty(name="Matte file path",
                                              description="path to the image that provides the matte image",
    matte_color = bpy.props.FloatVectorProperty(
            name="Matte chroma key",
            default=(0, 1, 0),
            min=0.0, max=1.0,
            description="color from the matte image that marks where the 3D scene should be overlaid"

    def execute(self, ctx):
            try :
                img = get_image(self.matte_filename)
            except :
      {'WARNING'}, "failed to load image from filename %r"%self.matte_filename)
                return {'FINISHED'}

            bounds = find_green_rectangle(img, self.matte_color)
            if bounds[2] is None:
      {'WARNING'}, "matte color %r does not appear in image"%self.matte_color)
                return {'FINISHED'}
            wipe_and_rebuild_compositor_nodes(ctx.scene, img)
            convert_bounds_to_node_parameters(ctx.scene, bounds[0], bounds[1], bounds[2]+1, bounds[3]+1, bounds[4], bounds[5])
            return {'FINISHED'}
        except BaseException as e:
  {'ERROR'}, e.args[0])
            return {'CANCELLED'}


def register():

def unregister():

if __name__ == "__main__":

if False:
    bounds = find_green_rectangle(get_image("/var/tmp/matte.png"))

    scn = bpy.context.scene
    convert_bounds_to_node_parameters(scn, bounds[0], bounds[1], bounds[2]+1, bounds[3]+1, bounds[4], bounds[5])

Blender python API quick-start

Syntax highlighting by Pygments.