fitsrs/
lib.rs

1//! This crate implements a fits image parser
2//!
3//! # Examples
4//!
5//! Basic usage:
6//!
7//! ```
8//! use std::fs::File;
9//! use std::io::BufReader;
10//!
11//! use fitsrs::{Fits, HDU, ImageData, Pixels};
12//!
13//! let f = File::open("samples/fits.gsfc.nasa.gov/HST_FOC.fits").unwrap();
14//! let reader = BufReader::new(f);
15//! let mut hdu_list = Fits::from_reader(reader);
16//! let hdu = hdu_list.next();
17//! if let Some(Ok(HDU::Primary(hdu))) = hdu {
18//!     let xtension = hdu.get_header().get_xtension();
19//!     let [naxis1, naxis2] = xtension.get_naxis() else {
20//!         panic!("Wrong number of axis in the header")
21//!     };
22//!
23//!     let image = hdu_list.get_data(&hdu);
24//!     if let Pixels::F32(it) = image.pixels() {
25//!         assert_eq!(it.count() as u64, naxis1 * naxis2);
26//!     } else {
27//!         panic!("expected data block containing f32");
28//!     }
29//! }
30//! ```
31
32#![doc = include_str!("../README.md")]
33#![warn(
34    trivial_casts,
35    trivial_numeric_casts,
36    clippy::uninlined_format_args,
37    clippy::match_same_arms
38)]
39
40extern crate async_trait;
41//extern crate byteorder;
42#[macro_use]
43extern crate quick_error;
44
45pub mod async_fits;
46pub mod card;
47pub mod error;
48pub mod file;
49pub mod fits;
50pub mod wcs;
51
52pub mod gz;
53pub mod hdu;
54
55pub use async_fits::AsyncFits;
56pub use file::FITSFile;
57pub use fits::Fits;
58pub use hdu::data::bintable::{DataValue, TableData, TableRowData};
59pub use hdu::data::image::{ImageData, Pixels};
60pub use hdu::data::iter::It;
61pub use hdu::{AsyncHDU, HDU};
62pub use wcs::{ImgXY, LonLat, WCSParams, WCS};
63
64#[cfg(test)]
65mod tests {
66    use crate::async_fits::AsyncFits;
67    use crate::fits::Fits;
68    use crate::hdu::data::image::Pixels;
69    use crate::hdu::data::DataStream;
70    use crate::hdu::AsyncHDU;
71    use crate::wcs::ImgXY;
72    use crate::FITSFile;
73
74    use crate::hdu::data::bintable::ColumnId;
75    use crate::hdu::header::extension::Xtension;
76    use crate::hdu::header::Bitpix;
77
78    use std::fs::File;
79    use std::io::Cursor;
80    use std::io::{BufReader, Read};
81
82    use futures::StreamExt;
83    use image::DynamicImage;
84    use test_case::test_case;
85
86    #[test]
87    fn test_fits_image_mandatory_kw() {
88        let f = BufReader::new(File::open("samples/hipsgen/Npix208.fits").unwrap());
89        let bytes: Result<Vec<_>, _> = f.bytes().collect();
90        let buf = bytes.unwrap();
91
92        let reader = Cursor::new(&buf[..]);
93        let mut hdu_list = Fits::from_reader(reader);
94        let hdu = hdu_list.next().unwrap().unwrap();
95        assert!(matches!(hdu, HDU::Primary(_)));
96
97        if let HDU::Primary(hdu) = hdu {
98            let header = hdu.get_header();
99            assert_eq!(header.get_xtension().get_naxis(), &[64, 64]);
100            assert_eq!(header.get_xtension().get_bitpix(), Bitpix::F32);
101        }
102
103        assert!(hdu_list.next().is_none());
104    }
105
106    #[test_case("samples/fits.gsfc.nasa.gov/Astro_UIT.fits",1,0,0,&[11520],&[524288])]
107    #[test_case("samples/fits.gsfc.nasa.gov/EUVE.fits",5,0,4,&[5760, 14400, 550080, 1788480, 3026880, 4262400, 4271040, 4279680, 4288320],&[0, 524288, 1228800, 1228800, 1228800, 48, 40, 40, 40])]
108    #[test_case("samples/fits.gsfc.nasa.gov/HST_FGS.fits",1,1,0,&[20160, 2537280],&[2511264, 693])]
109    #[test_case("samples/fits.gsfc.nasa.gov/IUE_LWP.fits",1,0,1,&[28800, 34560],&[0, 11535])]
110    #[test_case("samples/misc/ngc5457K.fits",1,0,0,&[14400],&[65116872])]
111    #[test_case("samples/fits.gsfc.nasa.gov/HST_FOC.fits",1,1,0,&[11520, 4216320],&[4194304, 312])]
112    #[test_case("samples/fits.gsfc.nasa.gov/HST_FOS.fits",1,1,0,&[14400, 40320],&[16512, 672])]
113    #[test_case("samples/fits.gsfc.nasa.gov/HST_HRS.fits",1,1,0,&[20160, 66240],&[32000, 1648])]
114    #[test_case("samples/fits.gsfc.nasa.gov/HST_NICMOS.fits",6,0,0,&[20160, 31680, 322560, 613440, 763200, 912960],&[0, 284040, 284040, 142020, 142020, 284040])]
115    #[test_case("samples/fits.gsfc.nasa.gov/HST_WFPC_II.fits",1,1,0,&[23040, 694080],&[640000, 3184])]
116    #[test_case("samples/fits.gsfc.nasa.gov/HST_WFPC_II_bis.fits",1,0,0,&[23040],&[40000])]
117    fn test_fits_count_hdu(
118        filename: &str,
119        num_image_ext: usize,
120        num_asciitable_ext: usize,
121        num_bintable_ext: usize,
122        byte_offsets: &[u64],
123        byte_lengths: &[u64],
124    ) {
125        let mut hdu_list = FITSFile::open(filename).unwrap();
126
127        let mut n_image_ext = 0; // the primary HDU is counted below
128        let mut n_bintable_ext = 0;
129        let mut n_asciitable_ext = 0;
130        let mut seen_byte_offsets = vec![];
131        let mut seen_byte_lengths = vec![];
132
133        while let Some(Ok(hdu)) = hdu_list.next() {
134            match &hdu {
135                HDU::Primary(_) | HDU::XImage(_) => {
136                    n_image_ext += 1;
137                }
138                HDU::XBinaryTable(_) => {
139                    n_bintable_ext += 1;
140                }
141                HDU::XASCIITable(_) => {
142                    n_asciitable_ext += 1;
143                }
144            };
145            seen_byte_lengths.push(hdu.get_data_unit_byte_size());
146            seen_byte_offsets.push(hdu.get_data_unit_byte_offset())
147        }
148
149        assert_eq!(n_image_ext, num_image_ext);
150        assert_eq!(n_bintable_ext, num_bintable_ext);
151        assert_eq!(n_asciitable_ext, num_asciitable_ext);
152        assert_eq!(seen_byte_offsets, byte_offsets);
153        assert_eq!(seen_byte_lengths, byte_lengths);
154    }
155
156    #[test]
157    fn test_fits_image_f32() {
158        let f = BufReader::new(File::open("samples/hipsgen/Npix208.fits").unwrap());
159        let bytes: Result<Vec<_>, _> = f.bytes().collect();
160        let buf = bytes.unwrap();
161
162        let reader = Cursor::new(&buf[..]);
163        let mut hdu_list = Fits::from_reader(reader);
164
165        let hdu = hdu_list.next().unwrap().unwrap();
166        assert!(matches!(hdu, HDU::Primary(_)));
167        if let HDU::Primary(hdu) = hdu {
168            let header = hdu.get_header();
169            let num_pixels = header.get_xtension().get_num_pixels();
170            let image = hdu_list.get_data(&hdu);
171            match image.pixels() {
172                Pixels::F32(it) => {
173                    assert!(it.count() as u64 == num_pixels);
174                }
175                _ => unreachable!(),
176            }
177        } else {
178            unreachable!();
179        }
180    }
181
182    #[test]
183    fn test_fits_i16() {
184        let mut f = File::open("samples/hipsgen/Npix4906.fits").unwrap();
185        let mut raw_bytes = Vec::<u8>::new();
186        f.read_to_end(&mut raw_bytes).unwrap();
187
188        let reader = Cursor::new(&raw_bytes[..]);
189        let mut hdu_list = Fits::from_reader(reader);
190
191        let hdu = hdu_list.next().unwrap().unwrap();
192        assert!(matches!(hdu, HDU::Primary(_)));
193        if let HDU::Primary(hdu) = hdu {
194            let header = hdu.get_header();
195            let num_pixels = header.get_xtension().get_num_pixels();
196            let image = hdu_list.get_data(&hdu);
197            match image.pixels() {
198                Pixels::I16(data) => {
199                    assert!(data.count() as u64 == num_pixels)
200                }
201                _ => unreachable!(),
202            }
203        } else {
204            unreachable!();
205        }
206    }
207
208    #[test_case("samples/fits.gsfc.nasa.gov/Astro_UIT.fits", true)]
209    #[test_case("samples/hipsgen/Npix8.fits", false)]
210    #[test_case("samples/hipsgen/Npix9.fits", false)]
211    #[test_case("samples/hipsgen/Npix132.fits", false)]
212    #[test_case("samples/hipsgen/Npix133.fits", false)]
213    #[test_case("samples/hipsgen/Npix134.fits", false)]
214    #[test_case("samples/hipsgen/Npix140.fits", false)]
215    #[test_case("samples/hipsgen/Npix208.fits", false)]
216    #[test_case("samples/hipsgen/Npix282.fits", false)]
217    #[test_case("samples/hipsgen/Npix4906.fits", false)]
218    #[test_case("samples/hipsgen/Npix691539.fits", false)]
219    #[test_case("samples/hips2fits/allsky_panstarrs.fits", false)]
220    #[test_case("samples/hips2fits/cutout-CDS_P_HST_PHAT_F475W.fits", false)]
221    #[test_case("samples/fits.gsfc.nasa.gov/EUVE.fits", false)]
222    #[test_case("samples/fits.gsfc.nasa.gov/HST_FGS.fits", false)]
223    #[test_case("samples/fits.gsfc.nasa.gov/HST_FOC.fits", false)]
224    #[test_case("samples/fits.gsfc.nasa.gov/HST_FOS.fits", false)]
225    #[test_case("samples/fits.gsfc.nasa.gov/HST_HRS.fits", false)]
226    #[test_case("samples/fits.gsfc.nasa.gov/HST_NICMOS.fits", false)]
227    #[test_case("samples/fits.gsfc.nasa.gov/HST_WFPC_II.fits", false)]
228    #[test_case("samples/fits.gsfc.nasa.gov/HST_WFPC_II_bis.fits", false)]
229    #[test_case("samples/vizier/NVSSJ235137-362632r.fits", false)]
230    #[test_case("samples/vizier/VAR.358.R.fits", false)]
231    #[test_case("samples/fits.gsfc.nasa.gov/IUE_LWP.fits", false)]
232    #[test_case("samples/misc/bonn.fits", false)]
233    #[test_case("samples/misc/EUC_MER_MOSAIC-VIS-FLAG_TILE100158585-1EC1C5_20221211T132329.822037Z_00.00.fits", false)]
234    #[test_case("samples/misc/P122_49.fits", false)]
235    #[test_case("samples/misc/skv1678175163788.fits", false)]
236    #[test_case("samples/misc/SN2923fxjA.fits", false)]
237    fn test_fits_opening(filename: &str, ground_truth: bool) {
238        let hdu_list = FITSFile::open(filename).expect("Can find fits file");
239
240        let mut corrupted = false;
241        for hdu in hdu_list {
242            if hdu.is_err() {
243                corrupted = true;
244            }
245        }
246
247        assert_eq!(ground_truth, corrupted);
248    }
249
250    #[test]
251    fn test_fits_not_fitting_in_memory() {
252        use std::fs::File;
253        use std::io::BufReader;
254        let f = File::open("samples/fits.gsfc.nasa.gov/EUVE.fits").unwrap();
255        let reader = BufReader::new(f);
256        let mut hdu_list = Fits::from_reader(reader);
257
258        while let Some(Ok(HDU::XImage(hdu))) = hdu_list.next() {
259            let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
260
261            // Try to access the WCS on a specific HDU image
262            if let Ok(wcs) = hdu.wcs() {
263                // and perform projection/unprojection using that image WCS
264                let xy = ImgXY::new(0.0, 0.0);
265                let _lonlat = wcs.unproj_lonlat(&xy).unwrap();
266            }
267
268            let image = hdu_list.get_data(&hdu);
269            assert_eq!(
270                num_pixels as usize,
271                match image.pixels() {
272                    Pixels::I16(it) => it.count(),
273                    Pixels::U8(it) => it.count(),
274                    Pixels::I32(it) => it.count(),
275                    Pixels::I64(it) => it.count(),
276                    Pixels::F32(it) => it.count(),
277                    Pixels::F64(it) => it.count(),
278                }
279            );
280        }
281    }
282
283    #[test]
284    fn test_fits_image_borrowed() {
285        use std::fs::File;
286
287        let mut f = File::open("samples/fits.gsfc.nasa.gov/HST_FOC.fits").unwrap();
288        let mut buf = Vec::new();
289        f.read_to_end(&mut buf).unwrap();
290
291        let reader = Cursor::new(&buf[..]);
292        let mut hdu_list = Fits::from_reader(reader);
293
294        if let Some(Ok(HDU::Primary(hdu))) = hdu_list.next() {
295            let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
296            let image = hdu_list.get_data(&hdu);
297            match image.pixels() {
298                Pixels::F32(data) => {
299                    assert_eq!(data.count(), num_pixels as usize);
300                }
301                _ => unreachable!(),
302            }
303        }
304    }
305
306    #[test]
307    fn test_fits_bintable() {
308        use std::fs::File;
309
310        let f = File::open("samples/vizier/II_278_transit.fits").unwrap();
311
312        let reader = BufReader::new(f);
313        let mut hdu_list = Fits::from_reader(reader);
314        let mut data_len = 0;
315        while let Some(Ok(hdu)) = hdu_list.next() {
316            if let HDU::XBinaryTable(hdu) = hdu {
317                let _ = hdu.get_header().get_xtension();
318                data_len = hdu_list.get_data(&hdu).table_data().count();
319            }
320        }
321
322        assert_eq!(177 * 23, data_len);
323    }
324
325    #[test]
326    fn test_fits_bintable_corr() {
327        use std::fs::File;
328
329        let f = File::open("samples/astrometry.net/corr.fits").unwrap();
330
331        let reader = BufReader::new(f);
332        let mut hdu_list = Fits::from_reader(reader);
333        while let Some(Ok(hdu)) = hdu_list.next() {
334            if let HDU::XBinaryTable(hdu) = hdu {
335                let data_len = hdu_list
336                    .get_data(&hdu)
337                    .table_data()
338                    .select_fields(&[
339                        // Note: fields are intentionally selected out of order
340                        // to test that we don't rely on the order of fields.
341                        ColumnId::Name("phot_bp_mean_mag"),
342                        ColumnId::Name("phot_rp_mean_mag"),
343                        ColumnId::Name("mag"),
344                    ])
345                    .count();
346
347                assert_eq!(data_len, 3 * 52);
348            }
349        }
350    }
351
352    #[test_case("samples/misc/SN2923fxjA.fits.gz", 5415.0, 6386.0)]
353    #[test_case("samples/misc/SN2923fxjA.fits", 5415.0, 6386.0)]
354    fn test_fits_open_external_gzipped_file(filename: &str, min: f32, max: f32) {
355        let mut hdu_list = FITSFile::open(filename).unwrap();
356        use std::iter::Iterator;
357
358        while let Some(Ok(hdu)) = hdu_list.next() {
359            match hdu {
360                HDU::Primary(hdu) | HDU::XImage(hdu) => {
361                    let xtension = hdu.get_header().get_xtension();
362                    let [naxis1, naxis2] = *xtension.get_naxis() else {
363                        panic!("Wrong number of axis in the header")
364                    };
365
366                    let image = hdu_list.get_data(&hdu);
367                    if let Pixels::F32(it) = image.pixels() {
368                        let c = it
369                            .map(|v| (((v - min) / (max - min)) * 255.0) as u8)
370                            .collect::<Vec<_>>();
371
372                        let imgbuf = DynamicImage::ImageLuma8(
373                            image::ImageBuffer::from_raw(naxis1 as u32, naxis2 as u32, c).unwrap(),
374                        );
375                        imgbuf.save(format!("{filename}.jpg")).unwrap();
376                    };
377                }
378                _ => (),
379            }
380        }
381    }
382
383    use super::hdu::HDU;
384    #[test]
385    fn test_fits_images_data_block() {
386        use std::fs::File;
387
388        let mut f = File::open("samples/fits.gsfc.nasa.gov/EUVE.fits").unwrap();
389        let mut buf = Vec::new();
390        f.read_to_end(&mut buf).unwrap();
391        let reader = Cursor::new(&buf[..]);
392
393        let mut hdu_list = Fits::from_reader(reader);
394
395        while let Some(Ok(hdu)) = hdu_list.next() {
396            match hdu {
397                HDU::XImage(hdu) | HDU::Primary(hdu) => {
398                    let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
399
400                    let image = hdu_list.get_data(&hdu);
401                    assert_eq!(
402                        num_pixels as usize,
403                        match image.pixels() {
404                            Pixels::U8(it) => it.count(),
405                            Pixels::I16(it) => it.count(),
406                            Pixels::I32(it) => it.count(),
407                            Pixels::I64(it) => it.count(),
408                            Pixels::F32(it) => it.count(),
409                            Pixels::F64(it) => it.count(),
410                        }
411                    );
412                }
413                HDU::XBinaryTable(hdu) => {
414                    let _num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
415                    let _data = hdu_list.get_data(&hdu);
416                    /*{
417                        It(mem) => assert_eq!(num_bytes as usize, mem.len()),
418                        _ => unreachable!(),
419                    }*/
420                }
421                HDU::XASCIITable(hdu) => {
422                    let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
423                    let bytes = hdu_list.get_data(&hdu);
424
425                    assert_eq!(num_bytes as usize, bytes.bytes().count());
426                }
427            }
428        }
429    }
430
431    #[test]
432    fn test_fits_images_data_block_bufreader() {
433        use std::fs::File;
434
435        let f = File::open("samples/fits.gsfc.nasa.gov/EUVE.fits").unwrap();
436        let reader = BufReader::new(f);
437
438        let mut hdu_list = Fits::from_reader(reader);
439
440        while let Some(Ok(hdu)) = hdu_list.next() {
441            match hdu {
442                HDU::XImage(hdu) => {
443                    let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
444
445                    let image = hdu_list.get_data(&hdu);
446                    assert_eq!(
447                        num_pixels as usize,
448                        match image.pixels() {
449                            Pixels::U8(it) => it.count(),
450                            Pixels::I16(it) => it.count(),
451                            Pixels::I32(it) => it.count(),
452                            Pixels::I64(it) => it.count(),
453                            Pixels::F32(it) => it.count(),
454                            Pixels::F64(it) => it.count(),
455                        }
456                    );
457                }
458                HDU::XBinaryTable(_) => {
459                    /*let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
460
461                    let it_bytes = hdu.get_data(&mut hdu_list);
462                    let data = it_bytes.collect::<Vec<_>>();
463                    assert_eq!(num_bytes as usize, data.len());*/
464                }
465                HDU::XASCIITable(hdu) => {
466                    let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
467
468                    assert_eq!(num_bytes as usize, hdu_list.get_data(&hdu).bytes().count());
469                }
470                _ => (),
471            }
472        }
473    }
474
475    #[test]
476    fn test_bad_bytes() {
477        let bytes: &[u8] = &[
478            60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 72, 84, 77, 76, 32, 80, 85, 66, 76, 73, 67, 32,
479            34, 45, 47, 47, 73, 69, 84, 70, 47, 47, 68, 84, 68, 32, 72, 84, 77, 76, 32, 50, 46, 48,
480            47, 47, 69, 78, 34, 62, 10, 60, 104, 116, 109, 108, 62, 60, 104, 101, 97, 100, 62, 10,
481            60, 116, 105, 116, 108, 101, 62, 52, 48, 52, 32, 78, 111, 116, 32, 70, 111, 117, 110,
482            100, 60, 47, 116, 105, 116, 108, 101, 62, 10, 60, 47, 104, 101, 97, 100, 62, 60, 98,
483            111, 100, 121, 62, 10, 60, 104, 49, 62, 78, 111, 116, 32, 70, 111, 117, 110, 100, 60,
484            47, 104, 49, 62, 10, 60, 112, 62, 84, 104, 101, 32, 114, 101, 113, 117, 101, 115, 116,
485            101, 100, 32, 85, 82, 76, 32, 47, 97, 108, 108, 115, 107, 121, 47, 80, 78, 82, 101,
486            100, 47, 78, 111, 114, 100, 101, 114, 55, 47, 68, 105, 114, 52, 48, 48, 48, 48, 47, 78,
487            112, 105, 120, 52, 52, 49, 49, 49, 46, 102, 105, 116, 115, 32, 119, 97, 115, 32, 110,
488            111, 116, 32, 102, 111, 117, 110, 100, 32, 111, 110, 32, 116, 104, 105, 115, 32, 115,
489            101, 114, 118, 101, 114, 46, 60, 47, 112, 62, 10, 60, 47, 98, 111, 100, 121, 62, 60,
490            47, 104, 116, 109, 108, 62, 10,
491        ];
492        let reader = Cursor::new(bytes);
493        let mut hdu_list = Fits::from_reader(reader);
494        assert!(hdu_list.next().unwrap().is_err());
495    }
496
497    // FIXME too slow, to retest when we implement the seek of the data unit part
498    //#[test_case("samples/misc/EUC_MER_MOSAIC-VIS-FLAG_TILE100158585-1EC1C5_20221211T132329.822037Z_00.00.fits")]
499    #[test_case("samples/fits.gsfc.nasa.gov/EUVE.fits")]
500    #[test_case("samples/fits.gsfc.nasa.gov/HST_FOC.fits")]
501    #[test_case("samples/vizier/new_url.fits")]
502    #[tokio::test]
503    async fn test_fits_images_data_block_bufreader_async(filename: &str) {
504        // Put it all in memory first (this is for the exemple)
505        // It is not good to do so for performance reasons
506        // Better prefer to pipe to a ReadableStream instead
507        let mut f = File::open(filename).unwrap();
508        let mut buf = Vec::new();
509        f.read_to_end(&mut buf).unwrap();
510
511        let reader = futures::io::BufReader::new(&buf[..]);
512
513        let mut hdu_list = AsyncFits::from_reader(reader);
514
515        while let Some(Ok(hdu)) = hdu_list.next().await {
516            match hdu {
517                AsyncHDU::XImage(hdu) | AsyncHDU::Primary(hdu) => {
518                    let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
519
520                    assert_eq!(
521                        num_pixels as usize,
522                        match hdu_list.get_data(&hdu) {
523                            DataStream::U8(st) => st.count().await,
524                            DataStream::I16(st) => st.count().await,
525                            DataStream::I32(st) => st.count().await,
526                            DataStream::I64(st) => st.count().await,
527                            DataStream::F32(st) => st.count().await,
528                            DataStream::F64(st) => st.count().await,
529                        }
530                    );
531                }
532                AsyncHDU::XBinaryTable(hdu) => {
533                    let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
534
535                    assert_eq!(num_bytes as usize, hdu_list.get_data(&hdu).count().await);
536                }
537                AsyncHDU::XASCIITable(hdu) => {
538                    let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
539
540                    assert_eq!(num_bytes as usize, hdu_list.get_data(&hdu).count().await);
541                }
542            }
543        }
544    }
545
546    #[test]
547    fn test_fits_euve() {
548        use std::fs::File;
549        use std::io::BufReader;
550
551        let f = File::open("samples/fits.gsfc.nasa.gov/EUVE.fits").unwrap();
552        let reader = BufReader::new(f);
553
554        let mut hdu_list = Fits::from_reader(reader);
555
556        while let Some(Ok(hdu)) = hdu_list.next() {
557            match hdu {
558                // skip the primary HDU
559                HDU::Primary(_) => (),
560                HDU::XImage(hdu) => {
561                    let num_pixels = hdu.get_header().get_xtension().get_num_pixels();
562
563                    let data = hdu_list.get_data(&hdu);
564                    assert_eq!(
565                        num_pixels as usize,
566                        match data.pixels() {
567                            Pixels::U8(it) => it.count(),
568                            Pixels::I16(it) => it.count(),
569                            Pixels::I32(it) => it.count(),
570                            Pixels::I64(it) => it.count(),
571                            Pixels::F32(it) => it.count(),
572                            Pixels::F64(it) => it.count(),
573                        }
574                    );
575                }
576                HDU::XBinaryTable(hdu) => {
577                    let num_rows = hdu.get_header().get_xtension().get_num_rows();
578
579                    assert_eq!(num_rows, hdu_list.get_data(&hdu).row_iter().count());
580                }
581                HDU::XASCIITable(hdu) => {
582                    let num_bytes = hdu.get_header().get_xtension().get_num_bytes_data_block();
583
584                    assert_eq!(num_bytes as usize, hdu_list.get_data(&hdu).bytes().count());
585                }
586            }
587        }
588    }
589}