No BSD License  

Highlights from
fits

from fits by Roberto Abraham
These files read, write, and scan the headers of FITS files.

output_image=fitsread(filename,subset,scaleQ)
function output_image=fitsread(filename,subset,scaleQ)
% FITSREAD reads a FITS image into a Matlab variable.
%
%  data = fitsread(filename); 
%  data = fitsread(filename,range);
%  data = fitsread(filename,range,scaleQ);
%
%  The first form indicates that the full image should be read in. The
%  data is scaled by the BZERO and BSCALE keywords, which are assumed to
%  exist in the file.
%
%  The second form indicates that a subimage should be read in. The
%  subimage is specified by a string of the form 'xlow:xhigh,ylow:yhigh'.
%  The string 'full' can also be used if the full image is to be read in.
%  The data will be scaled using the BZERO and BSCALE keywords, which
%  are assumed to exist in the file.
%
%  The third form is like the second form, except a 1 or 0 is used to
%  indicate whether the BZERO and BSCALE keywords should be searched
%  for and used to scale the data (1=yes, 0=no). This should be
%  unnecessary unless you're reading in non-standard FITS files where
%  the BZERO and BSCALE keywords do not exist.
%
%  Notes:
%    1. If a filename with no extension is supplied, this routine looks
%    for a file with an extension of '.fits'. 
%
%    2. If a sub-image is read in only the memory required to hold the
%    sub-image is used by the function (ie. the whole image is not read
%    in, which would be wasteful and possibly very slow).
%
%    3. To compare the image with one viewed with a standard image viewer
%    such as xv or SAOimage be sure to display the image with the 
%    following settings: 
%
%              axis xy; 
%              axis image;
%
%    so that the origin is at the lower left and the correct aspect ratio
%    is shown.
%
%  Examples:
%    im=fitsread('n2715_J');                        % Assumes .fits extension
%    im=fitsread('n2715_lJ.fits','10:313,15:175');  % Reads a subimage 
%    im=fitsread('n2715_lJ.fits','full',0);         % Full image, no scaling 
%
%  Known deficiences:
%  1. Does not read anything more complicated than a simple 2D image
%     ie. no binary tables or data cubes.
%
%  2. Only does a very cursory inspection of possible architectures
%     in order to figure out if the data needs to be byte swapped.
%     This is because I only test for architectures I am familiar with.
%     (I believe this routine will work with PC-compatibles, Macs, Sun
%     workstations, and possibly DEC Alphas). 
%
% Version 2.5
% R. Abraham, Institute of Astronomy, Cambridge University


% Changes
% Mar. 99 -- added the capability of extracting a subimage from the 
%            full image without having to read the whole image into 
%            memory.
% Dec. 98 -- removed option to specify number of 36 card header units.
%            An option to scale the data has been substituted instead.
% Oct. 98 -- added code to detect 80X86 and Alpha machines and to read
%            data into these with the big endian switch.

%
%Useful FITS Definitions:
%
%  "card" = 80 byte line of ASCII data in a file. This
%           contains keyword/value pairs and/or comments.
%
%  "Header area" = a set of 36 "cards". Note that
%          FITS files must have an integer number of header
%          areas (typically between 1 and 6, depending on how much
%          header information is stored in the file.)
%
%
%Reading FITS files:
%
%The first few cards of the header area must give information
%in a pre-defined order (eg. the data format, number of axes,
%size etc) but after that the header keywords can come in any
%order. The end of the last card giving information is flagged by
%the keyword END, after which blank lines are added to pad the
%last header area so it also contains 36 cards. After the last card
%the data begins, in a format specified by the BITPIX keyword.
%The dimensions of the data are specified by the NAXIS1 and
%NAXIS2 keywords.
%
%Reference:   NASA/Science Office of Standards and Technology
%           "Definition of the Flexible Image Transport System"
%                            NOST 100-1.0
%
%           This and other FITS documents are available on-line at:
%           http://www.gsfc.nasa.gov/astro/fits/basics_info.html



% First try to figure out if we need to swap bytes. This is
% imperfect as I don't know the endian-ness of each
% architecture, so I'm only looking for ones I know for
% sure.
friend = computer;
if strmatch(friend,'PCWIN')
   bswap = 'b';
elseif strmatch(friend,'LNX86')
   bswap = 'b';
elseif strmatch(friend,'ALPHA')
   bswap = 'b';
else
   bswap = 'l';
end


%Figure out how to scale the data by looking for the BSCALE and
%BZERO keywords. I think these can be anywhere in the header (ie.
%they don't have mandatory locations). We look for these using the
%"fitsheader" routine which scans through the whole header block
%looking for specific keywords (a slow process). We want to
%do this here before opening the file, because the fitsheader function 
%also opens the file internally and we want to avoid mixing calls to 
%"fitsheader" with explicit reads from the data file within this
%M-file, to avoid screwing up the  internal file indexing.
if nargin<2 
   subset = 'full';   
   scaleQ = 1;
elseif nargin<3
   scaleQ = 1;
end

if scaleQ==1
   bscale=fitsheader(filename,'BSCALE');
   bzero=fitsheader(filename,'BZERO');
   if isempty(bscale) | isempty(bzero)
      warning('BSCALE or BZERO keyword missing from the FITS file.')
      disp('Assuming BSCALE=1, BZERO=0');
      bscale=1.;
      bzero=0.;
   end
else
   bscale=1;
   bzero=0;
end


%Now we open the file and grab the required keywords from the first
%five cards, which MUST contain specific information according to
%the FITS standard. We take advantage of the mandatory locations 
%to try to speed things up by reading all the required keywords in
%a single pass.
fid=-1;
if ~isstr(filename)
   filename=setstr(filename);
end;
if (isempty(findstr(filename,'.'))==1)
   filename=[filename,'.fits'];
end
[file,message] = fopen(filename,'r',bswap);
if file == -1
   error(message);
end
[d,simple,d]=parse_card(setstr(fread(file,80,'uchar')'));
[d,bitpix,d]=parse_card(setstr(fread(file,80,'uchar')'));
[d,naxis,d]=parse_card(setstr(fread(file,80,'uchar')'));
[d,naxis1,d]=parse_card(setstr(fread(file,80,'uchar')'));
[d,naxis2,d]=parse_card(setstr(fread(file,80,'uchar')'));
n_card=5;

%Keep reading cards until one turns up with the keyword 'END'.
keyword='NIL';
while(~strcmp(deblank(upper(keyword)),'END'))
   n_card=n_card+1;
   card=setstr(fread(file,80,'uchar')');
   [keyword]=parse_card(card);
end;
%Go past the blank lines of padding before the start of the data
if (rem(n_card,36) ~= 0)
   n_blanks = 36 - rem(n_card,36);
   dummy=fread(file,n_blanks*80,'uchar');
end;

%work out the range of rows and columns to read
if strcmp('FULL',upper(deblank(subset)))
   lx=1;
   hx=naxis1;
   ly=1;
   hy=naxis2;
   nr=naxis2;
   nc=naxis1;
else
   [lx,hx,ly,hy] = lowhigh(subset);   %note order here
   nc = (hx-lx)+1;
   nr = (hy-ly)+1;
end


%
%   Geometry of image reading: Matlab's fread begins reading
%   at the lower left of the image and moves to the right, then
%   up. The x-axis is naxis1, and the y-axis is naxis2. 
%
%                ----------------------------------------------
%                |                                            |
%                |                                            |
%                |                                            |
%                |                                            |
%                |                                            |
%                |                                            |
%       naxis2   |                                            |
%                |                                            |
%                | NB. Matlab starts reading at X (lower left |
%                |     of frame) and moves to the right...    |
%                |                                            |
%                | X------->                                  |
%                ----------------------------------------------
%                                  naxis1
%


%work out how many pixels to skip to get to first byte of data
pixel_start_skip = (ly - 1)*naxis1 + (lx - 1);

%work out how many pixels to skip from the start of a row to the beginning
%of the next row
pixel_row_skip = naxis1 - nc; 

% move to first byte 
if bitpix==-64
   byte_start_skip = pixel_start_skip*8;
   byte_column_skip = pixel_row_skip*8;
   type = 'float32';   
   fseek(file,byte_start_skip,'cof');
elseif bitpix==-32
   byte_start_skip = pixel_start_skip*4;
   byte_column_skip = pixel_row_skip*4;   
   type = 'float';
   fseek(file,byte_start_skip,'cof');   
elseif bitpix==8
   byte_start_skip = pixel_start_skip*1;
   byte_column_skip = pixel_row_skip*1;  
   type = 'uint8';   
   fseek(file,byte_start_skip,'cof');
elseif bitpix==16
   byte_start_skip = pixel_start_skip*2;
   byte_column_skip = pixel_row_skip*2;
   type = 'short';   
   fseek(file,byte_start_skip,'cof');
elseif bitpix==32
   byte_start_skip = pixel_start_skip*4;
   byte_column_skip = pixel_row_skip*4;
   type = 'long';   
   fseek(file,byte_start_skip,'cof');
else
   error('data type specified by BITPIX keyword is not -64, -32, 8, 16, or 32');
end;


% now start reading in the data. We read a column at a time and then
% skip to the next row.
X=zeros(nr*nc,1);
startIndex = 1;
finIndex = nc;
for i=1:nr  
   if i>1
      startIndex=finIndex+1;
      finIndex=startIndex+nc-1;
   end
   X(startIndex:finIndex)=fread(file,nc,type);
   fseek(file,byte_column_skip,'cof');
end

%Scale the data
X = bscale*X + bzero;

%Clean up and output data
fclose(file);

%Reshape to a 2D image
output_image=reshape(X,nc,nr)';



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [keyword,value,comment] = parse_card(s)
%Parses a FITS header card.
%Reference:
%                NASA/Science Office of Standards and Technology
%           "Definition of the Flexible Image Transport System (FITS)"
%                        NOST 100-1.0    June 19, 1993

%Set defaults
keyword=[];value=[];comment=[];

%Get keyword in bytes 1 - 8
keyword=s(1:8);
if nargout==1
   return;
end

%If keyword is blank then the line is a comment
if strmatch(keyword,'       ')
    keyword=[];
    value=[];
    comment=deblank(s(11:80));
    return;
end;


%Keyword is non-blank. Check if there is a corresponding value/comment.
%If not then the only possibilities are that bytes 11:80 are a comment
%or that they are blank
if ~strmatch(s(9:10),'= ')
    keyword=deblank(keyword);
    value=[];
    comment=deblank(s(11:80));
    return;
end;

%Card is a standard keyword/value/comment structure. Break the value/comment
%string (bytes 11 - 80) into separate strings by tokenizing on "/" character.
%Remove the leading and trailing blanks on the value and the trailing blanks
%on the comment.

keyword=deblank(keyword);
[value,comment]=strtok(s(11:80),'/');
comment=deblank(comment);
value=fliplr(deblank(fliplr(deblank(value))));

%Now figure out whether to output the value as a string or as a number.
%The FITS standard requires all values that are strings to be in single
%quotes like this: 'foo bar', so I can simply look for occurences of a
%single quote to flag a string. However, logical variables can take the
%values T or F without having any single quotes, so I'll have to look
%out for those also.

%Test for logical. Return logical as a string.
if strmatch(upper(value),'T') | strmatch(upper(value),'F')
    return;
end;

%Test for string. Return string unconverted.
if length(findstr('''',value)) ~= 0
    return;
end;

%Only thing left is a number. Convert string to number.
value=str2num(value);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [lx,hx,ly,hy]=lowhigh(str)
% LOWHIGH extracts range elements from a range string 
% of the form 'XL:XH,YL:YH'. The elements are returned
% as numbers not as strings.

[dx,dy]=strtok(str,',');
[lx,hx]=strtok(dx,':');
[ly,hy]=strtok(dy,':');

% remove bogus characters
hx = strrep(hx,':','');
ly = strrep(ly,',','');
hy = strrep(hy,':','');

% return as a number instead of as a string
lx = str2double(lx);
hx = str2double(hx);
ly = str2double(ly);
hy = str2double(hy);

Contact us at files@mathworks.com