Just a suggestion- to make it even easier to diagnose, try a simple script
only processes a fits file, without  QT.

That will be a far simpler build, and it may be more obvious what’s missing.

-CHB


Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

[email protected]


On Sun, Mar 16, 2025 at 3:56 PM skyjudith JAI <[email protected]> wrote:

> 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
> <https://groups.google.com/d/msgid/pyinstaller/4da01cd9-0d2f-47ef-96f3-05b87ee1989bn%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
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/CALGmxE%2Bs7_q-cwRTU7abB3HYR3V2YjArywUQj_NyK2yZztSSag%40mail.gmail.com.

Reply via email to