{"id":33933225,"url":"https://github.com/cds-astro/fitsrs","last_synced_at":"2026-04-09T05:32:24.483Z","repository":{"id":61755565,"uuid":"331363290","full_name":"cds-astro/fitsrs","owner":"cds-astro","description":"FITS file reader library implemented in pure Rust","archived":false,"fork":false,"pushed_at":"2026-03-10T21:49:13.000Z","size":42670,"stargazers_count":30,"open_issues_count":5,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2026-03-11T03:14:37.767Z","etag":null,"topics":["astronomy","parser","rust"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/fitsrs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cds-astro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-01-20T16:21:41.000Z","updated_at":"2026-03-10T21:49:19.000Z","dependencies_parsed_at":"2025-05-02T15:36:50.224Z","dependency_job_id":"84a2a750-dc47-4cf6-81d2-59b7e0c0f229","html_url":"https://github.com/cds-astro/fitsrs","commit_stats":{"total_commits":68,"total_committers":4,"mean_commits":17.0,"dds":0.5147058823529411,"last_synced_commit":"3fc8d2f2c5ada7acaf25969f38227bf9833d1d84"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/cds-astro/fitsrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cds-astro%2Ffitsrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cds-astro%2Ffitsrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cds-astro%2Ffitsrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cds-astro%2Ffitsrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cds-astro","download_url":"https://codeload.github.com/cds-astro/fitsrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cds-astro%2Ffitsrs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31587820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astronomy","parser","rust"],"created_at":"2025-12-12T13:03:05.154Z","updated_at":"2026-04-09T05:32:24.472Z","avatar_url":"https://github.com/cds-astro.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"FITS file reader written in pure Rust\n-------------------------------------\n\n[![](https://img.shields.io/crates/v/fitsrs.svg)](https://crates.io/crates/fitsrs)\n[![](https://img.shields.io/crates/d/fitsrs.svg)](https://crates.io/crates/fitsrs)\n[![API Documentation on docs.rs](https://docs.rs/fitsrs/badge.svg)](https://docs.rs/fitsrs/)\n![testing CI](https://github.com/cds-astro/fitsrs/actions/workflows/rust.yml/badge.svg)\n\nThis parser was initiated for reading FITS images mapped onto HEALPix cells in the sky (See the [HiPS IVOA](https://www.ivoa.net/documents/HiPS/) standard) in order to use it in the [Aladin Lite](https://github.com/cds-astro/aladin-lite) web sky atlas.\n\nCurrently, fitsrs supports reading multiple HDU and is mainly dedicated to image extension reading.\nFor interpreting WCS keywords, see [wcs-rs](https://github.com/cds-astro/wcs-rs).\nA very new support of binary table extension has been added. This has been done mainly for supporting the [tiled compressed image convention](https://fits.gsfc.nasa.gov/registry/tilecompression.html) that describes the storing of tile images in variable length arrays of a binary table.\nThe ASCII table extension parsing has not been implemented but it is possible to get an iterator over the data bytes as well as its mandatory cards from the header.\n\nContributing\n------------\n\n\u003e [!WARNING]\n\u003e Running the test involves test files you can download [here](https://alasky.cds.unistra.fr/Aladin-Lite-test-files/fits-rs-test-files.tar). This tar is 2.2GB.\n\nOnce the tar file has been downloaded, put it into the root on your cloned repo and extract it:\n\n```bash\ntar -xvf fits-rs-test-files.tar\n```\n\nOnce the files have been extracted you can run the tests locally:\n\n```bash\ncargo test --release\n```\n\nFeatures\n--------\n\n* [X] Support single typed data block (i.e. image type data)\n* [X] Single HDU parsing, header and data units \n* [X] Support FITS files that may not fit in memory (iterator, possibility to seek directly to a specific pixel index/row)\n* [X] Async reading (requires to read the whole data. Seeking is not possible)\n* [X] Keeping COMMENTS, HISTORY and cards in the same order.\n* [X] CONTINUE Long String Keyword convention\n* [X] Keep all the cards in the original order\n* [X] Basic support of Bintable\n* [X] Tiled image convention for storing compressed images in FITS binary tables\n    - [X] Compression supported, GZIP, GZIP2 and RICE on u8, i16, i32 and f32.\n    - [ ] H_compress and PLI0 compressions\n    - [X] Dithering techniques for floating point images. Not well tested (test samples are welcome)\n    - [ ] `NULL_PIXEL_MASK` column and `ZMASKCMP` keyword is not supported\n* [ ] FITS writer/serializer\n* [ ] ESO HIERARCH keyword convention\n* [ ] ASCII table extension parsing\n* [X] Support of multiple HDU. Image and binary tables extension support. Provide an idiomatic Rust iterator over the list of HDU.\n* [X] WCS parsing, see [wcs-rs](https://github.com/cds-astro/wcs-rs)\n    - [X] Simple Imaging Polynomial (SIP) supported but not well tested\n    - [ ] TNX, TPV, ZPX (non-standard conventions)\n\n\u003e [!NOTE]\n\u003e Features not done are not planned to be done. If you want fitsrs to support a specific convention, please open an issue or send us a mail to inform us of your use case(s) and we can manage to support them. The FITS standard and its conventions are massive and it is a huge work to support all of it.\n\n\nLicense\n-------\n\nfitsrs has the double license MIT/Apache-2.0.\n\nIt uses code adapted from the famous [CFITSIO](https://github.com/HEASARC/cfitsio/blob/main/licenses/License.txt) library. Especially the RICE decompression source code has been ported from the original cfitsio [code](https://github.com/HEASARC/cfitsio/blob/main/ricecomp.c) to Rust.\n\nExample\n----------\n\n```rust\nuse std::fs::File;\nuse std::io::Cursor;\nuse fitsrs::{Fits, ImageData, Pixels, HDU, hdu::header::Xtension};\nuse fitsrs::wcs::{ImgXY, LonLat};\n\nuse std::io::{BufReader, Read};\n\nlet f = File::open(\"samples/fits.gsfc.nasa.gov/EUVE.fits\").unwrap();\nlet reader = BufReader::new(f);\n\nlet mut hdu_list = Fits::from_reader(reader);\n\nwhile let Some(Ok(hdu)) = hdu_list.next() {\n    match hdu {\n        // skip the primary HDU\n        HDU::Primary(_) =\u003e (),\n        HDU::XImage(hdu) =\u003e {\n            let xtension = hdu.get_header().get_xtension();\n\n            let [naxis1, naxis2] = xtension.get_naxis() else {\n                    panic!(\"Expected two axis in the image\");\n                };\n\n            let num_pixels = (naxis1 * naxis2) as usize;\n\n            // Try to access the WCS for an HDU image\n            if let Ok(wcs) = hdu.wcs() {\n                // Get the lonlat position on the sky of the pixel located at (0, 0) on the image\n                let xy = ImgXY::new(0.0, 0.0);\n                let lonlat = wcs\n                    .unproj_lonlat(\u0026xy)\n                    .unwrap();\n\n                // Get the pixel position in the image of a sky location\n                let xy_2 = wcs\n                    .proj_lonlat(\u0026lonlat)\n                    .unwrap();\n\n                assert!((xy.x() - xy_2.x()).abs() \u003c= 1e-9);\n                assert!((xy.y() - xy_2.y()).abs() \u003c= 1e-9);\n            }\n            let image = hdu_list.get_data(\u0026hdu);\n            match image.pixels() {\n                Pixels::U8(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n                Pixels::I16(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n                Pixels::I32(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n                Pixels::I64(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n                Pixels::F32(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n                Pixels::F64(it) =\u003e {\n                    let data = it.collect::\u003cVec\u003c_\u003e\u003e();\n                    assert_eq!(num_pixels, data.len())\n                },\n            }\n        },\n        HDU::XBinaryTable(hdu) =\u003e {\n            /*let data: Vec\u003c_\u003e = hdu_list\n                .get_data(\u0026hdu)\n                .table_data()\n                // Select specific fields with the select_fields method\n                .select_fields(\u0026[\n                    ColumnId::Name(\"mag\"),\n                    ColumnId::Name(\"phot_bp_mean_mag\"),\n                    ColumnId::Name(\"phot_rp_mean_mag\"),\n                ])\n                .collect();\n            */\n            let num_rows = hdu.get_header()\n                .get_xtension()\n                .get_num_rows();\n            let rows: Vec\u003c_\u003e = hdu_list\n                .get_data(\u0026hdu)\n                .row_iter()\n                .collect();\n\n            assert_eq!(num_rows, rows.len());\n        },\n        HDU::XASCIITable(hdu) =\u003e {\n            let num_bytes = hdu.get_header()\n                .get_xtension()\n                .get_num_bytes_data_block();\n\n            let data = hdu_list.get_data(\u0026hdu)\n                .bytes()\n                .collect::\u003cVec\u003c_\u003e\u003e();\n\n            assert_eq!(num_bytes as usize, data.len());\n        },\n    }\n}\n```\n\nIntegration with `image`\n------------------------\n\nEnable the optional `image` feature and register the decoding hook to let the `image` crate read FITS files:\n\n```toml\n[dependencies]\nfitsrs = { version = \"0.4.1\", features = [\"image\"] }\n```\n\n```rust,ignore\nuse fitsrs::image_integration::register_fits_decoding_hook;\nuse image::ImageReader;\n\nfn main() -\u003e image::ImageResult\u003c()\u003e {\n    register_fits_decoding_hook();\n\n    let img = ImageReader::open(\"samples/hipsgen/Npix8.fits\")?\n        .decode()?;\n\n    println!(\"decoded FITS dimensions: {}x{}\", img.width(), img.height());\n    Ok(())\n}\n```\n\nThe decoder scans all HDUs and picks the first suitable 2D image (including the first plane of image cubes). `BZERO` and `BSCALE` header keywords are applied during decoding when present.\n\nFITS does not mandate a display range for floating-point or high-bit-depth integer data. The pixel type mapping is therefore:\n\n| BITPIX | `ColorType` | Notes |\n|--------|-------------|-------|\n| 8 | `L8` | exact |\n| 16 | `L16` | BZERO/BSCALE applied; common case BZERO=32768 recovers unsigned u16 |\n| −32 | `Rgb32F` | full precision; R=G=B (grayscale stored as colour) |\n| 32 | `Rgb32F` | cast to f32; may lose precision for large integers |\n| 64 | `Rgb32F` | cast to f32; lossy |\n| −64 | `Rgb32F` | cast to f32; lossy |\n\nFor the float/integer depths the `Rgb32F` output preserves the original dynamic range; downstream consumers are responsible for tone-mapping or normalisation.\n\nFor async input readers:\n\n```rust\n#[tokio::test]\nasync fn parse_fits_async() {\n    use std::fs::File;\n    use std::io::Cursor;\n    use fitsrs::hdu::AsyncHDU;\n    use fitsrs::hdu::data::stream::Stream;\n    use fitsrs::async_fits::AsyncFits;\n    use fitsrs::hdu::header::extension::Xtension;\n\n    use std::io::{BufReader, Read};\n\n    // reader needs to implement futures::io::AsyncRead\n    let f = File::open(\"samples/fits.gsfc.nasa.gov/EUVE.fits\").unwrap();\n    let reader = BufReader::new(f);\n\n    let mut hdu_list = AsyncFits::from_reader(reader);\n\n    while let Some(Ok(mut hdu)) = hdu_list.next().await {\n        match hdu {\n            AsyncHDU::Primary(_) =\u003e (),\n            AsyncHDU::Image(hdu) =\u003e {\n                let xtension = hdu.get_header().get_xtension();\n\n                let [naxis1, naxis2] = xtension.get_naxis() else {\n                    panic!(\"Expected two axis in the image\");\n                };\n\n                let num_pixels = (naxis1 * naxis2) as usize;\n\n                match hdu_list.get_data(hdu) {\n                    Stream::U8(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                    Stream::I16(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                    Stream::I32(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                    Stream::I64(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                    Stream::F32(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                    Stream::F64(st) =\u003e {\n                        let data = st.collect::\u003cVec\u003c_\u003e\u003e().await;\n                        assert_eq!(num_pixels, data.len())\n                    },\n                }\n            },\n            AsyncHDU::XBinaryTable(hdu) =\u003e {\n                let num_bytes = hdu.get_header()\n                    .get_xtension()\n                    .get_num_bytes_data_block();\n\n                let it_bytes = hdu_list.get_data(hdu);\n                let data = it_bytes.collect::\u003cVec\u003c_\u003e\u003e().await;\n                assert_eq!(num_bytes as usize, data.len());\n            },\n            AsyncHDU::XASCIITable(hdu) =\u003e {\n                let num_bytes = xhdu.get_header()\n                    .get_xtension()\n                    .get_num_bytes_data_block();\n\n                let it_bytes = hdu_list.get_data(hdu);\n                let data = it_bytes.collect::\u003cVec\u003c_\u003e\u003e().await;\n                assert_eq!(num_bytes as usize, data.len());\n            },\n        }\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcds-astro%2Ffitsrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcds-astro%2Ffitsrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcds-astro%2Ffitsrs/lists"}