If seems the first decider us not being included by installer-. I would ask the astropy folks where/how the decoders are discovered.
In the meantime, maybe try setting astropy.io or even astropy.io.fits as a hidden import. Though if you aren’t getting an import error, then the (python) module is there — but it seems to rely on a data file, or library or something that isn’t being included. -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 Tue, Mar 18, 2025 at 6:32 AM skyjudith JAI <[email protected]> wrote: > Here is the second fits file. > > On Mon, Mar 17, 2025 at 5:05 PM 'Chris Barker' via PyInstaller < > [email protected]> wrote: > >> 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 >> <https://www.google.com/maps/search/7600+Sand+Point+Way+NE?entry=gmail&source=g> >> (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 >> <https://groups.google.com/d/msgid/pyinstaller/CALGmxE%2Bs7_q-cwRTU7abB3HYR3V2YjArywUQj_NyK2yZztSSag%40mail.gmail.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/CABuiZshvX0h0O2M6yJBQx8o-Oh4fBVoiVWcvyY0M2iXaLHQDdg%40mail.gmail.com > <https://groups.google.com/d/msgid/pyinstaller/CABuiZshvX0h0O2M6yJBQx8o-Oh4fBVoiVWcvyY0M2iXaLHQDdg%40mail.gmail.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/CALGmxEJBi03C4Ck6VSfDgndG%3DWN-NtQR_xViDXUH0yqik5i%2BPQ%40mail.gmail.com.
