Пример #1
0
def testRotationBasic():
  # general parameters
  o = SED.opts(lie='se2')
  m = getattr(lie, o.lie)
  mRot = getattr(lie, o.lieRot)

  # T, K = ( 10000, 4 )
  T, K = ( 10, 4 )

  T = 10
  y, z, x, omega, theta, Q, S, E = generateSyntheticK4(o, T)
  K = S.shape[0]

  Strans = S[:,:o.dy,:o.dy]
  Srot = S[:,o.dy:,o.dy:]
  
  thetaR = theta.copy()
  for t in range(1,T-1):
    for k in range(K):
      R_cur, d_cur = m.Rt(theta[t,k])
      zeroAlgebra = np.zeros(o.dxA)

      # test inference here
      R_prev, d_prev = m.Rt(theta[t-1,k])
      R_next, d_next = m.Rt(theta[t+1,k])

      lhs = x[t] @ omega[k]
      rhs = np.eye(o.dy+1)
      y_tk = y[t][z[t]==k]
      R_U = SED.sampleRotation(o, y_tk, d_cur, lhs, rhs, E[k], theta[t-1,k],
        S[k], nextU=theta[t+1,k], controlled=True)
      thetaR[t,k,:o.dy,:o.dy] = R_U

  colorsA = du.diffcolors(K, bgCols=[[0,0,0],[1,1,1]], alpha=0.5)
  colors = du.diffcolors(K, bgCols=[[0,0,0],[1,1,1]])
  def show(t):
    # plot x
    T_world_object = x[t]
    m.plot(T_world_object, np.tile([0,0,0], [2,1]), l=10.0)

    # plot theta[t,k]
    for k in range(K):
      T_world_part = x[t] @ omega[k] @ theta[t,k]
      m.plot(T_world_part, np.tile(colorsA[k], [2,1]), l=10.0)
      plt.scatter(*y[t][k].T, color=colors[k], s=1, alpha=0.25)

    # plot thetaR[t,k]
    for k in range(K):
      T_world_part = x[t] @ omega[k] @ thetaR[t,k]
      m.plot(T_world_part, np.tile(colors[k], [2,1]), l=10.0)

    plt.xlim(-100, 100)
    plt.ylim(-100, 100)

  du.ViewPlots(range(1,T-1), show)
  plt.show()
Пример #2
0
def show(o, y, x, omega, theta, E, z, t):
  o = SED.opts(lie='se2')
  m = getattr(lie, o.lie)
  mRot = getattr(lie, o.lieRot)
  K = E.shape[0]
  colors = du.diffcolors(K, bgCols=[[0,0,0],[1,1,1]])

  # plot x
  T_world_object = x[t]
  m.plot(T_world_object, np.tile([0,0,0], [2,1]), l=10.0)

  # plot x[t] @ omega[k] @ theta[t,k] in world coordinates
  for k in range(K):
    T_world_part = x[t] @ omega[k] @ theta[t,k]
    m.plot(T_world_part, np.tile(colors[k], [2,1]), l=10.0)

    yMu = SED.TransformPointsNonHomog(T_world_part, o.zeroObs)
    R = T_world_part[:-1,:-1]
    ySig = R.dot(E[k]).dot(R.T)
    plt.plot( *du.stats.Gauss2DPoints(yMu, ySig, deviations=2.0), color=colors[k])

  plt.scatter(*y[t].T, s=1, color='k')
  plt.gca().set_aspect('equal', 'box')

  plt.xlim(-50, 50)
  plt.ylim(-50, 50)
  plt.title(f'{t:04}')
Пример #3
0
def testControlledWalkCov():
  o = SED.opts(lie='se2')
  m = getattr(lie, o.lie)
  mRot = getattr(lie, o.lieRot)
  T = 10000
  y, z, x, omega, theta, Q, S, E = generateSyntheticK4(o, T, Nk=2)
  Strans = S[:,:o.dy,:o.dy]
  Srot = S[:,o.dy:,o.dy:]
  K = S.shape[0]

  plt.figure(figsize=(12,8))
  colors = du.diffcolors(K, bgCols=[[0,0,0],[1,1,1]])
  for k in range(K):
    plt.subplot(2,2,k+1)
    d_k = theta[:,k,:-1,-1]
    omega_trans = omega[k,:-1,-1]
    plt.scatter(*d_k.T, color=colors[k], s=1)
    plt.plot(*du.stats.Gauss2DPoints(o.zeroObs, Strans[k]), color=colors[k])
    plt.title(f'Part {k+1}')
    plt.gca().set_aspect('equal', 'box')
    # plt.plot(*du.stats.Gauss2DPoints(omega_trans, Strans), color=colors[k])

  path = 'reparam/controlled'
  plt.savefig(f'{path}/cov.png', dpi=300, bbox_inches='tight')
  plt.show()
Пример #4
0
def main(args):

  if args.sampleIdx is not None:
    samples = du.GetFilePaths(f'{args.resultPath}', 'gz')
    o, alpha, z, pi, theta, E, S, x, Q, omega, mL, ll, subsetIdx, datasetPath = \
      SED.loadSample(samples[int(args.sampleIdx)])
  else:
    o, alpha, z, pi, theta, E, S, x, Q, omega, mL, ll, subsetIdx, datasetPath = \
      SED.loadSample(args.resultPath)

  data = du.load(f'{datasetPath}/data')
  yAll = data['y']
  m = getattr(lie, o.lie)

  T = len(z)
  K = theta.shape[1]

  if args.drawMesh:
    meshFiles = du.GetFilePaths(f'{datasetPath}/mesh', 'obj')[:T]
    mesh = du.Parfor(tm.load, meshFiles)
    y = [ mesh[t].vertices for t in range(T) ]
    mL_const = np.mean([np.mean(mL[t]) for t in range(T)])
    mL = [ mL_const*np.ones(y[t].shape[0]) for t in range(T) ]

    for t in range(T):
      z[t] = SED.inferZ(o, y[t], pi, theta[t], E, x[t], omega, mL[t], max=args.maxZ)
  
  elif args.no_resampleZ:
    if subsetIdx is not None:
      y = [yt[subsetIdx[t]] for t, yt in enumerate(yAll)]
    else:
      y = yAll
  else:
    # don't use subsetIdx
    y = yAll
    mL_const = np.mean([np.mean(mL[t]) for t in range(T)])
    mL = [ mL_const*np.ones(y[t].shape[0]) for t in range(T) ]

    if args.maxZ: max=True
    else: max=False

    for t in range(T):
      z[t] = SED.inferZ(o, y[t], pi, theta[t], E, x[t], omega, mL[t], max=args.maxZ)

  # omegaTheta
  if args.omega:
    theta = np.tile(omega, (T,1,1,1))
  else:
    for t in range(T):
      for k in range(K):
        theta[t,k] = omega[k] @ theta[t,k]


  if args.noE: E = None
  if args.noTheta: theta = None
  if args.noZ: z = None
  noX = args.noX
  if args.noTitle: title = [ f'' for t in range(T) ]
  else: title = [ f'{t:05}' for t in range(T) ]

  if args.decimate > 0:
    nPts = args.decimate
    print('decimate')
    for t in range(T):
      decimateIdx = np.random.choice(range(len(y[t])), nPts)
      y[t] = y[t][decimateIdx]
      if z is not None: z[t] = z[t][decimateIdx]

  if args.wiggle:
    from scipy.stats import multivariate_normal as mvn
    for t in range(T):
      y[t] += mvn.rvs(np.zeros(3), args.wiggle_eps*np.eye(3), size=y[t].shape[0])
    # if theta is not None: theta[0,0][1,3] += 0.2

  if args.save:
    try: os.makedirs(args.save)
    except: pass
    fnames = [ f'{args.save}/img-{t:05}.png' for t in range(T) ]
  else:
    fnames = None

  def getImgs(path):
    imgPaths = du.GetImgPaths(imgPath)
    if len(imgPaths) > 100:
      du.imread(imgPaths[0]); imgs = du.ParforT(du.imread, imgPaths)
    else:
      imgs = du.For(du.imread, imgPaths)
    return imgs
  
  # three cases
  #   se2, se3 + draw2d, se3
  if o.lie == 'se2':
    imgPath = f'{datasetPath}/imgs'
    if isdir(imgPath): imgs = getImgs(imgPath)
    else: imgs = None

    scenes_or_none = drawSED.draw(o, y=y, x=x, theta=theta, E=E, img=imgs, z=z,
      filename=fnames, noX=noX, title=title)
    if not args.save: plt.show()

  elif o.lie == 'se3' and not args.draw2d and not args.drawMesh:
    scenes = drawSED.draw(o, y=y, x=x, theta=theta, E=E, z=z, noX=noX, title=title)
    transform = tmu.CameraFromScenes(scenes)

    # set transform as 1.25 of min z. This is specific to se3_marmoset for now
    #   multiply instead of divide because maybe camera is looking backwards?
    for scene in scenes:
      transform_t = scene.camera.transform.copy()
      transform_t[2,3] = transform[2,3] * 1.25
      scene.camera.transform = transform_t

    if args.save:
      for t in range(T): tmu.save_render(scenes[t], fnames[t])
    else:

      if args.single_frame:
        t = 0
        tmu.show_scene_with_bindings(scenes[t], res=[1920,1080])
      else:
        for t in range(T): tmu.show_scene_with_bindings(scenes[t], res=[1920,1080])

  elif o.lie == 'se3' and args.drawMesh:
    meshFiles = du.GetFilePaths(f'{datasetPath}/mesh', 'obj')[:T]
    mesh = du.Parfor(tm.load, meshFiles)

    mL_const = np.mean([np.mean(mL[t]) for t in range(T)])
    zCols = (255*du.diffcolors(100, bgCols=[[1,1,1],[0,0,0]], alpha=1.0)).astype(np.uint8)
    zColsFloat = du.diffcolors(100, bgCols=[[1,1,1],[0,0,0]], alpha=1.0)

    for t in range(T):
      mesh[t].visual.vertex_colors = zCols[z[t]]

    # draw but don't render scenes just to get camera
    scenes = drawSED.draw(o, y=y)
    transform = tmu.CameraFromScenes(scenes)

    # same colors as drawSED
    for t in range(T):
      scene = tm.scene.Scene()
      scene.add_geometry(mesh[t])

      # scene = mesh[t]
      transform_t = scene.camera.transform.copy()
      transform_t[2,3] = transform[2,3] * 1.5
      transform_t[2,2] = transform[2,2]
      scene.camera.transform = transform_t

      if not args.noX:
        scene.add_geometry(tmu.MakeAxes(0.2, x[t], np.tile([0, 0, 0, 255],
          [4,1]).astype(np.int), minor=0.01))


      if not args.orbit:
        if args.save:
          tmu.save_render(scene, fnames[t], res=[1920,1080])
        else:
          scene.show()
      else:
        nCams = 8
        cams = tmu.MakeCamerasOrbit(scene, nCams)

        for i in range(nCams):
          print('Time {t}, Cam {i}')
          cam = cams[i] @ transform_t
          scene.camera.transform = cam
          fname = f'{args.save}/camera-{i:02}-img-{t:05}.png' 
          tmu.save_render(scene, fname, res=[1920,1080])
    
    # re-associate to mesh vertices

  elif o.lie == 'se3' and args.draw2d:
    imgPath = f'{datasetPath}/rgb'
    assert isdir(imgPath)
    imgs = getImgs(imgPath)

    yImg = [ ]

    for t in range(T):
      mask = data['mask'][t]

      # get ordered image indices
      h, w = imgs[0].shape[:2]
      yy, xx = np.meshgrid(range(h), range(w), indexing='ij')
      xy = np.stack((xx.flatten(),yy.flatten()), axis=1) # N x 2
      xyFG = xy[mask.flatten()]
      idx_t = data['idx'][t] # indexes into xy

      if subsetIdx is not None and args.no_resampleZ:
        subsetIdx_t = subsetIdx[t]
        idx_t = idx_t[subsetIdx_t]
      # ip.embed()
      xyFG = xyFG[ idx_t ]
      yImg.append(xyFG)
    
    # make fake options with se2 for display
    oImg = SED.opts(lie='se2')
    drawSED.draw(oImg, y=yImg, z=z, img=imgs, filename=fnames, noX=noX, title=title)
    if not args.save: plt.show()
Пример #5
0
def testTranslationBasic():
  # general parameters
  o = SED.opts(lie='se2')
  m = getattr(lie, o.lie)
  mRot = getattr(lie, o.lieRot)

  T = 10
  y, z, x, omega, theta, Q, S, E = generateSyntheticK4(o, T)
  K = S.shape[0]

  Strans = S[:,:o.dy,:o.dy]
  Srot = S[:,o.dy:,o.dy:]

  # t, k = (5, 2)
  for t in range(1,T-1):
    for k in range(K):
      R_cur, d_cur = m.Rt(theta[t,k])
      zeroAlgebra = np.zeros(o.dxA)

      # test inference here
      R_prev, d_prev = m.Rt(theta[t-1,k])
      R_next, d_next = m.Rt(theta[t+1,k])
      def thetat_fwdBack(v):
        ll = 0.0

        # v is a translation; we'll already have a rotation so make the full
        V = SED.MakeRd(R_cur, v)

        # past
        phi = mRot.algi(mRot.logm(R_prev.T @ R_cur))
        phi = np.atleast_1d(phi)

        m = o.Bi @ (v - o.A @ d_prev)
        val = np.concatenate((m, phi))
        ll = mvn.logpdf(val, zeroAlgebra, S[k])

        # future
        phi_ = mRot.algi(mRot.logm(R_cur.T @ R_next))
        phi_ = np.atleast_1d(phi_)

        m_ = o.Bi @ (d_next - o.A @ v)
        val_ = np.concatenate((m_, phi_))
        ll += mvn.logpdf(val_, zeroAlgebra, S[k])
        
        return -ll
      
      mu, Sigma = SED.thetaTranslationDynamicsPosterior(o, R_cur, theta[t-1,k],
        S[k], nextU=theta[t+1,k])
      mu_noFuture, Sigma_noFuture = SED.thetaTranslationDynamicsPosterior(o, R_cur, theta[t-1,k],
        S[k])
      # print(f'd_cur:\n {d_cur}\n mu:\n {mu}\n Sigma:\n {Sigma}\n S:\n{S[k]}')

      g = nd.Gradient(thetat_fwdBack)
      map_est = so.minimize(thetat_fwdBack, np.zeros(o.dy), method='BFGS',
        jac=g)

      norm = np.linalg.norm(map_est.x - mu)
      print(f'norm: {norm:.6f}')
      assert norm < 1e-2, \
        f'SED.test5 bad, norm {norm:.6f}'

  plt.scatter(*d_cur, color='g')
  plt.plot(*du.stats.Gauss2DPoints(mu, Sigma), color='b')
  plt.plot(*du.stats.Gauss2DPoints(mu_noFuture, Sigma_noFuture), color='r')
  plt.scatter(*mu, color='b')
  plt.scatter(*mu_noFuture, color='r')
  plt.show()

  sys.exit()

  
  colors = du.diffcolors(K, bgCols=[[0,0,0],[1,1,1]])
  def show(t):
    # plot x
    T_world_object = x[t]
    m.plot(T_world_object, np.tile([0,0,0], [2,1]), l=10.0)

    # plot theta[t,k]
    for k in range(K):
      T_world_part = x[t] @ omega[k] @ theta[t,k]
      m.plot(T_world_part, np.tile(colors[k], [2,1]), l=10.0)

    plt.xlim(-100, 100)
    plt.ylim(-100, 100)

  path = 'reparam/controlled'
Пример #6
0
        y[t][idx] = igp.TransformPointsNonHomog(T_world_part, ytk_part)
        z[t][idx] = k

    # randomly permute y_t, z_t
    perm = np.random.permutation(range(Nt[t]))
    y[t] = y[t][perm]
    z[t] = z[t][perm]

# pi
pi = np.ones(K) / K

mins = np.array([np.min(y[t], axis=0) for t in range(T)])
maxs = np.array([np.max(y[t], axis=0) for t in range(T)])
xlim = np.array([np.min(mins[:, 0]), np.max(maxs[:, 0])])
ylim = np.array([np.min(mins[:, 1]), np.max(maxs[:, 1])])
cols = du.diffcolors(2, bgCols=[[1, 1, 1], [0, 0, 0]])

# # uncomment if we want to save new dataset
# du.save('%s/data' % path, {
#   'y': y, 'z': z,
#   'H_Q': H_Q, 'H_x': H_x,
#   'H_S': H_S, 'H_theta': H_theta,
#   'H_E': H_E,
#   'Q': Q, 'S': S, 'E': E,
#   'x': x, 'theta': theta, 'z': z,
#   'pi': pi
# })

oFake = igp.opts(lie='se2')
# filename = [ f'{path}/images/img-{t:05}.jpg' for t in range(T) ]
# title = [ f'Frame {t:05}' for t in range(T) ]
Пример #7
0
def draw_t_SE2(o, **kwargs):
    """ Draw or save model drawing for given time t.

  INPUT
    o (Namespace): options

  KEYWORD INPUT
    y (ndarray, [N, m.n]): observations
    x (ndarray, dxGm): latent global state
    z (ndarray, [N_t,]): Associations
    zCols (ndarray, [K,3/4]): Association Colors
    theta (ndarray, [K_est, dxGm]): latent part state
    E (ndarray, [K_est, dy, dy]): observation noise extent
    isObserved (ndarray, [K,]): boolean array of whether parts are observed
    xlim: min, max x plot limits
    ylim: min, max y plot limits
    title: plot title name
    filename: save path if desired, else returns figure reference
    style: string for modifying plot style. Currently ignored.
    reverseY: boolean, default False
    noX: boolean, default False
    img (ndarray, [nY, nX, 3]): image to draw on

    linestyle (str): linestyle for x, theta arrows
    deviations (float): deviations to draw E at
  """
    assert o.lie == 'se2'
    m = getattr(lie, o.lie)

    zCols = kwargs.get('zCols', None)
    if zCols is None:
        zCols = du.diffcolors(100, bgCols=[[1, 1, 1], [0, 0, 0]])

    reverseY = kwargs.get('reverseY', False)
    noX = kwargs.get('noX', False)
    linestyle = kwargs.get('linestyle', '-')
    deviations = kwargs.get('deviations', 1.25)

    img = kwargs.get('img', None)
    if img is None:
        # fillStyle = kwargs.get('fillStyle', 'full')
        import matplotlib
        marker = matplotlib.markers.MarkerStyle('o', 'full')

        y = kwargs.get('y', None)
        if y is not None:
            z = kwargs.get('z', None)
            if z is None:
                plt.scatter(y[:, 0], y[:, 1], color='k', s=0.1, zorder=0.1)
            else:
                assoc = z >= 0
                plt.scatter(y[assoc, 0],
                            y[assoc, 1],
                            color=zCols[z[assoc]],
                            s=0.1,
                            zorder=0.1,
                            marker=marker)
                noAssoc = np.logical_not(assoc)
                plt.scatter(y[noAssoc, 0],
                            y[noAssoc, 1],
                            color='gray',
                            alpha=0.5,
                            s=0.1,
                            zorder=0.1,
                            marker=marker)
    else:
        # assume y are foreground indices
        y = kwargs.get('y', None)
        if y is not None:
            z = kwargs.get('z', None)
            if z is not None:
                yInt = y.astype(np.int)
                if zCols.shape[1] == 3:
                    zColsA = np.concatenate((zCols, 0.5 * np.ones(
                        (zCols.shape[0], 1))),
                                            axis=1)
                else:
                    zColsA = zCols

                for k in np.unique(z[z >= 0]):
                    yk = yInt[z == k]
                    img = du.DrawOnImage(img, (yk[:, 1], yk[:, 0]), zColsA[k])
                gray = np.array([128, 128, 128, 0.5])
                yNoAssoc = yInt[z == -1]
                img = du.DrawOnImage(img, (yNoAssoc[:, 1], yNoAssoc[:, 0]),
                                     gray)
        plt.imshow(img)

    # both x, xTrue are black, need to handle linestyles
    x = kwargs.get('x', None)
    if x is not None and not noX:
        m.plot(x, np.tile([0, 0, 0], [2, 1]), l=10.0, linestyle=linestyle)

    theta = kwargs.get('theta', None)
    if theta is not None and x is not None:
        K = theta.shape[0]
        isObserved = kwargs.get('isObserved', np.ones(K, dtype=np.bool))

        for k in range(K):
            if not isObserved[k]: continue
            T_world_part = x.dot(theta[k])
            m.plot(T_world_part,
                   np.tile(zCols[k], [2, 1]),
                   l=10.0,
                   linestyle=linestyle)

    zero = np.zeros(2)
    E = kwargs.get('E', None)
    if E is not None and theta is not None and x is not None:
        K = theta.shape[0]
        for k in range(K):
            if not isObserved[k]: continue
            T_world_part = x.dot(theta[k])
            yMu = SED.TransformPointsNonHomog(T_world_part, zero)
            R = T_world_part[:-1, :-1]
            ySig = R.dot(E[k]).dot(R.T)
            plt.plot(*du.stats.Gauss2DPoints(yMu, ySig, deviations=deviations),
                     c=zCols[k],
                     linestyle=linestyle)

    ax = plt.gca()
    xlim = kwargs.get('xlim', None)
    if xlim is not None: ax.set_xlim(xlim)
    elif img is not None:
        xlim = (0, img.shape[1])
        ax.set_xlim(xlim)

    ylim = kwargs.get('ylim', None)
    if ylim is not None: ax.set_ylim(ylim)
    elif img is not None:
        ylim = (img.shape[0], 0)
        ax.set_ylim(ylim)

    plt.gca().set_aspect('equal', 'box')
    plt.gca().set_xticks([])
    plt.gca().set_yticks([])
    plt.grid(color=[.5, .5, .5], alpha=0.25)

    title = kwargs.get('title', None)
    if title is not None: plt.title(title)

    if reverseY: plt.gca().invert_yaxis()

    filename = kwargs.get('filename', None)
    if filename is not None:
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()
    else:
        return plt.gcf()
Пример #8
0
def draw_t_SE3(o, **kwargs):
    """ Draw or save model drawing for given time t.

  INPUT
    o (Namespace): options

  KEYWORD INPUT
    y (ndarray, [N, m.n]): observations
    x (ndarray, [dxGf,]): latent global state
    z (ndarray, [N_t,]): Associations
    zCols (ndarray, [K,3/4]): Association Colors
    xTrue (ndarray, [dx,]): true latent global state
    theta (ndarray, [K_est, dt]): latent part state
    thetaTrue (ndarray, [K, dt]): latent part state
    E (ndarray, [K_est, dy]): observation noise extent
    ETrue (ndarray, [K, dy]): true observation noise extent
    xlim: min, max x plot limits
    ylim: min, max y plot limits
    title: plot title name
    filename: save path if desired, else returns figure reference
    style: string for modifying plot style. Currently ignored.
    noX: bool, default False
  """
    assert o.lie == 'se3'
    m = getattr(lie, o.lie)

    zCols = kwargs.get('zCols', None)
    if zCols is None:
        zCols = du.diffcolors(100, bgCols=[[1, 1, 1], [0, 0, 0]])
        # zCols[1] = zCols[7]
        # zCols[4] = zCols[11]
        # zColsInt = (zCols*255).astype(np.int)

    scene = tm.scene.Scene()

    y = kwargs.get('y', None)
    if y is not None:
        z = kwargs.get('z', None)
        if z is None:
            pc = tmu.ConstructPointCloud(y)
            scene.add_geometry(pc)
        else:
            instIdx = z >= 0
            if np.sum(instIdx) > 0:
                zPart = z[instIdx]
                pc1 = tmu.ConstructPointCloud(y[instIdx], zCols[zPart])
                scene.add_geometry(pc1)

            noInstIdx = z == -1
            nNonInstantiated = np.sum(noInstIdx)
            if nNonInstantiated > 0:
                grayCols = np.tile(0.5 * np.ones(3), [nNonInstantiated, 1])
                if nNonInstantiated == 1 and len(y[noInstIdx].shape) == 1:
                    yNon = y[noInstIdx][np.newaxis]
                else:
                    yNon = y[noInstIdx]
                pc2 = tmu.ConstructPointCloud(yNon, grayCols)
                scene.add_geometry(pc2)

            # pc = tmu.ConstructPointCloud(y, zCols[z])
        # scene.add_geometry(pc)

    # todo: add part majorAxix (60 / 20 / 2 / 0.2) as a parameter
    # else: marmoset needs ~60 and articulated meshes need ~0.2

    x = kwargs.get('x', None)
    noX = kwargs.get('noX', None)
    if x is not None and not noX:
        scene.add_geometry(
            tmu.MakeAxes(0.1,
                         du.asShape(x, o.dxGm),
                         np.tile([0, 0, 0, 255], [4, 1]).astype(np.int),
                         minor=0.01))
        # scene.add_geometry(tmu.MakeAxes(0.2, du.asShape(x, o.dxGm),
        #   np.tile([0, 0, 0, 255], [4,1]).astype(np.int), minor=0.01))

        # scene.add_geometry(tmu.MakeAxes(2.0, du.asShape(x, o.dxGm),
        #   np.tile([0, 0, 0, 255], [4,1]).astype(np.int), minor=0.01))
        # scene.add_geometry(tmu.MakeAxes(60.0, du.asShape(x, o.dxGm),
        #   np.tile([0, 0, 0, 255], [4,1]).astype(np.int), minor=0.01))

    theta = kwargs.get('theta', None)
    if theta is not None and x is not None:
        K = theta.shape[0]
        for k in range(K):
            c = zCols[k]
            if len(c) == 3: c = np.concatenate((c, np.ones(1)))
            c = np.tile((c * 255).astype(np.uint8), [4, 1])

            T_world_part = du.asShape(x,
                                      o.dxGm).dot(du.asShape(theta[k], o.dxGm))

            scene.add_geometry(tmu.MakeAxes(0.1, T_world_part, c, minor=0.01))
            # scene.add_geometry(tmu.MakeAxes(0.2, T_world_part, c, minor=0.01))

            # scene.add_geometry(tmu.MakeAxes(20.0, T_world_part, c, minor=0.01))
            # scene.add_geometry(tmu.MakeAxes(60.0, T_world_part, c, minor=0.01))
            # scene.add_geometry(tmu.MakeAxes(2.0, T_world_part, c, minor=0.01))

    E = kwargs.get('E', None)
    if E is not None and theta is not None and x is not None:
        K = theta.shape[0]
        for k in range(K):
            c = zCols[k]
            if len(c) == 3: c = np.concatenate((c, 0.25 * np.ones(1)))
            c = (c * 255).astype(np.uint8)

            T_world_part = du.asShape(x,
                                      o.dxGm).dot(du.asShape(theta[k], o.dxGm))

            # TODO: eigvals not sorted, this is probably bad idea
            # ell = tmu.MakeEllipsoid(T_world_part,
            #   np.sqrt(np.linalg.eigvals(E[k]))*2, c)
            ell = tmu.MakeEllipsoid(T_world_part,
                                    np.sqrt(np.linalg.eigvals(E[k])) * 3, c)
            # setattr(ell, 'wire', True)
            scene.add_geometry(ell)

    filename = kwargs.get('filename', None)
    if filename is not None: tmu.save_render(scene, filename)
    else: return scene