Пример #1
0
static void DoApply( GenericImage<P>& image, const DisplayFunction& F )
{
   int i = image.FirstSelectedChannel();
   if ( i < image.NumberOfNominalChannels() )
   {
      int j = Max( image.LastSelectedChannel(), image.NumberOfNominalChannels()-1 );

      image.Status().Initialize( "Display function transformation", (1+j-i)*image.NumberOfSelectedPixels() );
      image.Status().DisableInitialization();

      try
      {
         image.PushSelections();
         for ( ; i <= j; ++i )
         {
            image.SelectChannel( i );
            F[i] >> image;
         }
         image.PopSelections();
         image.Status().EnableInitialization();
      }
      catch( ... )
      {
         image.Status().EnableInitialization();
         throw;
      }
   }
Пример #2
0
   template <class P> static
   void Apply( GenericImage<P>& image, const typename P::sample* lut )
   {
      if ( image.IsEmptySelection() )
         return;

      Rect r = image.SelectedRectangle();

      if ( image.Status().IsInitializationEnabled() )
         image.Status().Initialize( "LUT-based histogram transformation", image.NumberOfSelectedSamples() );

      for ( int c = image.FirstSelectedChannel(), w = r.Width(); c <= image.LastSelectedChannel(); ++c )
         for ( int y = r.y0; y < r.y1; ++y )
            for ( typename P::sample* p = image.ScanLine( y, c ) + r.x0, * pw = p + w; p < pw; ++p )
               *p = lut[*p];

      image.Status() += image.NumberOfSelectedSamples();
   }
Пример #3
0
static void LoadAndTransformImage( GenericImage<P>& transform, const GenericImage<P1>& image, bool parallel, int maxProcessors )
{
   Rect r = image.SelectedRectangle();
   if ( !r.IsRect() )
      return;

   int w = FFTC::OptimizedLength( r.Width() );
   int h = FFTC::OptimizedLength( r.Height() );

   int dw2 = (w - r.Width()) >> 1;
   int dh2 = (h - r.Height()) >> 1;

   transform.AllocateData( w, h, image.NumberOfSelectedChannels(),
                           (image.NumberOfSelectedChannels() < 3 || image.FirstSelectedChannel() != 0) ?
                                 ColorSpace::Gray : image.ColorSpace() );
   transform.Zero().Move( image, Point( dw2, dh2 ) );

   ApplyInPlaceFourierTransform( transform, FFTDirection::Forward, parallel, maxProcessors );
}
Пример #4
0
static void ApplyInPlaceFourierTransform( GenericImage<P>& image, FFTDirection::value_type dir, bool parallel, int maxProcessors )
{
   int w = FFTC::OptimizedLength( image.Width() );
   int h = FFTC::OptimizedLength( image.Height() );

   if ( w != image.Width() || h != image.Height() )
   {
      StatusCallback* s = image.GetStatusCallback(); // don't update status here
      image.SetStatusCallback( 0 );
      image.ShiftToCenter( w, h );
      image.SetStatusCallback( s );
   }

   bool statusInitialized = false;
   if ( image.Status().IsInitializationEnabled() )
   {
      image.Status().Initialize( (dir == FFTDirection::Backward) ? "Inverse FFT" : "FFT",
                                 image.NumberOfSelectedChannels()*size_type( w + h ) );
      image.Status().DisableInitialization();
      statusInitialized = true;
   }

   try
   {
      FFTC F( h, w, image.Status() );
      F.EnableParallelProcessing( parallel, maxProcessors );
      for ( int c = image.FirstSelectedChannel(); c <= image.LastSelectedChannel(); ++c )
         F( image[c], image[c], (dir == FFTDirection::Backward) ? PCL_FFT_BACKWARD : PCL_FFT_FORWARD );
      if ( statusInitialized )
         image.Status().EnableInitialization();
   }
   catch ( ... )
   {
      if ( statusInitialized )
         image.Status().EnableInitialization();
      throw;
   }
}
Пример #5
0
template <class P> inline
static void WriteJPEGImage( const GenericImage<P>& img, JPEGWriter& writer,
                            const ICCProfile& icc, JPEGFileData* fileData )
{
   if ( !writer.IsOpen() )
      throw JPEG::WriterNotInitialized( String() );

   if ( writer.Options().lowerRange >= writer.Options().upperRange )
      throw JPEG::InvalidNormalizationRange( writer.Path() );

   if ( img.IsEmptySelection() )
      throw JPEG::InvalidPixelSelection( writer.Path() );

   // Retrieve information on selected subimage.
   Rect r = img.SelectedRectangle();
   int width = r.Width();
   int height = r.Height();
   int channels = img.NumberOfSelectedChannels();

   // JPEG doesn't support alpha channels, just plain grayscale and RGB images.
   if ( channels != 1 && channels != 3 )
      throw JPEG::InvalidChannelSelection( writer.Path() );

   unsigned char qualityMarker[] =
   { 'J', 'P', 'E', 'G', 'Q', 'u', 'a', 'l', 'i', 't', 'y', 0, 0 };

   try
   {
      // Make safe copies of critical parameters.
      fileData->lowerRange = writer.Options().lowerRange;
      fileData->upperRange = writer.Options().upperRange;

      // Initialize status callback.
      if ( img.Status().IsInitializationEnabled() )
         img.Status().Initialize( String().Format(
                  "Compressing JPEG: %d channel(s), %dx%d pixels",
                  channels, width, height ), img.NumberOfSelectedSamples() );

      //
      // Allocate and initialize a JPEG compressor instance.
      //

      // This struct contains the JPEG compression parameters and pointers to
      // working space, which is allocated as needed by the IJG library.
      fileData->compressor = new ::jpeg_compress_struct;

      // Set up normal JPEG error routines, passing a pointer to our custom
      // error handler structure.
      jpeg_compressor->err = ::jpeg_std_error(
         (::jpeg_error_mgr*)(fileData->errorManager = new JPEGErrorManager( writer.Path() )) );

      // Override error_exit. JPEG library errors will be handled by throwing
      // exceptions from the JPEGErrorExit() routine.
      ((JPEGErrorManager*)fileData->errorManager)->error.error_exit = JPEGErrorExit;

      // Initialize the JPEG compression object.
      ::jpeg_create_compress( jpeg_compressor );

      //
      // Set parameters for compression.
      //

      jpeg_compressor->image_width = width;
      jpeg_compressor->image_height = height;
      jpeg_compressor->input_components = channels;
      jpeg_compressor->in_color_space = (channels == 1) ? JCS_GRAYSCALE : JCS_RGB;

      // Set default compression parameters.
      ::jpeg_set_defaults( jpeg_compressor );

      // Set any non-default parameters.

      if ( writer.JPEGOptions().quality == JPEGQuality::Unknown )
         const_cast<JPEGImageOptions&>( writer.JPEGOptions() ).quality = JPEGQuality::Default;

      // Set up compression according to specified JPEG quality.
      // We limit to baseline-JPEG values (TRUE arg in jpeg_set_quality).
      ::jpeg_set_quality( jpeg_compressor, writer.JPEGOptions().quality, TRUE );

      jpeg_compressor->optimize_coding = boolean( writer.JPEGOptions().optimizedCoding );
      jpeg_compressor->arith_code = boolean( writer.JPEGOptions().arithmeticCoding );
      jpeg_compressor->dct_method = JDCT_FLOAT;
      jpeg_compressor->write_JFIF_header = boolean( writer.JPEGOptions().JFIFMarker );
      jpeg_compressor->JFIF_major_version = writer.JPEGOptions().JFIFMajorVersion;
      jpeg_compressor->JFIF_minor_version = writer.JPEGOptions().JFIFMinorVersion;
      jpeg_compressor->density_unit = writer.Options().metricResolution ? 2 : 1;
      jpeg_compressor->X_density = RoundI( writer.Options().xResolution );
      jpeg_compressor->Y_density = RoundI( writer.Options().yResolution );

      if ( writer.JPEGOptions().progressive )
         ::jpeg_simple_progression( jpeg_compressor );

      IsoString path8 =
#ifdef __PCL_WINDOWS
         File::UnixPathToWindows( writer.Path() ).ToMBS();
#else
         writer.Path().ToUTF8();
#endif
      fileData->handle = ::fopen( path8.c_str(), "wb" );

      if ( fileData->handle == 0 )
         throw JPEG::UnableToCreateFile( writer.Path() );

      // Specify data destination (e.g., a file).
      ::jpeg_stdio_dest( jpeg_compressor, fileData->handle );

      // Start compressor.
      ::jpeg_start_compress( jpeg_compressor, TRUE );

      // Output ICC Profile data.
      // We use APP2 markers, as stated on ICC.1:2001-12.

      if ( writer.Options().embedICCProfile && icc.IsProfile() )
      {
         // Get ICC profile size in bytes from the ICC profile header
         uint32 byteCount = uint32( icc.ProfileSize() );

         if ( byteCount != 0 )
         {
            // The ICC profile must be writen as a sequence of chunks if its
            // size is larger than (roughly) 64K.

            // Number of whole chunks.
            int chunkCount = byteCount/0xFF00;

            // Remainder chunk size in bytes.
            int lastChunk = byteCount - chunkCount*0xFF00;

            // chunkCount includes a possible remainder from now on.
            if ( lastChunk != 0 )
               ++chunkCount;

            // Allocate a working buffer. 64K bytes to hold chunk data, ICC
            // marker id, etc.
            ByteArray iccData( 0xFFFF );

            // ICC JFIF marker identifier, as recommended by ICC.1:2001-12.
            // Note that this is a null-terminated string, thus the total
            // length is 12 bytes.
            ::memcpy( iccData.Begin(), "ICC_PROFILE", 12 );

            // Byte #12, next to id's null terminator, is a 1-based chunk index.
            // Byte #13 is the total count of chunks (including remainder).
            iccData[13] = uint8( chunkCount );

            // Write sequence of markers

            for ( int i = 1, offset = 0; i <= chunkCount; ++i )
            {
               // Size in bytes of this chunk.
               int size = (i == chunkCount) ? lastChunk : 0xFF00;

               // Copy ICC profile data for this chunk, preserve ICC id, etc.
               ::memcpy( iccData.At( 14 ), icc.ProfileData().At( offset ), size );

               // Keep track of this chunk's index number, one-based.
               iccData[12] = uint8( i );

               // Output an APP2 marker for this chunk.
               ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+2, (const JOCTET *)iccData.Begin(), size+14 );

               // Prepare for next chunk.
               offset += size;
            }
         }
      }

      /*
      // Output IPTC PhotoInfo data.
      // We use an APP13 marker, as recommended by IPTC.

      if ( iptc != nullptr )
      {
         size_type byteCount = iptc->MakeInfo( iptcPureData );

         if ( byteCount != 0 )
         {
            iptcData = new char[ byteCount+14 ];
            ::strcpy( (char*)iptcData, "IPTCPhotoInfo" );
            ::memcpy( ((char*)iptcData)+14, iptcPureData, byteCount );
            delete (uint8*)iptcPureData, iptcPureData = nullptr;

            ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+13,
                  (const JOCTET *)iptcData, unsigned( byteCount+14 ) );

            delete (uint8*)iptcData, iptcData = nullptr;
         }
      }
      */

      // Output JPEGQuality marker.
      // We use an APP15 marker for quality.

      qualityMarker[12] = writer.JPEGOptions().quality;
      ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+15, qualityMarker, 13 );

      //
      // Write pixels row by row.
      //

      // JSAMPLEs per row in image_buffer.
      int row_stride = width * channels;

      // Make a one-row-high sample array.
      Array<JSAMPLE> buffer( row_stride );

      // JPEG organization is chunky; PCL images are planar.
      Array<const typename P::sample*> v( channels );

      // Either rescaling or truncating to the [0,1] range is mandatory when
      // writing floating-point images to JPEG files.
      bool rescale = P::IsFloatSample() && (fileData->upperRange != 1 || fileData->lowerRange != 0);
      double k = 1.0;
      if ( rescale )
         k /= fileData->upperRange - fileData->lowerRange;

      while ( jpeg_compressor->next_scanline < jpeg_compressor->image_height )
      {
         JSAMPLE* b = buffer.Begin();

         for ( int c = 0; c < channels; ++c )
            v[c] = img.PixelAddress( r.x0,
                                     r.y0+jpeg_compressor->next_scanline,
                                     img.FirstSelectedChannel()+c );

         for ( int i = 0; i < width; ++i )
            for ( int c = 0; c < channels; ++c, ++b )
            {
               typename P::sample f = *v[c]++;

               if ( P::IsFloatSample() )
               {
                  f = typename P::sample( Range( double( f ), fileData->lowerRange, fileData->upperRange ) );
                  if ( rescale )
                     f = typename P::sample( k*(f - fileData->lowerRange) );
               }

               P::FromSample( *b, f );
            }

         b = buffer.Begin();
         ::jpeg_write_scanlines( jpeg_compressor, &b, 1 );

         img.Status() += width * channels;
         ++img.Status();
      }

      // Finish JPEG compression.
      ::jpeg_finish_compress( jpeg_compressor );

      // Close the output file.
      ::fclose( fileData->handle ), fileData->handle = nullptr;

      // Release the JPEG compression object.
      ::jpeg_destroy_compress( jpeg_compressor );
      delete jpeg_compressor, fileData->compressor = nullptr;
   }
   catch ( ... )
   {
      if ( fileData->handle != nullptr )
         ::fclose( fileData->handle ), fileData->handle = nullptr;
      throw;
   }
}