Author Topic: Fast Fits/Xisf header reader  (Read 6320 times)

Offline robyx

  • Newcomer
  • Posts: 47
Fast Fits/Xisf header reader
« on: 2019 November 01 18:56:27 »
Hi,

is there a way with PJSR to quickly read fits header table only without loading the entire image data from a fit/xisf?
by quick I mean milliseconds (10-20 max)...

Regards,
Robyx

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Fast Fits/Xisf header reader
« Reply #1 on: 2019 November 02 03:53:40 »
Hi Robyx,

Try the following script:

Code: [Select]
#include <pjsr/DataType.jsh>

function LoadFITSKeywords( fitsFilePath )
{
   // A keyword comments starts after a single slash character.
   function searchCommentSeparator( b )
   {
      let inString = false;
      for ( let i = 10; i < 80; ++i )
         switch ( b.at( i ) )
         {
         case 39: // single quote
            inString ^= true;
            break;
         case 47: // slash
            if ( !inString )
               return i;
            break;
         }
      return -1;
   }

   // In HIERARCH keywords the equal sign is after the real keyword name.
   function searchHierarchValueIndicator( b )
   {
      for ( let i = 9; i < 80; ++i )
         switch ( b.at( i ) )
         {
         case 39: // single quote, = cannot be later
            return -1;
         case 47: // slash, cannot be later
            return -1;
         case 61: // =, may be value indicator after all
            return i;
         }
      return -1;
   }

   var file = File.openFileForReading( fitsFilePath );
   let keywords = [];

   try
   {
      for ( ;; )
      {
         let rawData = file.read( DataType_ByteArray, 80 );
         let name = rawData.toString( 0, 8 );
         if ( name.trim().toUpperCase() == "END" ) // end of HDU keyword list?
            break;
         if ( file.isEOF )
            throw new Error( "Unexpected end of file reading FITS keywords: " + fitsFilePath );

         let value = "";
         let comment = "";
         let hasValue = false;

         // Is a value separator (an equal sign at byte 8) present?
         if ( rawData.at( 8 ) == 61 )
         {
            // This is a standard FITS keyword.
            hasValue = true;
            // Find comment separator slash
            let cmtPos = searchCommentSeparator( rawData );
            if ( cmtPos < 0 ) // no comment separator
               cmtPos = 80;
            // Value substring
            value = rawData.toString( 9, cmtPos-9 );
            // Comment substring
            if ( cmtPos < 80 )
               comment = rawData.toString( cmtPos+1, 80-cmtPos-1 );
         }
         else if ( name == "HIERARCH" )
         {
            // This keyword follows the nonstandard HIERARCH convention.
            let viPos = searchHierarchValueIndicator( rawData );
            if ( viPos > 0 )
            {
               hasValue = true;
               name = rawData.toString( 9, viPos-10 );
               // Find comment separator slash
               let cmtPos = searchCommentSeparator( rawData );
               if ( cmtPos < 0 ) // no comment
                  cmtPos = 80;
               // Value substring
               value = rawData.toString( viPos+1, cmtPos-viPos-1 );
               // Comment substring
               if ( cmtPos < 80 )
                  comment = rawData.toString( cmtPos+1, 80-cmtPos-1 );
            }
         }

         // If there is no value in this keyword (e.g., HISTORY)
         if ( !hasValue )
            comment = rawData.toString( 8, 80-8 ).trim();

         // Perform a naive sanity check: a valid FITS file must begin with a SIMPLE=T keyword.
         if ( keywords.length == 0 )
            if ( name != "SIMPLE  " && value.trim() != 'T' )
               throw new Error( "File does not seem to be a valid FITS file (SIMPLE T not found): " + fitsFilePath );

         // Add new keyword.
         let fitsKeyWord = new FITSKeyword( name, value, comment );
         fitsKeyWord.trim();
         keywords.push( fitsKeyWord );
      }
   }
   catch ( x )
   {
      file.close();
      throw x;
   }

   return keywords;
}

function main()
{
   let T = new ElapsedTime;
   let keywords = LoadFITSKeywords( "/foo/bar.fit" );
   let time = T.text;

   let maxValueLength = 0;
   for ( let i = 0; i < keywords.length; ++i )
      if ( keywords[i].value.length > maxValueLength )
         maxValueLength = keywords[i].value.length;
   for ( let i = 0; i < keywords.length; ++i )
      console.writeln( format( "%8s | %s%s | %s",
                               keywords[i].name,
                               keywords[i].value,
                               ' '.repeat( maxValueLength - keywords[i].value.length ),
                               keywords[i].comment ) );

   console.writeln();
   console.writeln( keywords.length, " keywords." );
   console.writeln( time );
}

main();

Replace "/foo/bar.fit" with the appropriate path to a FITS file in your filesystem. On the machine I am writing this, the script reports about 1 ms after working on a FITS file with 100 keywords. Let me know if you find this script useful.

A similar script could be written to work on XISF files. In this case the script would have to decode the relevant part of the XISF header, which is an XML document, but obviously without parsing the XML code if we want a really fast implementation. This should be slightly faster than the FITS case, since there is no uncertainty when finding XML tag attribute names and values.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline robyx

  • Newcomer
  • Posts: 47
Re: Fast Fits/Xisf header reader
« Reply #2 on: 2019 November 02 07:24:37 »
Hi Juan, thanks for this script.

In BatchPreprocessing-helper.js there is a similar one (code is different but logic looks the same) I guess I can rely on it's well.

Regards,
Robyx