2

我对 FITS 文件执行了一个非常简单的操作(数据是 numpy 数组格式),但我无法让 astropy 将其保存为新文件或覆盖现有文件。

我正在重新编写一些使用 numpy pyfits 模块来处理天文 FITS 文件的旧代码 - 我想更新它以使用 astropy.io fit 模块。具体来说,我使用的一些数据是 3D 的,而一些是 4D 的。4D 的东西只是一个约定 - 第 4 轴不包含有用的信息(可以在此处找到数据示例:http ://www.mpia.de/THINGS/Data_files/NGC_628_NA_CUBE_THINGS.FITS )。所以我更喜欢删除额外的轴,然后我的其余代码可以继续进行而无需任何特殊要求。

这是我使用的基于 pyfits 的旧代码,效果很好:

import numpy
import pyfits

filename = 'NGC628.fits'
outfile  = 'NGC628_reshaped.fits'

# Get the shape of the file
fitsfile=pyfits.open(filename)
image = fitsfile[0].data
header =fitsfile[0].header
z = image.shape[1]  # No. channels                  
y = image.shape[2]  # No. x pixels
x = image.shape[3]  # No. y pixels

newimage = numpy.reshape(image,[z,y,x])

pyfits.core.writeto(outfile,newimage,header, clobber=True)

那里没有什么复杂的,它只是重塑一个数组并将其保存到一个新文件中。奇妙。现在我想用 astropy fit 模块替换它。如果我做 :

import numpy
from astropy.io import fits as pyfits

fitsfile = pyfits.open('NGC628.fits', mode='update')
image  = fitsfile[0].data
header = fitsfile[0].header
z = image.shape[1]                  
y = image.shape[2]
x = image.shape[3]
image = numpy.reshape(image,[z,y,x])

......那么到目前为止,一切都很好。正如 image.shape 所证实的那样,“图像”数组的形状正确。但我终其一生都无法弄清楚如何将其保存到新的(或旧的)FITS 文件中。使用旧语法:

fitsfile.writeto('NGC628_2.fits',image,header)

...给出错误消息,“AttributeError:'numpy.ndarray'对象没有属性'lower'。如果相反,根据astropy文档,我只是省略图像和标题并尝试保存到原始文件:

fitsfile.writeto('NGC628.fits')

然后我得到一个文件已经存在的错误。如果我提供关键字“overwrite=True”,那么它会抱怨“WinError 32:该进程无法访问该文件,因为它正在被另一个进程使用:NGCC628.fits”。该文件绝对不会在任何其他程序中打开。

如果我指定新的文件名 NGC628_2.fits,那么 Python 会崩溃(将我返回到命令提示符)而没有错误。写入一个非常小的文件,其中仅包含标题数据,不包含任何图像数据。编辑:如果我使用正确的命令使用图像和标题数据编写新的 FITS 文件,则会发生完全相同的事情,例如 pyfits.writeto('NGC628_2.fits',image,header)。

只是为了让事情变得更加混乱,如果我做一个稍微简单的操作,比如说,将所有图像数据设置为一个常量值,然后关闭文件:

import numpy
from astropy.io import fits as pyfits
fitsfile = pyfits.open('NGC628.fits', mode='update')
image  = fitsfile[0].data
header = fitsfile[0].header
image[:,:,:,:] = 5.0
fitsfile.close()

然后这工作 - 原始文件现在是一个数组,其中每个值都等于 5。我从 astropy 文档中收集到,只需在更新模式下打开文件并关闭它就足够了,在这种情况下就是这样。但是在重塑图像时,同样的技巧不起作用- FITS 文件没有改变。

那么我到底做错了什么?更新原始文件或保存到新文件都可以(最好是后者),但我无法让任一操作正常工作。

编辑:我有 Python 版本 3.5.3、numpy 版本 1.17.3、astropy 版本 3.2.3,并且我正在运行 Windows 10。

4

1 回答 1

6

好吧,我认为你早先犯了一个小错误,你一直只是忽视,这让你陷入了一场疯狂的追逐,以寻找不同的解决方案。除了一个小而微妙的事情之外,你其他没有奏效的尝试也可能会奏效。我会先复习一下哪里出了问题,然后在后面的一些案例中解释哪里出了问题,所以这有点长......

首先,在您的原始代码中,您有:

pyfits.core.writeto(outfile,newimage,header, clobber=True)

这可以写得更简单:

pyfits.writeto(outfile, newimage, header, clobber=True)

pyfits.core模块是一个实现细节,几乎没有理由直接引用它。你可以直接调用这个函数作为pyfits.writeto().

如果你有这个,那么你现有的代码将通过将导入更改

from astropy.io import fits as pyfits

唯一需要注意的是,该clobber参数已重命名overwrite,尽管我认为clobber它仍然有效,但带有弃用警告。

第一个错误

相反,您将其更改为似乎被忽略的是:

fitsfile.writeto('NGC628_2.fits',image,header)

不是一回事。在第一种情况下,它是高级的writeto()“便利功能”。但现在你打电话给fitsfile.writeto. 这里fitsfile不是astropy.io.fits模块,而是文件对象本身:

>>> type(fitsfile)
<class 'astropy.io.fits.hdu.hdulist.HDUList'>

该类HDUList也有HDUList.writeto执行类似功能的方法,但它采用不同的参数。您可以通过多种方式进行检查,例如:

>>> help(fitsfile.writeto)
Help on method writeto in module astropy.io.fits.hdu.hdulist:

writeto(fileobj, output_verify='exception', overwrite=False, checksum=False) method of astropy.io.fits.hdu.hdulist.HDUList instance
    Write the `HDUList` to a new file.

它唯一需要的参数是要写入文件的文件名。与另一个不同的是writeto,它不接受数据或标头参数,因为HDUList它已经是 HDU 的集合,这些 HDU 已经具有关联的数据和标头。实际上,如果您只想对现有的 FITS 文件进行一些更改并将这些更改写入新文件,我不会writeto()像您最初那样使用高级别的 - 如果您从从头开始,只想快速将其写入新的 FITS 文件。所以你的原始代码也可以这样写:

import numpy as np
import astropy.io.fits as pyfits

filename = 'NGC628.fits'
outfile  = 'NGC628_reshaped.fits'

# Get the shape of the file
fitsfile = pyfits.open(filename)
image = fitsfile[0].data
z = image.shape[1]  # No. channels                  
y = image.shape[2]  # No. x pixels
x = image.shape[3]  # No. y pixels

# Replace the primary HDU's data in-place; this just manipulates the
# in-memory HDU data structure; it does not change anything on disk
fitsfile[0].data = np.reshape(image, [z,y,x])

# Write the new HDU structure to outfile
fitsfile.writeto(outfile, overwrite=True)

更新模式

接下来,您尝试通过使用 打开文件来修改文件mode='update',但这并不是真正打算使用更新模式的方式。 mode='update'更类似于以写入模式打开纯文本文件,例如open('foo.txt', 'w'):它旨在就地修改磁盘上的现有文件。当文件关闭时,您所做的任何修改都会刷新到磁盘,并且不需要使用writeto()或类似的东西。

你写了:

使用旧语法:

fitfile.writeto('NGC628_2.fits',图像,标题)

但正如我之前写的,这里没有“旧语法”,你只是想使用HDUList.writeto()方法而不是writeto() 函数

如果我提供关键字“overwrite=True”,那么它会抱怨“WinError 32:该进程无法访问该文件,因为它正在被另一个进程使用:NGCC628.fits”。该文件绝对不会在任何其他程序中打开。

我看到您使用的是 Windows——这是 Windows通常的一个特殊限制:如果该文件已经有任何打开的句柄,Windows 不允许写入、移动或删除该文件。我认为这里的错误消息具有误导性(此消息直接来自 Windows 本身):“它正在被另一个进程使用”。它应该类似于“它正在被这个进程或另一个进程使用”。当你这样做时:

fitsfile = pyfits.open('NGC628.fits', mode='update')

你现在有一个打开的文件句柄,所以 Windows 不会让你在没有先关闭它的情况下覆盖该文件(例如fitsfile.close())。

如果我指定新的文件名 NGC628_2.fits,那么 Python 会崩溃(将我返回到命令提示符)而没有错误。写入一个非常小的文件,其中仅包含标题数据,不包含任何图像数据。

现在听起来像是一个实际的错误。听起来您的 Python 解释器出现了段错误。我无法解释。但是我尝试按照您描述的相同步骤顺序复制它(在 Windows 上),但我无法做到。最后的fitsfile.writeto('NGC628_2.fits')工作没有问题。我能想象的一件事是,当你打开一个文件时mode='update',内部状态管理可能变得相当复杂,因为它必须跟踪您对 FITS 数据结构所做的所有更改,以便它可以正确地在磁盘上的现有文件中移动内容。在尝试一些轻微的病态操作的过程中(比如在文件更新的过程中尝试覆盖文件),某些东西可能会进入未定义的状态。我不知道如何做同样的事情。

就地修改文件的“正确”用法mode='update'可能类似于:

with pyfits.open(filename, mode='update') as fitsfile:
    image = fitsfile[0].data
    z, y, x = image.shape[1:]
    # Replace the original HDU data in-place
    fitsfile[0].data = image.reshape((z, y, x))

就是这样!我会习惯使用该with语句:当您使用时,with您执行任何需要您在with语句主体内打开文件的操作,然后在with退出语句时,它将确保您所做的所有更改(如果有) 被刷新到磁盘,并且文件被关闭。即使只是以只读模式打开文件,我也会使用它。这在 Windows 上尤为重要,因为正如您所发现的,Windows 对您是否正确关闭文件特别挑剔。

同样,您的最后一个示例可以重写:

with pyfits.open('NGC628.fits', mode='update') as fitsfile:
    image  = fitsfile[0].data
    header = fitsfile[0].header
    image[:,:,:,:] = 5.0

最后,你写道:

我从 astropy 文档中收集到,只需在更新模式下打开文件并关闭它就足够了,在这种情况下就足够了。但是在重塑图像时,同样的技巧不起作用- FITS 文件没有改变。

但我不确定你在这里尝试了什么;你没有表现出这种尝试。如果我不得不猜你写的是这样的:

    image = np.reshape(image, (z, y, x))

但这所做的只是用新数组替换变量指向的值。image不是更新原版fitsfile[0].data。前面的示例image[:] = 0.5有效,因为此处image指向与您相同的数组,fitsfile[0].data并且您正在就地修改其内容。但np.reshape(...)不是就地操作;它返回一个新数组。有关正确方法,请参阅我之前的示例。

于 2019-12-11T11:43:43.927 回答