Ejemplo n.º 1
0
void process (InputFile &file, const char *oname)
{
	typedef typename imf_ttrait<T>::type type;
	constexpr PixelType ptype = imf_ttrait<T>::PIXEL_TYPE;
	Array2D<type> r, g, b;

	FrameBuffer fb;
	Box2i dw = file.header().dataWindow();

	int w = dw.max.x - dw.min.x + 1;
	int h = dw.max.y - dw.min.y + 1;

	r.resizeErase(h, w);
	g.resizeErase(h, w);
	b.resizeErase(h, w);

	fb.insert("R",
		Slice(ptype, (char*)(&r[0][0] - dw.min.x - dw.min.y * w),
			sizeof(r[0][0]) * 1, sizeof(r[0][0]) * w, 1, 1, 0.0));
	fb.insert("G",
		Slice(ptype, (char*)(&g[0][0] - dw.min.x - dw.min.y * w),
			sizeof(g[0][0]) * 1, sizeof(g[0][0]) * w, 1, 1, 0.0));
	fb.insert("B",
		Slice(ptype, (char*)(&b[0][0] - dw.min.x - dw.min.y * w),
			sizeof(b[0][0]) * 1, sizeof(b[0][0]) * w, 1, 1, 0.0));

	file.setFrameBuffer(fb);
	file.readPixels(dw.min.y, dw.max.y);

	FILE *pgm = fopen(oname, "wb");
	float bw;

	fprintf(pgm, "P5\n");
	fprintf(pgm, "%d %d\n", w, h);
	fprintf(pgm, "255\n");

	// convert to gray scale
	for (int j = 0; j < h; j++) {
		for (int i = 0; i < w; i++) {
			bw = 0.30 * lin2srgb(r[j][i])
			   + 0.58 * lin2srgb(g[j][i])
			   + 0.12 * lin2srgb(b[j][i]);
			fputc((int)(bw * 255.0 + 0.5), pgm);
		}
	}
	fclose(pgm);
}
Ejemplo n.º 2
0
void
readGZ2 (const char fileName[],
	 Array2D<GZ> &pixels,
	 int &width, int &height)
{
    //
    // Read an image using class InputFile.  Try to read one channel,
    // G, of type HALF, and one channel, Z, of type FLOAT.  In memory,
    // the G and Z channels will be interleaved in a single buffer.
    //
    //	- open the file
    //	- allocate memory for the pixels
    //	- describe the layout of the GZ pixel buffer
    //	- read the pixels from the file
    //

    InputFile file (fileName);

    Box2i dw = file.header().dataWindow();
    width  = dw.max.x - dw.min.x + 1;
    height = dw.max.y - dw.min.y + 1;
    int dx = dw.min.x;
    int dy = dw.min.y;

    pixels.resizeErase (height, width);

    FrameBuffer frameBuffer;

    frameBuffer.insert ("G",					 // name
                        Slice (IMF::HALF,			 // type
			       (char *) &pixels[-dy][-dx].g,	 // base
				sizeof (pixels[0][0]) * 1,	 // xStride
				sizeof (pixels[0][0]) * width)); // yStride

    frameBuffer.insert ("Z",					 // name
                        Slice (IMF::FLOAT,			 // type
			       (char *) &pixels[-dy][-dx].z,	 // base
				sizeof (pixels[0][0]) * 1,	 // xStride
				sizeof (pixels[0][0]) * width)); // yStride

    file.setFrameBuffer (frameBuffer);
    file.readPixels (dw.min.y, dw.max.y);
}
Ejemplo n.º 3
0
struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags)
{
	struct ImBuf *ibuf = NULL;
	InputFile *file = NULL;
	
	if (imb_is_a_openexr(mem) == 0) return(NULL);
	
	try
	{
		Mem_IStream *membuf = new Mem_IStream(mem, size); 
		int is_multi;
		file = new InputFile(*membuf);
		
		Box2i dw = file->header().dataWindow();
		int width  = dw.max.x - dw.min.x + 1;
		int height = dw.max.y - dw.min.y + 1;
		
		//printf("OpenEXR-load: image data window %d %d %d %d\n", 
		//	   dw.min.x, dw.min.y, dw.max.x, dw.max.y);

		if(0) // debug
			exr_print_filecontents(file);
		
		is_multi= exr_is_renderresult(file);
		
		/* do not make an ibuf when */
		if(is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) 
		{
			printf("Error: can't process EXR multilayer file\n");
		}
		else {
		
			ibuf = IMB_allocImBuf(width, height, 32, 0);
			ibuf->ftype = OPENEXR;

			/* openEXR is linear as per EXR spec */
			ibuf->profile = IB_PROFILE_LINEAR_RGB;
			
			if (!(flags & IB_test))
			{
				if(is_multi) /* only enters with IB_multilayer flag set */
				{
					/* constructs channels for reading, allocates memory in channels */
					ExrHandle *handle= imb_exr_begin_read_mem(file, width, height);
					if(handle) {
						IMB_exr_read_channels(handle);
						ibuf->userdata= handle;			/* potential danger, the caller has to check for this! */
						return ibuf;
					}
				}
				else {
					FrameBuffer frameBuffer;
					float *first;
					int xstride = sizeof(float) * 4;
					int ystride = - xstride*width;
					
					imb_addrectfloatImBuf(ibuf);
					
					/* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
					first= ibuf->rect_float - 4*(dw.min.x - dw.min.y*width);
					/* but, since we read y-flipped (negative y stride) we move to last scanline */
					first+= 4*(height-1)*width;
					
					frameBuffer.insert ( exr_rgba_channelname(file, "R"), 
										Slice (FLOAT,  (char *) first, xstride, ystride));
					frameBuffer.insert ( exr_rgba_channelname(file, "G"), 
										Slice (FLOAT,  (char *) (first+1), xstride, ystride));
					frameBuffer.insert ( exr_rgba_channelname(file, "B"), 
										Slice (FLOAT,  (char *) (first+2), xstride, ystride));
																			
					frameBuffer.insert ( exr_rgba_channelname(file, "A"), 
										Slice (FLOAT,  (char *) (first+3), xstride, ystride, 1, 1, 1.0f)); /* 1.0 is fill value */

					if(exr_has_zbuffer(file)) 
					{
						float *firstz;
						
						addzbuffloatImBuf(ibuf);
						firstz= ibuf->zbuf_float - (dw.min.x - dw.min.y*width);
						firstz+= (height-1)*width;
						frameBuffer.insert ("Z", Slice (FLOAT,  (char *)firstz , sizeof(float), -width*sizeof(float)));
					}
					
					file->setFrameBuffer (frameBuffer);
					file->readPixels (dw.min.y, dw.max.y);

					// XXX, ImBuf has no nice way to deal with this.
					// ideally IM_rect would be used when the caller wants a rect BUT
					// at the moment all functions use IM_rect.
					// Disabling this is ok because all functions should check if a rect exists and create one on demand.
					//
					// Disabling this because the sequencer frees immediate.
					//
					// if(flag & IM_rect)
					//     IMB_rect_from_float(ibuf);
				}
			}
			
		}
		delete file;
		return(ibuf);
	}
	catch (const std::exception &exc)
	{
		std::cerr << exc.what() << std::endl;
		if (ibuf) IMB_freeImBuf(ibuf);
		delete file;
		
		return (0);
	}
	
}
Ejemplo n.º 4
0
struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
	struct ImBuf *ibuf = NULL;
	InputFile *file = NULL;

	if (imb_is_a_openexr(mem) == 0) return(NULL);

	colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);

	try
	{
		Mem_IStream *membuf = new Mem_IStream(mem, size);
		int is_multi;
		file = new InputFile(*membuf);

		Box2i dw = file->header().dataWindow();
		const int width  = dw.max.x - dw.min.x + 1;
		const int height = dw.max.y - dw.min.y + 1;

		//printf("OpenEXR-load: image data window %d %d %d %d\n",
		//	   dw.min.x, dw.min.y, dw.max.x, dw.max.y);

		if (0) // debug
			exr_print_filecontents(file);

		is_multi = exr_is_multilayer(file);

		/* do not make an ibuf when */
		if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
			printf("Error: can't process EXR multilayer file\n");
		}
		else {
			const int is_alpha = exr_has_alpha(file);

			ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);

			if (hasXDensity(file->header())) {
				ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f;
				ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio();
			}

			ibuf->ftype = OPENEXR;

			if (!(flags & IB_test)) {
				if (is_multi) { /* only enters with IB_multilayer flag set */
					/* constructs channels for reading, allocates memory in channels */
					ExrHandle *handle = imb_exr_begin_read_mem(file, width, height);
					if (handle) {
						IMB_exr_read_channels(handle);
						ibuf->userdata = handle;         /* potential danger, the caller has to check for this! */
					}
				}
				else {
					FrameBuffer frameBuffer;
					float *first;
					int xstride = sizeof(float) * 4;
					int ystride = -xstride * width;

					imb_addrectfloatImBuf(ibuf);

					/* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
					first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
					/* but, since we read y-flipped (negative y stride) we move to last scanline */
					first += 4 * (height - 1) * width;

					frameBuffer.insert(exr_rgba_channelname(file, "R"),
					                   Slice(Imf::FLOAT,  (char *) first, xstride, ystride));
					frameBuffer.insert(exr_rgba_channelname(file, "G"),
					                   Slice(Imf::FLOAT,  (char *) (first + 1), xstride, ystride));
					frameBuffer.insert(exr_rgba_channelname(file, "B"),
					                   Slice(Imf::FLOAT,  (char *) (first + 2), xstride, ystride));

					/* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
					frameBuffer.insert(exr_rgba_channelname(file, "A"),
					                   Slice(Imf::FLOAT,  (char *) (first + 3), xstride, ystride, 1, 1, 1.0f));

					if (exr_has_zbuffer(file)) {
						float *firstz;

						addzbuffloatImBuf(ibuf);
						firstz = ibuf->zbuf_float - (dw.min.x - dw.min.y * width);
						firstz += (height - 1) * width;
						frameBuffer.insert("Z", Slice(Imf::FLOAT,  (char *)firstz, sizeof(float), -width * sizeof(float)));
					}

					file->setFrameBuffer(frameBuffer);
					file->readPixels(dw.min.y, dw.max.y);

					// XXX, ImBuf has no nice way to deal with this.
					// ideally IM_rect would be used when the caller wants a rect BUT
					// at the moment all functions use IM_rect.
					// Disabling this is ok because all functions should check if a rect exists and create one on demand.
					//
					// Disabling this because the sequencer frees immediate.
					//
					// if (flag & IM_rect)
					//     IMB_rect_from_float(ibuf);

					/* file is no longer needed */
					delete file;
				}
			}

			if (flags & IB_alphamode_detect)
				ibuf->flags |= IB_alphamode_premul;
		}
		return(ibuf);
	}
	catch (const std::exception &exc)
	{
		std::cerr << exc.what() << std::endl;
		if (ibuf) IMB_freeImBuf(ibuf);
		delete file;

		return (0);
	}

}
Ejemplo n.º 5
0
void
makeMultiView (const vector <string> &viewNames,
	       const vector <const char *> &inFileNames,
	       const char *outFileName,
	       Compression compression,
	       bool verbose)
{
    Header header;
    Image image;
    FrameBuffer outFb;

    //
    // Find the size of the dataWindow, check files
    //
    
    Box2i d;
    
    
    for (int i = 0; i < viewNames.size(); ++i)
    {
	InputFile in (inFileNames[i]);

	if (verbose)
	{
	    cout << "reading file " << inFileNames[i] << " "
		    "for " << viewNames[i] << " view" << endl;
	}

	if (hasMultiView (in.header()))
	{
	    THROW (IEX_NAMESPACE::NoImplExc,
		   "The image in file " << inFileNames[i] << " is already a "
		   "multi-view image.  Cannot combine multiple multi-view "
		   "images.");
	}

        header = in.header();
	if (i == 0)
        {
             d=header.dataWindow();
	}else{
             d.extendBy(header.dataWindow());
        }
    }
    
    
    image.resize (d);
    
    header.dataWindow()=d;
    
    // blow away channels; we'll rebuild them
    header.channels()=ChannelList();
    
    
    //
    // Read the input image files
    //

    for (int i = 0; i < viewNames.size(); ++i)
    {
	InputFile in (inFileNames[i]);

	if (verbose)
	{
	    cout << "reading file " << inFileNames[i] << " "
		    "for " << viewNames[i] << " view" << endl;
	}

	FrameBuffer inFb;

	for (ChannelList::ConstIterator j = in.header().channels().begin();
	     j != in.header().channels().end();
	     ++j)
	{
	    const Channel &inChannel = j.channel();
	    string inChanName = j.name();
	    string outChanName = insertViewName (inChanName, viewNames, i);

	    image.addChannel (outChanName, inChannel);
            image.channel(outChanName).black();
            
	    header.channels().insert (outChanName, inChannel);

	    inFb.insert  (inChanName,  image.channel(outChanName).slice());
	    outFb.insert (outChanName, image.channel(outChanName).slice());
	}

	in.setFrameBuffer (inFb);
	in.readPixels (in.header().dataWindow().min.y, in.header().dataWindow().max.y);
    }

    //
    // Write the output image file
    //

    {
	header.compression() = compression;
	addMultiView (header, viewNames);

	OutputFile out (outFileName, header);

	if (verbose)
	    cout << "writing file " << outFileName << endl;

	out.setFrameBuffer (outFb);

	out.writePixels
	    (header.dataWindow().max.y - header.dataWindow().min.y + 1);
    }
}
Ejemplo n.º 6
0
float  *ReadImageEXR(const char fileName[], Box2i &dataWindow, Box2i &displayWindow, float *&alpha)
{
    
    
    try {
        // Open EXR file
        
        InputFile file (fileName);
        
        // Get image dimensions.
        dataWindow = file.header().dataWindow();
        displayWindow = file.header().displayWindow();
        
        int width = dataWindow.max.x - dataWindow.min.x + 1;
        int height = dataWindow.max.y - dataWindow.min.y + 1;
        
        const bool hasAlpha = file.header().channels().findChannel("A") != NULL;
        
        // Allocate memory to read image bits. We will only try to read R, G and B
        // here, but additional channels like A (alpha) could also be added...
        float *pixelsR = new float[width * height];
        float *pixelsG = new float[width * height];
        float *pixelsB = new float[width * height];
        alpha = hasAlpha ? new float[width * height] : NULL;
        
        // Now create the frame buffer to feed the image reader with. We will use
        // the Slice method flexibility to directly read R, G and B data in an
        // interlaced manner, using appropriate x & y stride values.
        FrameBuffer frameBuffer;
        
        frameBuffer.insert("R", Slice(FLOAT, (char*)(&pixelsR[0] -
                                                     dataWindow.min.x - dataWindow.min.y*width),
                                      sizeof(float), 
                                      width * sizeof(float), 
                                      1, 1, // x/y sampling
                                      0.0));
        frameBuffer.insert("G", Slice(FLOAT, (char*)(&pixelsG[0] -
                                                     dataWindow.min.x -dataWindow.min.y*width),
                                      sizeof(float), 
                                      width * sizeof(float),
                                      1, 1, // x/y sampling
                                      0.0));
        frameBuffer.insert("B", Slice(FLOAT, (char*)(&pixelsB[0] -
                                                     dataWindow.min.x -dataWindow.min.y*width),
                                      sizeof(float), 
                                      width * sizeof(float),
                                      1, 1, // x/y sampling
                                      0.0));
        if (alpha)
            frameBuffer.insert("A", Slice(FLOAT, (char*)(alpha -
                                                     dataWindow.min.x -dataWindow.min.y*width),
                                      sizeof(float), 
                                      width * sizeof(float),
                                      1, 1, // x/y sampling
                                      0.0));
        
        file.setFrameBuffer(frameBuffer);
        
        // Saving images
        float *data = new float[3*width * height];
        
        
        try {
            file.readPixels (dataWindow.min.y, dataWindow.max.y);
            
        }
        catch (const std::exception &) {
            data = NULL;
        }
        
        
        for (int i=0; i < width * height; i++) 
        {
            data[i] =  pixelsR[i];
            data[i+width*height] =  pixelsG[i];
            data[i+2*width*height] =  pixelsB[i];
        }
        
        
        delete[] pixelsR;
        delete[] pixelsG;
        delete[] pixelsB;
        
        return data;
    }
    
    catch (const std::exception &) {
        printf("Error reading file: %s\n",fileName);
        exit(-1);
    }
    
}
Ejemplo n.º 7
0
float * readMultiImageEXR(const char fileName[],
                          int *width, int *height, int *nbins)
{
    
    float *data;
    
    try {
        InputFile file (fileName);
        
        Box2i dw = file.header().dataWindow();
        
        *width = dw.max.x - dw.min.x + 1;
        *height = dw.max.y - dw.min.y + 1;
        int nhnc = (*width) * (*height);
        
        const ChannelList &channelList = file.header().channels();
        
        FrameBuffer frameBuffer;
        
        char ch_name[10];
        int nbin =0;
        sprintf(ch_name,"Bin_%04d",nbin);

        const Channel *channelPtr = channelList.findChannel(ch_name);
        
        
        while(channelPtr)
        {
            nbin++;
            sprintf(ch_name,"Bin_%04d",nbin);
            // printf("%s\n",ch_name);
            channelPtr = channelList.findChannel(ch_name);
        }
        
        
        //reserve memory for the whole array
        data = new float[nhnc*nbin];
        
        
        for(int i=0;i<nbin;i++)
        {        
            sprintf(ch_name,"Bin_%04d",i);
            
            frameBuffer.insert(ch_name, 
                               Slice(FLOAT, 
                                     (char*)(&data[i*nhnc] -
                                     dw.min.x -dw.min.y**width), 
                                     sizeof(float), 
                                     (*width) * sizeof(float)));
            
        }
        
        
        file.setFrameBuffer (frameBuffer);
        
        try {
            file.readPixels (dw.min.y, dw.max.y);
            
        }
        catch (const std::exception &) {
            data = NULL;
        }
        
        
        *nbins = nbin;
        return data;
        
    }
    catch (const std::exception &) {
        printf("Error reading file: %s\n",fileName);
        exit(-1);
    }
    
}
Ejemplo n.º 8
0
void
FileReadingThread::run ()
{
    try
    {
	int i = 0;	// index of the image buffer we will fill next
	int frame = _firstFrame;

	while (true)
	{
	    //
	    // Check if the display thread wants us to exit.
	    //

	    if (_imageBuffers.exitSemaphore1.tryWait())
	    {
		_imageBuffers.exitSemaphore2.post();
		return;
	    }

	    //
	    // Wait for an image buffer to become available.
	    //

	    _imageBuffers.emptyBuffersSemaphore.wait();

	    //
	    // Generate the file name for this frame
	    // and open the corresponding OpenEXR file.
	    //

	    string fileName = fileNameForFrame (_fileNameTemplate, frame);
	    InputFile in (fileName.c_str());

	    //
	    // Verify that this frame has the same data window
	    // as all other frames. (We do not dynamically resize
	    // our image buffers.)
	    //

	    if (in.header().dataWindow() != _imageBuffers.dataWindow)
		THROW (ArgExc,
		       "Data window of frame " << frame << " "
		       "differs from data window of "
		       "frame " << _firstFrame << ".");

	    //
	    // Read the OpenEXR file, storing the pixels in
	    // image buffer i.
	    //

	    in.setFrameBuffer (_imageBuffers.frameBuffer (i));

	    in.readPixels (_imageBuffers.dataWindow.min.y,
			   _imageBuffers.dataWindow.max.y);

	    //
	    // Mark the image buffer as full; the display
	    // thread can now display this frame.
	    //

	    _imageBuffers.frameNumber (i) = frame;
	    _imageBuffers.fullBuffersSemaphore.post();

	    //
	    // Advance to the next frame
	    //

	    if (_imageBuffers.forward)
	    {
		if (frame >= _lastFrame)
		    frame = _firstFrame;
		else
		    frame += 1;
	    }
	    else
	    {
		if (frame <= _firstFrame)
		    frame = _lastFrame;
		else
		    frame -= 1;
	    }

	    i = (i + 1) % _imageBuffers.numBuffers();
	}
    }
    catch (const std::exception &exc)
    {
	//
	// If anything goes wrong, print an eror message and exit.
	//

	cerr << exc.what() << endl;

	_imageBuffers.exitSemaphore2.post();
	_imageBuffers.fullBuffersSemaphore.post();
	return;
    }
}
Ejemplo n.º 9
0
void
readGZ1 (const char fileName[],
	 Array2D<half> &rPixels,
	 Array2D<half> &gPixels,
	 Array2D<float> &zPixels,
	 int &width, int &height)
{
    //
    // Read an image using class InputFile.  Try to read two
    // channels, R and G, of type HALF, and one channel, Z,
    // of type FLOAT.  Store the R, G, and Z pixels in three
    // separate memory buffers.
    // If a channel is missing in the file, the buffer for that
    // channel will be filled with an appropriate default value.
    //
    //	- open the file
    //	- allocate memory for the pixels
    //	- describe the layout of the R, G, and Z pixel buffers
    //	- read the pixels from the file
    //

    InputFile file (fileName);

    Box2i dw = file.header().dataWindow();
    width  = dw.max.x - dw.min.x + 1;
    height = dw.max.y - dw.min.y + 1;

    rPixels.resizeErase (height, width);
    gPixels.resizeErase (height, width);
    zPixels.resizeErase (height, width);

    FrameBuffer frameBuffer;

    frameBuffer.insert ("R",					// name
			Slice (IMF::HALF,			// type
			       (char *) (&rPixels[0][0] -	// base
					 dw.min.x -
					 dw.min.y * width),
			       sizeof (rPixels[0][0]) * 1,	// xStride
			       sizeof (rPixels[0][0]) * width,	// yStride
			       1, 1,				// x/y sampling
			       0.0));				// fillValue

    frameBuffer.insert ("G",					// name
			Slice (IMF::HALF,			// type
			       (char *) (&gPixels[0][0] -	// base
					 dw.min.x -
					 dw.min.y * width),
			       sizeof (gPixels[0][0]) * 1,	// xStride
			       sizeof (gPixels[0][0]) * width,	// yStride
			       1, 1,				// x/y sampling
			       0.0));				// fillValue

    frameBuffer.insert ("Z",					// name
			Slice (IMF::FLOAT,			// type
			       (char *) (&zPixels[0][0] -	// base
					 dw.min.x -
					 dw.min.y * width),
			       sizeof (zPixels[0][0]) * 1,	 // xStride
			       sizeof (zPixels[0][0]) * width,	// yStride
			       1, 1,				// x/y sampling
			       FLT_MAX));			// fillValue

    file.setFrameBuffer (frameBuffer);
    file.readPixels (dw.min.y, dw.max.y);
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    char *exrFileName;
    double *mxData;
    int width = 0;
    int height = 0;
    int nSlices = 0;
    
    if (nrhs != 1 || !mxIsChar(prhs[0])) {
        mexErrMsgTxt("Usage: ReadMultichannelEXR('exrFile')\n");
    }
    
    // read the exr file with OpenEXR general interface
    exrFileName = mxArrayToString(prhs[0]);
    InputFile file (exrFileName);
    
    // query general file properties
    Box2i dw = file.header().dataWindow();
    width = dw.max.x - dw.min.x + 1;
    height = dw.max.y - dw.min.y + 1;
    
    const ChannelList &channels = file.header().channels();
    for (ChannelList::ConstIterator iter = channels.begin();
    iter != channels.end();
    ++iter) {
        nSlices++;
    }
    
    mexPrintf("Read \"%s\": width=%d height=%d nSlices=%d\n",
            exrFileName, width, height, nSlices);
    mxFree(exrFileName);
    
    // return a struct with info about image slices
    mwSize nDims = 2;
    mwSize infoDims[] = {1, 0};
    infoDims[1] = nSlices;
    const char* fieldNames[] = {"name", "pixelType",
    "xSampling", "ySampling", "isLinear"};
    int nFields = sizeof(fieldNames)/sizeof(fieldNames[0]);
    plhs[0] = mxCreateStructArray(nDims, infoDims, nFields, fieldNames);
    
    // return a double array with size [height width nSlices]
    nDims = 3;
    mwSize dataDims[] = {0, 0, 0};
    dataDims[0] = height;
    dataDims[1] = width;
    dataDims[2] = nSlices;
    plhs[1] = mxCreateNumericArray(nDims, dataDims, mxDOUBLE_CLASS, mxREAL);
    if (NULL == plhs[1]) {
        mexPrintf("Could not allocate image array of size [%d %d %d]\n",
                dataDims[0], dataDims[1], dataDims[2]);
        return;
    }
    double* data = mxGetPr(plhs[1]);
    
    // fill in info struct and data array
    int channelIndex = 0;
    for (ChannelList::ConstIterator iter = channels.begin(); iter != channels.end(); ++iter) {
        const Channel &channel = iter.channel();
        
        // fill in info struct for this channel
        mxSetField(plhs[0], channelIndex, "name", mxCreateString(iter.name()));
        mxSetField(plhs[0], channelIndex, "xSampling", mxCreateDoubleScalar((double)channel.xSampling));
        mxSetField(plhs[0], channelIndex, "ySampling", mxCreateDoubleScalar((double)channel.ySampling));
        mxSetField(plhs[0], channelIndex, "isLinear", mxCreateLogicalScalar((mxLogical)channel.pLinear));
        
        // fill in a slice of the data matrix for this channel
        //  memory allocation depends on slice pixel type
        mxArray* typeName = NULL;
        FrameBuffer frameBuffer;
        switch (channel.type) {
            case UINT:{
                typeName = mxCreateString("UINT");
                
                // copy slice data from file to a frame buffer
                Array2D<MATLAB_UINT32> slicePixels;
                slicePixels.resizeErase(height, width);
                frameBuffer.insert(iter.name(),
                        Slice(HALF,
                        (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width),
                        sizeof(slicePixels[0][0]) * 1,
                        sizeof(slicePixels[0][0]) * width,
                        channel.xSampling,
                        channel.ySampling,
                        0.0));
                file.setFrameBuffer (frameBuffer);
                file.readPixels (dw.min.y, dw.max.y);
                
                // copy slice data from frame buffer to data array
                for (int i = 0; i < height; i++) {
                    for (int j = 0; j < width; j++) {
                        data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j];
                    }
                }
                
                break;
            }
            
            case HALF:{
                typeName = mxCreateString("HALF");
                
                // copy slice data from file to a frame buffer
                Array2D<half> slicePixels;
                slicePixels.resizeErase(height, width);
                frameBuffer.insert(iter.name(),
                        Slice(HALF,
                        (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width),
                        sizeof(slicePixels[0][0]) * 1,
                        sizeof(slicePixels[0][0]) * width,
                        channel.xSampling,
                        channel.ySampling,
                        0.0));
                file.setFrameBuffer (frameBuffer);
                file.readPixels (dw.min.y, dw.max.y);
                
                // copy slice data from frame buffer to data array
                for (int i = 0; i < height; i++) {
                    for (int j = 0; j < width; j++) {
                        data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j];
                    }
                }
                
                break;
            }
            
            case FLOAT:{
                typeName = mxCreateString("FLOAT");
                
                // copy slice data from file to a frame buffer
                Array2D<half> slicePixels;
                slicePixels.resizeErase(height, width);
                frameBuffer.insert(iter.name(),
                        Slice(HALF,
                        (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width),
                        sizeof(slicePixels[0][0]) * 1,
                        sizeof(slicePixels[0][0]) * width,
                        channel.xSampling,
                        channel.ySampling,
                        0.0));
                file.setFrameBuffer (frameBuffer);
                file.readPixels (dw.min.y, dw.max.y);
                
                // copy slice data from frame buffer to data array
                for (int i = 0; i < height; i++) {
                    for (int j = 0; j < width; j++) {
                        data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j];
                    }
                }
                
                break;
            }
            
            default:{
                typeName = mxCreateString("UNKNOWN");
                break;
            }
        }
        mxSetField(plhs[0], channelIndex, "pixelType",  typeName);
        
        channelIndex++;
        
        //mexPrintf("  channel \"%s\": type=%d xSampling=%d ySampling=%d, isLinear=%d\n",
        //        iter.name(), channel.type,
        //        channel.xSampling, channel.ySampling, channel.pLinear);
    }
}