Ejemplo n.º 1
0
DataPtr JPEGImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow, int channelOffset )
{
	int area = ( dataWindow.size().x + 1 ) * ( dataWindow.size().y + 1 );
	assert( area >= 0 );
	int dataWidth = 1 + dataWindow.size().x;
	int dataY = 0;

	typedef TypedData< std::vector< V > > TargetVector;
	
	typename TargetVector::Ptr dataContainer = new TargetVector();
	typename TargetVector::ValueType &data = dataContainer->writable();
	data.resize( area );

	ScaledDataConversion< unsigned char, V> converter;

	for ( int y = dataWindow.min.y; y <= dataWindow.max.y; ++y, ++dataY )
	{
		typename TargetVector::ValueType::size_type dataOffset = dataY * dataWidth;
		for ( int x = dataWindow.min.x; x <= dataWindow.max.x; ++x, ++dataOffset )
		{
			assert( dataOffset < data.size() );
			data[dataOffset] = converter( m_buffer[ m_numChannels * ( y * m_bufferWidth + x ) + channelOffset ] );
		}
	}
	return dataContainer;
}
Ejemplo n.º 2
0
void ImageStats::compute( ValuePlug *output, const Context *context ) const
{
	const int colorIndex = ::colorIndex( output );
	if( colorIndex == -1 )
	{
		// Not a plug we know about
		ComputeNode::compute( output, context );
		return;
	}

	const std::string channelName = this->channelName( colorIndex );
	const Imath::Box2i area = areaPlug()->getValue();

	if( channelName.empty() || BufferAlgo::empty( area ) )
	{
		output->setToDefault();
		return;
	}

	// Loop over the ROI and compute the min, max and average channel values and then set our outputs.
	Sampler s( inPlug(), channelName, area );

	float min = Imath::limits<float>::max();
	float max = Imath::limits<float>::min();
	double sum = 0.;

	for( int y = area.min.y; y < area.max.y; ++y )
	{
		for( int x = area.min.x; x < area.max.x; ++x )
		{
			float v = s.sample( x, y );
			min = std::min( v, min );
			max = std::max( v, max );
			sum += v;
		}
	}

	if( output->parent<Plug>() == minPlug() )
	{
		static_cast<FloatPlug *>( output )->setValue( min );
	}
	else if( output->parent<Plug>() == maxPlug() )
	{
		static_cast<FloatPlug *>( output )->setValue( max );
	}
	else if( output->parent<Plug>() == averagePlug() )
	{
		static_cast<FloatPlug *>( output )->setValue(
			sum / double( (area.size().x) * (area.size().y) )
		);
	}
}
Ejemplo n.º 3
0
void make_color_bars( const image_view_t& view, const Imath::Box2i& domain, const Imath::Box2i& defined)
{
    typedef detail::color_bars_fn deref_t;
    typedef deref_t::point_t point_t;
    typedef boost::gil::virtual_2d_locator<deref_t,false> locator_t;
    typedef boost::gil::image_view<locator_t> my_virt_view_t;

    point_t dims( domain.size().x+1, domain.size().y+1);
    my_virt_view_t bars( dims, locator_t( point_t(0,0), point_t(1,1), deref_t( dims)));

    boost::gil::copy_pixels( boost::gil::subimage_view( bars, defined.min.x - domain.min.x,
								defined.min.y - domain.min.y,
								defined.size().x+1, defined.size().y+1), view);
}
Ejemplo n.º 4
0
Imath::M33f Transform2DPlug::matrix( const Imath::Box2i &displayWindow, double pixelAspect ) const
{
	// We need to transform from image space (with 0x0 being the bottom left)
	// to Gadget space (where 0x0 is the top left). To do this, we need to know the
	// size of the Format.
	
	///\todo: We don't handle the pixel aspect of the format here but we should!
	float formatHeight = displayWindow.size().y + 1;
	
	M33f p;
	V2f pivotVec = pivotPlug()->getValue();
	pivotVec.y = formatHeight - pivotVec.y;
	p.translate( pivotVec );
	
	M33f t;
	V2f translateVec = translatePlug()->getValue();
	translateVec.y *= -1.;
	t.translate( translateVec );
	
	M33f r;
	r.rotate( IECore::degreesToRadians( rotatePlug()->getValue() ) );
	M33f s;
	s.scale( scalePlug()->getValue() );
	
	M33f pi;
	pi.translate( pivotVec*Imath::V2f(-1.f) );
	M33f result = pi * s * r * t * p;

	return result;
}
Ejemplo n.º 5
0
		virtual void imageData( const Imath::Box2i &box, const float *data, size_t dataSize )
		{
			Box2i yUpBox = m_gafferFormat.yDownToFormatSpace( box );
			const V2i boxMinTileOrigin = ImagePlug::tileOrigin( yUpBox.min );
			const V2i boxMaxTileOrigin = ImagePlug::tileOrigin( yUpBox.max );
			for( int tileOriginY = boxMinTileOrigin.y; tileOriginY <= boxMaxTileOrigin.y; tileOriginY += ImagePlug::tileSize() )
			{
				for( int tileOriginX = boxMinTileOrigin.x; tileOriginX <= boxMaxTileOrigin.x; tileOriginX += ImagePlug::tileSize() )
				{
					for( int channelIndex = 0, numChannels = channelNames().size(); channelIndex < numChannels; ++channelIndex )
					{
						const V2i tileOrigin( tileOriginX, tileOriginY );
						ConstFloatVectorDataPtr tileData = getTile( tileOrigin, channelIndex );
						if( !tileData )
						{
							// we've been sent data outside of the data window
							continue;
						}

						// we must create a new object to hold the updated tile data,
						// because the old one might well have been returned from
						// computeChannelData and be being held in the cache.
						FloatVectorDataPtr updatedTileData = tileData->copy();
						vector<float> &updatedTile = updatedTileData->writable();

						const Box2i tileBound( tileOrigin, tileOrigin + Imath::V2i( GafferImage::ImagePlug::tileSize() - 1 ) );
						const Box2i transferBound = IECore::boxIntersection( tileBound, yUpBox );
						for( int y = transferBound.min.y; y<=transferBound.max.y; ++y )
						{
							int srcY = m_gafferFormat.formatToYDownSpace( y );
							size_t srcIndex = ( ( srcY - box.min.y ) * ( box.size().x + 1 ) * numChannels ) + ( transferBound.min.x - box.min.x ) + channelIndex;
							size_t dstIndex = ( y - tileBound.min.y ) * ImagePlug::tileSize() + transferBound.min.x - tileBound.min.x;
							const size_t srcEndIndex = srcIndex + transferBound.size().x * numChannels;
							while( srcIndex <= srcEndIndex )
							{
								updatedTile[dstIndex] = data[srcIndex];
								srcIndex += numChannels;
								dstIndex++;
							}
						}

						setTile( tileOrigin, channelIndex, updatedTileData );
					}
				}
			}

			dataReceivedSignal()( this, box );
		}
Ejemplo n.º 6
0
DataPtr CINImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow )
{
	typedef TypedData< std::vector< V > > TargetVector;

	// figure out the offset into the bitstream for the given channel
	assert( m_header->m_channelOffsets.find( name ) != m_header->m_channelOffsets.end() );
	int channelOffset = m_header->m_channelOffsets[name];

	assert( (int)m_header->m_imageInformation.channel_information[channelOffset].bpp == 10 );
	int bpp = m_header->m_imageInformation.channel_information[channelOffset].bpp;
	// todo: make sure ushort is enough for the current bpp
	int ushortShift = sizeof(unsigned short)*8 - bpp;

	// form the mask for this channel
	unsigned int mask = 0;
	for (int pi = 0; pi < bpp; ++pi)
	{
		mask = 1 + (mask << 1);
	}
	mask <<= ((32 - bpp) - channelOffset * bpp);

	ScaledDataConversion< unsigned short, V> converter;

	typename TargetVector::Ptr dataContainer = new TargetVector();
	typename TargetVector::ValueType &data = dataContainer->writable();
	int area = ( dataWindow.size().x + 1 ) * ( dataWindow.size().y + 1 );
	assert( area >= 0 );
	data.resize( area );

	int dataWidth = 1 + dataWindow.size().x;

	Box2i wholeDataWindow = this->dataWindow();

	const int yMin = dataWindow.min.y - wholeDataWindow.min.y;
	const int yMax = dataWindow.max.y - wholeDataWindow.min.y;

	const int xMin = dataWindow.min.x - wholeDataWindow.min.x;
	const int xMax = dataWindow.max.x - wholeDataWindow.min.x;

	int dataY = 0;
	for ( int y = yMin ; y <= yMax ; ++y, ++dataY )
	{
		typename TargetVector::ValueType::size_type dataOffset = dataY * dataWidth;
		std::vector<unsigned int>::size_type bufferOffset = y * m_bufferWidth + xMin;

		for ( int x = xMin;  x <= xMax ; ++x, ++dataOffset, ++bufferOffset  )
		{
			assert( dataOffset < data.size() );

			unsigned int cell = m_buffer[ bufferOffset ];
			if ( m_reverseBytes )
			{
				cell = reverseBytes( cell );
			}

			// assume we have 10bit log, two wasted bits aligning to 32 longword
			unsigned short cv = (unsigned short) ( ( mask & cell ) >> ( 2 + ( 2 - channelOffset ) * bpp ) );
			assert( cv < 1024 );
			data[dataOffset] = converter( (cv << ushortShift) + ((1 << ushortShift) - 1) );
		}
	}
	return dataContainer;
}
Ejemplo n.º 7
0
gray_image_view_t buffer_t::gray_subimage_view( const Imath::Box2i& area) const
{
    check_area_inside_image( area);
    return boost::gil::subimage_view( gray_view(), area.min.x - bounds_.min.x, area.min.y - bounds_.min.y,
										area.size().x+1, area.size().y+1);
}
Ejemplo n.º 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();
	}
}
Ejemplo n.º 9
0
DataPtr EXRImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow, const Imf::Channel *channel )
{
	assert( channel );
	Imath::V2i pixelDimensions = dataWindow.size() + Imath::V2i( 1 );
	unsigned numPixels = pixelDimensions.x * pixelDimensions.y;

	typedef TypedData<vector<T> > DataType;
	typename DataType::Ptr data = new DataType;
	data->writable().resize( numPixels );

	Imath::Box2i fullDataWindow = this->dataWindow();
	if( fullDataWindow.min.x==dataWindow.min.x && fullDataWindow.max.x==dataWindow.max.x )
	{
		// the width we want to read matches the width in the file, so we can read straight
		// into the result buffer
		FrameBuffer frameBuffer;
		T *buffer00 = data->baseWritable() - dataWindow.min.y * pixelDimensions.x - fullDataWindow.min.x;
		Slice slice( channel->type, (char *)buffer00, sizeof(T), sizeof(T) * pixelDimensions.x );
		frameBuffer.insert( name.c_str(), slice );
		m_inputFile->setFrameBuffer( frameBuffer );
		// exr library will choose the best order to read scanlines automatically (increasing or decreasing)
		try
		{
			m_inputFile->readPixels( dataWindow.min.y, dataWindow.max.y );
		}
		catch( Iex::InputExc &e )
		{
			// so we can read incomplete files
			msg( Msg::Warning, "EXRImageReader::readChannel", e.what() );
			return data;
		}
	}
	else
	{
		// widths don't match, we need to read into a temporary buffer and then transfer just
		// the bits we need into the result buffer.
		int numTmpPixels = fullDataWindow.size().x + 1;
		vector<T> tmpBuffer( numTmpPixels );
		T *tmpBufferTransferStart = &(tmpBuffer[0]) + dataWindow.min.x - fullDataWindow.min.x;
		size_t tmpBufferTransferLength = pixelDimensions.x * sizeof( T );
		T *transferDestination = &(data->writable()[0]);

		// slice has yStride of 0 so each successive scanline just overwrites the previous one
		Slice slice( channel->type, (char *)(&(tmpBuffer[0]) - fullDataWindow.min.x), sizeof(T), 0 );
		FrameBuffer frameBuffer;
		frameBuffer.insert( name.c_str(), slice );
		m_inputFile->setFrameBuffer( frameBuffer );

		int yStart = dataWindow.min.y;
		int yEnd = dataWindow.max.y;
		int yStep = 1;
		try
		{
			for( int y=yStart; y!=(yEnd+yStep); y+=yStep )
			{
				m_inputFile->readPixels( y );
				memcpy( (char *)transferDestination, (const char *)tmpBufferTransferStart, tmpBufferTransferLength );
				transferDestination += pixelDimensions.x;
			}
		}
		catch( Iex::InputExc &e )
		{
			// so we can read incomplete files
			msg( Msg::Warning, "EXRImageReader::readChannel", e.what() );
			return data;
		}
	}

#ifndef NDEBUG
	for ( typename DataType::ValueType::const_iterator it = data->readable().begin(); it != data->readable().end(); ++it )
	{
		assert( *it == *it ); // Will fail iff NaN
	}
#endif

	return data;
}
Ejemplo n.º 10
0
void viewport_t::resize( const Imath::Box2i& device)
{
	world_.max.x = world_.min.x + ( device.size().x / zoom_x());
	world_.max.y = world_.min.y + ( device.size().y / zoom_y());
	device_ = device;
}
Ejemplo n.º 11
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 );
	}
}
Ejemplo n.º 12
0
void HitMissTransform::modifyChannels( const Imath::Box2i &displayWindow, const Imath::Box2i &dataWindow, ChannelVector &channels )
{
	
	// process the structuring elements, including making rotated versions if requested.
	
	bool rotateElements = rotateStructuringElementsParameter()->getTypedValue();
	
	vector<int> masks;
	vector<int> elements;
	const std::vector<M33f> &matrices = structuringElementsParameter()->getTypedValue();
	for( unsigned i=0; i<matrices.size(); i++ )
	{
		int mask = 0;
		int element = 0;
		processMatrix( matrices[i], mask, element );
		masks.push_back( mask );
		elements.push_back( element );
		if( rotateElements )
		{
			M33f m = matrices[i];
			for( unsigned r=0; r<3; r++ )
			{
				M33f m2( m[0][2], m[1][2], m[2][2], m[0][1], m[1][1], m[2][1], m[0][0], m[1][0], m[2][0] );
				processMatrix( m2, mask, element );
				masks.push_back( mask );
				elements.push_back( element );
				m = m2;
			}
		}
	}
		
	// apply the operation to each channel
	
	char value = valueParameter()->getNumericValue() > thresholdParameter()->getNumericValue() ? 1 : 0;
	char borderValue = borderValueParameter()->getNumericValue() > thresholdParameter()->getNumericValue() ? 1 : 0;
	bool applyAlternately = applyElementsAlternatelyParameter()->getTypedValue();
	int numIterations = iterationsParameter()->getNumericValue();
	if( applyAlternately )
	{
		numIterations *= elements.size();
	}
	V2i size = dataWindow.size() + V2i( 1 );
	V2i paddedSize = size + V2i( 2 );
	
	std::vector<char> pixels;
	std::vector<char> pixels2; 
	for( unsigned i=0; i<channels.size(); i++ )
	{
		// threshold the image into a temporary pixels structure
		Thresholder thresholder( thresholdParameter()->getNumericValue(), borderValue, size, pixels );
		despatchTypedData<Thresholder, TypeTraits::IsNumericVectorTypedData>( channels[i], thresholder );
		pixels2.clear(); pixels2.resize( pixels.size(), borderValue );
		
		// do the work
		unsigned iterationsSinceChange = 0;
		for( int n=0; n<numIterations || numIterations==0; n++ )
		{
			iterationsSinceChange++;
			for( int y=0; y<size.y; y++ )
			{
				const char *r0 = &(pixels[y * paddedSize.x]) + 1;
				const char *r1 = r0 + paddedSize.x;
				const char *r2 = r1 + paddedSize.x;
				char *ro = &(pixels2[(y+1) * paddedSize.x]) + 1;
				
				for( int x=0; x<size.x; x++ )
				{
					if( *r1==value )
					{
						// no point doing the work if the existing value is the one we'd change it to anyway
						*ro = value;
					}
					else
					{
						int v = r0[-1] 			| 	r0[0] << 2 		| 	r0[1] << 4 		|
								r1[-1] << 6 	| 	r1[0] << 8 		| 	r1[1] << 10 	|
								r2[-1] << 12 	| 	r2[0] << 14 	| 	r2[1] << 16;

						bool matches = false;
						if( applyAlternately )
						{
							size_t e = n % elements.size();
							if( (v & masks[e])==elements[e] )
							{
								matches = true;
							}				
						}
						else
						{
							for( size_t e=0; e<elements.size(); e++ )
							{
								if( (v & masks[e])==elements[e] )
								{
									matches = true;
									break;
								}
							}
						}

						if( matches )
						{
							*ro = value;
							iterationsSinceChange = 0;
						}
						else
						{
							*ro = *r1;
						}
					}
					
					ro++;
					r0++;
					r1++;
					r2++;
				}
			}
			pixels.swap( pixels2 );
			
			if( (applyAlternately && iterationsSinceChange==elements.size()) || ( !applyAlternately && iterationsSinceChange ) )
			{
				break;
			}
			
		}
		
		// and copy back into the original structure
		Copyer copyer( size, pixels );
		despatchTypedData<Copyer, TypeTraits::IsNumericVectorTypedData>( channels[i], copyer );
	}
}
Ejemplo n.º 13
0
void ImageThinner::modifyChannels( const Imath::Box2i &displayWindow, const Imath::Box2i &dataWindow, ChannelVector &channels )
{
	float threshold = thresholdParameter()->getNumericValue();

	const V2i size = dataWindow.size() + V2i( 1 );

	for( unsigned i=0; i<channels.size(); i++ )
	{
		FloatVectorDataPtr floatData = runTimeCast<FloatVectorData>( channels[i] );
		if( !floatData )
		{
			throw Exception( "ImageThinner::modifyChannels : only float channels supported." );
		}
		std::vector<float> &channel = floatData->writable();

		// threshold the image first
		for( std::vector<float>::iterator it=channel.begin(); it!=channel.end(); it++ )
		{
			*it = *it < threshold ? 0.0f : 1.0f;
		}

		// then apply the graphics gems magic that i don't understand in the slightest
		//////////////////////////////////////////////////////////////////////////////

		boost::multi_array_ref<float, 2> image( &channel[0], boost::extents[size.y][size.x] );

		int	p, q; // Neighborhood maps of adjacent cells
		std::vector<unsigned char> qb; // Neighborhood maps of previous scanline
		qb.resize( size.x );
		qb[qb.size()-1] = 0; // Used for lower-right pixel

		int count = 1; // Deleted pixel count
		while( count )
		{
			count = 0;

			for( int i = 0; i < 4 ; i++ )
			{

				int m = g_masks[i];

				// Build initial previous scan buffer

				p = image[0][0] > 0.5f;
				for( int x = 0; x < size.x-1; x++ )
				{
					qb[x] = p = ((p<<1)&0006) | (image[0][x+1] > 0.5f);
				}

				// Scan image for pixel deletion candidates

				for( int y = 0; y < size.y-1; y++ )
				{

					q = qb[0];
					p = ((q<<3)&0110) | (image[y+1][0] > 0.5f);

					for( int x = 0; x < size.x-1; x++ )
					{
						q = qb[x];
						p = ((p<<1)&0666) | ((q<<3)&0110) |
							(image[y+1][x+1] > 0.5f);
						qb[x] = p;
						if( ((p&m) == 0) && g_delete[p] )
						{
							count++;
							image[y][x] = 0.f;
						}
					}

					// Process right edge pixel

					p = (p<<1)&0666;
					if( (p&m) == 0 && g_delete[p] )
					{
						count++;
						image[y][size.x-1] = 0.f;
					}

				}

				// Process bottom scan line

				for( int x = 0 ; x < size.x ; x++ )
				{
					q = qb[x];
					p = ((p<<1)&0666) | ((q<<3)&0110);
					if( (p&m) == 0 && g_delete[p] )
					{
						count++;
						image[size.y-1][x] = 0.f;
					}
				}
			}
		}
	}
}