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; }
ObjectPtr EXRImageReader::doOperation( const CompoundObject *operands ) { ImagePrimitivePtr image = staticPointerCast< ImagePrimitive >( ImageReader::doOperation( operands ) ); if ( image ) { headerToCompoundData( m_inputFile->header(), image->blindData() ); } return image; }
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; }
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> ¢roids = 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 ); }
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 ); } }
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; }
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; }