Exemple #1
0
void ImageStats::channelNameFromOutput( const ValuePlug *output, std::string &channelName ) const
{
	IECore::ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue();
	std::vector<std::string> maskChannels = channelNamesData->readable();
	channelsPlug()->maskChannels( maskChannels );

	/// As the channelMaskPlug allows any combination of channels to be input we need to make sure that
	/// the channels that it masks each have a distinct channelIndex. Otherwise multiple channels would be
	/// outputting to the same plug.
	std::vector<std::string> uniqueChannels = maskChannels;
	GafferImage::ChannelMaskPlug::removeDuplicateIndices( uniqueChannels );

	for( int channelIndex = 0; channelIndex < 4; ++channelIndex )
	{
		if ( output == minPlug()->getChild( channelIndex ) ||
			 output == maxPlug()->getChild( channelIndex ) ||
			 output == averagePlug()->getChild( channelIndex )
		   )
		{
			for( std::vector<std::string>::iterator it( uniqueChannels.begin() ); it != uniqueChannels.end(); ++it )
			{
				if( ImageAlgo::colorIndex( *it ) == channelIndex )
				{
					channelName = *it;
					return;
				}
			}
		}
	}
	return;
}
void ParameterisedHolderModificationCmd::restoreClassParameterStates( const IECore::CompoundData *classes, IECore::Parameter *parameter, const std::string &parentParameterPath )
{
	std::string parameterPath = parentParameterPath;
	if( parentParameterPath.size() )
	{
		parameterPath += ".";
	}
	parameterPath += parameter->name();
		
	if( parameter->isInstanceOf( "ClassParameter" ) )
	{				
		const CompoundData *c = classes->member<const CompoundData>( parameterPath );
		if( c )
		{
			ClassParameterHandler::setClass(
				parameter,
				c->member<const IECore::StringData>( "className" )->readable().c_str(),
				c->member<const IECore::IntData>( "classVersion" )->readable(),
				c->member<const IECore::StringData>( "searchPathEnvVar" )->readable().c_str()
			);
		}
	}
	else if( parameter->isInstanceOf( "ClassVectorParameter" ) )
	{		
		const CompoundData *c = classes->member<const CompoundData>( parameterPath );
		if( c )
		{
			IECore::ConstStringVectorDataPtr parameterNames = c->member<const IECore::StringVectorData>( "parameterNames" );
			IECore::ConstStringVectorDataPtr classNames = c->member<const IECore::StringVectorData>( "classNames" );
			IECore::ConstIntVectorDataPtr classVersions = c->member<const IECore::IntVectorData>( "classVersions" );
			MStringArray mParameterNames;
			MStringArray mClassNames;
			MIntArray mClassVersions;
			int numClasses = parameterNames->readable().size();
			for( int i=0; i<numClasses; i++ )
			{
				mParameterNames.append( parameterNames->readable()[i].c_str() );
				mClassNames.append( classNames->readable()[i].c_str() );
				mClassVersions.append( classVersions->readable()[i] );
			}
			ClassVectorParameterHandler::setClasses( parameter, mParameterNames, mClassNames, mClassVersions );
		}
	}
	
	if( parameter->isInstanceOf( IECore::CompoundParameter::staticTypeId() ) )
	{
		CompoundParameter *compoundParameter = static_cast<CompoundParameter *>( parameter );
		const CompoundParameter::ParameterVector &childParameters = compoundParameter->orderedParameters();
		for( CompoundParameter::ParameterVector::const_iterator it = childParameters.begin(); it!=childParameters.end(); it++ )
		{
			restoreClassParameterStates( classes, it->get(), parameterPath );
		}
	}
}
bool ChannelDataProcessor::channelEnabled( const std::string &channel ) const
{
	if( !ImageProcessor::channelEnabled( channel ) )
	{
		return false;
	}

	IECore::ConstStringVectorDataPtr channelMaskData = channelMaskPlug()->getValue();
	const std::vector<std::string> &channelMask = channelMaskData->readable();
	return std::find( channelMask.begin(), channelMask.end(), channel ) != channelMask.end();
}
Exemple #4
0
std::string ImageStats::channelName( int colorIndex ) const
{
	IECore::ConstStringVectorDataPtr channelsData = channelsPlug()->getValue();
	const vector<string> &channels = channelsData->readable();
	if( channels.size() <= (size_t)colorIndex )
	{
		return "";
	}

	IECore::ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue();
	const vector<string> &channelNames = channelNamesData->readable();
	if( find( channelNames.begin(), channelNames.end(), channels[colorIndex] ) != channelNames.end() )
	{
		return channels[colorIndex];
	}

	return "";
}
Exemple #5
0
void Merge::hashChannelData( const GafferImage::ImagePlug *output, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	ImageProcessor::hashChannelData( output, context, h );

	const std::string channelName = context->get<std::string>( ImagePlug::channelNameContextName );
	const V2i tileOrigin = context->get<V2i>( ImagePlug::tileOriginContextName );
	const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) );

	for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it )
	{
		if( !(*it)->getInput<ValuePlug>() )
		{
			continue;
		}

		IECore::ConstStringVectorDataPtr channelNamesData = (*it)->channelNamesPlug()->getValue();
		const std::vector<std::string> &channelNames = channelNamesData->readable();

		if( channelExists( channelNames, channelName ) )
		{
			(*it)->channelDataPlug()->hash( h );
		}

		if( channelExists( channelNames, "A" ) )
		{
			h.append( (*it)->channelDataHash( "A", tileOrigin ) );
		}

		// The hash of the channel data we include above represents just the data in
		// the tile itself, and takes no account of the possibility that parts of the
		// tile may be outside of the data window. This simplifies the implementation of
		// nodes like Constant (where all tiles are identical, even the edge tiles) and
		// Crop (which does no processing of tiles at all). For most nodes this doesn't
		// matter, because they don't change the data window, or they use a Sampler to
		// deal with invalid pixels. But because our data window is the union of all
		// input data windows, we may be using/revealing the invalid parts of a tile. We
		// deal with this in computeChannelData() by treating the invalid parts as black,
		// and must therefore hash in the valid bound here to take that into account.
		const Box2i validBound = boxIntersection( tileBound, (*it)->dataWindowPlug()->getValue() );
		h.append( validBound );
	}

	operationPlug()->hash( h );
}
Exemple #6
0
void ImageStats::hash( const ValuePlug *output, const Context *context, IECore::MurmurHash &h ) const
{
	ComputeNode::hash( output, context, h);

	bool earlyOut = true;
	for( int i = 0; i < 4; ++i )
	{
		if (
				output == minPlug()->getChild(i) ||
				output == maxPlug()->getChild(i) ||
				output == averagePlug()->getChild(i)
		   )
		{
			earlyOut = false;
			break;
		}
	}
	if( earlyOut )
	{
		return;
	}

	const Imath::Box2i regionOfInterest( regionOfInterestPlug()->getValue() );
	regionOfInterestPlug()->hash( h );
	inPlug()->channelNamesPlug()->hash( h );
	inPlug()->dataWindowPlug()->hash( h );

	IECore::ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue();
	std::vector<std::string> maskChannels = channelNamesData->readable();
	channelsPlug()->maskChannels( maskChannels );
	const int nChannels( maskChannels.size() );

	if ( nChannels > 0 )
	{
		std::vector<std::string> uniqueChannels = maskChannels;
		GafferImage::ChannelMaskPlug::removeDuplicateIndices( uniqueChannels );
		std::string channel;
		channelNameFromOutput( output, channel );

		if ( !channel.empty() )
		{
			h.append( channel );
			Sampler s( inPlug(), channel, regionOfInterest );
			s.hash( h );
			return;
		}
	}

	// If our node is not enabled then we just append the default value that we will give the plug.
	if(
			output == maxPlug()->getChild(3) ||
			output == minPlug()->getChild(3) ||
			output == averagePlug()->getChild(3)
	  )
	{
		h.append( 0 );
	}
	else
	{
		h.append( 1 );
	}
}
Exemple #7
0
IECore::ConstFloatVectorDataPtr Merge::merge( F f, const std::string &channelName, const Imath::V2i &tileOrigin ) const
{
	FloatVectorDataPtr resultData = NULL;
	// Temporary buffer for computing the alpha of intermediate composited layers.
	FloatVectorDataPtr resultAlphaData = NULL;

	const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) );

	for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it )
	{
		if( !(*it)->getInput<ValuePlug>() )
		{
			continue;
		}

		IECore::ConstStringVectorDataPtr channelNamesData = (*it)->channelNamesPlug()->getValue();
		const std::vector<std::string> &channelNames = channelNamesData->readable();

		ConstFloatVectorDataPtr channelData;
		ConstFloatVectorDataPtr alphaData;

		if( channelExists( channelNames, channelName ) )
		{
			channelData = (*it)->channelDataPlug()->getValue();
		}
		else
		{
			channelData = ImagePlug::blackTile();
		}

		if( channelExists( channelNames, "A" ) )
		{
			alphaData = (*it)->channelData( "A", tileOrigin );
		}
		else
		{
			alphaData = ImagePlug::blackTile();
		}

		const Box2i validBound = boxIntersection( tileBound, (*it)->dataWindowPlug()->getValue() );

		if( !resultData )
		{
			// The first connected layer, with which we must initialise our result.
			// There's no guarantee that this layer actually covers the full data
			// window though (the data window could have been expanded by the upper
			// layers) so we must take care to mask out any invalid areas of the input.
			/// \todo I'm not convinced this is correct - if we have no connection
			/// to in[0] then should that not be treated as being a black image, so
			/// we should unconditionally initaliase with in[0] and then always use
			/// the operation for in[1:], even if in[0] is disconnected. In other
			/// words, shouldn't multiplying a white constant over an unconnected
			/// in[0] produce black?
			resultData = channelData->copy();
			resultAlphaData = alphaData->copy();
			float *B = &resultData->writable().front();
			float *b = &resultAlphaData->writable().front();
			for( int y = tileBound.min.y; y < tileBound.max.y; ++y )
			{
				const bool yValid = y >= validBound.min.y && y < validBound.max.y;
				for( int x = tileBound.min.x; x < tileBound.max.x; ++x )
				{
					if( !yValid || x < validBound.min.x || x >= validBound.max.x )
					{
						*B = *b = 0.0f;
					}
					++B; ++b;
				}
			}
		}
		else
		{
			// A higher layer (A) which must be composited over the result (B).
			const float *A = &channelData->readable().front();
			float *B = &resultData->writable().front();
			const float *a = &alphaData->readable().front();
			float *b = &resultAlphaData->writable().front();

			for( int y = tileBound.min.y; y < tileBound.max.y; ++y )
			{
				const bool yValid = y >= validBound.min.y && y < validBound.max.y;
				for( int x = tileBound.min.x; x < tileBound.max.x; ++x )
				{
					const bool valid = yValid && x >= validBound.min.x && x < validBound.max.x;

					*B = f( valid ? *A : 0.0f, *B, valid ? *a : 0.0f, *b );
					*b = f( valid ? *a : 0.0f, *b, valid ? *a : 0.0f, *b );

					++A; ++B; ++a; ++b;
				}
			}
		}
	}

	return resultData;
}
Exemple #8
0
///\todo: It seems that if a JPG is written with RGBA channels the output is wrong but it should be supported. Find out why and fix it.
/// There is a test case in ImageWriterTest which checks the output of the jpg writer against an incorrect image and it will fail if it is equal to the writer output.
void ImageWriter::execute( const Contexts &contexts ) const
{
	if( !inPlug()->getInput<ImagePlug>() )
	{
		throw IECore::Exception( "No input image." );
	}

	// Loop over the execution contexts...
	for( Contexts::const_iterator it = contexts.begin(), eIt = contexts.end(); it != eIt; it++ )
	{
		Context::Scope scopedContext( it->get() );
		
		std::string fileName = fileNamePlug()->getValue();
		fileName = (*it)->substitute( fileName );
		
		boost::shared_ptr< ImageOutput > out( ImageOutput::create( fileName.c_str() ) );
		if ( !out )
		{
			throw IECore::Exception( boost::str( boost::format( "Invalid filename: %s" ) % fileName ) );
		}
		
		// Grab the intersection of the channels from the "channels" plug and the image input to see which channels we are to write out.
		IECore::ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue();
		std::vector<std::string> maskChannels = channelNamesData->readable();
		channelsPlug()->maskChannels( maskChannels );
		const int nChannels = maskChannels.size();
		
		// Get the image channel data.
		IECore::ImagePrimitivePtr imagePtr( inPlug()->image() );
		
		// Get the image's display window.
		const Imath::Box2i displayWindow( imagePtr->getDisplayWindow() );
		const int displayWindowWidth = displayWindow.size().x+1;
		const int displayWindowHeight = displayWindow.size().y+1;

		// Get the image's data window and if it then set a flag.
		bool imageIsBlack = false;
		Imath::Box2i dataWindow( imagePtr->getDataWindow() );
		if ( inPlug()->dataWindowPlug()->getValue().isEmpty() )
		{
			dataWindow = displayWindow;
			imageIsBlack = true;
		}

		int dataWindowWidth = dataWindow.size().x+1;
		int dataWindowHeight = dataWindow.size().y+1;
	
		// Create the image header. 
		ImageSpec spec( dataWindowWidth, dataWindowHeight, nChannels, TypeDesc::FLOAT );

		// Add the channel names to the header whilst getting pointers to the channel data. 
		std::vector<const float*> channelPtrs;
		spec.channelnames.clear();
		for ( std::vector<std::string>::iterator channelIt( maskChannels.begin() ); channelIt != maskChannels.end(); channelIt++ )
		{
			spec.channelnames.push_back( *channelIt );
			IECore::FloatVectorDataPtr dataPtr = imagePtr->getChannel<float>( *channelIt );
			channelPtrs.push_back( &(dataPtr->readable()[0]) );

			// OIIO has a special attribute for the Alpha and Z channels. If we find some, we should tag them...
			if ( *channelIt == "A" )
			{
				spec.alpha_channel = channelIt-maskChannels.begin();
			} else if ( *channelIt == "Z" )
			{
				spec.z_channel = channelIt-maskChannels.begin();
			}
		}
		
		// Specify the display window.
		spec.full_x = displayWindow.min.x;
		spec.full_y = displayWindow.min.y;
		spec.full_width = displayWindowWidth;
		spec.full_height = displayWindowHeight;
		spec.x = dataWindow.min.x;
		spec.y = dataWindow.min.y;
	
		if ( !out->open( fileName, spec ) )
		{
			throw IECore::Exception( boost::str( boost::format( "Could not open \"%s\", error = %s" ) % fileName % out->geterror() ) );
		}

		// Only allow tiled output if our file format supports it.	
		int writeMode = writeModePlug()->getValue() & out->supports( "tile" );
	
		if ( writeMode == Scanline )
		{
			// Create a buffer for the scanline.
			float scanline[ nChannels*dataWindowWidth ];
			
			if ( imageIsBlack )
			{
				memset( scanline, 0, sizeof(float) * nChannels*dataWindowWidth );

				for ( int y = spec.y; y < spec.y + dataWindowHeight; ++y )
				{
					if ( !out->write_scanline( y, 0, TypeDesc::FLOAT, &scanline[0] ) )
					{
						throw IECore::Exception( boost::str( boost::format( "Could not write scanline to \"%s\", error = %s" ) % fileName % out->geterror() ) );
					}
				}
			}
			else
			{
				// Interleave the channel data and write it by scanline to the file.	
				for ( int y = spec.y; y < spec.y + dataWindowHeight; ++y )
				{
					for ( std::vector<const float *>::iterator channelDataIt( channelPtrs.begin() ); channelDataIt != channelPtrs.end(); channelDataIt++ )
					{
						float *outPtr = &scanline[0] + (channelDataIt - channelPtrs.begin()); // The pointer that we are writing to.
						// The row that we are reading from is flipped (in the Y) as we use a different image space internally to OpenEXR and OpenImageIO.
						const float *inRowPtr = (*channelDataIt) + ( y - spec.y ) * dataWindowWidth;
						const int inc = channelPtrs.size();
						for ( int x = 0; x < dataWindowWidth; ++x, outPtr += inc )
						{
							*outPtr = *inRowPtr++;
						}
					}

					if ( !out->write_scanline( y, 0, TypeDesc::FLOAT, &scanline[0] ) )
					{
						throw IECore::Exception( boost::str( boost::format( "Could not write scanline to \"%s\", error = %s" ) % fileName % out->geterror() ) );
					}
				}
			}
		}
		// Tiled output
		else
		{
			// Create a buffer for the tile.
			const int tileSize = ImagePlug::tileSize();
			float tile[ nChannels*tileSize*tileSize ];

			if ( imageIsBlack )
			{
				memset( tile, 0,  sizeof(float) * nChannels*tileSize*tileSize );
				for ( int tileY = 0; tileY < dataWindowHeight; tileY += tileSize )
				{
					for ( int tileX = 0; tileX < dataWindowWidth; tileX += tileSize )
					{
						if ( !out->write_tile( tileX+dataWindow.min.x, tileY+spec.y, 0, TypeDesc::FLOAT, &tile[0] ) )
						{
							throw IECore::Exception( boost::str( boost::format( "Could not write tile to \"%s\", error = %s" ) % fileName % out->geterror() ) );
						}
					}
				}
			}
			else
			{
				// Interleave the channel data and write it to the file tile-by-tile.
				for ( int tileY = 0; tileY < dataWindowHeight; tileY += tileSize )
				{
					for ( int tileX = 0; tileX < dataWindowWidth; tileX += tileSize )
					{
						float *outPtr = &tile[0];	

						const int r = std::min( tileSize+tileX, dataWindowWidth );
						const int t = std::min( tileSize+tileY, dataWindowHeight );

						for ( int y = 0; y < t; ++y )
						{
							for ( std::vector<const float *>::iterator channelDataIt( channelPtrs.begin() ); channelDataIt != channelPtrs.end(); channelDataIt++ )
							{
								const int inc = channelPtrs.size();
								const float *inRowPtr = (*channelDataIt) + ( tileY + t - y - 1 ) * dataWindowWidth;
								for ( int x = 0; x < r; ++x, outPtr += inc )
								{
									*outPtr = *inRowPtr+(tileX+x);
								}
							}
						}

						if ( !out->write_tile( tileX+dataWindow.min.x, tileY+spec.y, 0, TypeDesc::FLOAT, &tile[0] ) )
						{
							throw IECore::Exception( boost::str( boost::format( "Could not write tile to \"%s\", error = %s" ) % fileName % out->geterror() ) );
						}
					}
				}
			}

		}

		out->close();
	}
}
Exemple #9
0
IECore::ConstFloatVectorDataPtr Mix::computeChannelData( const std::string &channelName, const Imath::V2i &tileOrigin, const Gaffer::Context *context, const ImagePlug *parent ) const
{

	const float mix = mixPlug()->getValue();

	if( mix == 0.0f )
	{
		return inPlugs()->getChild< ImagePlug>( 0 )->channelDataPlug()->getValue();
	}
	else if( mix == 1.0f && !maskPlug()->getInput<ValuePlug>() )
	{
		return inPlugs()->getChild< ImagePlug >( 1 )->channelDataPlug()->getValue();
	}

	const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) );

	IECore::ConstStringVectorDataPtr maskChannelNamesData;
	Box2i maskDataWindow;
	{
		ImagePlug::GlobalScope c( Context::current() );
		maskChannelNamesData = maskPlug()->channelNamesPlug()->getValue();
		maskDataWindow = maskPlug()->dataWindowPlug()->getValue();
	}

	const std::string &maskChannel = maskChannelPlug()->getValue();
	ConstFloatVectorDataPtr maskData = NULL;
	Box2i maskValidBound;
	if( maskPlug()->getInput<ValuePlug>() && ImageAlgo::channelExists( maskChannelNamesData->readable(), maskChannel ) )
	{
		maskData = maskPlug()->channelData( maskChannel, tileOrigin );
		maskValidBound = boxIntersection( tileBound, maskDataWindow );
	}

	ConstFloatVectorDataPtr channelData[2];
	Box2i validBound[2];

	int i = 0;
	for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it,++i )
	{
		IECore::ConstStringVectorDataPtr channelNamesData;
		Box2i dataWindow;
		{
			ImagePlug::GlobalScope c( Context::current() );
			channelNamesData = (*it)->channelNamesPlug()->getValue();
			dataWindow = (*it)->dataWindowPlug()->getValue();
		}

		const std::vector<std::string> &channelNames = channelNamesData->readable();


		if( ImageAlgo::channelExists( channelNames, channelName ) )
		{
			channelData[i] = (*it)->channelDataPlug()->getValue();
			validBound[i] = boxIntersection( tileBound, dataWindow );
		}
		else
		{
			channelData[i] = NULL;
			validBound[i] = Box2i();
		}

	}


	FloatVectorDataPtr resultData = ImagePlug::blackTile()->copy();
	float *R = &resultData->writable().front();
	const float *A = channelData[0] ? &channelData[0]->readable().front() : NULL;
	const float *B = channelData[1] ? &channelData[1]->readable().front() : NULL;
	const float *M = maskData ? &maskData->readable().front() : NULL;

	for( int y = tileBound.min.y; y < tileBound.max.y; ++y )
	{
		const bool yValidIn0 = y >= validBound[0].min.y && y < validBound[0].max.y;
		const bool yValidIn1 = y >= validBound[1].min.y && y < validBound[1].max.y;
		const bool yValidMask = y >= maskValidBound.min.y && y < maskValidBound.max.y;

		for( int x = tileBound.min.x; x < tileBound.max.x; ++x )
		{
			float a = 0;
			if( yValidIn0 && x >= validBound[0].min.x && x < validBound[0].max.x )
			{
				a = *A;
			}

			float b = 0;
			if( yValidIn1 && x >= validBound[1].min.x && x < validBound[1].max.x )
			{
				b = *B;
			}

			float m = mix;
			if( yValidMask && x >= maskValidBound.min.x && x < maskValidBound.max.x )
			{
				m *= std::max( 0.0f, std::min( 1.0f, *M ) );
			}

			*R = a * ( 1 - m ) + b * m;

			++R; ++A; ++B; ++M;
		}
	}

	return resultData;
}