propose imdiff task
Jason Quinn wrote on Jan 25, 2009
I think IRAF would benefit from a very simple task, perhaps called imdiff, that merely states if the pixels values of two images are different or identical. It should work very similar to the way that the UNIX diff command works on binary files.
Needing to know if two images are identical is a not an uncommon question during reduction; especially when working with bad pixel masks that you last worked with months ago. It would be nice to be able to do this as easily as possible.
Way back I would just create a subtracted image and display it as a real quick check but these days with large CCD images, it's not even possible to see single pixel differences if you are zoomed out too far.
If there's some super simple way to test this that I am unaware about, let me know.
Jason
Here's a dirty working version:
Needing to know if two images are identical is a not an uncommon question during reduction; especially when working with bad pixel masks that you last worked with months ago. It would be nice to be able to do this as easily as possible.
Way back I would just create a subtracted image and display it as a real quick check but these days with large CCD images, it's not even possible to see single pixel differences if you are zoomed out too far.
If there's some super simple way to test this that I am unaware about, let me know.
Jason
Here's a dirty working version:
#Reports if two images are identical in the pixels (ignores headers).
procedure imdiff(image1,image2)
string image1 {"", prompt="first image name"}
string image2 {"", prompt="second image name"}
begin
string image1alias,image2alias,tmpimage
real min,max
int nx1,ny1,nx2,ny2
image1alias=image1
image2alias=image2
hsel(image1alias, "naxis[1]", "yes", missing="INDEF") | scanf("%d",nx1)
hsel(image1alias, "naxis[2]", "yes", missing="INDEF") | scanf("%d",ny1)
hsel(image2alias, "naxis[1]", "yes", missing="INDEF") | scanf("%d",nx2)
hsel(image2alias, "naxis[2]", "yes", missing="INDEF") | scanf("%d",ny2)
if ( nx1!=nx2 || ny1!=ny2 ) {
printf("The image dimensions differ! (%d X %d) vs (%d X %d), respectively.\n",nx1,ny1,nx2,ny2)
bye()
}
tmpimage=mktemp('tmp$imdiff')
imarith(image1alias, "-", image2alias, tmpimage, title="diff image", divzero=0., hparams="", pixtype="", calctype="", verbose=no, noact=no)
imstat(tmpimage, fields="min,max", lower=INDEF, upper=INDEF, nclip=0, lsigma=3., usigma=3., binwidth=0.1, format=no, cache=no) | scanf("%f %f",min,max)
if ( min==0 && max==0 )
print("The images are identical. (headers not compared)")
else
print("The images have differing pixels. (headers not compared)")
imdel(tmpimage,ver-)
end
Philip Massey wrote on Jan 25, 2009
Jason---
I do this all of the time too, but rather than displaying the images to look
for differences, I use "imstat" on the image; i.e., imarith imgA - imgB imgC
and then imstat imgC. If the min and max are anything other than 0 that tells
me that the two images are not identical. Your procedure has the advantage of
being a one-step process.
---phil
I do this all of the time too, but rather than displaying the images to look
for differences, I use "imstat" on the image; i.e., imarith imgA - imgB imgC
and then imstat imgC. If the min and max are anything other than 0 that tells
me that the two images are not identical. Your procedure has the advantage of
being a one-step process.
---phil
Jason Quinn wrote on Jan 25, 2009
massey
Jason---
Your procedure has the advantage of
being a one-step process.
---phil
The code above does exactly what you describe: imarith for difference and then imstat for min and max to be 0. My contention is that it having a one-step task is the logical thing and inline with a "UNIX-y" philosophy of isolating the primitive questions and having a task that does it that can be chained together to express more complicated ideas. It is my belief that this is something that should be an IRAF primitive. If the task returned a truth value, such a task could even be used in scripts with minimal code. I would also put forth that doing the check by hand with imarith and imstat also requires checking or even unlearning the epar parameters before you can be 100% sure of the result. So it's more like a 4 step process.
Jason
James Turner wrote on Jan 25, 2009
STScI have also written a task called "fitsdiff" that compares headers as well as pixel values, though it is actually a Python script rather than CL. Anyway, I see it has the usual AURA licence attached, so I'll post it here in case you're interested:
James.
James.
#!sr/stsci/pyssgdev/Python-2.5/bin/python
# -*- coding: latin-1 -*-
# $Id: fitsdiff.py 675 2007-03-02 21:38:08Z dencheva $
"""
fitsdiff: Compare two FITS image files and report the differences
in header keywords and data.
License: http://www.stsci.edu/resources/software_hardware/pyraf/LICENSE
Usage:
fitsdiff.py [options] filename1 filename2
where filename1 filename2 are the two files to be compared.
they can be wild cards, in such cases, they must be enclosed
by double or single quotes. they can also be directory names:
if both are directory names, all files in each of the
directories will be included, if only one is directory name,
then the directory name will be prefixed to the file name(s)
specified by the other argument. for example:
fitsdiff.py "*.fits" "/machine/data1"
will compare all FITS files in the current directory to the
corresponding files in the directory /machine/data1
Options are one or more of:
-c (list of keywords)
a list of keywords whose comments will not be compared.
If want to exclude all keywords, use "*", make sure to
have double or single quotes around the asterisk.
default = None
-k (list of keywords)
a list of keywords not to be compared.
If want to exclude all keywords, use "*", make sure to
have double or single quotes around the asterisk.
default = None
-f (list of column names)
a list of fields (i.e. columns) not to be compared.
If want to exclude all columns, use "*", make sure to
have double or single quotes around the asterisk.
default = None
-n (non-negative integer)
max number of different data (image pixel or table
element) to report per extension,
default = 10
-d (non-negative number)
relative difference level below which data are
considered equal, this criterion only applies to
floating point numbers, both data and keyword values,
it does not apply to integers.
default = 0.
-b
means trailing blanks in string values (both in header
keywords and column values) are significant, i.e.
'ABC ' and 'ABC' mean different things if this
swithch is set.
-o (output file name)
output file name where the result goes
-h
print the help (this text)
If the two files are identical within the specified conditions,
it will report "No difference is found."
If the value(s) of -c and -k takes the form '@filename',
list is in the text file 'filename', and each line in that
text file contains one keyword.
Example:
fitsdiff.py -k filename,filtnam1 -n 5 -d 1.e-6 test1.fits test2
this command will compare files test1.fits and test2.fits,
report maximum of 5 different pixels values per extension, only
report data values larger than 1.e-6 relative to each other,
and will neglect the different values of keywords FILENAME
and FILTNAM1 (or their very existence).
"""
import numerixenv
numerixenv.check()
# This version needs python 2.2 and numarray 0.6, or later.
# Developed by Science Software Group, STScI, USA.
__version__ = "1.4 (23 August 2006)"
import sys, types
import pyfits
import numpy as num
from numpy import char
def fitsdiff (input1, input2, comment_excl_list='', value_excl_list='', field_excl_list='', maxdiff=10, delta=0., neglect_blanks=1, output=None):
global nodiff
# if sending output somewhere?
if output:
if type(output) == types.StringType:
outfd = open(output, 'w')
else:
outfd = output
sys.stdout = outfd
fname = (input1, input2)
# Parse lists of excluded keyword values and/or keyword comments.
value_excl_list = list_parse(value_excl_list)
comment_excl_list = list_parse(comment_excl_list)
field_excl_list = list_parse(field_excl_list)
# print out heading and parameter values
print "\n fitsdiff: ", __version__
print " file1 = %s\n file2 = %s" % fname
print " Keyword(s) not to be compared: ", value_excl_list
print " Keyword(s) whose comments not to be compared: ", \
comment_excl_list
print " Column(s) not to be compared: ", field_excl_list
print " Maximum number of different pixels to be reported: ", maxdiff
print " Data comparison level: ", delta
nodiff = 1 # difference-free flag
# open input files
im1 = open_and_read(input1)
im2 = open_and_read(input2)
# compare numbers of extensions
nexten1, nexten2 = len(im1), len(im2)
if nexten1 != nexten2:
raise "Different no. of HDU's: file1 has %d, file2 has %d" % (nexten1, nexten2)
# compare extension header and data
for i in range(nexten1):
# print out the extension heading
if i == 0:
xtension = ''
print "\nPrimary HDU:"
else:
xtension = im1[i].header['XTENSION'].strip()
print "\n%s Extension %d HDU:" % (xtension, i)
# build dictionaries of keyword values and comments
(dict_value1, dict_comment1) = keyword_dict(im1[i].header.ascard, neglect_blanks)
(dict_value2, dict_comment2) = keyword_dict(im2[i].header.ascard, neglect_blanks)
# pick out the "extra" keywords
extra_keywords(dict_value1, dict_value2, fname)
# compare keywords' values and comments
if value_excl_list != ['*']:
compare_keyword_value(dict_value1, dict_value2, \
value_excl_list, fname, delta)
if comment_excl_list != ['*']:
compare_keyword_comment(dict_comment1, dict_comment2, \
comment_excl_list, fname)
# compare the data
# First, get the dimensions of the data
dim = compare_dim(im1[i], im2[i])
_maxdiff = max(0, maxdiff)
if dim != None:
# if the extension is tables
if xtension in ('BINTABLE', 'TABLE'):
if field_excl_list != ['*']:
compare_table(im1[i], im2[i], delta, _maxdiff, dim, xtension, field_excl_list)
else:
compare_img(im1[i], im2[i], delta, _maxdiff, dim)
# if there is no difference
if nodiff:
print "\nNo difference is found."
# close files
im1.close()
im2.close()
# reset sys.stdout back to default
sys.stdout = sys.__stdout__
return nodiff
#-------------------------------------------------------------------------------
def list_parse (name_list):
""" Parse a name list (a string list, not a Python list)
including the case when the list is in a text file, each string
value is in a different line
"""
# list in a text file
if (len(name_list) > 0 and name_list[0] == '@'):
try:
fd = open(name_list[1:])
text = fd.read()
fd.close()
kw_list = (text.upper()).split()
# if the file only have blanks
if kw_list == []: kw_list = ['']
return kw_list
except IOError:
print "CAUTION: File %s does not exist, assume null list" % name_list[1:]
return([''])
else:
return (name_list.upper()).split(',')
#-------------------------------------------------------------------------------
def open_and_read (filename):
"""Open and read in the whole FITS file"""
try:
im = pyfits.open(filename)
except IOError:
raise IOError, "\nCan't open or read file %s" % filename
return im
#-------------------------------------------------------------------------------
def keyword_dict(header, neglect_blanks=1):
"""Build dictionaries of header keyword values and comments.
Each dictionary item's value list, so we can pick out keywords with
duplicate entries, including COMMENT and HISTORY, and if they are
out of order.
Input parameter, header, is a FITS HDU header.
Output is a 2-element tuple of dictionaries of keyword values and
keyword comments respectively.
"""
dict_value = {}
dict_comment = {}
for i in range(len(header)):
keyword = header[i].key
value = header[i].value
try:
comment = header[i].comment
except:
comment = ''
# keep trailing blanks for a string value?
if type(value) == types.StringType and neglect_blanks:
value = value.rstrip()
# existing keyword
if dict_value.has_key(keyword):
dict_value[keyword].append(value)
dict_comment[keyword].append(comment)
# new keyword
else:
dict_value[keyword] = [value]
dict_comment[keyword] = [comment]
return (dict_value, dict_comment)
#-------------------------------------------------------------------------------
def extra_keywords (dict1, dict2, name):
"""Pick out extra keywords between the two input dictionaries
each dictionary's value is a list, this routine also works if the same
keyword has different number of values in diffferent dictionary.
name is a 2-element tuple of files names corresponding to
dictionaries dict1 and dict2.
"""
global nodiff
keys = dict1.keys()
keys.sort()
for kw in keys:
if kw not in dict2.keys():
nodiff = 0
print " Extra keyword %-8s in %s" % (kw, name[0])
else:
# compare the number of occurrence
nval1 = len(dict1[kw])
nval2 = len(dict2[kw])
if nval1 != nval2:
nodiff = 0
print " Inconsistent occurrence of keyword %-8s %s has %d, %s has %d" % (kw, name[0], nval1, name[1], nval2)
for kw in dict2.keys():
if kw not in dict1.keys():
nodiff = 0
print " Extra keyword %-8s in %s" % (kw, name[1])
#-------------------------------------------------------------------------------
def row_parse (row, img):
"""Parse a row in a text table into a list of values
These value correspond to the fields (columns).
"""
result = []
for col in range(len(row)):
# get the format (e.g. I8, A10, or G25.16) of the field (column)
tform = img.header['TFORM'+str(col+1)]
item = row[col].strip()
# evaluate the substring
if (tform[0] != 'A'):
item = eval(item)
result.append(item)
return result
#-------------------------------------------------------------------------------
def compare_keyword_value (dict1, dict2, keywords_to_skip, name, delta):
""" Compare header keyword values
compare header keywords' values by using the value dictionary,
the value(s) for each keyword is in the form of a list. Don't do
the comparison if the keyword is in the keywords_to_skip list.
"""
global nodiff # no difference flag
keys = dict1.keys()
keys.sort()
for kw in keys:
if kw in dict2.keys() and kw not in keywords_to_skip:
values1 = dict1[kw]
values2 = dict2[kw]
# if the same keyword has different number of entries
# in different files, it is regarded as extra and will
# be dealt with in a separate routine.
nvalues = min(len(values1), len(values2))
for i in range(nvalues):
if diff_obj(values1[i], values2[i], delta):
indx = ''
if i > 0: indx = `[i+1]`
print " Keyword %-8s%s has different values: " % (kw, indx)
print ' %s: %s' % (name[0], values1[i])
print ' %s: %s' % (name[1], values2[i])
nodiff = 0
#-------------------------------------------------------------------------------
def compare_keyword_comment (dict1, dict2, keywords_to_skip, name):
"""Compare header keywords' comments
compare header keywords' comments by using the comment dictionary, the
comment(s) for each keyword is in the form of a list. Don't do the
comparison if the keyword is in the keywords_to_skip list.
"""
global nodiff # no difference flag
keys = dict1.keys()
keys.sort()
for kw in keys:
if kw in dict2.keys() and kw not in keywords_to_skip:
comments1 = dict1[kw]
comments2 = dict2[kw]
# if the same keyword has different number of entries
# in different files, it is regarded as extra and it
# taken care of in a separate routine.
ncomments = min(len(comments1), len(comments2))
for i in range(ncomments):
if comments1[i] != comments2[i]:
indx = ''
if i > 0: indx = `[i+1]`
print ' Keyword %-8s%s has different comments: ' % (kw, indx)
print ' %s: %s' % (name[0], comments1[i])
print ' %s: %s' % (name[1], comments2[i])
nodiff = 0
#-------------------------------------------------------------------------------
def diff_obj (obj1, obj2, delta = 0):
"""Compare two objects
return 1 if they are different, for two floating numbers, if their
relative difference is within delta, they are treated as same numbers.
"""
if type(obj1) == types.FloatType and type(obj2) == types.FloatType:
diff = abs(obj2-obj1)
a = diff > abs(obj1*delta)
b = diff > abs(obj2*delta)
return a or b
else:
return (obj1 != obj2)
#-------------------------------------------------------------------------------
def diff_num(num1, num2, delta=0):
"""Compare two num/char-arrays
If their relative difference is larger than delta,
returns a tuple of index arrays where there is difference.
The number of elements in the tuple is the dimension of the images
been compared. Each index array in the tuple is 1-D and its length is
the number of differences found.
"""
# num1 = num.asarray(num1)
# num2 = num.asarray(num2)
# if arrays are chararrays
if isinstance (num1, char.chararray):
delta = 0
# if delta is zero, it is a simple case. Use the more general __ne__()
if delta == 0:
diff = num1.__ne__(num2) # diff is a boolean array
else:
diff = num.absolute(num2-num1)/delta # diff is a float array
diff_indices = num.nonzero(diff) # a tuple of (shorter) arrays
# how many occurrences of difference
n_nonzero = diff_indices[0].size
# if there is no difference, or delta is zero, stop here
if n_nonzero == 0 or delta == 0:
return diff_indices
# if the difference occurrence is rare enough (less than one-third
# of all elements), use an algorithm which saves space.
# Note: "compressed" arrays are 1-D only.
elif n_nonzero < (diff.size)/3:
cram1 = num.compress(diff.__ne__(0.0).ravel(), num1)
cram2 = num.compress(diff.__ne__(0.0).ravel(), num2)
cram_diff = num.compress(diff.__ne__(0.0).ravel(), diff)
a = num.greater(cram_diff, num.absolute(cram1))
b = num.greater(cram_diff, num.absolute(cram2))
r = num.logical_or(a, b)
list = []
for i in range(len(diff_indices)):
list.append(num.compress(r, diff_indices[i]))
return tuple(list)
# regular and more expensive way
else:
a = num.greater(diff, num.absolute(num1))
b = num.greater(diff, num.absolute(num2))
r = num.logical_or(a, b)
return num.nonzero(r)
#-------------------------------------------------------------------------------
def compare_dim (im1, im2):
"""Compare the dimensions of two images
If the two images (extensions) have the same dimensions and are
not zero, return the dimension as a list, i.e.
[NAXIS, NAXIS1, NAXIS2,...]. Otherwise, return None.
"""
global nodiff
dim1 = []
dim2 = []
# compare the values of NAXIS first
dim1.append(im1.header['NAXIS'])
dim2.append(im2.header['NAXIS'])
if dim1[0] != dim2[0]:
nodiff = 0
print "Input files have different dimensions"
return None
if dim1[0] == 0:
print "Input files have naught dimensions"
return None
# compare the values of NAXISi
for k in range(dim1[0]):
dim1.append(im1.header['NAXIS'+`k+1`])
dim2.append(im2.header['NAXIS'+`k+1`])
if dim1 != dim2:
nodiff = 0
print "Input files have different dimensions"
return None
return dim1
#-------------------------------------------------------------------------------
def compare_table (img1, img2, delta, maxdiff, dim, xtension, field_excl_list):
"""Compare data in FITS tables"""
global nodiff
ndiff = 0
ncol1 = img1.header['TFIELDS']
ncol2 = img2.header['TFIELDS']
if ncol1 != ncol2:
print "Different no. of columns: file1 has %d, file2 has %d" % (ncol1, ncol2)
nodiff = 0
ncol = min(ncol1, ncol2)
# check for None data
if img1.data is None or img2.data is None:
if img1.data is None and img2.data is None:
return
else:
print "One file has no data and the other does."
nodiff = 0
# compare the tables column by column
for col in range(ncol):
field1 = img1.header['TFORM'+`col+1`]
field2 = img2.header['TFORM'+`col+1`]
if field1 != field2:
print "Different data type at column %d: file1 is %s, file2 is %s" % (col, field1, field2)
continue
name1 = img1.data.names[col].upper()
name2 = img2.data.names[col].upper()
if name1 in field_excl_list or name2 in field_excl_list:
continue
found = diff_num (img1.data.field(col), img2.data.field(col), delta)
_ndiff = found[0].shape[0]
ndiff += _ndiff
nprint = min(maxdiff, _ndiff)
maxdiff -= _ndiff
dim = len(found)
base1 = num.ones(dim)
if nprint > 0:
print " Data differ at column %d: " % (col+1)
index = num.zeros(dim)
for p in range(nprint):
# start from the fastest axis
for i in range(dim):
index[i] = found[i][p]
# translate the 0-based 1-D locations to 1-based
# naxis-D locations. Also the "fast axes"
# order is properly treated here.
loc = index[-1::-1] + base1
index_ = tuple(index)
if (dim) == 1:
str = ''
else:
str = ' at %s,' % loc[:-1]
print " Row %3d, %s file 1: %16s file 2: %16s" % (loc[-1], str, img1.data.field(col)[index_], img2.data.field(col)[index_])
print ' There are %d different data points.' % ndiff
if ndiff > 0:
nodiff = 0
#-------------------------------------------------------------------------------
def compare_img (img1, img2, delta, maxdiff, dim):
"""Compare the image data"""
global nodiff
ndiff = 0
thresh = delta
bitpix = img1.header['BITPIX']
if (bitpix > 0): thresh = 0 # for integers, exact comparison is made
# compare the two images
found = diff_num (img1.data, img2.data, thresh)
ndiff = found[0].shape[0]
nprint = min(maxdiff, ndiff)
dim = len(found)
base1 = num.ones(dim, dtype=num.int16)
if nprint > 0:
index = num.zeros(dim, dtype=num.int16)
for p in range(nprint):
# start from the fastest axis
for i in range(dim):
index[i] = int(found[i][p])
# translate the 0-based 1-D locations to 1-based
# naxis-D locations. Also the "fast axes" order is
# properly treated here.
loc = index[-1::-1] + base1
index_ = tuple(index)
print " Data differ at %16s, file 1: %11.5G file 2: %11.5G" % (list(loc), img1.data[index_], img2.data[index_])
print ' There are %d different data points.' % ndiff
if ndiff > 0:
nodiff = 0
#-------------------------------------------------------------------------------
def attach_dir (dirname, list):
"""Attach a directory name to a list of file names"""
import os
new_list = list[:]
for i in range(len(new_list)):
basename = os.path.basename(new_list[i])
new_list[i] = os.path.join(dirname, basename)
return new_list
#-------------------------------------------------------------------------------
def parse_path(f1, f2):
"""Parse two input arguments and return two lists of file names"""
import glob, os
if os.path.isdir(f1):
# if both arguments are directory, use all files
if os.path.isdir(f2):
f1 = os.path.join(f1, '*')
f2 = os.path.join(f2, '*')
# if one is directory, one is not, recreate the first by
# attaching the directory name to the other.
# use glob to parse the wild card, if any
else:
list2 = glob.glob(f2)
list1 = attach_dir (f1, list2)
return list1, list2
else:
if os.path.isdir(f2):
list1 = glob.glob(f1)
list2 = attach_dir (f2, list1)
return list1, list2
list1 = glob.glob(f1)
list2 = glob.glob(f2)
if (list1 == [] or list2 == []):
str = ""
if (list1 == []): str += "File `%s` does not exist. " % f1
if (list2 == []): str += "File `%s` does not exist. " % f2
raise IOError, str
else:
return list1, list2
#-------------------------------------------------------------------------------
# special initialization when this is the main program
if __name__ == "__main__":
import getopt
try:
optlist, args = getopt.getopt(sys.argv[1:], 'c:k:f:n:d:o:bh')
except getopt.error, e:
print str(e)
print __doc__
print "\t", __version__
# initialize default values
help = 0
comment_excl_list = ''
value_excl_list = ''
field_excl_list = ''
maxdiff = 10
delta = 0.
output = None
neglect_blanks = 1
# read options
for opt, value in optlist:
if opt == "-c":
comment_excl_list = value
elif opt == "-k":
value_excl_list = value
elif opt == "-f":
field_excl_list = value
elif opt == "-n":
maxdiff = eval(value)
elif opt == "-d":
delta = eval(value)
# delta must be positive
if delta < 0:
delta = 0
elif opt == "-o":
output = value
elif opt == "-b":
neglect_blanks = 0
elif opt == "-h":
help = 1
if (help):
print __doc__
print "\t", __version__
else:
if len(args) == 2:
(list1, list2) = parse_path (args[0], args[1])
npairs = min (len(list1), len(list2))
for i in range(npairs):
fitsdiff(list1[i], list2[i], comment_excl_list, value_excl_list, field_excl_list, maxdiff, delta, neglect_blanks, output)
else:
print "Needs pair(s) of input files. Use -h for help"
"""
Copyright (C) 2003 Association of Universities for Research in Astronomy (AURA)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
3. The name of AURA and its representatives may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
"""
[/code]
Jason Quinn wrote on Jan 25, 2009
jturner
STScI have also written a task called "fitsdiff" that compares headers as well as pixel values, though it is actually a Python script rather than CL. Anyway, I see it has the usual AURA licence attached, so I'll post it here in case you're interested:
James.
Cool. There's also a related type of task called hdiff in stsdas.toolbox.headers that works on headers.
Jason
Last post on Jan 25, 2009