Beispiel #1
0
		void operator()( const blocked_range3d<size_t>& r ) const
		{
			ContextPtr context = new Context( *m_parentContext, Context::Borrowed );
			Context::Scope scope( context.get() );
			
			const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) );
			V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min );
			V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max );
			size_t imageStride = m_dataWindow.size().x + 1;
			
			for( size_t channelIndex = r.pages().begin(); channelIndex < r.pages().end(); ++channelIndex )
			{
				context->set( ImagePlug::channelNameContextName, m_channelNames[channelIndex] );
				float *channelBegin = m_imageChannelData[channelIndex];
				
				for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize )
				{
					for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize )
					{
						context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) );
						
						Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) );
						Box2i b = boxIntersection( tileBound, operationWindow );
						size_t tileStrideSize = sizeof(float) * ( b.size().x + 1 );
						
						ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue();
						const float *tileDataBegin = &(tileData->readable()[0]);

						for( int y = b.min.y; y<=b.max.y; y++ )
						{
							const float *tilePtr = tileDataBegin + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX);
							float *channelPtr = channelBegin + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x);
							std::memcpy( channelPtr, tilePtr, tileStrideSize );
						}
					}
				}
			}
		}
Beispiel #2
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 TGAImageWriter::writeImage( const vector<string> &names, const ImagePrimitive * image, const Box2i &dataWindow ) const
{
	vector<string>::const_iterator rIt = std::find( names.begin(), names.end(), "R" );
	vector<string>::const_iterator gIt = std::find( names.begin(), names.end(), "G" );
	vector<string>::const_iterator bIt = std::find( names.begin(), names.end(), "B" );
	vector<string>::const_iterator aIt = std::find( names.begin(), names.end(), "A" );

	int numChannels = 0;
	if ( rIt != names.end() && gIt != names.end() && bIt != names.end() )
	{
		numChannels = 3;
	}
	if ( aIt != names.end() )
	{
		numChannels ++;
	}

	if ( numChannels != 3 && numChannels != 4 )
	{
		throw IOException( "TGAImageWriter: Unsupported channel names specified." );
	}

	std::ofstream out;
	out.open( fileName().c_str() );
	if ( !out.is_open() )
	{
		throw IOException( "TGAImageWriter: Could not open " + fileName() );
	}

	vector<string> desiredChannelOrder;
	desiredChannelOrder.push_back( "B" );
	desiredChannelOrder.push_back( "G" );
	desiredChannelOrder.push_back( "R" );
	desiredChannelOrder.push_back( "A" );

	vector<string> namesCopy = names;
	vector<string> filteredNames;

	for ( vector<string>::const_iterator it = desiredChannelOrder.begin(); it != desiredChannelOrder.end(); ++it )
	{
		vector<string>::iterator res = find( namesCopy.begin(), namesCopy.end(), *it );
		if ( res != namesCopy.end() )
		{
			namesCopy.erase( res );
			filteredNames.push_back( *it );
		}
	}

	for ( vector<string>::const_iterator it = namesCopy.begin(); it != namesCopy.end(); ++it )
	{
		filteredNames.push_back( *it );
	}

	assert( names.size() == filteredNames.size() );

	Box2i displayWindow = image->getDisplayWindow();

	int displayWidth  = 1 + displayWindow.size().x;
	int displayHeight = 1 + displayWindow.size().y;

	// Write the header

	/// ID Length
	writeLittleEndian<uint8_t>( out, 0 );

	/// Color Map Type
	writeLittleEndian<uint8_t>( out, 0 );

	/// Image Type
	writeLittleEndian<uint8_t>( out, 2 );

	/// Color Map Specification
	writeLittleEndian<uint16_t>( out, 0 );
	writeLittleEndian<uint16_t>( out, 0 );
	writeLittleEndian<uint8_t>( out, 0 );

	/// Image Specification
	writeLittleEndian<uint16_t>( out, 0 );
	writeLittleEndian<uint16_t>( out, 0 );
	writeLittleEndian<uint16_t>( out, displayWidth );
	writeLittleEndian<uint16_t>( out, displayHeight );
	writeLittleEndian<uint8_t>( out, numChannels * 8 );
	writeLittleEndian<uint8_t>( out, ( numChannels == 4 ? 8 : 0 ) + 32 );

	// Encode the image buffer
	std::vector<uint8_t> imageBuffer( displayWidth*displayHeight*numChannels, 0 );

	int offset = 0;
	for ( vector<string>::const_iterator it = filteredNames.begin(); it != filteredNames.end(); ++it )
	{
		const std::string &name = *it;
		if ( !( name == "R" || name == "G" || name == "B" || name == "A" ) )
		{
			msg( Msg::Warning, "TGAImageWriter::write", format( "Channel \"%s\" was not encoded." ) % name );
		}
		else
		{
			assert( image->variables.find( name ) != image->variables.end() );
			DataPtr dataContainer = image->variables.find( name )->second.data;
			assert( dataContainer );
			ChannelConverter converter( name, image, dataWindow, numChannels, offset, imageBuffer );
			despatchTypedData<
			ChannelConverter,
			TypeTraits::IsNumericVectorTypedData,
			ChannelConverter::ErrorHandler
			>( dataContainer, converter );

			++offset;
		}
	}

	/// Write the Buffer
	for ( int i = 0; i < displayWidth*displayHeight*numChannels; ++i )
	{
		assert( i < ( int )imageBuffer.size() );
		writeLittleEndian( out, imageBuffer[i] );
		if ( out.fail() )
		{
			throw IOException( "TGAImageWriter: Error writing to " + fileName() );
		}
	}
}
DataPtr SGIImageReader::readTypedChannel( const std::string &name, const Box2i &dataWindow )
{
	typedef TypedData< std::vector< V > > TargetVector;

	assert( open() );

	assert( m_header );

	typename TypedData< std::vector<T > >::ConstPtr dataBuffer = assertedStaticCast< TypedData< std::vector<T > > >( m_buffer );
	assert( dataBuffer );

	const typename TypedData< std::vector<T > >::ValueType &buffer = dataBuffer->readable();

	assert( m_header->m_channelOffsets.find( name ) != m_header->m_channelOffsets.end() );
	int channelOffset = m_header->m_channelOffsets[name];

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

	int wholeDataHeight = 1 + wholeDataWindow.size().y;
	int wholeDataWidth = 1 + wholeDataWindow.size().x;

	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;

	ScaledDataConversion<T, V> converter;

	if ( m_header->m_fileHeader.m_storageFormat >= 1 ) /// RLE
	{
		int dataY = 0;
		for ( int y = yMin ; y <= yMax ; ++y, ++dataY )
		{
			assert( yMin >= 0 );
			assert( yMin < wholeDataHeight );

			/// Images are unencoded "upside down" (flipped in Y), for our purposes
			uint32_t rowOffset = m_header->m_offsetTable[ channelOffset * wholeDataHeight + ( wholeDataHeight - 1 - y ) ];

			if ( rowOffset >= buffer.size() )
			{
				throw IOException( "SGIImageReader: Invalid RLE row offset found while reading " + fileName() );
			}

			std::vector<T> scanline( wholeDataWidth );

			uint32_t scanlineOffset = 0;
			bool done = false;

			while ( !done )
			{
				T pixel = buffer[ rowOffset ++ ];

				T count = pixel & 0x7f;

				if ( count == 0 )
				{
					done = true;
				}
				else
				{
					if ( pixel & 0x80 )
					{
						if ( scanlineOffset + count - 1 >= scanline.size() || rowOffset + count - 1 >= buffer.size() )
						{
							throw IOException( "SGIImageReader: Invalid RLE data found while reading " + fileName() );
						}

						while ( count -- )
						{
							assert( scanlineOffset < scanline.size() );
							assert( rowOffset < buffer.size() );
							scanline[ scanlineOffset++ ] = buffer[ rowOffset ++ ];
						}
					}
					else
					{
						if ( scanlineOffset + count - 1 >= scanline.size() || rowOffset - 1 >= buffer.size() )
						{
							throw IOException( "SGIImageReader: Invalid RLE data found while reading " + fileName() );
						}

						assert( rowOffset < buffer.size() );
						pixel = buffer[ rowOffset ++ ];

						while ( count -- )
						{
							assert( scanlineOffset < scanline.size() );
							scanline[ scanlineOffset++ ] = pixel;
						}
					}
				}
			}

			if ( scanlineOffset != scanline.size() )
			{
				throw IOException( "SGIImageReader: Error occurred during RLE decode while reading " + fileName() );
			}

			int dataOffset = dataY * dataWidth;

			for ( int x = xMin; x <= xMax ; ++x, ++dataOffset  )
			{
				data[dataOffset] = converter( scanline[x] );
			}
		}
	}
	else /// Not RLE
	{
		int dataY = 0;

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

				/// Images are unencoded "upside down" (flipped in Y), for our purposes
				data[dataOffset] = converter( buffer[ ( channelOffset * wholeDataHeight * wholeDataWidth ) + ( wholeDataHeight - 1 - y  ) * wholeDataWidth + x  ] );
			}
		}
	}

	return dataContainer;
}
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 DPXImageWriter::writeImage( const vector<string> &names, const ImagePrimitive *image, const Box2i &dataWindow ) const
{
	// write the dpx in the standard 10bit log format
	ofstream out;
	out.open(fileName().c_str());
	if ( !out.is_open() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	/// We'd like RGB to be at the front, in that order, because it seems that not all readers support the channel identifiers!
	vector<string> desiredChannelOrder;
	desiredChannelOrder.push_back( "R" );
	desiredChannelOrder.push_back( "G" );
	desiredChannelOrder.push_back( "B" );

	vector<string> namesCopy = names;
	vector<string> filteredNames;

	for ( vector<string>::const_iterator it = desiredChannelOrder.begin(); it != desiredChannelOrder.end(); ++it )
	{
		vector<string>::iterator res = find( namesCopy.begin(), namesCopy.end(), *it );
		if ( res != namesCopy.end() )
		{
			namesCopy.erase( res );
			filteredNames.push_back( *it );
		}
	}

	for ( vector<string>::const_iterator it = namesCopy.begin(); it != namesCopy.end(); ++it )
	{
		filteredNames.push_back( *it );
	}

	assert( names.size() == filteredNames.size() );

	Box2i displayWindow = image->getDisplayWindow();

	int displayWidth  = 1 + displayWindow.size().x;
	int displayHeight = 1 + displayWindow.size().y;

	// build the header
	DPXFileInformation fi;
	memset(&fi, 0, sizeof(fi));

	DPXImageInformation ii;
	memset(&ii, 0, sizeof(ii));

	DPXImageOrientation ioi;
	memset(&ioi, 0, sizeof(ioi));

	DPXMotionPictureFilm mpf;
	memset(&mpf, 0, sizeof(mpf));

	DPXTelevisionHeader th;
	memset(&th, 0, sizeof(th));

	fi.magic = asBigEndian<>( 0x53445058 );

	// compute data offsets
	fi.gen_hdr_size = sizeof(fi) + sizeof(ii) + sizeof(ioi);
	fi.gen_hdr_size = asBigEndian<>(fi.gen_hdr_size);

	fi.ind_hdr_size = sizeof(mpf) + sizeof(th);
	fi.ind_hdr_size = asBigEndian<>(fi.ind_hdr_size);

	int header_size = sizeof(fi) + sizeof(ii) + sizeof(ioi) + sizeof(mpf) + sizeof(th);
	fi.image_data_offset = header_size;
	fi.image_data_offset = asBigEndian<>(fi.image_data_offset);

	strcpy((char *) fi.vers, "V2.0");

	strncpy( (char *) fi.file_name, fileName().c_str(), sizeof( fi.file_name ) );

	// compute the current date and time
	boost::posix_time::ptime utc = boost::posix_time::second_clock::universal_time();
	boost::gregorian::date date = utc.date();
	boost::posix_time::time_duration time = utc.time_of_day();

	snprintf((char *) fi.create_time,  sizeof( fi.create_time ), "%04d:%02d:%02d:%02d:%02d:%02d:%s",
	        static_cast<int>( date.year() ),
		static_cast<int>( date.month() ),
		static_cast<int>( date.day() ),
	        time.hours(),
		time.minutes(),
		time.seconds(),
		"UTC"
	);

	snprintf((char *) fi.creator, sizeof( fi.creator ), "cortex");
	snprintf((char *) fi.project, sizeof( fi.project ), "cortex");
	snprintf((char *) fi.copyright, sizeof( fi.copyright ), "Unknown");

	ii.orientation = 0;    // left-to-right, top-to-bottom
	ii.element_number = 1;
	ii.pixels_per_line = displayWidth;
	ii.lines_per_image_ele = displayHeight;

	ii.element_number      = asBigEndian<>(ii.element_number);
	ii.pixels_per_line     = asBigEndian<>(ii.pixels_per_line);
	ii.lines_per_image_ele = asBigEndian<>(ii.lines_per_image_ele);

	for (int c = 0; c < 8; ++c)
	{
		DPXImageInformation::_image_element &ie = ii.image_element[c];
		ie.data_sign = 0;

		/// \todo Dcoument these constants
		ie.ref_low_data = 0;
		ie.ref_low_quantity = 0.0;
		ie.ref_high_data = 1023;
		ie.ref_high_quantity = 2.046;

		ie.ref_low_data = asBigEndian<>(ie.ref_low_data);
		ie.ref_low_quantity = asBigEndian<>(ie.ref_low_quantity);
		ie.ref_high_data = asBigEndian<>(ie.ref_high_data);
		ie.ref_high_quantity = asBigEndian<>(ie.ref_high_quantity);

		/// \todo Dcoument these constants
		ie.transfer = 1;
		ie.packing =  asBigEndian<short>( 1 );
		ie.bit_size = 10;
		ie.descriptor = 50;

		ie.data_offset = fi.image_data_offset;
	}

	ioi.x_offset = 0;
	ioi.y_offset = 0;

	// Write the header
	int image_data_size = sizeof( unsigned int ) * displayWidth * displayHeight;
	fi.file_size = header_size + image_data_size;

	fi.file_size = asBigEndian<>(fi.file_size);

	out.write(reinterpret_cast<char *>(&fi),  sizeof(fi));
	if ( out.fail() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	out.write(reinterpret_cast<char *>(&ii),  sizeof(ii));
	if ( out.fail() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	out.write(reinterpret_cast<char *>(&ioi), sizeof(ioi));
	if ( out.fail() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	out.write(reinterpret_cast<char *>(&mpf), sizeof(mpf));
	if ( out.fail() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	out.write(reinterpret_cast<char *>(&th),  sizeof(th));
	if ( out.fail() )
	{
		throw IOException( "DPXImageWriter: Error writing to " + fileName() );
	}

	// write the data
	vector<unsigned int> imageBuffer( displayWidth*displayHeight, 0 );

	int offset = 0;
	vector<string>::const_iterator i = filteredNames.begin();
	while (i != filteredNames.end())
	{
		if (!(*i == "R" || *i == "G" || *i == "B"))
		{
			msg( Msg::Warning, "DPXImageWriter::write", format( "Channel \"%s\" was not encoded." ) % *i );
			++i;
			continue;
		}

		int bpp = 10;
		unsigned int shift = (32 - bpp) - (offset*bpp);

		assert( image->variables.find( *i ) != image->variables.end() );
		DataPtr dataContainer = image->variables.find( *i )->second.data;
		assert( dataContainer );

		ChannelConverter converter( *i, image, dataWindow, shift, imageBuffer );

		despatchTypedData<
			ChannelConverter,
			TypeTraits::IsNumericVectorTypedData,
			ChannelConverter::ErrorHandler
		>( dataContainer.get(), converter );

		++i;
		++offset;
	}

	// write the buffer
	for (int i = 0; i < displayWidth * displayHeight; ++i)
	{
		imageBuffer[i] = asBigEndian<>(imageBuffer[i]);
		out.write( (const char *) (&imageBuffer[i]), sizeof(unsigned int) );
		if ( out.fail() )
		{
			throw IOException( "DPXImageWriter: Error writing to " + fileName() );
		}
	}
}