Пример #1
0
IECore::ImagePrimitivePtr ImagePlug::image() const
{
	Format format = formatPlug()->getValue();
	Box2i dataWindow = dataWindowPlug()->getValue();
	Box2i newDataWindow( Imath::V2i(0) );
	
	if( dataWindow.isEmpty() )
	{
		dataWindow = Box2i( Imath::V2i(0) );
	}
	else
	{
		newDataWindow = format.yDownToFormatSpace( dataWindow );
	}
	
	ImagePrimitivePtr result = new ImagePrimitive( newDataWindow, format.getDisplayWindow() );

	ConstStringVectorDataPtr channelNamesData = channelNamesPlug()->getValue();
	const vector<string> &channelNames = channelNamesData->readable();
	
	vector<float *> imageChannelData;
	for( vector<string>::const_iterator it = channelNames.begin(), eIt = channelNames.end(); it!=eIt; it++ )
	{
		FloatVectorDataPtr cd = new FloatVectorData;
		vector<float> &c = cd->writable();
		c.resize( result->variableSize( PrimitiveVariable::Vertex ), 0.0f );
		result->variables[*it] = PrimitiveVariable( PrimitiveVariable::Vertex, cd );
		imageChannelData.push_back( &(c[0]) );
	}
	
	parallel_for( blocked_range2d<size_t>( 0, dataWindow.size().x+1, tileSize(), 0, dataWindow.size().y+1, tileSize() ),
		      GafferImage::Detail::CopyTiles( imageChannelData, channelNames, channelDataPlug(), dataWindow, Context::current(), tileSize()) );
	
	return result;
}
Пример #2
0
ObjectPtr EXRImageReader::doOperation( const CompoundObject *operands )
{
	ImagePrimitivePtr image = staticPointerCast< ImagePrimitive >( ImageReader::doOperation( operands ) );
	if ( image )
	{
		headerToCompoundData( m_inputFile->header(), image->blindData() );
	}
	return image;
}
Пример #3
0
IECore::ImagePrimitivePtr ImagePlug::image() const
{
	Format format = formatPlug()->getValue();
	Box2i dataWindow = dataWindowPlug()->getValue();
	Box2i newDataWindow( Imath::V2i(0) );

	if( dataWindow.isEmpty() )
	{
		dataWindow = Box2i( Imath::V2i(0) );
	}
	else
	{
		newDataWindow = format.yDownToFormatSpace( dataWindow );
	}

	// use the default format if we don't have an explicit one.
	/// \todo: remove this once FormatPlug is handling it for
	/// us during ExecutableNode::execute (see issue #887).
	if( format.getDisplayWindow().isEmpty() )
	{
		format = Context::current()->get<Format>( Format::defaultFormatContextName, Format() );
	}
	
	ImagePrimitivePtr result = new ImagePrimitive( newDataWindow, format.getDisplayWindow() );
	
	ConstCompoundObjectPtr metadata = metadataPlug()->getValue();
	compoundObjectToCompoundData( metadata.get(), result->blindData() );
	
	ConstStringVectorDataPtr channelNamesData = channelNamesPlug()->getValue();
	const vector<string> &channelNames = channelNamesData->readable();

	vector<float *> imageChannelData;
	for( vector<string>::const_iterator it = channelNames.begin(), eIt = channelNames.end(); it!=eIt; it++ )
	{
		FloatVectorDataPtr cd = new FloatVectorData;
		vector<float> &c = cd->writable();
		c.resize( result->variableSize( PrimitiveVariable::Vertex ), 0.0f );
		result->variables[*it] = PrimitiveVariable( PrimitiveVariable::Vertex, cd );
		imageChannelData.push_back( &(c[0]) );
	}

	parallel_for( blocked_range3d<size_t>( 0, imageChannelData.size(), 1, 0, dataWindow.size().x+1, tileSize(), 0, dataWindow.size().y+1, tileSize() ),
		      GafferImage::Detail::CopyTiles( imageChannelData, channelNames, channelDataPlug(), dataWindow, Context::current(), tileSize()) );

	return result;
}
Пример #4
0
ObjectPtr EnvMapSampler::doOperation( const CompoundObject * operands )
{
	ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy();
	Box2i dataWindow = image->getDataWindow();

	// find the rgb channels
	ConstFloatVectorDataPtr redData = image->getChannel<float>( "R" );
	ConstFloatVectorDataPtr greenData = image->getChannel<float>( "G" );
	ConstFloatVectorDataPtr blueData = image->getChannel<float>( "B" );
	if( !(redData && greenData && blueData) )
	{
		throw Exception( "Image does not contain valid RGB float channels." );
	}
	const vector<float> &red = redData->readable();
	const vector<float> &green = greenData->readable();
	const vector<float> &blue = blueData->readable();

	// get a luminance channel
	LuminanceOpPtr luminanceOp = new LuminanceOp();
	luminanceOp->inputParameter()->setValue( image );
	luminanceOp->copyParameter()->getTypedValue() = false;
	luminanceOp->removeColorPrimVarsParameter()->getTypedValue() = false;
	luminanceOp->operate();

	// do the median cut thing to get some samples
	MedianCutSamplerPtr sampler = new MedianCutSampler;
	sampler->imageParameter()->setValue( image );
	sampler->subdivisionDepthParameter()->setNumericValue( subdivisionDepthParameter()->getNumericValue() );
	ConstCompoundObjectPtr samples = boost::static_pointer_cast<CompoundObject>( sampler->operate() );
	const vector<V2f> &centroids = boost::static_pointer_cast<V2fVectorData>( samples->members().find( "centroids" )->second )->readable();
	const vector<Box2i> &areas = boost::static_pointer_cast<Box2iVectorData>( samples->members().find( "areas" )->second )->readable();

	// get light directions and colors from the samples
	V3fVectorDataPtr directionsData = new V3fVectorData;
	Color3fVectorDataPtr colorsData = new Color3fVectorData;
	vector<V3f> &directions = directionsData->writable();
	vector<Color3f> &colors = colorsData->writable();

	float radiansPerPixel = M_PI / (dataWindow.size().y + 1);
	float angleAtTop = ( M_PI - radiansPerPixel ) / 2.0f;

	for( unsigned i=0; i<centroids.size(); i++ )
	{
		const Box2i &area = areas[i];
		Color3f color( 0 );
		for( int y=area.min.y; y<=area.max.y; y++ )
		{
			int yRel = y - dataWindow.min.y;

			float angle = angleAtTop - yRel * radiansPerPixel;
			float weight = cosf( angle );
			int index = (area.min.x - dataWindow.min.x) + (dataWindow.size().x + 1 ) * yRel;
			for( int x=area.min.x; x<=area.max.x; x++ )
			{
				color[0] += weight * red[index];
				color[1] += weight * green[index];
				color[2] += weight * blue[index];
				index++;
			}
		}
		color /= red.size();
		colors.push_back( color );

		float phi = angleAtTop - (centroids[i].y - dataWindow.min.y) * radiansPerPixel;

		V3f direction;
		direction.y = sinf( phi );
		float r = cosf( phi );
		float theta = 2 * M_PI * lerpfactor( (float)centroids[i].x, (float)dataWindow.min.x, (float)dataWindow.max.x );
		direction.x = r * cosf( theta );
		direction.z = r * sinf( theta );

		directions.push_back( -direction ); // negated so we output the direction the light shines in
	}

	// return the result
	CompoundObjectPtr result = new CompoundObject;
	result->members()["directions"] = directionsData;
	result->members()["colors"] = colorsData;
	return result;
}
void ColorSpaceTransformOp::modifyTypedPrimitive( ImagePrimitive * image, const CompoundObject * operands )
{
	const InputColorSpace &inputColorSpace = m_inputColorSpaceParameter->getTypedValue();
	const OutputColorSpace &outputColorSpace = m_outputColorSpaceParameter->getTypedValue();

	if ( inputColorSpace == outputColorSpace )
	{
		return;
	}

	std::vector< ConversionInfo > conversions;
	findConversion( inputColorSpace, outputColorSpace, conversions );

	if ( !conversions.size() )
	{
		throw InvalidArgumentException( ( boost::format( "ColorSpaceTransformOp: Cannot find appropriate conversion from '%s' to '%s'" ) % inputColorSpace % outputColorSpace ).str() );
	}

	std::vector< std::string > channelNames;
	typedef std::vector< std::vector< std::string > > ChannelSets;
	ChannelSets channelSets;
	std::vector< std::string > channels;

	for ( std::vector< std::string >::const_iterator it = channelsParameter()->getTypedValue().begin(); it != channelsParameter()->getTypedValue().end(); ++it )
	{
		const std::string &channelName = *it;

		channelNames.push_back( channelName );

		PrimitiveVariableMap::iterator channelIt = image->variables.find( channelName );
		if( channelIt==image->variables.end() || !(channelIt->second.data) )
		{
			throw Exception( str( format( "Channel \"%s\" does not exist." ) % channelName ) );
		}

		TypeId type = channelIt->second.data->typeId();

		if ( type == Color3fVectorDataTypeId || type == Color3dVectorDataTypeId )
		{
			// color data types define the three channels.

			if ( channels.size() )
			{
				/// silently ignores malformed sets.
				channels.clear();
			}
			channels.push_back( channelName );
			channelSets.push_back( channels );
			channels.clear();
		}
		else
		{
			// simple data types are assigned to one image channel.

			channels.push_back( channelName );

			if ( channels.size() == 3 )
			{
				channelSets.push_back( channels );
				channels.clear();
			}
		}
	}

	bool first = true;
	ConversionInfo previous;
	ConversionInfo current;
	for( std::vector< ConversionInfo >::const_iterator it = conversions.begin() ; it != conversions.end(); ++it )
	{
		current = *it;


		if ( first )
		{
			assert( current.get<1>() == inputColorSpace );
			first = false;
		}
		else
		{
			assert( previous.get<2>() == current.get<1>() );
		}
		ModifyOpPtr currentConversion = (*current.get<0>())( current.get<1>(), current.get<2>() );
		assert( currentConversion );

		if ( !currentConversion->isInstanceOf( ChannelOpTypeId ) && !currentConversion->isInstanceOf( ColorTransformOpTypeId ) )
		{
			throw InvalidArgumentException( ( boost::format( "ColorSpaceTransformOp: '%s' to '%s' conversion registered unsupported Op type '%s'" ) % inputColorSpace % outputColorSpace % currentConversion->typeName()).str() );
		}

		currentConversion->inputParameter()->setValue( image );
		currentConversion->copyParameter()->setTypedValue( false );

		ImagePrimitivePtr result = 0;
		if ( currentConversion->isInstanceOf( ChannelOpTypeId )	)
		{
			// The channel Op doesn't handle any unpremultiplication of the colour channels.
			// So we apply an unpremult before and a premult after, if premultiplied is on and alpha exists
			if( premultipliedParameter()->getTypedValue() )
			{
				std::string alphaPrimVar = alphaPrimVarParameter()->getTypedValue();
				if( image->variables.find( alphaPrimVar ) != image->variables.end() )
				{
					ImageUnpremultiplyOpPtr unpremultOp = new ImageUnpremultiplyOp();
					unpremultOp->alphaChannelNameParameter()->setTypedValue( alphaPrimVar );
					unpremultOp->channelNamesParameter()->setTypedValue( channelNames );
					unpremultOp->copyParameter()->setTypedValue( false );
					unpremultOp->inputParameter()->setValue( image );
					unpremultOp->operate();
				}
			}

			
			ChannelOpPtr op = assertedStaticCast< ChannelOp >( currentConversion );
			op->channelNamesParameter()->setTypedValue( channelNames );
			result = runTimeCast< ImagePrimitive >( op->operate() );
			
			if( premultipliedParameter()->getTypedValue() )
			{
				std::string alphaPrimVar = alphaPrimVarParameter()->getTypedValue();
				if( result->variables.find( alphaPrimVar ) != result->variables.end() )
				{
					ImagePremultiplyOpPtr premultOp = new ImagePremultiplyOp();
					premultOp->alphaChannelNameParameter()->setTypedValue( alphaPrimVar );
					premultOp->channelNamesParameter()->setTypedValue( channelNames );
					premultOp->copyParameter()->setTypedValue( false );
					premultOp->inputParameter()->setValue( result );
					premultOp->operate();
				}
			}
		}
		else
		{
			assert( currentConversion->isInstanceOf( ColorTransformOpTypeId ) );

			ColorTransformOpPtr op = boost::dynamic_pointer_cast< ColorTransformOp >( currentConversion );

			for ( ChannelSets::const_iterator it = channelSets.begin(); it != channelSets.end(); ++it )
			{
				op->inputParameter()->setValue( image );
				op->copyParameter()->setTypedValue( false );

				op->alphaPrimVarParameter()->setValue( alphaPrimVarParameter()->getValue() );
				op->premultipliedParameter()->setValue( premultipliedParameter()->getValue() );

				if ( it->size() == 1 )
				{
					op->colorPrimVarParameter()->setTypedValue( (*it)[0] );

					op->redPrimVarParameter()->setValue( op->redPrimVarParameter()->defaultValue()->copy() );
					op->greenPrimVarParameter()->setValue( op->greenPrimVarParameter()->defaultValue()->copy() );
					op->bluePrimVarParameter()->setValue( op->bluePrimVarParameter()->defaultValue()->copy() );
				}
				else
				{
					assert( it->size() == 3 );

					op->redPrimVarParameter()->setTypedValue( (*it)[0] );
					op->greenPrimVarParameter()->setTypedValue( (*it)[1] );
					op->bluePrimVarParameter()->setTypedValue( (*it)[2] );

					op->colorPrimVarParameter()->setValue( op->colorPrimVarParameter()->defaultValue()->copy() );

				}
				result = runTimeCast< ImagePrimitive >( op->operate() );
				assert( result.get() == image );
			}
		}

		assert( result.get() == image );
		( void ) result;

		previous = current;

	}
	assert( current.get<2>() == outputColorSpace );
}
Пример #6
0
void ImageCompositeOp::composite( CompositeFn fn, DataWindowResult dwr, ImagePrimitive * imageB, const CompoundObject * operands )
{
	assert( fn );
	assert( imageB );
	assert( operands );

	const StringVectorParameter::ValueType &channelNames = channelNamesParameter()->getTypedValue();
	if ( !channelNames.size() )
	{
		throw InvalidArgumentException( "ImageCompositeOp: No channels specified" );
	}

	ImagePrimitivePtr imageA = runTimeCast< ImagePrimitive > ( imageAParameter()->getValue() );
	assert( imageA );

	const std::string &alphaChannel = alphaChannelNameParameter()->getTypedValue();
	if ( !imageA->arePrimitiveVariablesValid() )
	{
		throw InvalidArgumentException( "ImageCompositeOp: Input image has invalid channels" );
	}

	const int inputMode = m_inputModeParameter->getNumericValue();
	if ( inputMode == Unpremultiplied )
	{
		ImagePremultiplyOpPtr premultOp = new ImagePremultiplyOp();
		premultOp->alphaChannelNameParameter()->setTypedValue( alphaChannel );
		premultOp->channelNamesParameter()->setTypedValue( channelNames );

		if ( imageA->variables.find( alphaChannel ) != imageA->variables.end() )
		{
			/// Make a new imageA, premultiplied
			premultOp->copyParameter()->setTypedValue( true );
			premultOp->inputParameter()->setValue( imageA );

			imageA = assertedStaticCast< ImagePrimitive >( premultOp->operate() );
			assert( imageA->arePrimitiveVariablesValid() );
		}

		if ( imageB->variables.find( alphaChannel ) != imageB->variables.end() )
		{
			/// Premultiply imageB in-place
			premultOp->copyParameter()->setTypedValue( false );
			premultOp->inputParameter()->setValue( imageB );
			premultOp->operate();
		}
	}
	else
	{
		assert( inputMode == Premultiplied );
	}


	const Imath::Box2i displayWindow = imageB->getDisplayWindow();

	Imath::Box2i newDataWindow = imageB->getDataWindow();

	if ( dwr == Union )
	{
		newDataWindow.extendBy( imageA->getDataWindow() );
	}
	else
	{
		assert( dwr == Intersection );
		newDataWindow = boxIntersection( newDataWindow, imageA->getDataWindow() );
	}

	newDataWindow = boxIntersection( newDataWindow, displayWindow );

	ImageCropOpPtr cropOp = new ImageCropOp();

	/// Need to make sure that we don't create a new image here - we want to modify the current one in-place. So,
	/// we turn off the "copy" parameter of ModifyOp.
	cropOp->copyParameter()->setTypedValue( false );
	cropOp->inputParameter()->setValue( imageB );
	cropOp->cropBoxParameter()->setTypedValue( newDataWindow );
	cropOp->matchDataWindowParameter()->setTypedValue( true );
	cropOp->resetOriginParameter()->setTypedValue( false );

	cropOp->operate();

	assert( imageB->arePrimitiveVariablesValid() );
	assert( imageB->getDataWindow() == newDataWindow );

	/// \todo Use the "reformat" parameter of the ImageCropOp to do this, when it's implemented
	imageB->setDisplayWindow( displayWindow );

	FloatVectorDataPtr aAlphaData = getChannelData( imageA.get(), alphaChannel, false );
	FloatVectorDataPtr bAlphaData = getChannelData( imageB, alphaChannel, false );

	const int newWidth = newDataWindow.size().x + 1;
	const int newHeight = newDataWindow.size().y + 1;
	const int newArea = newWidth * newHeight;

	assert( newArea == (int)imageB->variableSize( PrimitiveVariable::Vertex ) );

	for( unsigned i=0; i<channelNames.size(); i++ )
	{
		const StringVectorParameter::ValueType::value_type &channelName = channelNames[i];

		FloatVectorDataPtr aData = getChannelData( imageA.get(), channelName );
		assert( aData->readable().size() == imageA->variableSize( PrimitiveVariable::Vertex ) );
		FloatVectorDataPtr bData = getChannelData( imageB, channelName );
		assert( bData->readable().size() == imageB->variableSize( PrimitiveVariable::Vertex ) );
		FloatVectorDataPtr newBData = new FloatVectorData();

		newBData->writable().resize( newArea );
		imageB->variables[ channelName ].data = newBData;

		for ( int y = newDataWindow.min.y; y <= newDataWindow.max.y; y++ )
		{
			int offset = (y - newDataWindow.min.y ) * newWidth;
			for ( int x = newDataWindow.min.x; x <= newDataWindow.max.x; x++, offset++ )
			{
				float aVal = readChannelData( imageA.get(), aData.get(), V2i( x, y ) );
				float bVal = readChannelData( imageB, bData.get(), V2i( x, y ) );

				float aAlpha = aAlphaData ? readChannelData( imageA.get(), aAlphaData.get(), V2i( x, y ) ) : 1.0f;
				float bAlpha = bAlphaData ? readChannelData( imageB, bAlphaData.get(), V2i( x, y ) ) : 1.0f;

				assert( offset >= 0 );
				assert( offset < (int)newBData->readable().size() );
				newBData->writable()[ offset  ] = fn( aVal, aAlpha, bVal, bAlpha );
			}
		}
	}

	/// displayWindow should be unchanged
	assert( imageB->getDisplayWindow() == displayWindow );
	assert( imageB->arePrimitiveVariablesValid() );

	/// If input images were unpremultiplied, then ensure that the output is also
	if ( inputMode == Unpremultiplied && imageB->variables.find( alphaChannel ) != imageB->variables.end() )
	{
		ImageUnpremultiplyOpPtr unpremultOp = new ImageUnpremultiplyOp();
		/// Unpremultiply imageB in-place
		unpremultOp->copyParameter()->setTypedValue( false );
		unpremultOp->channelNamesParameter()->setTypedValue( channelNames );
		unpremultOp->alphaChannelNameParameter()->setTypedValue( alphaChannel );
		unpremultOp->inputParameter()->setValue( imageB );
		unpremultOp->operate();
		assert( imageB->arePrimitiveVariablesValid() );
	}
	else
	{
		assert( inputMode == Premultiplied );
	}
}
Пример #7
0
ObjectPtr MedianCutSampler::doOperation( const CompoundObject * operands )
{
	ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy();
	Box2i dataWindow = image->getDataWindow();

	// find the right channel
	const std::string &channelName = m_channelNameParameter->getTypedValue();
	FloatVectorDataPtr luminance = image->getChannel<float>( channelName );
	if( !luminance )
	{
		throw Exception( str( format( "No FloatVectorData channel named \"%s\"." ) % channelName ) );
	}

	// if the projection requires it, weight the luminances so they're less
	// important towards the poles of the sphere
	Projection projection = (Projection)m_projectionParameter->getNumericValue();
	if( projection==LatLong )
	{
		float radiansPerPixel = M_PI / (dataWindow.size().y + 1);
		float angle = ( M_PI - radiansPerPixel ) / 2.0f;

		float *p = &(luminance->writable()[0]);

		for( int y=dataWindow.min.y; y<=dataWindow.max.y; y++ )
		{
			float *pEnd = p + dataWindow.size().x + 1;
			float w = cosf( angle );
			while( p < pEnd )
			{
				*p *= w;
				p++;
			}
			angle -= radiansPerPixel;
		}

	}

	// make a summed area table for speed
	FloatVectorDataPtr summedLuminance = luminance;
	luminance = luminance->copy(); // we need this for the centroid computation

	SummedAreaOpPtr summedAreaOp = new SummedAreaOp();
	summedAreaOp->inputParameter()->setValue( image );
	summedAreaOp->copyParameter()->setTypedValue( false );
	summedAreaOp->channelNamesParameter()->getTypedValue().clear();
	summedAreaOp->channelNamesParameter()->getTypedValue().push_back( "Y" );
	summedAreaOp->operate();

	// do the median cut thing
	CompoundObjectPtr result = new CompoundObject;
	V2fVectorDataPtr centroids = new V2fVectorData;
	Box2iVectorDataPtr areas = new Box2iVectorData;
	result->members()["centroids"] = centroids;
	result->members()["areas"] = areas;

	dataWindow.max -= dataWindow.min;
	dataWindow.min -= dataWindow.min; // let's start indexing from 0 shall we?
	Array2D array( &(luminance->writable()[0]), extents[dataWindow.size().x+1][dataWindow.size().y+1], fortran_storage_order() );
	Array2D summedArray( &(summedLuminance->writable()[0]), extents[dataWindow.size().x+1][dataWindow.size().y+1], fortran_storage_order() );
	medianCut( array, summedArray, projection, dataWindow, areas->writable(), centroids->writable(), 0, subdivisionDepthParameter()->getNumericValue() );

	return result;
}
Пример #8
0
MStatus ImageFile::open( MString pathName, MImageFileInfo* info )
{
	ImagePrimitivePtr image;
	ImageReaderPtr reader;

	try
	{
		reader = runTimeCast<ImageReader>( Reader::create( pathName.asChar() ) );
		if (!reader)
		{
			return MS::kFailure;
		}

		image = runTimeCast< ImagePrimitive >( reader->read() );
		if (!image)
		{
			return MS::kFailure;
		}

		if (!reader->isComplete())
		{
			return MS::kFailure;
		}
	}
	catch ( std::exception &e )
	{
		return MS::kFailure;
	}

	m_width = image->getDataWindow().size().x + 1;
	m_height = image->getDataWindow().size().y + 1;

	std::vector<std::string> channelNames;
	image->channelNames( channelNames );

	if ( std::find( channelNames.begin(), channelNames.end(), "R" ) == channelNames.end()
		|| std::find( channelNames.begin(), channelNames.end(), "G" ) == channelNames.end()
		|| std::find( channelNames.begin(), channelNames.end(), "B" ) == channelNames.end() )
	{
		return MS::kFailure;
	}

	m_numChannels = 3;
	if ( std::find( channelNames.begin(), channelNames.end(), "A" ) != channelNames.end() )
	{
		m_numChannels = 4;
	}

	assert( m_numChannels == 3 || m_numChannels == 4 );

	try
	{

		ChannelConverter converter;
		converter.m_pathName = pathName.asChar();

		DataPtr rData = image->variables["R"].data;
		if (! rData )
		{
			return MS::kFailure;
		}

		converter.m_channelName = "R";
		m_rData = despatchTypedData<
				ChannelConverter,
				TypeTraits::IsNumericVectorTypedData,
				ChannelConverter::ErrorHandler
			>( rData.get(), converter );

		DataPtr gData = image->variables["G"].data;
		if (! gData )
		{
			return MS::kFailure;
		}

		converter.m_channelName = "G";
		m_gData = despatchTypedData<
				ChannelConverter,
				TypeTraits::IsNumericVectorTypedData,
				ChannelConverter::ErrorHandler
			>( gData.get(), converter );

		DataPtr bData = image->variables["B"].data;
		if (! bData )
		{
			return MS::kFailure;
		}

		converter.m_channelName = "B";
		m_bData = despatchTypedData<
				ChannelConverter,
				TypeTraits::IsNumericVectorTypedData,
				ChannelConverter::ErrorHandler
			>( bData.get(), converter );

		if ( m_numChannels == 4 )
		{
			DataPtr aData = image->variables["A"].data;
			if (! aData )
			{
				return MS::kFailure;
			}

			converter.m_channelName = "A";
			m_aData = despatchTypedData<
				ChannelConverter,
				TypeTraits::IsNumericVectorTypedData,
				ChannelConverter::ErrorHandler
			>( aData.get(), converter );
		}

	}
	catch ( std::exception &e )
	{
		MGlobal::displayError( e.what() );
		return MS::kFailure;
	}

	if (info)
	{
		info->width( m_width );
		info->height( m_height );

		info->channels( m_numChannels );
		info->numberOfImages( 1 );

		info->imageType( MImageFileInfo::kImageTypeColor );
		info->pixelType( MImage::kFloat  );
		info->hardwareType( MImageFileInfo::kHwTexture2D  );
	}

	return MS::kSuccess;
}