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); }
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); }
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); } }
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); } }
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); } }
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); } }
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); } }
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; } }
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); } }