My complete code is very long but I've made a 'minimumtest.py' which shows
the same behaviour, i.e. works in python but fails after pyinstaller. The
minimum code is below (sorry I don't see where I can include it as an
attachment). I was aligning 9 fits images but each is 7.4 Mb. Is there
somewhere I can put the fits files?
Judith
import sys
import os
import numpy as np
from astropy.io import fits
import astroalign as aa
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton,
QFileDialog, QLabel
)
from PyQt5.QtCore import QTimer
import pyqtgraph as pg
import warnings
warnings.simplefilter("ignore", DeprecationWarning)
class FITSAlignmentApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("FITS Alignment Test")
self.setGeometry(100, 100, 800, 600)
# Main widget
central_widget = QWidget(self)
self.setCentralWidget(central_widget)
# Layout
layout = QVBoxLayout(central_widget)
# Load button
self.load_button = QPushButton("Load FITS Files", self)
self.load_button.clicked.connect(self.load_fits_files)
layout.addWidget(self.load_button)
# Status label
self.status_label = QLabel("Select FITS files to align", self)
layout.addWidget(self.status_label)
# PyQtGraph ImageView
self.image_view = pg.ImageView()
layout.addWidget(self.image_view)
# Timer for sequential loading
self.timer = QTimer()
self.timer.timeout.connect(self.show_next_image)
self.image_index = 0
self.images = []
self.aligned_images = []
self.transforms = []
def load_fits_files(self):
""" Open file dialog and load multiple FITS files with delay """
files, _ = QFileDialog.getOpenFileNames(
self, "Select FITS Files", os.getcwd(), # Start in the current
working directory
"FITS Files (*.fits *.fit *.fts *.FITS *.FIT *.FTS);;All Files
(*)"
)
if not files:
self.status_label.setText("No files selected.")
return
# Load images
self.images = []
for file in files:
try:
with fits.open(file) as hdul:
data = hdul[0].data
if data is not None:
self.images.append(data.astype(np.float64))
except Exception as e:
self.status_label.setText(f"Error loading {file}: {e}")
return
if len(self.images) < 2:
self.status_label.setText("Need at least 2 images to align.")
return
self.status_label.setText(f"Loaded {len(self.images)} images.
Aligning...")
# Use the first image as the reference
self.reference_image = self.images[0]
self.aligned_images = [self.reference_image] # First image is
already "aligned"
# Align each image to the reference
self.transforms = []
for i, img in enumerate(self.images[1:], start=1):
try:
# Compute the transformation parameters
transform, footprint = aa.find_transform(img,
self.reference_image)
dx, dy = transform.translation # Extract x, y shifts
rotation_rad = transform.rotation # Extract rotation in
radians
rotation_deg = np.degrees(rotation_rad) # Convert to
degrees
# Apply the transformation to align the image
print(f"Original Image {i} Shape: {img.shape}")
aligned_img, footprint = aa.apply_transform(transform, img,
self.reference_image)
# Ensure aligned_img is a NumPy array
aligned_img = np.array(aligned_img, dtype=np.float64)
print(f"Transformed Image {i} Shape: {aligned_img.shape}")
aligned_img = np.array(aligned_img, dtype=np.float64)
print(f"Image {i}: Δx = {dx:.2f}, Δy = {dy:.2f}, Δrot =
{rotation_deg:.2f}°")
# Ensure aligned image has the same shape as the reference
image
aligned_img = np.array(aligned_img, dtype=np.float64)
if aligned_img.shape != self.reference_image.shape:
print(f"Warning: Shape mismatch in Image {i}.
Resizing...")
min_shape = (
min(aligned_img.shape[0],
self.reference_image.shape[0]),
min(aligned_img.shape[1],
self.reference_image.shape[1]),
)
aligned_img = aligned_img[:min_shape[0], :min_shape[1]]
self.reference_image =
self.reference_image[:min_shape[0], :min_shape[1]]
self.aligned_images.append(aligned_img)
self.transforms.append(transform)
except Exception as e:
print(f"Error aligning image {i}: {e}")
self.aligned_images.append(img) # If alignment fails, keep
original
self.status_label.setText("Alignment complete. Displaying
images...")
self.image_index = 0
self.timer.start(1000) # 1-second delay between images
def extract_transform(self, transform):
""" Extract translation and rotation from transformation matrix. """
dx = transform[0, 2] # Translation in x
dy = transform[1, 2] # Translation in y
rotation_radians = np.arctan2(transform[1, 0], transform[0, 0])
rotation_degrees = np.degrees(rotation_radians) # Convert to
degrees
return dx, dy, rotation_degrees
def show_next_image(self):
""" Show aligned images one by one before displaying the median """
if self.image_index < len(self.aligned_images):
current_image = self.aligned_images[self.image_index]
focus_min, focus_max = self.compute_focus_range(current_image)
self.image_view.setImage(current_image, levels=(focus_min,
focus_max))
self.status_label.setText(f"Showing aligned image
{self.image_index + 1}/{len(self.aligned_images)}")
self.image_index += 1
else:
# Stop the timer and compute the median
self.timer.stop()
self.display_median_image()
def display_median_image(self):
""" Compute and display the median-combined image """
self.status_label.setText("Computing median image...")
median_image = np.nanmedian(np.stack(self.aligned_images), axis=0)
focus_min, focus_max = self.compute_focus_range(median_image)
self.image_view.setImage(median_image, levels=(focus_min,
focus_max))
self.status_label.setText("Showing median image.")
def compute_focus_range(self, data):
""" Compute focus_min and focus_max dynamically """
valid_data = data[data != 0] # Exclude exact zero values
if valid_data.size > 0:
median = np.nanmedian(valid_data)
std = np.nanstd(valid_data)
min_val = np.nanmin(valid_data)
max_val = np.nanmax(valid_data)
else:
# Fallback if all values are zero
median = np.nanmedian(data)
std = np.nanstd(data)
min_val = np.nanmin(data)
max_val = np.nanmax(data)
# Expand range slightly for better visualization
focus_min = max(median - std, min_val)
focus_max = min(median + std, max_val)
return focus_min, focus_max
if __name__ == "__main__":
app = QApplication(sys.argv)
window = FITSAlignmentApp()
window.show()
sys.exit(app.exec_())
On Sunday, March 16, 2025 at 2:52:33 PM UTC-4 bwoodsend wrote:
Presumably whatever submodule/package/data file is responsible for
supporting these FITS files is not being collected. I can't help beyond
that without either a minimal example + FITS file to play with or a some
clue as to what package/file is issuing that error message which will tell
us where to look for what exactly is missing.
--
You received this message because you are subscribed to the Google Groups
"PyInstaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/pyinstaller/4da01cd9-0d2f-47ef-96f3-05b87ee1989bn%40googlegroups.com.