def saveLiveJPG(self, array, startTime): """ Save a live.jpg file to the data directory with the latest compressed image. """ # Name of the file live_name = 'live.jpg' # Generate the name for the file timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(startTime)) maxpixel, _, _, _ = np.split(array, 4, axis=0) maxpixel = np.array(maxpixel[0]) # Draw text to image font = cv2.FONT_HERSHEY_SIMPLEX text = self.config.stationID + " " + timestamp + " UTC" cv2.putText(maxpixel, text, (10, maxpixel.shape[0] - 6), font, 0.4, (255, 255, 255), 1, \ cv2.LINE_AA) # Save the labelled image to disk try: # Save the file to disk saveImage(os.path.join(self.config.data_dir, live_name), maxpixel) except: log.error("Could not save {:s} to disk!".format(live_name))
def batchFFtoImage(dir_path, fmt): # Go through all files in the given folder for file_name in os.listdir(dir_path): # Check if the file is an FF file if validFFName(file_name): # Read the FF file ff = readFF(dir_path, file_name) # Skip the file if it could not be read if ff is None: continue # Make a filename for the image img_file_name = file_name.replace('fits', '') + fmt print('Saving: ', img_file_name) # Save the maxpixel to disk saveImage(os.path.join(dir_path, img_file_name), ff.maxpixel)
def stackFFs(dir_path, file_format, deinterlace=False, subavg=False, filter_bright=False, flat_path=None, file_list=None, mask=None): """ Stack FF files in the given folder. Arguments: dir_path: [str] Path to the directory with FF files. file_format: [str] Image format for the stack. E.g. jpg, png, bmp Keyword arguments: deinterlace: [bool] True if the image shoud be deinterlaced prior to stacking. False by default. subavg: [bool] Whether the average pixel image should be subtracted form the max pixel image. False by default. filter_bright: [bool] Whether images with bright backgrounds (after average subtraction) should be skipped. False by defualt. flat_path: [str] Path to the flat calibration file. None by default. Will only be used if subavg is False. file_list: [list] A list of file for stacking. False by default, in which case all FF files in the given directory will be used. mask: [MaskStructure] Mask to apply to the stack. None by default. Return: stack_path, merge_img: - stack_path: [str] Path of the save stack. - merge_img: [ndarray] Numpy array of the stacked image. """ # Load the flat if it was given flat = None if flat_path != '': # Try finding the default flat if flat_path is None: flat_path = dir_path flat_file = 'flat.bmp' else: flat_path, flat_file = os.path.split(flat_path) flat_full_path = os.path.join(flat_path, flat_file) if os.path.isfile(flat_full_path): # Load the flat flat = loadFlat(flat_path, flat_file) print('Loaded flat:', flat_full_path) first_img = True n_stacked = 0 total_ff_files = 0 merge_img = None # If the list of files was not given, take all files in the given folder if file_list is None: file_list = sorted(os.listdir(dir_path)) # List all FF files in the current dir for ff_name in file_list: if validFFName(ff_name): # Load FF file ff = readFF(dir_path, ff_name) # Skip the file if it is corruped if ff is None: continue total_ff_files += 1 maxpixel = ff.maxpixel avepixel = ff.avepixel # Dinterlace the images if deinterlace: maxpixel = deinterlaceBlend(maxpixel) avepixel = deinterlaceBlend(avepixel) # If the flat was given, apply it to the image, only if no subtraction is done if (flat is not None) and not subavg: maxpixel = applyFlat(maxpixel, flat) avepixel = applyFlat(avepixel, flat) # Reject the image if the median subtracted image is too bright. This usually means that there # are clouds on the image which can ruin the stack if filter_bright: img = maxpixel - avepixel # Compute surface brightness median = np.median(img) # Compute top detection pixels top_brightness = np.percentile(img, 99.9) # Reject all images where the median brightness is high # Preserve images with very bright detections if (median > 10) and (top_brightness < (2**(8 * img.itemsize) - 10)): print('Skipping: ', ff_name, 'median:', median, 'top brightness:', top_brightness) continue # Subtract the average from maxpixel if subavg: img = maxpixel - avepixel else: img = maxpixel if first_img: merge_img = np.copy(img) first_img = False continue print('Stacking: ', ff_name) # Blend images 'if lighter' merge_img = blendLighten(merge_img, img) n_stacked += 1 # If the number of stacked image is less than 20% of the given images, stack without filtering if filter_bright and (n_stacked < 0.2 * total_ff_files): return stackFFs(dir_path, file_format, deinterlace=deinterlace, subavg=subavg, filter_bright=False, flat_path=flat_path, file_list=file_list) # If no images were stacked, do nothing if n_stacked == 0: return None, None # Extract the name of the night directory which contains the FF files night_dir = os.path.basename(dir_path) stack_path = os.path.join( dir_path, night_dir + '_stack_{:d}_meteors.'.format(n_stacked) + file_format) print("Saving stack to:", stack_path) # Stretch the levels merge_img = adjustLevels(merge_img, np.percentile(merge_img, 0.5), 1.3, np.percentile(merge_img, 99.9)) # Apply the mask, if given if mask is not None: merge_img = MaskImage.applyMask(merge_img, mask) # Save the blended image saveImage(stack_path, merge_img) return stack_path, merge_img
def processNight(night_data_dir, config, detection_results=None, nodetect=False): """ Given the directory with FF files, run detection and archiving. Arguments: night_data_dir: [str] Path to the directory with FF files. config: [Config obj] Keyword arguments: detection_results: [list] An optional list of detection. If None (default), detection will be done on the the files in the folder. nodetect: [bool] True if detection should be skipped. False by default. Return: night_archive_dir: [str] Path to the night directory in ArchivedFiles. archive_name: [str] Path to the archive. detector: [QueuedPool instance] Handle to the detector. """ # Remove final slash in the night dir if night_data_dir.endswith(os.sep): night_data_dir = night_data_dir[:-1] # Extract the name of the night night_data_dir_name = os.path.basename(os.path.abspath(night_data_dir)) platepar = None # If the detection should be run if (not nodetect): # If no detection was performed, run it if detection_results is None: # Run detection on the given directory calstars_name, ftpdetectinfo_name, ff_detected, \ detector = detectStarsAndMeteorsDirectory(night_data_dir, config) # Otherwise, save detection results else: # Save CALSTARS and FTPdetectinfo to disk calstars_name, ftpdetectinfo_name, ff_detected = saveDetections(detection_results, \ night_data_dir, config) # If the files were previously detected, there is no detector detector = None # Get the platepar file platepar, platepar_path, platepar_fmt = getPlatepar(config, night_data_dir) # Run calibration check and auto astrometry refinement if (platepar is not None) and (calstars_name is not None): # Read in the CALSTARS file calstars_list = CALSTARS.readCALSTARS(night_data_dir, calstars_name) # Run astrometry check and refinement platepar, fit_status = autoCheckFit(config, platepar, calstars_list) # If the fit was sucessful, apply the astrometry to detected meteors if fit_status: log.info('Astrometric calibration SUCCESSFUL!') # Save the refined platepar to the night directory and as default platepar.write(os.path.join(night_data_dir, config.platepar_name), fmt=platepar_fmt) platepar.write(platepar_path, fmt=platepar_fmt) else: log.info('Astrometric calibration FAILED!, Using old platepar for calibration...') # # Calculate astrometry for meteor detections # applyAstrometryFTPdetectinfo(night_data_dir, ftpdetectinfo_name, platepar_path) # If a flat is used, disable vignetting correction if config.use_flat: platepar.vignetting_coeff = 0.0 log.info("Recalibrating astrometry on FF files with detections...") # Recalibrate astrometry on every FF file and apply the calibration to detections recalibrateIndividualFFsAndApplyAstrometry(night_data_dir, os.path.join(night_data_dir, \ ftpdetectinfo_name), calstars_list, config, platepar) log.info("Converting RMS format to UFOOrbit format...") # Convert the FTPdetectinfo into UFOOrbit input file FTPdetectinfo2UFOOrbitInput(night_data_dir, ftpdetectinfo_name, platepar_path) # Generate a calibration report log.info("Generating a calibration report...") try: generateCalibrationReport(config, night_data_dir, platepar=platepar) except Exception as e: log.debug('Generating calibration report failed with the message:\n' + repr(e)) log.debug(repr(traceback.format_exception(*sys.exc_info()))) # Perform single station shower association log.info("Performing single station shower association...") try: showerAssociation(config, [os.path.join(night_data_dir, ftpdetectinfo_name)], \ save_plot=True, plot_activity=True) except Exception as e: log.debug('Shower association failed with the message:\n' + repr(e)) log.debug(repr(traceback.format_exception(*sys.exc_info()))) else: ff_detected = [] detector = None log.info('Plotting field sums...') # Plot field sums try: plotFieldsums(night_data_dir, config) except Exception as e: log.debug('Plotting field sums failed with message:\n' + repr(e)) log.debug(repr(traceback.format_exception(*sys.exc_info()))) # Archive all fieldsums to one archive archiveFieldsums(night_data_dir) # List for any extra files which will be copied to the night archive directory. Full paths have to be # given extra_files = [] log.info('Making a flat...') # Make a new flat field image try: flat_img = makeFlat(night_data_dir, config) except Exception as e: log.debug('Making a flat failed with message:\n' + repr(e)) log.debug(repr(traceback.format_exception(*sys.exc_info()))) flat_img = None # If making flat was sucessfull, save it if flat_img is not None: # Save the flat in the night directory, to keep the operational flat updated flat_path = os.path.join(night_data_dir, os.path.basename(config.flat_file)) saveImage(flat_path, flat_img) log.info('Flat saved to: ' + flat_path) # Copy the flat to the night's directory as well extra_files.append(flat_path) else: log.info('Making flat image FAILED!') ### Add extra files to archive # Add the config file to the archive too extra_files.append(os.path.join(os.getcwd(), '.config')) # Add the mask if (not nodetect): if os.path.exists(config.mask_file): mask_path = os.path.abspath(config.mask_file) extra_files.append(mask_path) # Add the platepar to the archive if it exists if (not nodetect): if os.path.exists(platepar_path): extra_files.append(platepar_path) # Add the json file with recalibrated platepars to the archive if (not nodetect): recalibrated_platepars_path = os.path.join(night_data_dir, config.platepars_recalibrated_name) if os.path.exists(recalibrated_platepars_path): extra_files.append(recalibrated_platepars_path) ### ### # If the detection should be run if (not nodetect): # Make a CAL file and a special CAMS FTPdetectinfo if full CAMS compatibility is desired if (config.cams_code > 0) and (platepar is not None): log.info('Generating a CAMS FTPdetectinfo file...') # Write the CAL file to disk cal_file_name = writeCAL(night_data_dir, config, platepar) # Check if the CAL file was successfully generated if cal_file_name is not None: cams_code_formatted = "{:06d}".format(int(config.cams_code)) # Load the FTPdetectinfo _, fps, meteor_list = readFTPdetectinfo(night_data_dir, ftpdetectinfo_name, \ ret_input_format=True) # Replace the camera code with the CAMS code for met in meteor_list: # Replace the station name and the FF file format ff_name = met[0] ff_name = ff_name.replace('.fits', '.bin') ff_name = ff_name.replace(config.stationID, cams_code_formatted) met[0] = ff_name # Write the CAMS compatible FTPdetectinfo file writeFTPdetectinfo(meteor_list, night_data_dir, \ ftpdetectinfo_name.replace(config.stationID, cams_code_formatted),\ night_data_dir, cams_code_formatted, fps, calibration=cal_file_name, \ celestial_coords_given=(platepar is not None)) night_archive_dir = os.path.join(os.path.abspath(config.data_dir), config.archived_dir, night_data_dir_name) log.info('Archiving detections to ' + night_archive_dir) # Archive the detections archive_name = archiveDetections(night_data_dir, night_archive_dir, ff_detected, config, \ extra_files=extra_files) return night_archive_dir, archive_name, detector
def makeFRmosaic(dir_path, border=5): """ Make a mosaic out of an FR bin file. """ dir_path, file_name = os.path.split(dir_path) # Load the FR file fr = readFR(dir_path, file_name) # Go through all lines in the file for i in range(fr.lines): # Determine the maximum size of the window max_size = max([fr.size[i][z] for z in range(fr.frameNum[i])]) # Determine the width and the height in frame segments height = int(np.ceil(np.sqrt(fr.frameNum[i]))) width = int(np.ceil(fr.frameNum[i] / height)) # Compute the image width and height w_img = int(np.ceil(width * (border + max_size))) h_img = int(np.ceil(height * (border + max_size))) # Create an empty mosaic image mosaic_img = np.zeros((h_img, w_img), dtype=np.uint8) x_min = w_img x_max = 0 y_min = h_img y_max = 0 # Go through all frames for z in range(fr.frameNum[i]): # # Get the center position of the detection on the current frame # yc = fr.yc[i][z] # xc = fr.xc[i][z] # # Get the frame number # t = fr.t[i][z] # Get the size of the window size = fr.size[i][z] # Compute the position of the frame on the image (tiling) h_ind = int(z % height) v_ind = int(z / height) # Compute the position of the frame on the image in image coordinates frame_x = h_ind * max_size + border + (max_size - size) // 2 + 1 frame_y = v_ind * max_size + border + (max_size - size) // 2 + 1 # Get the frame size fr_y, fr_x = fr.frames[i][z].shape # Assign the frame to the mosaic mosaic_img[frame_y:(frame_y + fr_y), frame_x:(frame_x + fr_x)] = fr.frames[i][z] # Keep track of the min and max value of the extent of the frames x_min = min(x_min, frame_x) x_max = max(x_max, frame_x + fr_x) y_min = min(y_min, frame_y) y_max = max(y_max, frame_y + fr_y) # Draw a grid for h_ind in range(height + 1): # Draw horizontal lines mosaic_img[h_ind * max_size + border, :] = 255 for v_ind in range(width + 1): # Draw horizontal lines mosaic_img[:, v_ind * max_size + border] = 255 # Cut the image to the size of the frames mosaic_img = mosaic_img[y_min:y_max, x_min:x_max] # Save the image to disk img_file_name = ".".join(file_name.split('.')[:-1]) + '_mosaic.png' saveImage(os.path.join(dir_path, img_file_name), mosaic_img) # Plot the image plt.imshow(mosaic_img, cmap='gray', vmin=0, vmax=255) plt.show()
# Deinterlace the image if cml_args.deinterlace: img = deinterlaceBlend(img) if first_img: merge_img = np.copy(img) first_img = False continue # Blend images 'if lighter' merge_img = blendLighten(merge_img, img) stack_path = os.path.join(dir_path, 'stacked.' + cml_args.output_file_format[0]) print("Saving to:", stack_path) # Save the blended image saveImage(stack_path, merge_img) # Plot the blended image plt.imshow(merge_img, cmap='gray') plt.show()
help="""Use image files (bmp, png, jpg) for flat instead of FF files. Images of the file type with the higest frequency in the directory will be taken.""") # Parse the command line arguments cml_args = arg_parser.parse_args() ######################### dir_path = cml_args.dir_path[0] # Load the configuration file config = cr.parse(".config") # Make the flat ff_median = makeFlat(dir_path, config, nostars=cml_args.nostars, use_images=cml_args.images) if ff_median is not None: # Save the flat in the input directory input_dir_flat = os.path.join(dir_path, config.flat_file) saveImage(input_dir_flat, ff_median) print('Flat saved to:', input_dir_flat) import matplotlib.pyplot as plt plt.imshow(ff_median, cmap='gray', vmin=0, vmax=255) plt.show() else: print('Flat filed could not be made!')
help='File format of the image, e.g. jpg or png.') # Parse the command line arguments cml_args = arg_parser.parse_args() ######################### dir_path = cml_args.dir_path[0] # Go through all files in the given folder for file_name in os.listdir(dir_path): # Check if the file is an FF file if validFFName(file_name): # Read the FF file ff = readFF(dir_path, file_name) # Skip the file if it could not be read if ff is None: continue # Make a filename for the image img_file_name = file_name.replace('fits', '') + cml_args.file_format[0] print('Saving: ', img_file_name) # Save the maxpixel to disk saveImage(os.path.join(dir_path, img_file_name), ff.maxpixel)