// Assumes all images have the same dimensions
void montage(imageList_t &images, int nPerRow, IplImage *&result, 
             int Hspacing, int Vspacing, CvScalar bgcolor)
{
    int nimages = (int) images.size();
    int W = images[0]->width, H = images[0]->height;
    int montageWidth = nPerRow*W + (nPerRow-1)*Hspacing;
    int montageHeight = (int)( ceil((float)nimages/nPerRow)*(Vspacing + H) - Vspacing);

    // Create the resulting montage image
    result = cvCreateImage(cvSize(montageWidth,montageHeight),IPL_DEPTH_8U, 3);
    cvSet(result, bgcolor);
    
    // Copy each image onto the montage image
    for (int i = 0, x = 0, y = 0, n = 0; i < nimages; i++, n++, x+=W+Hspacing)
    {
        // See if we've reached the end of the row
        if (n >= nPerRow)
        {
            n = x = 0;
            y += H + Vspacing;
        }

        // Skip images that are NULL (used for blank space)
        if (!images[i])
            continue;

        IplImage *tmp = images[i];
        
        // Convert to color if necessary
        if (images[i]->nChannels == 1)
        {
            tmp = cvCreateImage( cvSize(W, H), IPL_DEPTH_8U, 3);
            cvCvtColor(images[i], tmp, CV_GRAY2BGR);
        }

        // If the image is a 32-bit FP image, assume 0-1 valued pixels, so
        // convert to 8 bit and scale by 255
        if (images[i]->depth == IPL_DEPTH_32F)
        {
            tmp = cvCreateImage( cvSize(W, H), IPL_DEPTH_8U, 3);
            cvConvertScale(images[i], tmp, 255.);
        }
        
        IplROI roi = { 0, x, y, W, H };
        result->roi = &roi;
        cvCopy(tmp, result);
        result->roi = NULL;

        if (tmp != images[i])
            cvReleaseImage(&tmp);
    }
}

