Set origin to bottom center of multiple objects
Move the origin to bottom of all selected mesh objects.

EDIT updated for 2.8.  See prior revision for prior.

Object mode script. Moves origin to bottom of mesh, leaving object in place. Pass the matrix world to move origin to global z.


Looks at the 8 coords of objects bounding box.  The bounding box is
in local coordinates. To change space pass a matrix. eg the matrix
world of the object will determine lowest z in global space.
o is the bounding box center. Set o.z for new origin from min z of
bounding box points.
Transform all vertices by -o
Move the object back globally by o to keep the object in place.


&nbsp;

import bpy
from mathutils import Matrix, Vector


def origin_to_bottom(ob, matrix=Matrix()):
    me = ob.data
    mw = ob.matrix_world
    local_verts = [matrix @ Vector(v[:]) for v in ob.bound_box]
    o = sum(local_verts, Vector()) / 8
    o.z = min(v.z for v in local_verts)
    o = matrix.inverted() @ o
    me.transform(Matrix.Translation(-o))

    mw.translation = mw @ o

for o in bpy.context.scene.objects:
    if o.type == 'MESH':
        origin_to_bottom(o)
        #origin_to_bottom(o, matrix=o.matrix_world) # global


With some numpy

The global version above drops in global z direction from bbox center of geom, which can lead to an origin outside the bounding box.

Instead here is a version that finds all z minima and sets the origin to their mean

from mathutils import Matrix, Vector
import numpy as np

def origin_to_bottom(ob, matrix=Matrix(), use_verts=False):
    me = ob.data
    mw = ob.matrix_world
    if use_verts:
        data = (v.co for v in me.vertices)
    else:
        data = (Vector(v) for v in ob.bound_box)


    coords = np.array([matrix @ v for v in data])
    z = coords.T[2]
    mins = np.take(coords, np.where(z == z.min())[0], axis=0)

    o = Vector(np.mean(mins, axis=0))
    o = matrix.inverted() @ o
    me.transform(Matrix.Translation(-o))

    mw.translation = mw @ o    


This could be further enhanced by using methods outlined in this excellent Q / A

Replace matrix @ vector list comprehensions with something more efficient

Note: Remember objects can share a mesh. The test code above runs over all mesh objects in the scene without checking for this.

Related: Origins to the down of the object by default