def detectMeteors(ff_directory, ff_name, config, flat_struct=None): """ Detect meteors on the given FF bin image. Here are the steps in the detection: - input image (FF bin format file) is thresholded (converted to black and white) - several morphological operations are applied to clean the image - image is then broken into several image "windows" (these "windows" are reconstructed from the input FF file, given an input frame range (e.g. 64-128) which helps reduce the noise further) - on each "window" the Kernel-based Hough transform is performed to find any lines on the image - similar lines are joined - stripe around the lines is extracted - 3D line finding (third dimension is time) is applied to check if the line propagates in time - centroiding is performed, which calculates the position and intensity of meteor on each frame Arguments: ff_directory: [string] an absolute path to the input FF bin file ff_name: [string] file name of the FF bin file on which to run the detection on config: [config object] configuration object (loaded from the .config file) Keyword arguments: flat_struct: [Flat struct] Structure containing the flat field. None by default. Return: meteor_detections: [list] a list of detected meteors, with these elements: - rho: [float] meteor line distance from image center (polar coordinates, in pixels) - theta: [float] meteor line angle from image center (polar coordinates, in degrees) - centroids: [list] [frame, X, Y, level] list of meteor points """ t1 = time() t_all = time() # Load the FF bin file ff = FFfile.read(ff_directory, ff_name) # Load the mask file mask = MaskImage.loadMask(config.mask_file) # Mask the FF file ff = MaskImage.applyMask(ff, mask, ff_flag=True) # Apply the flat to maxpixel and avepixel if flat_struct is not None: ff.maxpixel = Image.applyFlat(ff.maxpixel, flat_struct) ff.avepixel = Image.applyFlat(ff.avepixel, flat_struct) # At the end, a check that the detection has a surface brightness above the background will be performed. # The assumption here is that the peak of the meteor should have the intensity which is at least # that of a patch of 4x4 pixels that are of the mean background brightness min_patch_intensity = 4 * 4 * (np.mean(ff.maxpixel - ff.avepixel) + config.k1_det * np.mean(ff.stdpixel) + config.j1) # # Show the maxpixel image # show2(ff_name+' maxpixel', ff.maxpixel) # Get lines on the image line_list = getLines(ff, config.k1_det, config.j1_det, config.time_slide, config.time_window_size, config.max_lines_det, config.max_white_ratio, config.kht_lib_path) logDebug('List of lines:', line_list) # Init meteor list meteor_detections = [] # Only if there are some lines in the image if len(line_list): # Join similar lines line_list = mergeLines(line_list, config.line_min_dist, ff.ncols, ff.nrows) logDebug('Time for finding lines:', time() - t1) logDebug('Number of KHT lines: ', len(line_list)) logDebug(line_list) # Plot lines # plotLines(ff, line_list) # Threshold the image img_thres = thresholdImg(ff, config.k1_det, config.j1_det) filtered_lines = [] # Analyze stripes of each line for line in line_list: rho, theta, frame_min, frame_max = line logDebug('rho, theta, frame_min, frame_max') logDebug(rho, theta, frame_min, frame_max) # Bounded the thresholded image by min and max frames img = selectFrames(np.copy(img_thres), ff, frame_min, frame_max) # Remove lonely pixels img = morph.clean(img) # Get indices of stripe pixels around the line stripe_indices = getStripeIndices(rho, theta, config.stripe_width, img.shape[0], img.shape[1]) # Extract the stripe from the thresholded image stripe = np.zeros((ff.nrows, ff.ncols), np.uint8) stripe[stripe_indices] = img[stripe_indices] # Show stripe #COMMENTED # show2("stripe", stripe*255) # Show 3D could # show3DCloud(ff, stripe) # Get stripe positions stripe_positions = stripe.nonzero() xs = stripe_positions[1] ys = stripe_positions[0] zs = ff.maxframe[stripe_positions] # Limit the number of points to search if too large if len(zs) > config.max_points_det: # Extract weights of each point maxpix_elements = ff.maxpixel[ys, xs].astype(np.float64) weights = maxpix_elements / np.sum(maxpix_elements) # Random sample the point, sampling is weighted by pixel intensity indices = np.random.choice(len(zs), config.max_points_det, replace=False, p=weights) ys = ys[indices] xs = xs[indices] zs = zs[indices] # Make an array to feed into the gropuing algorithm stripe_points = np.vstack((xs, ys, zs)) stripe_points = np.swapaxes(stripe_points, 0, 1) # Sort stripe points by frame stripe_points = stripe_points[stripe_points[:, 2].argsort()] t1 = time() logDebug('finding lines...') # Find a single line in the point cloud detected_line = find3DLines(stripe_points, time(), config, fireball_detection=False) logDebug('time for GROUPING: ', time() - t1) # Extract the first and only line if any if detected_line: detected_line = detected_line[0] # logDebug(detected_line) # Show 3D cloud # show3DCloud(ff, stripe, detected_line, stripe_points, config) # Add the line to the results list filtered_lines.append(detected_line) # Merge similar lines in 3D filtered_lines = merge3DLines(filtered_lines, config.vect_angle_thresh) logDebug('after filtering:') logDebug(filtered_lines) for detected_line in filtered_lines: # Get frame range frame_min = detected_line[4] frame_max = detected_line[5] # Check if the line covers a minimum frame range if (abs(frame_max - frame_min) + 1 < config.line_minimum_frame_range_det): continue # Extand the frame range for several frames, just to be sure to catch all parts of a meteor frame_min -= config.frame_extension frame_max += config.frame_extension # Cap values to 0-255 frame_min = max(frame_min, 0) frame_max = min(frame_max, 255) logDebug(detected_line) # Get coordinates of 2 points that describe the line x1, y1, z1 = detected_line[0] x2, y2, z2 = detected_line[1] # Convert Cartesian line coordinates to polar rho, theta = getPolarLine(x1, y1, x2, y2, ff.nrows, ff.ncols) # Convert Cartesian line coordinate to CAMS compatible polar coordinates (flipped Y axis) rho_cams, theta_cams = getPolarLine(x1, ff.nrows - y1, x2, ff.nrows - y2, ff.nrows, ff.ncols) logDebug('converted rho, theta') logDebug(rho, theta) # Bounded the thresholded image by min and max frames img = selectFrames(np.copy(img_thres), ff, frame_min, frame_max) # Remove lonely pixels img = morph.clean(img) # Get indices of stripe pixels around the line stripe_indices = getStripeIndices(rho, theta, int(config.stripe_width * 1.5), img.shape[0], img.shape[1]) # Extract the stripe from the thresholded image stripe = np.zeros((ff.nrows, ff.ncols), np.uint8) stripe[stripe_indices] = img[stripe_indices] # Show detected line # show('detected line: '+str(frame_min)+'-'+str(frame_max), stripe) # Get stripe positions stripe_positions = stripe.nonzero() xs = stripe_positions[1] ys = stripe_positions[0] zs = ff.maxframe[stripe_positions] # Make an array to feed into the centroiding algorithm stripe_points = np.vstack((xs, ys, zs)) stripe_points = np.swapaxes(stripe_points, 0, 1) # Sort stripe points by frame stripe_points = stripe_points[stripe_points[:, 2].argsort()] # Show 3D cloud # show3DCloud(ff, stripe, detected_line, stripe_points, config) # Get points of the given line line_points = getAllPoints(stripe_points, x1, y1, z1, x2, y2, z2, config, fireball_detection=False) # Skip if no points were returned if not line_points.any(): continue # Skip if the points cover too small a frame range if abs(np.max(line_points[:, 2]) - np.min(line_points[:, 2]) ) + 1 < config.line_minimum_frame_range_det: continue # Calculate centroids centroids = [] for i in range(frame_min, frame_max + 1): # Select pixel indicies belonging to a given frame frame_pixels_inds = np.where(line_points[:, 2] == i) # Get pixel positions in a given frame (pixels belonging to a found line) frame_pixels = line_points[frame_pixels_inds].astype(np.int64) # Get pixel positions in a given frame (pixels belonging to the whole stripe) frame_pixels_stripe = stripe_points[np.where( stripe_points[:, 2] == i)].astype(np.int64) # Skip if there are no pixels in the frame if not len(frame_pixels): continue # Calculate weights for centroiding max_avg_corrected = ff.maxpixel - ff.avepixel flattened_weights = (max_avg_corrected).astype( np.float32) / ff.stdpixel # Calculate centroids by half-frame for half_frame in range(2): # Apply deinterlacing if it is present in the video if config.deinterlace_order >= 0: # Deinterlace by fields (line lixels) half_frame_pixels = frame_pixels[ frame_pixels[:, 1] % 2 == (config.deinterlace_order + half_frame) % 2] # Deinterlace by fields (stripe pixels) half_frame_pixels_stripe = frame_pixels_stripe[ frame_pixels_stripe[:, 1] % 2 == (config.deinterlace_order + half_frame) % 2] # Skip if there are no pixels in the half-frame if not len(half_frame_pixels): continue # Calculate half-frame value frame_no = i + half_frame * 0.5 # No deinterlacing else: # Skip the second half frame if half_frame == 1: continue half_frame_pixels = frame_pixels half_frame_pixels_stripe = frame_pixels_stripe frame_no = i # Get maxpixel-avepixel values of given pixel indices (this will be used as weights) max_weights = flattened_weights[half_frame_pixels[:, 1], half_frame_pixels[:, 0]] # Calculate weighted centroids x_weighted = half_frame_pixels[:, 0] * np.transpose( max_weights) x_centroid = np.sum(x_weighted) / float( np.sum(max_weights)) y_weighted = half_frame_pixels[:, 1] * np.transpose( max_weights) y_centroid = np.sum(y_weighted) / float( np.sum(max_weights)) # Calculate intensity as the sum of threshold passer pixels on the stripe #intensity_values = max_avg_corrected[half_frame_pixels[:,1], half_frame_pixels[:,0]] intensity_values = max_avg_corrected[ half_frame_pixels_stripe[:, 1], half_frame_pixels_stripe[:, 0]] intensity = np.sum(intensity_values) logDebug("centroid: ", frame_no, x_centroid, y_centroid, intensity) centroids.append( [frame_no, x_centroid, y_centroid, intensity]) # Filter centroids centroids = filterCentroids(centroids, config.centroids_max_deviation, config.centroids_max_distance) # Convert to numpy array for easy slicing centroids = np.array(centroids) # Reject the solution if there are too few centroids if len(centroids) < config.line_minimum_frame_range_det: continue # Check that the detection has a surface brightness above the background # The assumption here is that the peak of the meteor should have the intensity which is at least # that of a patch of 4x4 pixels that are of the mean background brightness if np.max(centroids[:, 3]) < min_patch_intensity: continue # Check the detection if it has the proper angular velocity if not checkAngularVelocity(centroids, config): continue # Append the result to the meteor detections meteor_detections.append([rho_cams, theta_cams, centroids]) logDebug('time for processing:', time() - t_all) # # Plot centroids to image # fig, (ax1, ax2) = plt.subplots(nrows=2) # ax1.imshow(ff.maxpixel - ff.avepixel, cmap='gray') # ax1.scatter(centroids[:,1], centroids[:,2], s=5, c='r', edgecolors='none') # # Plot lightcurve # ax2.plot(centroids[:,0], centroids[:,3]) # # # Plot relative angular velocity # # ang_vels = [] # # fr_prev, x_prev, y_prev, _ = centroids[0] # # for fr, x, y, _ in centroids[1:]: # # dx = x - x_prev # # dy = y - y_prev # # dfr = fr - fr_prev # # ddist = np.sqrt(dx**2 + dy**2) # # dt = dfr/config.fps # # ang_vels.append(ddist/dt) # # x_prev = x # # y_prev = y # # fr_prev = fr # # ax2.plot(ang_vels) # plt.show() return meteor_detections
def generateThumbnails(dir_path, config, mosaic_type, file_list=None): """ Generates a mosaic of thumbnails from all FF files in the given folder and saves it as a JPG image. Arguments: dir_path: [str] Path of the night directory. config: [Conf object] Configuration. mosaic_type: [str] Type of the mosaic (e.g. "Captured" or "Detected") Keyword arguments: file_list: [list] A list of file names (without full path) which will be searched for FF files. This is used when generating separate thumbnails for captured and detected files. Return: file_name: [str] Name of the thumbnail file. """ if file_list is None: file_list = sorted(os.listdir(dir_path)) # Make a list of all FF files in the night directory ff_list = [] for file_name in file_list: if FFfile.validFFName(file_name): ff_list.append(file_name) # Calculate the dimensions of the binned image bin_w = int(config.width/config.thumb_bin) bin_h = int(config.height/config.thumb_bin) ### RESIZE AND STACK THUMBNAILS ### ########################################################################################################## timestamps = [] stacked_imgs = [] for i in range(0, len(ff_list), config.thumb_stack): img_stack = np.zeros((bin_h, bin_w)) # Stack thumb_stack images using the 'if lighter' method for j in range(config.thumb_stack): if (i + j) < len(ff_list): # Read maxpixel image img = FFfile.read(dir_path, ff_list[i + j]).maxpixel # Resize the image img = cv2.resize(img, (bin_w, bin_h)) # Stack the image img_stack = stackIfLighter(img_stack, img) else: break # Save the timestamp of the first image in the stack timestamps.append(FFfile.filenameToDatetime(ff_list[i])) # Save the stacked image stacked_imgs.append(img_stack) # cv2.imshow('test', img_stack) # cv2.waitKey(0) # cv2.destroyAllWindows() ########################################################################################################## ### ADD THUMBS TO ONE MOSAIC IMAGE ### ########################################################################################################## header_height = 20 timestamp_height = 10 # Calculate the number of rows for the thumbnail image n_rows = int(np.ceil(float(len(ff_list))/config.thumb_stack/config.thumb_n_width)) # Calculate the size of the mosaic mosaic_w = int(config.thumb_n_width*bin_w) mosaic_h = int((bin_h + timestamp_height)*n_rows + header_height) mosaic_img = np.zeros((mosaic_h, mosaic_w), dtype=np.uint8) # Write header text header_text = 'Station: ' + str(config.stationID) + ' Night: ' + os.path.basename(dir_path) \ + ' Type: ' + mosaic_type cv2.putText(mosaic_img, header_text, (0, header_height//2), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,255,255), 1) for row in range(n_rows): for col in range(config.thumb_n_width): # Calculate image index indx = row*config.thumb_n_width + col if indx < len(stacked_imgs): # Calculate position of the text text_x = col*bin_w text_y = row*bin_h + (row + 1)*timestamp_height - 1 + header_height # Add timestamp text cv2.putText(mosaic_img, timestamps[indx].strftime('%H:%M:%S'), (text_x, text_y), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) # Add the image to the mosaic img_pos_x = col*bin_w img_pos_y = row*bin_h + (row + 1)*timestamp_height + header_height mosaic_img[img_pos_y : img_pos_y + bin_h, img_pos_x : img_pos_x + bin_w] = stacked_imgs[indx] else: break ########################################################################################################## thumb_name = "{:s}_{:s}_{:s}_thumbs.jpg".format(str(config.stationID), os.path.basename(dir_path), \ mosaic_type) # Save the mosaic cv2.imwrite(os.path.join(dir_path, thumb_name), mosaic_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) return thumb_name
def view(dir_path, ff_path, fr_path, config, save_frames=False): """ Shows the detected fireball stored in the FR file. Arguments: dir_path: [str] Current directory. ff: [str] path to the FF bin file fr: [str] path to the FR bin file config: [conf object] configuration structure """ name = fr_path fr = FRbin.read(dir_path, fr_path) if ff_path is None: #background = np.zeros((config.height, config.width), np.uint8) # Get the maximum extent of the meteor frames y_size = max( max(np.array(fr.yc[0]) + np.array(fr.size[0]) // 2) for i in range(fr.lines)) x_size = max( max(np.array(fr.xc[0]) + np.array(fr.size[0]) // 2) for i in range(fr.lines)) # Make the image square img_size = max(y_size, x_size) background = np.zeros((img_size, img_size), np.uint8) else: background = FFfile.read(dir_path, ff_path).maxpixel print("Number of lines:", fr.lines) first_image = True for current_line in range(fr.lines): print('Frame, Y , X , size') for z in range(fr.frameNum[current_line]): # Get the center position of the detection on the current frame yc = fr.yc[current_line][z] xc = fr.xc[current_line][z] # Get the frame number t = fr.t[current_line][z] # Get the size of the window size = fr.size[current_line][z] print(" {:3d}, {:3d}, {:3d}, {:d}".format(t, yc, xc, size)) img = np.copy(background) # Paste the frames onto the big image y_img = np.arange(yc - size // 2, yc + size // 2) x_img = np.arange(xc - size // 2, xc + size // 2) Y_img, X_img = np.meshgrid(y_img, x_img) y_frame = np.arange(len(y_img)) x_frame = np.arange(len(x_img)) Y_frame, X_frame = np.meshgrid(y_frame, x_frame) img[Y_img, X_img] = fr.frames[current_line][z][Y_frame, X_frame] # Save frame to disk if save_frames: frame_file_name = fr_path.replace( '.bin', '') + "_frame_{:03d}.png".format(t) cv2.imwrite(os.path.join(dir_path, frame_file_name), img) # Show the frame cv2.imshow(name, img) # If this is the first image, move it to the upper left corner if first_image: cv2.moveWindow(name, 0, 0) first_image = False cv2.waitKey(2 * int(1000.0 / config.fps)) cv2.destroyWindow(name)
# Add star info to the star list star_list.append([ff_name, star_data]) # Print found stars print(' ROW COL amplitude intensity') for x, y, max_ampl, level in star_data: print(' {:06.2f} {:06.2f} {:6d} {:6d}'.format( round(y, 2), round(x, 2), int(max_ampl), int(level))) # # Show stars if there are only more then 10 of them # if len(x2) < 20: # continue # # Load the FF bin file # ff = FFfile.read(ff_dir, ff_name) # plotStars(ff, x2, y2) # Load data about the image ff = FFfile.read(ff_dir, ff_name) # Generate the name for the CALSTARS file calstars_name = 'CALSTARS_' + "{:s}".format(str(config.stationID)) + '_' \ + os.path.basename(ff_dir) + '.txt' # Write detected stars to the CALSTARS file CALSTARS.writeCALSTARS(star_list, ff_dir, calstars_name, ff.camno, ff.nrows, ff.ncols) print('Total time taken: ', time.clock() - time_start)
def extractStars(ff_dir, ff_name, config=None, max_global_intensity=150, border=10, neighborhood_size=10, intensity_threshold=5, flat_struct=None): """ Extracts stars on a given FF bin by searching for local maxima and applying PSF fit for star confirmation. Source of one part of the code: http://stackoverflow.com/questions/9111711/get-coordinates-of-local-maxima-in-2d-array-above-certain-value Arguments: ff: [ff bin struct] FF bin file loaded in the FF bin structure config: [config object] configuration object (loaded from the .config file) max_global_intensity: [int] maximum mean intensity of an image before it is discared as too bright border: [int] apply a mask on the detections by removing all that are too close to the given image border (in pixels) neighborhood_size: [int] size of the neighbourhood for the maximum search (in pixels) intensity_threshold: [float] a threshold for cutting the detections which are too faint (0-255) flat_struct: [Flat struct] Structure containing the flat field. None by default. Return: x2, y2, background, intensity: [list of ndarrays] - x2: X axis coordinates of the star - y2: Y axis coordinates of the star - background: background intensity - intensity: intensity of the star """ # Load parameters from config if given if config: max_global_intensity = config.max_global_intensity border = config.border neighborhood_size = config.neighborhood_size intensity_threshold = config.intensity_threshold # Load the FF bin file ff = FFfile.read(ff_dir, ff_name) # Load the mask file mask = MaskImage.loadMask(config.mask_file) # Mask the FF file ff = MaskImage.applyMask(ff, mask, ff_flag=True) # Apply the flat to maxpixel and avepixel if flat_struct is not None: ff.maxpixel = Image.applyFlat(ff.maxpixel, flat_struct) ff.avepixel = Image.applyFlat(ff.avepixel, flat_struct) # Calculate image mean and stddev global_mean = np.mean(ff.avepixel) # Check if the image is too bright and skip the image if global_mean > max_global_intensity: return [[], [], [], []] data = ff.avepixel.astype(np.float32) # Apply a mean filter to the image to reduce noise data = ndimage.filters.convolve(data, weights=np.full((2, 2), 1.0 / 4)) # Locate local maxima on the image data_max = filters.maximum_filter(data, neighborhood_size) maxima = (data == data_max) data_min = filters.minimum_filter(data, neighborhood_size) diff = ((data_max - data_min) > intensity_threshold) maxima[diff == 0] = 0 # Apply a border mask border_mask = np.ones_like(maxima) * 255 border_mask[:border, :] = 0 border_mask[-border:, :] = 0 border_mask[:, :border] = 0 border_mask[:, -border:] = 0 maxima = MaskImage.applyMask(maxima, (True, border_mask)) # Find and label the maxima labeled, num_objects = ndimage.label(maxima) # Skip the image if there are too many maxima to process if num_objects > config.max_stars: print('Too many candidate stars to process! {:d}/{:d}'.format( num_objects, config.max_stars)) return [[], [], [], []] # Find centres of mass of each labeled objects xy = np.array( ndimage.center_of_mass(data, labeled, range(1, num_objects + 1))) # Remove all detection on the border #xy = xy[np.where((xy[:, 1] > border) & (xy[:,1] < ff.ncols - border) & (xy[:,0] > border) & (xy[:,0] < ff.nrows - border))] # Unpack star coordinates y, x = np.hsplit(xy, 2) # # Plot stars before the PSF fit # plotStars(ff, x, y) # Fit a PSF to each star x2, y2, amplitude, intensity = fitPSF(ff, global_mean, x, y, config=config) # x2, y2, amplitude, intensity = list(x), list(y), [], [] # Skip PSF fit # # Plot stars after PSF fit filtering # plotStars(ff, x2, y2) return x2, y2, amplitude, intensity
# Adjust levels img_data = adjustLevels(img_data, 100, 1.2, 240) plt.imshow(img_data, cmap='gray') plt.show() #### Apply the flat # Load an FF file dir_path = "/home/dvida/Dropbox/Apps/Elginfield RPi RMS data/ArchivedFiles/CA0001_20171018_230520_894458_detected" file_name = "FF_CA0001_20171019_092744_161_1118976.fits" ff = FFfile.read(dir_path, file_name) # Load the flat flat_struct = loadFlat(os.getcwd(), config.flat_file) t1 = time.clock() # Apply the flat img = applyFlat(ff.maxpixel, flat_struct) print('Flat time:', time.clock() - t1) plt.imshow(img, cmap='gray', vmin=0, vmax=255) plt.show()
def extractStars(ff_dir, ff_name, config=None, max_global_intensity=150, border=10, neighborhood_size=10, intensity_threshold=5, flat_struct=None, dark=None, mask=None): """ Extracts stars on a given FF bin by searching for local maxima and applying PSF fit for star confirmation. Source of one part of the code: http://stackoverflow.com/questions/9111711/get-coordinates-of-local-maxima-in-2d-array-above-certain-value Arguments: ff: [ff bin struct] FF bin file loaded in the FF bin structure config: [config object] configuration object (loaded from the .config file) max_global_intensity: [int] maximum mean intensity of an image before it is discared as too bright border: [int] apply a mask on the detections by removing all that are too close to the given image border (in pixels) neighborhood_size: [int] size of the neighbourhood for the maximum search (in pixels) intensity_threshold: [float] a threshold for cutting the detections which are too faint (0-255) flat_struct: [Flat struct] Structure containing the flat field. None by default. dark: [ndarray] Dark frame. None by default. mask: [ndarray] Mask image. None by default. Return: x2, y2, background, intensity, sigma_fitted: [list of ndarrays] - x2: X axis coordinates of the star - y2: Y axis coordinates of the star - background: background intensity - intensity: intensity of the star - Gaussian stddev of fitted stars """ # This will be returned if there was an error error_return = [[], [], [], []] # Load parameters from config if given if config: max_global_intensity = config.max_global_intensity border = config.border neighborhood_size = config.neighborhood_size intensity_threshold = config.intensity_threshold # Load the FF bin file ff = FFfile.read(ff_dir, ff_name) # If the FF file could not be read, skip star extraction if ff is None: return error_return # Apply the dark frame if dark is not None: ff.avepixel = Image.applyDark(ff.avepixel, dark) # Apply the flat if flat_struct is not None: ff.avepixel = Image.applyFlat(ff.avepixel, flat_struct) # Mask the FF file if mask is not None: ff = MaskImage.applyMask(ff, mask, ff_flag=True) # Calculate image mean and stddev global_mean = np.mean(ff.avepixel) # Check if the image is too bright and skip the image if global_mean > max_global_intensity: return error_return data = ff.avepixel.astype(np.float32) # Apply a mean filter to the image to reduce noise data = ndimage.filters.convolve(data, weights=np.full((2, 2), 1.0/4)) # Locate local maxima on the image data_max = filters.maximum_filter(data, neighborhood_size) maxima = (data == data_max) data_min = filters.minimum_filter(data, neighborhood_size) diff = ((data_max - data_min) > intensity_threshold) maxima[diff == 0] = 0 # Apply a border mask border_mask = np.ones_like(maxima)*255 border_mask[:border,:] = 0 border_mask[-border:,:] = 0 border_mask[:,:border] = 0 border_mask[:,-border:] = 0 maxima = MaskImage.applyMask(maxima, border_mask, image=True) # Find and label the maxima labeled, num_objects = ndimage.label(maxima) # Skip the image if there are too many maxima to process if num_objects > config.max_stars: print('Too many candidate stars to process! {:d}/{:d}'.format(num_objects, config.max_stars)) return error_return # Find centres of mass of each labeled objects xy = np.array(ndimage.center_of_mass(data, labeled, range(1, num_objects+1))) # Remove all detection on the border #xy = xy[np.where((xy[:, 1] > border) & (xy[:,1] < ff.ncols - border) & (xy[:,0] > border) & (xy[:,0] < ff.nrows - border))] # Unpack star coordinates y, x = np.hsplit(xy, 2) # # Plot stars before the PSF fit # plotStars(ff, x, y) # Fit a PSF to each star x2, y2, amplitude, intensity, sigma_y_fitted, sigma_x_fitted = fitPSF(ff, global_mean, x, y, config) # x2, y2, amplitude, intensity = list(x), list(y), [], [] # Skip PSF fit # # Plot stars after PSF fit filtering # plotStars(ff, x2, y2) # Compute one dimensional sigma sigma_x_fitted = np.array(sigma_x_fitted) sigma_y_fitted = np.array(sigma_y_fitted) sigma_fitted = np.sqrt(sigma_x_fitted**2 + sigma_y_fitted**2) return ff_name, x2, y2, amplitude, intensity, sigma_fitted
plot(points, ff.nrows//config.f, ff.ncols//config.f) def plot(points, y_dim, x_dim): fig = plt.figure() ax = fig.add_subplot(111, projection='3d') plt.title(name) y = points[:,0] x = points[:,1] z = points[:,2] # Plot points in 3D ax.scatter(x, y, z) # Set axes limits ax.set_zlim(0, 255) plt.xlim([0, x_dim]) plt.ylim([0, y_dim]) ax.set_ylabel("Y") ax.set_xlabel("X") ax.set_zlabel("Time") plt.show() if __name__ == "__main__": ff = FFfile.read(sys.argv[1], sys.argv[2], array=True) view(ff)
def generateThumbnails(dir_path, config, mosaic_type, file_list=None): """ Generates a mosaic of thumbnails from all FF files in the given folder and saves it as a JPG image. Arguments: dir_path: [str] Path of the night directory. config: [Conf object] Configuration. mosaic_type: [str] Type of the mosaic (e.g. "Captured" or "Detected") Keyword arguments: file_list: [list] A list of file names (without full path) which will be searched for FF files. This is used when generating separate thumbnails for captured and detected files. Return: file_name: [str] Name of the thumbnail file. """ if file_list is None: file_list = sorted(os.listdir(dir_path)) # Make a list of all FF files in the night directory ff_list = [] for file_name in file_list: if FFfile.validFFName(file_name): ff_list.append(file_name) # Calculate the dimensions of the binned image bin_w = int(config.width/config.thumb_bin) bin_h = int(config.height/config.thumb_bin) ### RESIZE AND STACK THUMBNAILS ### ########################################################################################################## timestamps = [] stacked_imgs = [] for i in range(0, len(ff_list), config.thumb_stack): img_stack = np.zeros((bin_h, bin_w)) # Stack thumb_stack images using the 'if lighter' method for j in range(config.thumb_stack): if (i + j) < len(ff_list): tmp_file_name = ff_list[i + j] # Read the FF file ff = FFfile.read(dir_path, tmp_file_name) # Skip the FF if it is corruped if ff is None: continue img = ff.maxpixel # Resize the image img = cv2.resize(img, (bin_w, bin_h)) # Stack the image img_stack = stackIfLighter(img_stack, img) else: break # Save the timestamp of the first image in the stack timestamps.append(FFfile.filenameToDatetime(ff_list[i])) # Save the stacked image stacked_imgs.append(img_stack) # cv2.imshow('test', img_stack) # cv2.waitKey(0) # cv2.destroyAllWindows() ########################################################################################################## ### ADD THUMBS TO ONE MOSAIC IMAGE ### ########################################################################################################## header_height = 20 timestamp_height = 10 # Calculate the number of rows for the thumbnail image n_rows = int(np.ceil(float(len(ff_list))/config.thumb_stack/config.thumb_n_width)) # Calculate the size of the mosaic mosaic_w = int(config.thumb_n_width*bin_w) mosaic_h = int((bin_h + timestamp_height)*n_rows + header_height) mosaic_img = np.zeros((mosaic_h, mosaic_w), dtype=np.uint8) # Write header text header_text = 'Station: ' + str(config.stationID) + ' Night: ' + os.path.basename(dir_path) \ + ' Type: ' + mosaic_type cv2.putText(mosaic_img, header_text, (0, header_height//2), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,255,255), 1) for row in range(n_rows): for col in range(config.thumb_n_width): # Calculate image index indx = row*config.thumb_n_width + col if indx < len(stacked_imgs): # Calculate position of the text text_x = col*bin_w text_y = row*bin_h + (row + 1)*timestamp_height - 1 + header_height # Add timestamp text cv2.putText(mosaic_img, timestamps[indx].strftime('%H:%M:%S'), (text_x, text_y), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) # Add the image to the mosaic img_pos_x = col*bin_w img_pos_y = row*bin_h + (row + 1)*timestamp_height + header_height mosaic_img[img_pos_y : img_pos_y + bin_h, img_pos_x : img_pos_x + bin_w] = stacked_imgs[indx] else: break ########################################################################################################## thumb_name = "{:s}_{:s}_{:s}_thumbs.jpg".format(str(config.stationID), os.path.basename(dir_path), \ mosaic_type) # Save the mosaic cv2.imwrite(os.path.join(dir_path, thumb_name), mosaic_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) return thumb_name
def view(dir_path, ff_path, fr_path, config, save_frames=False): """ Shows the detected fireball stored in the FR file. Arguments: dir_path: [str] Current directory. ff: [str] path to the FF bin file fr: [str] path to the FR bin file config: [conf object] configuration structure """ name = fr_path fr = FRbin.read(dir_path, fr_path) print('------------------------') print('Showing file:', fr_path) if ff_path is None: #background = np.zeros((config.height, config.width), np.uint8) # Get the maximum extent of meteor frames y_size = max([max(np.array(fr.yc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)]) x_size = max([max(np.array(fr.xc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)]) # Make the image square img_size = max(y_size, x_size) background = np.zeros((img_size, img_size), np.uint8) else: background = FFfile.read(dir_path, ff_path).maxpixel print("Number of lines:", fr.lines) first_image = True wait_time = 2*int(1000.0/config.fps) pause_flag = False for current_line in range(fr.lines): print('Frame, Y , X , size') for z in range(fr.frameNum[current_line]): # Get the center position of the detection on the current frame yc = fr.yc[current_line][z] xc = fr.xc[current_line][z] # Get the frame number t = fr.t[current_line][z] # Get the size of the window size = fr.size[current_line][z] print(" {:3d}, {:3d}, {:3d}, {:d}".format(t, yc, xc, size)) img = np.copy(background) # Paste the frames onto the big image y_img = np.arange(yc - size//2, yc + size//2) x_img = np.arange(xc - size//2, xc + size//2) Y_img, X_img = np.meshgrid(y_img, x_img) y_frame = np.arange(len(y_img)) x_frame = np.arange(len(x_img)) Y_frame, X_frame = np.meshgrid(y_frame, x_frame) img[Y_img, X_img] = fr.frames[current_line][z][Y_frame, X_frame] # Save frame to disk if save_frames: frame_file_name = fr_path.replace('.bin', '') + "_line_{:02d}_frame_{:03d}.png".format(current_line, t) cv2.imwrite(os.path.join(dir_path, frame_file_name), img) # Show the frame cv2.imshow(name, img) # If this is the first image, move it to the upper left corner if first_image: cv2.moveWindow(name, 0, 0) first_image = False if pause_flag: wait_time = 0 else: wait_time = 2*int(1000.0/config.fps) # Space key: pause display. # 1: previous file. # 2: next line. # q: Quit. key = cv2.waitKey(wait_time) & 0xFF if key == ord("1"): cv2.destroyWindow(name) return -1 elif key == ord("2"): break elif key == ord(" "): # Pause/unpause video pause_flag = not pause_flag elif key == ord("q") : os._exit(0) cv2.destroyWindow(name)
# RPi Meteor Station # Copyright (C) 2015 Dario Zubovic # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import cv2 import sys, os from RMS.Formats import FFfile if __name__ == "__main__": ff = FFfile.read(sys.argv[1], sys.argv[2]) cv2.imwrite(sys.argv[2] + "_max.png", ff.maxpixel) cv2.imwrite(sys.argv[2] + "_frame.png", ff.maxframe) cv2.imwrite(sys.argv[2] + "_avg.png", ff.avepixel) cv2.imwrite(sys.argv[2] + "_stddev.png", ff.stdpixel)
def plot(points, y_dim, x_dim): fig = plt.figure() ax = fig.add_subplot(111, projection='3d') plt.title(name) y = points[:, 0] x = points[:, 1] z = points[:, 2] # Plot points in 3D ax.scatter(x, y, z) # Set axes limits ax.set_zlim(0, 255) plt.xlim([0, x_dim]) plt.ylim([0, y_dim]) ax.set_ylabel("Y") ax.set_xlabel("X") ax.set_zlabel("Time") plt.show() if __name__ == "__main__": ff = FFfile.read(sys.argv[1], sys.argv[2], array=True) view(ff)
# Import old morph from RMS.OLD import MorphologicalOperations as morph # Cython init import pyximport pyximport.install(setup_args={'include_dirs': [np.get_include()]}) from RMS.Routines.MorphCy import morphApply # Run tests # Extract file and directory head, ff_name = os.path.split(sys.argv[1]) ff_path = os.path.abspath(head) + os.sep # Load the FF bin file ff = FFfile.read(ff_path, ff_name) img_thresh = thresholdImg(ff, 1.8, 9) show('thresh', img_thresh) # Convert img to integer img = img_thresh.astype(np.uint8) # Old morph img_old = np.copy(img) t1 = time.clock() img_old = morph.clean(img_old) img_old = morph.bridge(img_old) img_old = morph.close(img_old)
def view(dir_path, ff_path, fr_path, config): """ Shows the detected fireball stored in the FR file. Arguments: dir_path: [str] Current directory. ff: [str] path to the FF bin file fr: [str] path to the FR bin file config: [conf object] configuration structure """ name = fr_path fr = FRbin.read(dir_path, fr_path) if ff_path is None: #background = np.zeros((config.height, config.width), np.uint8) # Get the maximum extent of the meteor frames y_size = max(max(np.array(fr.yc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)) x_size = max(max(np.array(fr.xc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)) # Make the image square img_size = max(y_size, x_size) background = np.zeros((img_size, img_size), np.uint8) else: background = FFfile.read(dir_path, ff_path).maxpixel print("Number of lines:", fr.lines) first_image = True for i in range(fr.lines): print('Frame, Y , X , size') 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] print(" {:3d}, {:3d}, {:3d}, {:d}".format(t, yc, xc, size)) y2 = 0 # Assign the detection pixels to the background image for y in range(yc - size//2, yc + size//2): x2 = 0 for x in range(xc - size//2, xc + size//2): background[y, x] = fr.frames[i][z][y2, x2] x2 += 1 y2 += 1 cv2.imshow(name, background) # If this is the first image, move it to the upper left corner if first_image: cv2.moveWindow(name, 0, 0) first_image = False cv2.waitKey(2*int(1000.0/config.fps)) cv2.destroyWindow(name)
def view(dir_path, ff_path, fr_path, config, save_frames=False, extract_format='png', hide=False): """ Shows the detected fireball stored in the FR file. Arguments: dir_path: [str] Current directory. ff: [str] path to the FF bin file fr: [str] path to the FR bin file config: [conf object] configuration structure Keyword arguments: save_frames: [bool] Save FR frames to disk. False by defualt. extract_format: [str] Format of saved images. png by default. hide: [bool] Don't show frames on the screen. """ if extract_format is None: extract_format = 'png' name = fr_path fr = FRbin.read(dir_path, fr_path) print('------------------------') print('Showing file:', fr_path) if ff_path is None: #background = np.zeros((config.height, config.width), np.uint8) # Get the maximum extent of meteor frames y_size = max([ max(np.array(fr.yc[i]) + np.array(fr.size[i]) // 2) for i in range(fr.lines) ]) x_size = max([ max(np.array(fr.xc[i]) + np.array(fr.size[i]) // 2) for i in range(fr.lines) ]) # Make the image square img_size = max(y_size, x_size) background = np.zeros((img_size, img_size), np.uint8) else: background = FFfile.read(dir_path, ff_path).maxpixel print("Number of lines:", fr.lines) first_image = True wait_time = 2 * int(1000.0 / config.fps) pause_flag = False for current_line in range(fr.lines): print('Frame, Y , X , size') for z in range(fr.frameNum[current_line]): # Get the center position of the detection on the current frame yc = fr.yc[current_line][z] xc = fr.xc[current_line][z] # Get the frame number t = fr.t[current_line][z] # Get the size of the window size = fr.size[current_line][z] print(" {:3d}, {:3d}, {:3d}, {:d}".format(t, yc, xc, size)) img = np.copy(background) # Paste the frames onto the big image y_img = np.arange(yc - size // 2, yc + size // 2) x_img = np.arange(xc - size // 2, xc + size // 2) Y_img, X_img = np.meshgrid(y_img, x_img) y_frame = np.arange(len(y_img)) x_frame = np.arange(len(x_img)) Y_frame, X_frame = np.meshgrid(y_frame, x_frame) img[Y_img, X_img] = fr.frames[current_line][z][Y_frame, X_frame] # Save frame to disk if save_frames: frame_file_name = fr_path.replace('.bin', '') \ + "_line_{:02d}_frame_{:03d}.{:s}".format(current_line, t, extract_format) cv2.imwrite(os.path.join(dir_path, frame_file_name), img) if not hide: # Show the frame cv2.imshow(name, img) # If this is the first image, move it to the upper left corner if first_image: cv2.moveWindow(name, 0, 0) first_image = False if pause_flag: wait_time = 0 else: wait_time = 2 * int(1000.0 / config.fps) # Space key: pause display. # 1: previous file. # 2: next line. # q: Quit. key = cv2.waitKey(wait_time) & 0xFF if key == ord("1"): cv2.destroyWindow(name) return -1 elif key == ord("2"): break elif key == ord(" "): # Pause/unpause video pause_flag = not pause_flag elif key == ord("q"): os._exit(0) if not hide: cv2.destroyWindow(name)
def generateThumbnails(dir_path, config, mosaic_type, file_list=None, no_stack=False): """ Generates a mosaic of thumbnails from all FF files in the given folder and saves it as a JPG image. Arguments: dir_path: [str] Path of the night directory. config: [Conf object] Configuration. mosaic_type: [str] Type of the mosaic (e.g. "Captured" or "Detected") Keyword arguments: file_list: [list] A list of file names (without full path) which will be searched for FF files. This is used when generating separate thumbnails for captured and detected files. Return: file_name: [str] Name of the thumbnail file. no_stack: [bool] Don't stack the images using the config.thumb_stack option. A max of 1000 images are supported with this option. If there are more, stacks will be done according to the config.thumb_stack option. """ if file_list is None: file_list = sorted(os.listdir(dir_path)) # Make a list of all FF files in the night directory ff_list = [] for file_name in file_list: if FFfile.validFFName(file_name): ff_list.append(file_name) # Calculate the dimensions of the binned image bin_w = int(config.width / config.thumb_bin) bin_h = int(config.height / config.thumb_bin) ### RESIZE AND STACK THUMBNAILS ### ########################################################################################################## timestamps = [] stacked_imgs = [] thumb_stack = config.thumb_stack # Check if no stacks should be done (max 1000 images for no stack) if no_stack and (len(ff_list) < 1000): thumb_stack = 1 for i in range(0, len(ff_list), thumb_stack): img_stack = np.zeros((bin_h, bin_w)) # Stack thumb_stack images using the 'if lighter' method for j in range(thumb_stack): if (i + j) < len(ff_list): tmp_file_name = ff_list[i + j] # Read the FF file ff = FFfile.read(dir_path, tmp_file_name) # Skip the FF if it is corruped if ff is None: continue img = ff.maxpixel # Resize the image img = cv2.resize(img, (bin_w, bin_h)) # Stack the image img_stack = stackIfLighter(img_stack, img) else: break # Save the timestamp of the first image in the stack timestamps.append(FFfile.filenameToDatetime(ff_list[i])) # Save the stacked image stacked_imgs.append(img_stack) # cv2.imshow('test', img_stack) # cv2.waitKey(0) # cv2.destroyAllWindows() ########################################################################################################## ### ADD THUMBS TO ONE MOSAIC IMAGE ### ########################################################################################################## header_height = 20 timestamp_height = 10 # Calculate the number of rows for the thumbnail image n_rows = int( np.ceil(float(len(ff_list)) / thumb_stack / config.thumb_n_width)) # Calculate the size of the mosaic mosaic_w = int(config.thumb_n_width * bin_w) mosaic_h = int((bin_h + timestamp_height) * n_rows + header_height) mosaic_img = np.zeros((mosaic_h, mosaic_w), dtype=np.uint8) # Write header text header_text = 'Station: ' + str(config.stationID) + ' Night: ' + os.path.basename(dir_path) \ + ' Type: ' + mosaic_type cv2.putText(mosaic_img, header_text, (0, header_height//2), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,255,255), 1) for row in range(n_rows): for col in range(config.thumb_n_width): # Calculate image index indx = row * config.thumb_n_width + col if indx < len(stacked_imgs): # Calculate position of the text text_x = col * bin_w text_y = row * bin_h + ( row + 1) * timestamp_height - 1 + header_height # Add timestamp text cv2.putText(mosaic_img, timestamps[indx].strftime('%H:%M:%S'), (text_x, text_y), \ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) # Add the image to the mosaic img_pos_x = col * bin_w img_pos_y = row * bin_h + ( row + 1) * timestamp_height + header_height mosaic_img[img_pos_y:img_pos_y + bin_h, img_pos_x:img_pos_x + bin_w] = stacked_imgs[indx] else: break ########################################################################################################## # Only add the station ID if the dir name already doesn't start with it dir_name = os.path.basename(os.path.abspath(dir_path)) if dir_name.startswith(config.stationID): prefix = dir_name else: prefix = "{:s}_{:s}".format(config.stationID, dir_name) thumb_name = "{:s}_{:s}_thumbs.jpg".format(prefix, mosaic_type) # Save the mosaic if USING_IMAGEIO: # Use imageio to write the image imwrite(os.path.join(dir_path, thumb_name), mosaic_img, quality=80) else: # Use OpenCV to save the image imwrite(os.path.join(dir_path, thumb_name), mosaic_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) return thumb_name
# Extract the directory name from the given argument bin_dir = sys.argv[1] # Load config file config = cr.parse(".config") print('Directory:', bin_dir) for ff_name in os.listdir(bin_dir): if 'FF' in ff_name: print(ff_name) # Load compressed file compressed = FFfile.read(bin_dir, ff_name, array=True, full_filename=True).array # Show maxpixel ff = FFfile.read(bin_dir, ff_name, full_filename=True) plt.imshow(ff.maxpixel, cmap='gray') plt.show() plt.clf() plt.close() # Dummy frames from FF file frames = FFfile.reconstruct(ff) # Add avepixel to all reconstructed frames frames += ff.avepixel
def extractStars(ff_dir, ff_name, config=None, max_global_intensity=150, border=10, neighborhood_size=10, intensity_threshold=5, flat_struct=None, dark=None, mask=None): """ Extracts stars on a given FF bin by searching for local maxima and applying PSF fit for star confirmation. Source of one part of the code: http://stackoverflow.com/questions/9111711/get-coordinates-of-local-maxima-in-2d-array-above-certain-value Arguments: ff_dir: [str] Path to directory where FF files are. ff_name: [str] Name of the FF file. config: [config object] configuration object (loaded from the .config file) max_global_intensity: [int] maximum mean intensity of an image before it is discared as too bright border: [int] apply a mask on the detections by removing all that are too close to the given image border (in pixels) neighborhood_size: [int] size of the neighbourhood for the maximum search (in pixels) intensity_threshold: [float] a threshold for cutting the detections which are too faint (0-255) flat_struct: [Flat struct] Structure containing the flat field. None by default. dark: [ndarray] Dark frame. None by default. mask: [ndarray] Mask image. None by default. Return: x2, y2, background, intensity, fwhm: [list of ndarrays] - x2: X axis coordinates of the star - y2: Y axis coordinates of the star - background: background intensity - intensity: intensity of the star - Gaussian Full width at half maximum (FWHM) of fitted stars """ # This will be returned if there was an error error_return = [[], [], [], [], [], []] # Load parameters from config if given if config: max_global_intensity = config.max_global_intensity border = config.border neighborhood_size = config.neighborhood_size intensity_threshold = config.intensity_threshold # Load the FF bin file ff = FFfile.read(ff_dir, ff_name) # If the FF file could not be read, skip star extraction if ff is None: return error_return # Apply the dark frame if dark is not None: ff.avepixel = Image.applyDark(ff.avepixel, dark) # Apply the flat if flat_struct is not None: ff.avepixel = Image.applyFlat(ff.avepixel, flat_struct) # Mask the FF file if mask is not None: ff = MaskImage.applyMask(ff, mask, ff_flag=True) # Calculate image mean and stddev global_mean = np.mean(ff.avepixel) # Check if the image is too bright and skip the image if global_mean > max_global_intensity: return error_return data = ff.avepixel.astype(np.float32) # Apply a mean filter to the image to reduce noise data = ndimage.filters.convolve(data, weights=np.full((2, 2), 1.0 / 4)) # Locate local maxima on the image data_max = filters.maximum_filter(data, neighborhood_size) maxima = (data == data_max) data_min = filters.minimum_filter(data, neighborhood_size) diff = ((data_max - data_min) > intensity_threshold) maxima[diff == 0] = 0 # Apply a border mask border_mask = np.ones_like(maxima) * 255 border_mask[:border, :] = 0 border_mask[-border:, :] = 0 border_mask[:, :border] = 0 border_mask[:, -border:] = 0 maxima = MaskImage.applyMask(maxima, border_mask, image=True) # Remove all detections close to the mask image if mask is not None: erosion_kernel = np.ones((5, 5), mask.img.dtype) mask_eroded = cv2.erode(mask.img, erosion_kernel, iterations=1) maxima = MaskImage.applyMask(maxima, mask_eroded, image=True) # Find and label the maxima labeled, num_objects = ndimage.label(maxima) # Skip the image if there are too many maxima to process if num_objects > config.max_stars: print('Too many candidate stars to process! {:d}/{:d}'.format( num_objects, config.max_stars)) return error_return # Find centres of mass of each labeled objects xy = np.array( ndimage.center_of_mass(data, labeled, range(1, num_objects + 1))) # Remove all detection on the border #xy = xy[np.where((xy[:, 1] > border) & (xy[:,1] < ff.ncols - border) & (xy[:,0] > border) & (xy[:,0] < ff.nrows - border))] # Unpack star coordinates y, x = np.hsplit(xy, 2) # # Plot stars before the PSF fit # plotStars(ff, x, y) # Fit a PSF to each star x2, y2, amplitude, intensity, sigma_y_fitted, sigma_x_fitted = fitPSF( ff, global_mean, x, y, config) # x2, y2, amplitude, intensity = list(x), list(y), [], [] # Skip PSF fit # # Plot stars after PSF fit filtering # plotStars(ff, x2, y2) # Compute FWHM from one dimensional sigma sigma_x_fitted = np.array(sigma_x_fitted) sigma_y_fitted = np.array(sigma_y_fitted) sigma_fitted = np.sqrt(sigma_x_fitted**2 + sigma_y_fitted**2) fwhm = 2.355 * sigma_fitted return ff_name, x2, y2, amplitude, intensity, fwhm
def sensorCharacterization(config, dir_path): """ Characterize the standard deviation of the background and the FWHM of stars on every image. """ # Find the CALSTARS file in the given folder that has FWHM information found_good_calstars = False for cal_file in os.listdir(dir_path): if ('CALSTARS' in cal_file) and ('.txt' in cal_file) and (not found_good_calstars): # Load the calstars file calstars_list = CALSTARS.readCALSTARS(dir_path, cal_file) if len(calstars_list) > 0: # Check that at least one image has good FWHM measurements for ff_name, star_data in calstars_list: if len(star_data) > 1: star_data = np.array(star_data) # Check if the calstars file have FWHM information fwhm = star_data[:, 4] # Check that FWHM values have been computed well if np.all(fwhm > 1): found_good_calstars = True print('CALSTARS file: ' + cal_file + ' loaded!') break # If the FWHM information is not present, run the star extraction if not found_good_calstars: print() print("No FWHM information found in existing CALSTARS files!") print() print("Rerunning star detection...") print() found_good_calstars = False # Run star extraction again, and now FWHM will be computed calstars_list = extractStarsAndSave(config, dir_path) if len(calstars_list) == 0: found_good_calstars = False # Check for a minimum of detected stars for ff_name, star_data in calstars_list: if len(star_data) >= config.ff_min_stars: found_good_calstars = True break # If no good calstars exist, stop computing the flux if not found_good_calstars: print("No stars were detected in the data!") return False # Dictionary which holds information about FWHM and standard deviation of the image background sensor_data = {} # Compute median FWHM per FF file for ff_name, star_data in calstars_list: # Check that the FF file exists in the data directory if ff_name not in os.listdir(dir_path): continue star_data = np.array(star_data) # Compute the median star FWHM fwhm_median = np.median(star_data[:, 4]) # Load the FF file and compute the standard deviation of the background ff = FFfile.read(dir_path, ff_name) # Compute the median stddev of the background stddev_median = np.median(ff.stdpixel) # Store the values to the dictionary sensor_data[ff_name] = [fwhm_median, stddev_median] print("{:s}, {:5.2f}, {:5.2f}".format(ff_name, fwhm_median, stddev_median)) return sensor_data
# # Show stars if there are only more then 10 of them # if len(x2) < 20: # continue # # Load the FF bin file # ff = FFfile.read(ff_dir, ff_name) # plotStars(ff, x2, y2) for ff_name in extraction_list: # Load data about the image ff = FFfile.read(ff_dir, ff_name) # Break when an FF file was successfully loaded if ff is not None: break # Generate the name for the CALSTARS file calstars_name = 'CALSTARS_' + "{:s}".format(str(config.stationID)) + '_' \ + os.path.basename(ff_dir) + '.txt' # Write detected stars to the CALSTARS file CALSTARS.writeCALSTARS(star_list, ff_dir, calstars_name, ff.camno, ff.nrows, ff.ncols) # Delete QueudPool backed up files