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