
EuclidRust:用 Rust 写的 FITS 读取器,比 astropy 快 10 倍
来源标题:EuclidRust – Rust FITS reader for Euclid, 10x faster than astropy
EuclidRust 是一个用 Rust 编写、经 PyO3 提供 Python 扩展的 FITS 读取器,号称比 astropy 快 10 倍。该项目为 ESA Euclid 任务的数据处理工具而建,也可用于任意 FITS 工作流,可通过 pip 安装并导入为 euclidrust。项目代码与基准由 Claude Code 编写,作者定位为实验性项目。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
面向 Python 的高性能 FITS I/O,用 Rust 编写,并通过 PyO3 以普通的、可用 pip 安装的 Python 扩展模块形式暴露。它原本是作为 ESA Euclid 任务数据处理工具的试验平台而构建的,但适用于任何基于 FITS 的工作流。其中包含的 Python 包,以及你导入时使用的名称,是 euclidrust。 import euclidrust stats = euclidrust.fits_stats("tile.fits") cutout = euclidrust.extract_cutout("tile.fits", ra_deg=83.82, dec_deg=-5.39, width=50, height=50) euclidrust.export_cutout_parquet("tile.fits", 83.82, -5.39, 50, 50, "cutout.parquet") 关于本项目。这里的代码、基准测试和文档均由 Claude Code(Anthropic 的编码智能体)编写。作者决定构建什么、以什么顺序构建、如何衡量以及何时停止,并在整个过程中引导该智能体。请将此项目视为实验性项目:阅读代码,自己运行基准测试,依据数字而非作者来判断。 目录: 支持的文件类型 · Python 与 Rust 如何协同 · 安装 · 使用 · 一次处理多张图像 · NVIDIA GPU · 测试 · 运行基准测试 · 故障排除 · 动机 · 基准测试结果 · 限制 · 路线图 简短回答:二维 FITS 图像,原始或 tile 压缩(Rice / GZIP),无论图像位于主 HDU 还是扩展 HDU 中。 FITS(Flexible Image Transport System)是天文学的标准文件格式。一个 FITS 文件是一系列称为 HDU(“头/数据单元”)的块的序列:每个 HDU 有一个文本头和一段载荷,载荷可以是图像或表。euclidrust 读取图像:一个矩形像素网格(天空拼接图、探测器帧……),外加说明像素如何映射到天空(WCS)的头关键字。 图像所在的位置取决于数据产品。Euclid MER 拼接图将其图像保存在第一个 HDU(“主 HDU”)中,这也是 euclidrust 默认读取的位置。校准帧(VIS、NISP)和原始帧的主 HDU 为空,每个探测器对应一个图像扩展,名称如 DET11.SCI 或 1-1.E。对于这些文件,你用 hdu= 参数(索引或名称)选择 HDU,而 list_hdus(path) 会显示文件包含的内容。 不支持的情况。其他一切都会以 ValueError 拒绝并说明原因,而不是被错误地读取(这些情况由 tests/test_input_validation.py 覆盖): 空像素。浮点图像中的 NaN 像素不计入 min、max 和 mean,而是计入 n_nan(无穷大是值,仍会保留)。整数图像的 BLANK 空值不被支持:这些像素会被当作普通值计数。 如果你在一个主 HDU 为空的文件上忘记指定 hdu=,错误信息会列出它找到的图像 HDU,并告诉你该传入什么,例如 [1] DET11.SCI (2040x2040, BITPIX -32) ... Pass hdu=<index or EXTNAME>。 这对 Euclid Q1 数据意味着什么。我读取了公开 Q1 发布中每个产品系列里少量文件的头(这是抽样,不是对整个存档的全面调查),并在其中四个系列的真实文件上运行了代码,与 astropy 进行对比: 我查看的 Q1 图像文件都不是 tile 压缩的,因此没有可测试的压缩 Euclid 文件。压缩图像支持是在我用 astropy/cfitsio 压缩的真实 Euclid 数据副本上验证的(见压缩图像)。 要在对文件运行任何操作之前自行检查,请列出它的 HDU: import euclidrust for hdu in euclidrust.list_hdus("file.fits"): print(hdu)
<HduInfo index=0 kind=PRIMARY shape=- bitpix=8 readable=no (no image data)>
<HduInfo index=1 name="DET11.SCI" kind=IMAGE shape=2040x2040 bitpix=-32 readable=yes>
或者用 astropy 查看头关键字:
from astropy.io import fits
h = fits.getheader("file.fits", 1) # 0 = 主 HDU,1 = 第一个扩展……
print("NAXIS", h["NAXIS"], "| BITPIX", h["BITPIX"])
print("CTYPE", h.get("CTYPE1"), h.get("CTYPE2"))
print("linear matrix keywords:", [k for k in ("CD1_1", "CDELT1", "PC1_1", "CROTA2") if k in h])
你需要 NAXIS 为 2、BITPIX 为上表中的某个值,并且对于 cutout,需要一个来自上述投影列表的 CTYPE 组合以及一些线性矩阵关键字(CD1_1 或 CDELT1)。
如果你是一位懂 Python 但从未接触过 Rust 的天文学家,只需理解一件事:你永远不需要自己编写或运行 Rust。你像安装 numpy 或 astropy 一样安装这个包并导入它。
PyO3 在 lib.rs 中体现为少量注解。以下是取自真实代码的完整映射:
#[derive(FromPyObject)] // -> 接受 Python int 或 str
enum HduSelector { Index(usize), Name(String) }
#[pyfunction] // -> 一个 Python 函数
fn fits_stats(path: &str, hdu: Option<HduSelector>) -> PyResult<FitsStats> { /* ... / }
#[pyclass] // -> 一个 Python 类
struct FitsStats {
#[pyo3(get)] min: f64, // -> 可读属性:stats.min
#[pyo3(get)] max: f64,
#[pyo3(get)] mean: f64,
/ ... /
#[pymodule] // -> import euclidrust
fn euclidrust(py: Python<'>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(fits_stats, m)?)?;
/ ... */
Ok(())
而在 Python 侧不过是这样:
import euclidrust # 加载编译好的 Rust 库
s = euclidrust.fits_stats("tile.fits") # 调用 Rust 函数
print(s.min, s.max, s.mean) # 读取 Rust 结构体的字段
因此构建链是:Rust 源码 ->(由 pip 运行的 maturin)-> 编译好的库,如 euclidrust.cpython-313-x86_64-linux-gnu.so(Windows 上为 .pyd)-> 安装到你的虚拟环境 -> import euclidrust。scripts/ 中的脚本不含任何 Rust 代码和 PyO3 代码;它们只导入 euclidrust。在 Rust 中抛出的错误会以普通 ValueError 异常的形式到达 Python。
- 前置条件 你的机器上需要三样东西: 其他一切(maturin、PyO3 和其他 Rust 库)都会在构建过程中自动下载,因此首次安装需要网络连接。 已在 Debian 13(x86_64)、Python 3.13 和 Rust 1.100 上测试,硬件为 Intel Core i7-8559U(4 核 / 8 线程),31 GiB 内存。macOS 和 Windows 尚未测试;如果你尝试了,请开一个 issue 说明情况。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。