Using MImage to merge channels

I recently had to combine the RGB channels of one image with the Alpha channel of another image for a Maya tool I was writing.  For image processing I usually turn to PIL, however I wanted to try and do this without having to import an outside module.  At work our standalone python installation is 32-bit and our Maya installation is 64-bit, thus any module with a dll involved is not compatible with both.  For this reason I try and stick to pure python modules for use in Maya whenever I can or better yet just use the Maya API.

So it turns out it can be done with the MImage object.  At first I wasn’t sure it was possible since there wasn’t any obvious way to do this in the documentation.  Even now I’m not positive there isn’t an easier way, but it turns out we can do it just by writing the pixels ourselves.

First to create the base of the image we start with the color channels of one image.  Then at each pixel we look up the value of the alpha image at that same index.  Write that value into the pixel array of the color image.  When we’re done save as a new image name and you’re done.

Below is the function I came up with for the tool I wrote.  It makes one major assumption, the resolution of the color and alpha images are the same.  The three function parameters are the file paths of the color image, alpha image, and output image.

def mergeImages(pColorImg, pAlphaImg, pOutImg):
   lImage = OpenMaya.MImage()
   lExt = os.path.splitext(pOutImg)[1]

   lAlphaImage = OpenMaya.MImage()

   util = OpenMaya.MScriptUtil()
   widthUtil = OpenMaya.MScriptUtil()
   heightUtil = OpenMaya.MScriptUtil()

   widthPtr = widthUtil.asUintPtr()
   heightPtr = heightUtil.asUintPtr()

   lImage.getSize(widthPtr, heightPtr)
   width = util.getUint(widthPtr)
   height = util.getUint(heightPtr)

   depth = 4
   # Be sure and check for a z-depth channel
   if lImage.haveDepth(): depth = 5

   lPixels = lImage.pixels()
   lAlphaPixels = lAlphaImage.pixels()

   # We iterate by the depth of the image
   # The pixels array is the list of channel values one after another
   # 0 is the red value of the first pixel, 1 is the green value of
   # the first pixel, 2 is the blue value of the first pixel, 3 is the
   # alpha value of the first pixel, 4 is the red value of the second
   # pixel (assuming no z-depth channel is present), and so on
   for i in xrange( 0, (width*height*depth), depth ):
      # The alpha value is the fourth pixel in the array
      aIndex = i+3
      # Read the value from the alpha image
      value = util.getUcharArrayItem(lAlphaPixels, aIndex)
      # and write the value to the same position in the color image
      OpenMaya.MScriptUtil.setUcharArray( lPixels, aIndex, value )

   # Make a new image for the output
   lOutImage = OpenMaya.MImage()
   lOutImage.setPixels( lPixels, width, height )
   lOutImage.writeToFile(pOutImg, lExt)