A full featured python library to read from and write to FITS files
—
FITS image data handling functionality providing efficient reading, writing, and manipulation of image HDUs with support for subsets, compression, various data types, and numpy-style array operations.
Handler for FITS image extensions with support for reading, writing, compression, and array-like access patterns.
class ImageHDU:
def read(self, **kwargs):
"""
Read image data.
Parameters:
- **kwargs: additional read options
Returns:
numpy array, image data
"""
def write(self, img, start=0):
"""
Write image data.
Parameters:
- img: array-like, image data to write
- start: int/tuple, starting position for writing subset
"""
def get_dims(self):
"""
Get image dimensions.
Returns:
tuple of int, image dimensions (NAXIS1, NAXIS2, ...)
"""
def reshape(self, dims):
"""
Change image dimensions on disk.
Parameters:
- dims: tuple of int, new dimensions
"""
def is_compressed(self):
"""
Check if image is tile-compressed.
Returns:
bool, True if tile-compressed
"""
def get_comptype(self):
"""
Get compression type.
Returns:
str, compression algorithm name
"""
def has_data(self):
"""
Check if HDU contains image data.
Returns:
bool, True if HDU has data
"""
def __getitem__(self, key):
"""
Access image data using numpy-style slicing.
Parameters:
- key: slice/tuple, numpy-style slice notation
Returns:
numpy array, image subset
"""
# Inherited from HDUBase
def get_info(self):
"""Get complete HDU information."""
def get_offsets(self):
"""Get byte offsets (header_start, data_start, data_end)."""
def get_extnum(self):
"""Get extension number."""
def get_extname(self):
"""Get extension name."""
def get_extver(self):
"""Get extension version."""
def get_exttype(self, num=False):
"""Get extension type."""
def read_header(self):
"""Read header as FITSHDR object."""
def write_key(self, name, value, comment=""):
"""Write single header keyword."""
def write_keys(self, records, clean=True):
"""Write multiple header keywords."""
def write_checksum(self):
"""Write DATASUM/CHECKSUM keywords."""
def verify_checksum(self):
"""Verify data integrity."""def write(filename, data, compress=None, qlevel=None, qmethod=None,
hcomp_scale=None, **kwargs):
"""
Write image with compression options.
Parameters:
- filename: str, output file path
- data: array-like, image data
- compress: str, compression type ('rice', 'gzip', 'plio', 'hcompress')
- qlevel: float, quantization level (for lossy compression)
- qmethod: str, quantization method
- hcomp_scale: float, HCOMPRESS scale parameter
- **kwargs: additional options
"""# Supported numpy data types for images:
# - np.uint8, np.int8
# - np.uint16, np.int16
# - np.uint32, np.int32
# - np.uint64, np.int64
# - np.float32 (BITPIX = -32)
# - np.float64 (BITPIX = -64)
# - np.complex64, np.complex128NOCOMPRESS = 0
RICE_1 = 11 # Rice compression
GZIP_1 = 21 # GZIP compression level 1
GZIP_2 = 22 # GZIP compression level 2
PLIO_1 = 31 # PLIO compression
HCOMPRESS_1 = 41 # HCOMPRESS compression
# Quantization methods for lossy compression
NO_DITHER = -1
SUBTRACTIVE_DITHER_1 = 1
SUBTRACTIVE_DITHER_2 = 2
# Default compression parameters
DEFAULT_QLEVEL = 4.0
DEFAULT_QMETHOD = 'SUBTRACTIVE_DITHER_1'
DEFAULT_HCOMP_SCALE = 0.0import fitsio
import numpy as np
# Read entire image
image = fitsio.read('image.fits')
print(f"Image shape: {image.shape}")
# Read specific HDU
image = fitsio.read('multi.fits', ext=1)
# Read with FITS object for more control
with fitsio.FITS('image.fits') as fits:
# Get image information
hdu = fits[0]
dims = hdu.get_dims()
is_compressed = hdu.is_compressed()
print(f"Dimensions: {dims}")
print(f"Compressed: {is_compressed}")
# Read entire image
full_image = hdu.read()
# Read image subsets using slicing
subset = hdu[100:200, 50:150] # 100x100 subset
row = hdu[500, :] # Single row
column = hdu[:, 300] # Single column
# Advanced slicing
every_other = hdu[::2, ::2] # Every other pixel
flipped = hdu[::-1, :] # Vertically flippedimport fitsio
import numpy as np
# Create sample image
image = np.random.random((512, 512)).astype(np.float32)
# Write uncompressed image
fitsio.write('output.fits', image)
# Write with compression
fitsio.write('compressed.fits', image, compress='rice')
# Write with compression options
fitsio.write('lossy.fits', image, compress='rice',
qlevel=8.0, qmethod='SUBTRACTIVE_DITHER_2')
# Write integer data with GZIP
int_image = (image * 1000).astype(np.int16)
fitsio.write('int_compressed.fits', int_image, compress='gzip')
# Write to specific HDU
with fitsio.FITS('multi.fits', 'rw', clobber=True) as fits:
fits.write(image, extname='IMAGE1')
fits.write(image * 2, extname='IMAGE2', compress='plio')import fitsio
import numpy as np
# Create large image file
large_image = np.random.random((4096, 4096)).astype(np.float32)
fitsio.write('large.fits', large_image)
with fitsio.FITS('large.fits', 'rw') as fits:
hdu = fits[0]
# Read small regions without loading entire image
corner = hdu[0:100, 0:100]
center = hdu[2000:2100, 2000:2100]
# Write to subset of existing image
new_data = np.ones((50, 50), dtype=np.float32)
hdu.write(new_data, start=[1000, 1000])
# Modify image dimensions
hdu.reshape([2048, 8192]) # Reshape to different aspect ratioimport fitsio
import numpy as np
# Test different compression methods
image = np.random.random((1024, 1024)).astype(np.float32)
# Lossless compression
fitsio.write('lossless_rice.fits', image, compress='rice', qlevel=None)
fitsio.write('lossless_gzip.fits', image, compress='gzip', qlevel=None)
# Lossy compression with quality control
fitsio.write('lossy_rice.fits', image, compress='rice',
qlevel=4.0, qmethod='SUBTRACTIVE_DITHER_1')
# HCOMPRESS for smooth images
smooth = np.outer(np.linspace(0, 1, 1024), np.linspace(0, 1, 1024))
fitsio.write('hcompress.fits', smooth, compress='hcompress',
hcomp_scale=2.0)
# Check compression ratios
import os
original_size = os.path.getsize('uncompressed.fits')
compressed_size = os.path.getsize('lossless_rice.fits')
ratio = original_size / compressed_size
print(f"Compression ratio: {ratio:.2f}")import fitsio
import numpy as np
# Different data types
uint8_data = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
int16_data = np.random.randint(-32768, 32767, (100, 100), dtype=np.int16)
float32_data = np.random.random((100, 100)).astype(np.float32)
float64_data = np.random.random((100, 100)).astype(np.float64)
# Write each type
fitsio.write('uint8.fits', uint8_data)
fitsio.write('int16.fits', int16_data)
fitsio.write('float32.fits', float32_data)
fitsio.write('float64.fits', float64_data)
# Read back and check types
data = fitsio.read('uint8.fits')
print(f"Data type: {data.dtype}") # Should be uint8
# Handle scaling (BSCALE/BZERO)
with fitsio.FITS('scaled.fits') as fits:
# Check for scaling keywords
header = fits[0].read_header()
if 'BSCALE' in header:
print(f"BSCALE: {header['BSCALE']}")
print(f"BZERO: {header.get('BZERO', 0.0)}")
# Data is automatically scaled on read
scaled_data = fits[0].read()import fitsio
import numpy as np
# 3D data cube
cube = np.random.random((50, 256, 256)).astype(np.float32)
fitsio.write('cube.fits', cube)
with fitsio.FITS('cube.fits') as fits:
hdu = fits[0]
dims = hdu.get_dims()
print(f"Cube dimensions: {dims}") # (256, 256, 50) - FITS order
# Access slices
first_plane = hdu[:, :, 0] # First image plane
middle_plane = hdu[:, :, 25] # Middle plane
spectrum = hdu[128, 128, :] # Spectrum at center pixel
# 4D hypercube
hypercube = np.random.random((10, 20, 100, 100)).astype(np.float32)
fitsio.write('hypercube.fits', hypercube)import fitsio
with fitsio.FITS('image.fits') as fits:
hdu = fits[0]
# Get complete HDU information
info = hdu.get_info()
print(f"HDU info: {info}")
# Check basic properties
has_data = hdu.has_data()
is_compressed = hdu.is_compressed()
comp_type = hdu.get_comptype() if is_compressed else None
# Get file offsets
offsets = hdu.get_offsets()
header_start, data_start, data_end = offsets
print(f"Header starts at byte: {header_start}")
print(f"Data starts at byte: {data_start}")
print(f"Data ends at byte: {data_end}")Install with Tessl CLI
npx tessl i tessl/pypi-fitsio