void YUVImageWriter::writeImage( const vector<string> &names, const ImagePrimitive * image, const Box2i &dataWindow ) const
{
	const V2f &kBkR = m_kBkRParameter->getTypedValue();
	const Box3f &range = m_rangeParameter->getTypedValue();

	if ( range.isEmpty() )
	{
		throw InvalidArgumentException("YUVImageWriter: Empty YUV range specified ");
	}

	const float kB = kBkR.x;
	const float kR = kBkR.y;

	int displayWidth  = 1 + image->getDisplayWindow().size().x;
	int displayHeight = 1 + image->getDisplayWindow().size().y;

	if ( displayWidth % 2 != 0 || displayHeight % 2 != 0 )
	{
		throw IOException("YUVImageWriter: Unsupported resolution");
	}

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

	if ( rIt == names.end() || gIt == names.end() || bIt == names.end() )
	{
		throw IOException("YUVImageWriter: Unsupported channel names specified");
	}

	std::ofstream outFile( fileName().c_str(), std::ios::trunc | std::ios::binary | std::ios::out );
	if (! outFile.is_open() )
	{
		throw IOException("Could not open '" + fileName() + "' for writing.");
	}

	Color3fVectorDataPtr rgbData = new Color3fVectorData();
	rgbData->writable().resize( displayWidth * displayHeight, Color3f(0,0,0) );

	try
	{
		for ( vector<string>::const_iterator it = names.begin(); it != names.end(); it++ )
		{
			const string &name = *it;

			if (!( name == "R" || name == "G" || name == "B" ) )
			{
				msg( Msg::Warning, "YUVImageWriter", format( "Channel \"%s\" was not encoded." ) % name );
				continue;
			}

			int channelOffset = 0;
			if ( name == "R" )
			{
				channelOffset = 0;
			}
			else if ( name == "G" )
			{
				channelOffset = 1;
			}
			else
			{
				assert( name == "B" );
				channelOffset = 2;
			}

			// get the image channel
			assert( image->variables.find( name ) != image->variables.end() );
			DataPtr dataContainer = image->variables.find( name )->second.data;
			assert( dataContainer );

			ChannelConverter converter( *it, image, dataWindow, channelOffset, rgbData );

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

		}

		V3fVectorDataPtr yuvData = new V3fVectorData();
		yuvData->writable().resize( displayWidth * displayHeight, V3f(0,0,0) );

		assert( yuvData->readable().size() == rgbData->readable().size() );

		for ( int i = 0; i < displayWidth * displayHeight; i ++ )
		{
			Color3f rgb = rgbData->readable()[i];

			if ( rgb.x < 0.0 ) rgb.x = 0;
			if ( rgb.x > 1.0 ) rgb.x = 1.0;

			if ( rgb.y < 0.0 ) rgb.y = 0;
			if ( rgb.y > 1.0 ) rgb.y = 1.0;

			if ( rgb.z < 0.0 ) rgb.z = 0;
			if ( rgb.z > 1.0 ) rgb.z = 1.0;

			V3f yPbPr;

			float &Y = yPbPr.x;
			float &Pb = yPbPr.y;
			float &Pr = yPbPr.z;

			Y = kR * rgb.x + ( 1.0 - kR - kB ) * rgb.y + kB * rgb.z;
			Pb = 0.5 * ( rgb.z - Y ) / ( 1.0 - kB );
			Pr = 0.5 * ( rgb.x - Y ) / ( 1.0 - kR );

			V3f yCbCr = yPbPr;

			/// Map from 0-1
			yCbCr.y += 0.5;
			yCbCr.z += 0.5;

			/// Apply any scaling for "head-room" and "toe-room"
			yCbCr.x	= ( yCbCr.x * ( range.max.x - range.min.x ) ) + range.min.x;
			yCbCr.y	= ( yCbCr.y * ( range.max.y - range.min.y ) ) + range.min.y;
			yCbCr.z	= ( yCbCr.z * ( range.max.z - range.min.z ) ) + range.min.z;

			yuvData->writable()[i] = yCbCr;
		}

		/// \todo Chroma-filtering. Ideally we should do a proper sampled downsize of the chroma, rather than skipping data
		/// elements. This would avoid any aliasing.

		int lumaStepX = 1;
		int lumaStepY = 1;

		int chromaUStepX = 1;
		int chromaUStepY = 1;

		int chromaVStepX = 1;
		int chromaVStepY = 1;

		switch ( m_formatParameter->getNumericValue() )
		{
			case YUV420P :
				/// Half res in U and V
				chromaUStepX = 2;
				chromaUStepY = 2;
				chromaVStepX = 2;
				chromaVStepY = 2;
				break;
			case YUV422P :
				/// Half horizonal res in U and V
				chromaUStepX = 2;
				chromaUStepY = 1;
				chromaVStepX = 2;
				chromaVStepY = 1;
				break;
			case YUV444P :
				/// Full res in U and V
				break;
			default :
				assert( false );
		}

		ScaledDataConversion<float, unsigned char> converter;
		/// Y-plane
		for ( int y = 0; y < displayHeight; y += lumaStepX )
		{
			for ( int x = 0; x < displayWidth; x += lumaStepY )
			{
				const V3f yCbCr = yuvData->readable()[y*displayWidth + x];

				const unsigned char val = converter( yCbCr.x );

				outFile.write( (const char*)&val, 1 );
			}
		}

		/// U-plane
		for ( int y = 0; y < displayHeight; y+=chromaUStepX )
		{
			for ( int x = 0; x < displayWidth; x+=chromaUStepY )
			{
				const V3f yCbCr = yuvData->readable()[y*displayWidth + x];

				const unsigned char val = converter( yCbCr.y );

				outFile.write( (const char*)&val, 1 );
			}
		}

		/// V-plane
		for ( int y = 0; y < displayHeight; y+=chromaVStepX )
		{
			for ( int x = 0; x < displayWidth; x+=chromaVStepY )
			{
				const V3f yCbCr = yuvData->readable()[y*displayWidth + x];

				const unsigned char val = converter( yCbCr.z );

				outFile.write( (const char*)&val, 1 );
			}
		}
	}
	catch ( std::exception &e )
	{
		throw IOException( ( boost::format( "YUVImageWriter : %s" ) % e.what() ).str() );
	}
	catch ( ... )
	{
		throw IOException( "YUVImageWriter: Unexpected error" );
	}
}
Example #2
0
void LuminanceOp::modifyPrimitive( Primitive * primitive, const CompoundObject * operands )
{
	DataPtr luminanceData = 0;
	PrimitiveVariable::Interpolation interpolation = PrimitiveVariable::Invalid;
	int steps[3] = { 1, 1, 1 };

	PrimitiveVariableMap::iterator colorIt = primitive->variables.find( m_colorPrimVarParameter->getTypedValue() );
	if( colorIt!=primitive->variables.end() && colorIt->second.data )
	{
		// RGB in a single channel
		switch( colorIt->second.data->typeId() )
		{
			case Color3fDataTypeId :
				{
					FloatDataPtr l = new FloatData;
					const float *d = staticPointerCast<Color3fData>( colorIt->second.data )->baseReadable();
					calculate( d, d + 1, d + 2, steps, 1, l->baseWritable() );
					luminanceData = l;
				}
				break;
			case Color3fVectorDataTypeId :
				{
					FloatVectorDataPtr l = new FloatVectorData;
					Color3fVectorDataPtr d = staticPointerCast<Color3fVectorData>( colorIt->second.data );
					l->writable().resize( d->readable().size() );
					const float *dd = d->baseReadable();
					steps[0] = steps[1] = steps[2] = 3;
					calculate( dd, dd + 1, dd + 2, steps, d->readable().size(), l->baseWritable() );
					luminanceData = l;
				}
				break;
			default :
				throw Exception( "PrimitiveVariable has unsupported type." );
				break;
		}
		interpolation = colorIt->second.interpolation;
	}
	else
	{
		// separate RGB channels?
		PrimitiveVariableMap::iterator rIt = primitive->variables.find( m_redPrimVarParameter->getTypedValue() );
		PrimitiveVariableMap::iterator gIt = primitive->variables.find( m_greenPrimVarParameter->getTypedValue() );
		PrimitiveVariableMap::iterator bIt = primitive->variables.find( m_bluePrimVarParameter->getTypedValue() );
		if( rIt==primitive->variables.end() || gIt==primitive->variables.end() || bIt==primitive->variables.end() )
		{
			throw Exception( "Primitive does not have appropriately named PrimitiveVariables." );
		}
		TypeId type = rIt->second.data->typeId();
		if( gIt->second.data->typeId() != type || bIt->second.data->typeId() != type )
		{
			throw Exception( "PrimitiveVariable types do not match." );
		}
		size_t rSize = despatchTypedData<TypedDataSize>( rIt->second.data );
		size_t gSize = despatchTypedData<TypedDataSize>( gIt->second.data );
		size_t bSize = despatchTypedData<TypedDataSize>( bIt->second.data );
		if( rSize!=gSize || rSize!=bSize )
		{
			throw Exception( "PrimitiveVariable sizes do not match." );
		}
		switch( type )
		{
			case HalfDataTypeId :
				{
					HalfDataPtr l = new HalfData;
					calculate(
						staticPointerCast<HalfData>( rIt->second.data )->baseReadable(),
						staticPointerCast<HalfData>( gIt->second.data )->baseReadable(),
						staticPointerCast<HalfData>( bIt->second.data )->baseReadable(),
						steps,
						rSize,
						l->baseWritable()
					);
					luminanceData = l;
				}
				break;
			case HalfVectorDataTypeId :
				{
					HalfVectorDataPtr l = new HalfVectorData;
					l->writable().resize( rSize );
					calculate(
						staticPointerCast<HalfVectorData>( rIt->second.data )->baseReadable(),
						staticPointerCast<HalfVectorData>( gIt->second.data )->baseReadable(),
						staticPointerCast<HalfVectorData>( bIt->second.data )->baseReadable(),
						steps,
						rSize,
						l->baseWritable()
					);
					luminanceData = l;
				}
				break;
			case FloatDataTypeId :
				{
					FloatDataPtr l = new FloatData;
					calculate(
						staticPointerCast<FloatData>( rIt->second.data )->baseReadable(),
						staticPointerCast<FloatData>( gIt->second.data )->baseReadable(),
						staticPointerCast<FloatData>( bIt->second.data )->baseReadable(),
						steps,
						rSize,
						l->baseWritable()
					);
					luminanceData = l;
				}
				break;
			case FloatVectorDataTypeId :
				{
					FloatVectorDataPtr l = new FloatVectorData;
					l->writable().resize( rSize );
					calculate(
						staticPointerCast<FloatVectorData>( rIt->second.data )->baseReadable(),
						staticPointerCast<FloatVectorData>( gIt->second.data )->baseReadable(),
						staticPointerCast<FloatVectorData>( bIt->second.data )->baseReadable(),
						steps,
						rSize,
						l->baseWritable()
					);
					luminanceData = l;
				}
				break;
			default :
				throw Exception( "PrimitiveVariables have unsupported type." );
				break;
		}
		interpolation = rIt->second.interpolation;
	}

	assert( interpolation != PrimitiveVariable::Invalid );
	assert( luminanceData );

	primitive->variables[luminancePrimVarParameter()->getTypedValue()] = PrimitiveVariable( interpolation, luminanceData );

	if( removeColorPrimVarsParameter()->getTypedValue() )
	{
		primitive->variables.erase( colorPrimVarParameter()->getTypedValue() );
		primitive->variables.erase( redPrimVarParameter()->getTypedValue() );
		primitive->variables.erase( greenPrimVarParameter()->getTypedValue() );
		primitive->variables.erase( bluePrimVarParameter()->getTypedValue() );
	}
}