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