适合页眉#

在接下来的三章中,我们将分别解释处理FITS头、图像/数组数据和表数据的更详细信息,包括示例。

HDU的标头#

每个报头数据单元(HDU)通常有两个组件:报头和数据。在 astropy 这两个组件通过HDU的两个属性访问, hdu.headerhdu.data .

而HDU可能有空数据(即 .data 属性是 None ),任何HDU都将始终具有标头。使用构造函数创建HDU时(例如。, hdu = PrimaryHDU(data, header) ),用户可以从现有HDU的报头提供报头值,并从 numpy 数组。如果使用默认值(无),则新的HDU将具有该类型HDU所需的最少关键字:

>>> from astropy.io import fits
>>> hdu = fits.PrimaryHDU()
>>> hdu.header  # show the all of the header cards
SIMPLE  =                    T / conforms to FITS standard
BITPIX  =                    8 / array data type
NAXIS   =                    0 / number of array dimensions
EXTEND  =                    T

用户可以使用任何头和任何数据来构造新的HDU。 astropy 将删除所有描述数据结构的关键字,只留下信息性关键字。稍后,它将重新添加所需的结构关键字,以便与新的HDU和添加到其中的任何数据兼容。因此,用户可以使用表HDU的头来构造图像HDU,反之亦然。构造函数还将确保头中的数据类型和维度信息与数据一致。

Header属性#

价值访问、更新和创建#

如图所示 Getting Started 教程中,关键字值可以通过HDU头属性的关键字名称或索引访问。您也可以使用通配符 * 以获取与搜索字符串匹配的关键字-值对。下面是一个简短的总结:

>>> fits_image_filename = fits.util.get_testdata_filepath('test0.fits')
>>> hdul = fits.open(fits_image_filename)  # open a FITS file
>>> hdr = hdul[0].header  # the primary HDU header
>>> print(hdr[34])  # get the 2nd keyword's value
96
>>> hdr[34] = 20  # change its value
>>> hdr['DARKCORR']  # get the value of the keyword 'darkcorr'
'OMIT'
>>> hdr['DARKCOR*']  # get keyword values using wildcard matching
DARKCORR= 'OMIT              ' / Do dark correction: PERFORM, OMIT, COMPLETE
>>> hdr['darkcorr'] = 'PERFORM'  # change darkcorr's value

关键字名称不区分大小写,但在一些特殊情况下除外(请参阅有关层次卡和记录值卡的部分)。因此, hdr['abc']hdr['ABC']hdr['aBc'] 都是等价的。

和Python的一样 dict 类型,也可以使用赋值语法将新关键字添加到标头中:

>>> hdr = hdul[1].header
>>> 'DARKCORR' in hdr  # Check for existence
False
>>> hdr['DARKCORR'] = 'OMIT'  # Add a new DARKCORR keyword

也可以添加新值 and 通过将它们指定为元组进行注释::

>>> hdr['DARKCORR'] = ('OMIT', 'Dark Image Subtraction')

备注

在向标题中添加新关键字时,需要注意的一点是,默认情况下不会附加关键字 立即 直到文件的结尾。相反,它们被附加到最后一个非注释关键字之后。这是为了支持始终将所有历史关键字组合在一起的常见用例。一个新的非注释关键字将添加到现有关键字的末尾,但在标题末尾的任何历史/注释关键字之前。

有几种方法可以覆盖此功能:

  • 使用 Header.append() 方法与 end=True 论点:

    >>> hdr.append(('DARKCORR', 'OMIT', 'Dark Image Subtraction'), end=True)
    

    这将强制在标头的实际结尾添加新关键字。

  • 这个 Header.insert() 方法将始终在您要求的位置插入新关键字:

    >>> del hdr['DARKCORR']  # Delete previous insertion for doctest
    >>> hdr.insert(20, ('DARKCORR', 'OMIT', 'Dark Image Subtraction'))
    

    这将在头中的第20个关键字之前插入DARKCORR关键字,而不管它是什么。

可以使用相同的索引/名称语法删除关键字(及其对应的卡片):

>>> del hdr[3]  # delete the 2nd keyword
>>> del hdr['DARKCORR']  # delete the value of the keyword 'DARKCORR'

注意,与常规Python列表一样,索引在每次删除后都会更新,因此如果 del hdr[3] 连续执行两次时,第四个和第五个关键字将从原始标题中删除。同样, del hdr[-1] 将删除页眉中的最后一张卡。

也可以使用slice语法删除整个卡片范围:

>>> del hdr[3:5]

方法 Header.set() 是更新与现有关键字关联的值或注释或创建新关键字的另一种方法。它的大部分功能可以用上面所示的dict类语法复制。但在某些情况下,情况可能会更清楚。它还有一个优点,允许用户在标题中移动卡片,或者指定新卡片相对于现有卡片的位置:

>>> hdr.set('target', 'NGC1234', 'target name')
>>> # place the next new keyword before the 'TARGET' keyword
>>> hdr.set('newkey', 666, before='TARGET')  # comment is optional
>>> # place the next new keyword after the 21st keyword
>>> hdr.set('newkey2', 42.0, 'another new key', after=20)

在FITS标题中,每个关键字也可能有一个与之相关联的注释来解释其用途。与每个关键字关联的注释通过 comments 属性:

>>> hdr['NAXIS']
2
>>> hdr.comments['NAXIS']
'number of data axes'
>>> hdr.comments['NAXIS'] = 'The number of image axes'  # Update
>>> hdul.close()  # close the HDUList again

可以通过与访问值相同的所有方式访问注释,无论是通过关键字名称还是卡片索引。切片也是可能的。唯一的区别是你要经历 hdr.comments 与其只是 hdr 单打独斗。

注释、历史记录和空白关键字#

FITS标题中的大多数关键字都有唯一的名称。如果有两个以上的卡共享同一个名称,则当按名称引用时,它是第一个被访问的卡。副本只能通过数字索引访问。

有三个特殊的关键字(它们的关联卡片有时被称为评论卡片),它们通常会出现在FITS标题中多次。它们是(1)空白关键字,(2)历史,(3)注释。与其他关键字不同,当访问这些关键字时,它们以列表形式返回:

>>> filename = fits.util.get_testdata_filepath('history_header.fits')
>>> with fits.open(filename) as hdul:  # open a FITS file
...     hdr = hdul[0].header

>>> hdr['HISTORY']
I updated this file on 02/03/2011
I updated this file on 02/04/2011

这些列表可以像其他列表一样进行切片。例如,要只显示最后一个历史条目,请使用 hdr['history'][-1] . 现有的评论卡也可以通过使用该卡的适当索引号进行更新。

通过使用dict-like关键字赋值语法,或使用 Header.set() 我是说。但是,与其他关键字不同,新的评论卡总是添加并附加到最后一个注释卡中,该卡片具有相同的关键字,而不是在标题的末尾。

例子#

添加新的评论卡:

>>> hdu.header['HISTORY'] = 'history 1'
>>> hdu.header[''] = 'blank 1'
>>> hdu.header['COMMENT'] = 'comment 1'
>>> hdu.header['HISTORY'] = 'history 2'
>>> hdu.header[''] = 'blank 2'
>>> hdu.header['COMMENT'] = 'comment 2'

修改后的标题中的部分变为:

HISTORY history 1
HISTORY history 2
        blank 1
        blank 2
COMMENT comment 1
COMMENT comment 2

用户还可以使用 Header.insert() 方法。

备注

讽刺的是,评论卡中没有注释,只有字符串值。

未定义的值#

FITS头可以有未定义的值,这些值在Python中用特殊值表示 None . None 在将值赋给 HeaderCard .

>>> hdr = fits.Header()
>>> hdr['UNDEF'] = None
>>> hdr['UNDEF'] is None
True
>>> repr(hdr)
'UNDEF   =                                                                       '
>>> hdr.append('UNDEF2')
>>> hdr['UNDEF2'] is None
True
>>> hdr.append(('UNDEF3', None, 'Undefined value'))
>>> str(hdr.cards[-1])
'UNDEF3  =  / Undefined value                                                    '

卡片图像#

FITS标题由卡片图像组成。

FITS标题中的卡片图像由关键字名称、值和可选的注释组成。从物理上讲,在FITS文件的存储格式中,它需要80列(字节)-不带回车符。在 astropy ,每个卡片图像都由 Card 对象。还有一些特殊种类的卡片:评论卡片(见上文)和卡片图像,它可以拍摄超过一张80列的卡片图像。后者将在稍后讨论。

大多数时候处理卡片的细节是由 Header 对象,而不必直接操作卡片。事实上,大多数 Header 接受 (keyword, value)(keyword, value, comment) 元组作为参数也可以使用 Card 对象作为参数。 Card 对象只是围绕这样的元组的包装器,这些元组提供解析和格式化头中单个卡的逻辑。手动使用 Card 对象,但在实际将卡添加到标头之前检查它在标头中的显示方式。

创建一个新的Card对象 Card 构造函数: Card(key, value, comment) .

例子#

要创建新的卡片对象:

>>> c1 = fits.Card('TEMP', 80.0, 'temperature, floating value')
>>> c2 = fits.Card('DETECTOR', 1)  # comment is optional
>>> c3 = fits.Card('MIR_REVR', True,
...                'mirror reversed? Boolean value')
>>> c4 = fits.Card('ABC', 2+3j, 'complex value')
>>> c5 = fits.Card('OBSERVER', 'Hubble', 'string value')

>>> print(c1); print(c2); print(c3); print(c4); print(c5)  # show the cards
TEMP    =                 80.0 / temperature, floating value
DETECTOR=                    1
MIR_REVR=                    T / mirror reversed? Boolean value
ABC     =           (2.0, 3.0) / complex value
OBSERVER= 'Hubble  '           / string value

卡片有属性 .keyword.value.comment .两者都是 .value.comment 可以改变但不能改变 .keyword 属性。换句话说,一旦创建了一张卡,它就为一个特定的、不可变的关键字创建。

这个 Card() 构造器将检查给定的参数是否符合FITS标准并且具有固定的卡片图像格式。如果用户想要创建一个自定义格式的卡,甚至是一个不符合FITS标准的卡(例如,出于测试目的),则 Card.fromstring() 可以使用类方法。

卡片可以用 Card.verify() . 非标准卡片 c2 在下面的例子中,通过这样的验证来标记。有关验证的详细信息 astropy 将在后面的章节中讨论。

>>> c1 = fits.Card.fromstring('ABC     = 3.456D023')
>>> c2 = fits.Card.fromstring("P.I. ='Hubble'")
>>> print(c1)
ABC     = 3.456D023
>>> print(c2)  
P.I. ='Hubble'
>>> c2.verify()  
Output verification result:
Unfixable error: Illegal keyword name 'P.I.'

一份清单 Card 对象 Header 对象可以通过 Header.cards 属性。不应该仅仅是为了观察而被操纵。事实上,它只是一个拷贝-对它的修改不会影响它来自的头。使用 Header 改为类。

连续卡片#

事实上,FITS标准只允许最多8个字符作为关键字名称,80个字符可以包含关键字、值和注释,这对某些应用程序是有限制的。为了允许关键字使用长字符串值,提出了一个建议:

在包含关键字的常规80列之后使用CONTINUE关键字。 astropy 支持此约定,这是自4.0版以来的FITS标准的一部分。

实例#

下面的示例显示,对于长字符串值,CONTINUE是自动使用的:

>>> hdr = fits.Header()
>>> hdr['abc'] = 'abcdefg' * 20
>>> hdr
ABC     = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&'
CONTINUE  'efgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga&'
CONTINUE  'bcdefg'
>>> hdr['abc']
'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg'
>>> # both value and comments are long
>>> hdr['abc'] = ('abcdefg' * 10, 'abcdefg' * 10)
>>> hdr
ABC     = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&'
CONTINUE  'efg&'
CONTINUE  '&' / abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga
CONTINUE  '' / bcdefg

请注意,当使用CONTINUE卡时,在每个80个字符的卡片图像的末尾,会出现一个与号。和号不是字符串值的一部分。此外,在CONTINUE之后的第九列没有“=”。在第一个示例中,整个240个字符由 astropy 作为一张卡片。因此,如果它是标题中的第n张卡片,(n+1)第张卡片指的是下一个关键字,而不是下一张CONTINUE卡片。因此,CONTINUE卡由 astropy 作为一个单一的逻辑卡,一般不必担心格式的细节。可以像常规关键字一样访问和更新解析为一组CONTINUE卡片的关键字。

等级卡#

对于长度超过8个字符的关键字,欧洲南方天文台(ESO)制定了一个惯例,以便于使用。它使用一个特殊的关键字层次结构,后面是实际的长关键字。 astropy 也支持这项公约。

如果关键字包含八个以上的字符 astropy 将自动使用分层卡,但也会发出警告,以防出错。但是,您可以通过在关键字前面加上“HIERARCH”来显式地请求层次结构卡(就像它将出现在标题中一样)。例如, hdr['HIERARCH abcdefghi'] 将创建关键字 abcdefghi 没有显示警告。创建后,可以像访问其他关键字一样访问层次结构关键字: hdr['abcdefghi'] ,而不在关键字前面添加“HIERARCH”。

实例#

astropy 当关键字包含超过八个字符时,将使用分层卡并发出警告:

>>> # this will result in a Warning because a HIERARCH card is implicitly created
>>> c = fits.Card('abcdefghi', 10)  
>>> print(c)  
HIERARCH abcdefghi = 10
>>> c = fits.Card('hierarch abcdefghi', 10)
>>> print(c)
HIERARCH abcdefghi = 10
>>> hdu = fits.PrimaryHDU()
>>> hdu.header['hierarch abcdefghi'] =  99
>>> hdu.header['abcdefghi']
99
>>> hdu.header['abcdefghi'] = 10
>>> hdu.header['abcdefghi']
10
>>> hdu.header
SIMPLE  =                    T / conforms to FITS standard
BITPIX  =                    8 / array data type
NAXIS   =                    0 / number of array dimensions
EXTEND  =                    T
HIERARCH abcdefghi = 10

备注

关于 Header 类的大部分设计都是为了抽象出有关FITS格式的怪癖。这就是为什么,例如,它会自动创建CONTINUE和HIERARCH卡。头只是一个数据结构,作为用户,您不必担心它最终如何被序列化为FITS文件中的头。

尽管有些地方几乎不可能隐藏FITS格式的怪癖, astropy 试着让你尽量少去想它。如果有任何地方是模糊的或难以理解的头部是如何构造的,请让我们知道,因为可能有一些领域可以改进,甚至更多。