예제 #1
0
def show3DCloud(ff, stripe, detected_line=None, stripe_points=None, config=None):
    """ Shows 3D point cloud of stripe points.
    """

    if detected_line is None:
        detected_line = []

    stripe_indices = stripe.nonzero()

    xs = stripe_indices[1]
    ys = stripe_indices[0]
    zs = ff.maxframe[stripe_indices]

    logDebug('points: ', len(xs))

    # Plot points in 3D
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    ax.scatter(xs, ys, zs)

    if detected_line and len(stripe_points):

        xs = [detected_line[0][1], detected_line[1][1]]
        ys = [detected_line[0][0], detected_line[1][0]]
        zs = [detected_line[0][2], detected_line[1][2]]
        ax.plot(ys, xs, zs, c = 'r')

        x1, x2 = ys
        y1, y2 = xs
        z1, z2 = zs

        detected_points = getAllPoints(stripe_points, x1, y1, z1, x2, y2, z2, config, 
            fireball_detection=False)

        if detected_points.any():

            detected_points = np.array(detected_points)

            xs = detected_points[:,0]
            ys = detected_points[:,1]
            zs = detected_points[:,2]

            ax.scatter(xs, ys, zs, c = 'r', s = 40)

    # Set limits
    plt.xlim((0, ff.ncols))
    plt.ylim((0, ff.nrows))
    ax.set_zlim((0, 255))

    plt.show()

    plt.clf()
    plt.close()
예제 #2
0
                        # detected_line = detected_line[0]
                        xs = [detected_line[0][0], detected_line[1][0]]
                        ys = [detected_line[0][1], detected_line[1][1]]
                        zs = [detected_line[0][2], detected_line[1][2]]
                        ax.plot(ys, xs, zs, c=ln_colors[i % 6])

                    # Plot grouped points
                    for i, detected_line in enumerate(line_list):

                        x1, x2 = detected_line[0][0], detected_line[1][0]

                        y1, y2 = detected_line[0][1], detected_line[1][1]

                        z1, z2 = detected_line[0][2], detected_line[1][2]

                        detected_points = getAllPoints(event_points, x1, y1,
                                                       z1, x2, y2, z2, config)

                        if not detected_points.any():
                            continue

                        detected_points = np.array(detected_points)

                        xs = detected_points[:, 1]
                        ys = detected_points[:, 0]
                        zs = detected_points[:, 2]

                        ax.scatter(xs, ys, zs, c=ln_colors[i % 6], s=40)

                print('Elapsed time: ', elapsed_time)
                plt.show()
예제 #3
0
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