def hedley_remove_glint(input_dir,
                        output_dir,
                        train_cube_path,
                        recursive_src=False):
    """ Apply Hedley glint removal to image batch

    # Required arguments:
    input_dir:      Directory contating input ENVI files (*.hdr).
                    All files will be included in the batch.
    output_dir:     Directory for saving output files.
    train_cube_path: Path to ENVI file with data used for training the glint
                    model (homogenous background with variations in glint).

    # Optional arguments:
    recursive_src:  Whether to search the input_dir for .hdr files recursively

    """

    # Load training data
    print('Loading training data ' + train_cube_path)
    (train, wl, rgb_ind, metadata) = hyspec_io.load_envi_image(train_cube_path)

    # Reshape to 2D
    train = train.reshape((-1, train.shape[2]))

    # Create and train sun glint model
    print('Training Hedley sun glint correction model')
    sgm = preprocess.HedleySunGlint()
    sgm.fit(train, wl)

    # Find input files
    file_list = misc.file_pattern_search(input_dir,
                                         '*.hdr',
                                         recursive=recursive_src)
    print('Found ' + str(len(file_list)) + ' images in input folder.')

    # Loop over all input files
    for input_file in file_list:
        # Load data
        print('Loading input file ' + input_file)
        (im, wl, rgb_ind, metadata) = hyspec_io.load_envi_image(input_file)

        # Remove glint
        im_sgc = sgm.remove_glint(im)

        # Update metadata
        metadata['wavelength'] = [
            metadata['wavelength'][ii] for ii in range(len(wl))
            if sgm.vis_ind[ii]
        ]

        # Create path for output file
        output_file = misc.build_newdir_filepath([input_file], output_dir)[0]
        print('Saving sun glint corrected file as ' + output_file)

        # Save file
        hyspec_io.save_envi_image(output_file, im_sgc, metadata)

        # Copy .lcf and .times files if they exist
        misc.copy_hyspec_aux_files(input_file, output_dir)
def detect_saturated(input_dir, recursive_src=False, **kwargs):
    """ Detect saturated pixels in image batch, save as PNG files

    # Required arguments:
    input_dir:      Directory contating input ENVI files (*.hdr).
                    All files will be included in the batch.

    # Optional arguments:
    recursive_src:  Whether to search the input_dir for .hdr files recursively
    **kwargs:       Keyword arguments are passed on to detect_saturated()
    """

    file_list = misc.file_pattern_search(input_dir,
                                         '*.hdr',
                                         recursive=recursive_src)

    for file in file_list:
        # Load image
        print('Loading ' + file)
        (im_cube, wl, rgb_ind, metadata) = hyspec_io.load_envi_image(file)

        # Detect saturated pixels in image
        mask = preprocess.detect_saturated(im_cube, **kwargs)

        # Save mask as PNG image with same base name and folder as image
        mask_file = file.split(sep='.')[0] + '_sat.png'
        print('Saving saturation mask as ' + mask_file)
        skimage.io.imsave(mask_file, skimage.img_as_ubyte(mask))
Exemple #3
0
def get_video_data(video_dir):
    """ Get info about videos in folder, organized as dataframe

    # Usage:
    video_data = get_video_data(video_dir)

    # Required:
    video_dir:      Path to folder with video file(s).
                    Files are assumed to be from a single continuous "take",
                    split into files with names ordered alphabetiacally
                    according to recording order.

    # Returns:
    video_data:     Pandas dataframe with columns
                    'FileName', 'DurationSec','StartTimeSec','StopTimeSec'
                    Start and stop times are relative to start of first file.

    """

    video_data = pd.DataFrame(
        {
            'FileName': '',
            'DurationSec': float(),
            'StartTimeSec': float(),
            'StopTimeSec': float()
        },
        index=[])

    # Get list of video files, insert into dataframe
    # Use brackets to find both .mp4 and .MP4 files
    video_data['FileName'] = misc.file_pattern_search(video_dir, '*.[Mm][Pp]4')

    # Get duration and frame rate for each video file
    for ii, file in enumerate(video_data['FileName']):
        probe_data = ffmpeg.probe(file)
        video_data.loc[ii, 'DurationSec'] = pd.to_numeric(
            probe_data['streams'][0]['duration'])

    video_data.iloc[0, video_data.columns.get_loc('StartTimeSec')] = 0.0
    video_data.iloc[
        1:, video_data.columns.get_loc('StartTimeSec')] = np.cumsum(
            video_data.iloc[:-1,
                            video_data.columns.get_loc('DurationSec')])
    video_data[
        'StopTimeSec'] = video_data['StartTimeSec'] + video_data['DurationSec']

    return video_data
def absolute_stretch(input_dir,
                     output_dir,
                     limits,
                     file_ext='png',
                     recursive_src=False):
    """ Perform absolute stretch on image batch

    # Required arguments:
    input_dir:      Directory contating input image files.
                    All files will be included in the batch.
    output_dir:     Directory for saving output image files.
    limits:         Limits for stretch, see misc.absolute_stretch

    # Optional arguments:
    file_ext:      File extension (file type) to look for in input_dir
    recursive_src:  Whether to search the input_dir recursively

    """

    # Find input files
    input_files = misc.file_pattern_search(input_dir,
                                           '*.' + file_ext,
                                           recursive=recursive_src)
    print('Found ' + str(len(input_files)) + ' images in input folder.')

    # Make output file paths
    output_files = misc.build_newdir_filepath(input_files, output_dir)

    for input_file, output_file in zip(input_files, output_files):
        # Load data
        print('Loading input file ' + input_file)
        input_image = skimage.io.imread(input_file)

        # Stretch file
        output_image = image_render.absolute_stretch(input_image, limits)

        # Save file
        print('Saving stretched image file as ' + output_file)
        skimage.io.imsave(output_file, output_image.astype(input_image.dtype))
def collect_annotated_data(class_dict, hyspec_dir, annotation_dir):
    """ Loop through set of annotated images and extract spectra

    # Usage:
    data = collect_annotated_data(label_file, hyspec_dir, annotation_dir)

    # Required arguments:
    class_dict:     Dictionary with key = class name and value = png index
    hyspec_dir:     Folder containing hyperspectral files
    annotation_dir: Folder contating annotation images (.png)

    # Returns
    data:   List of dictionaries, with each dictionary containing data from a
            single file. The dictionary contains paths to the original files,
            annotation mask, mask indicating non-zero data points, and a
            dictionary containing spectra for each class in class_dict.

    # Note:
    - The JSON and .png files are assumed to be generated in hasty.ai
    - The hyperspectral files and annotation files are assument to share the
      same file name (except the file extensions).
    - If you are only interested in a few classes, limit the class_dict to
      these classes. This can also help if you have a large dataset causing
      memory issues. 

    """

    # Find names of annotated images
    ann_im_fullpath = misc.file_pattern_search(annotation_dir, '*.png')

    # Extract filename base (common for hyspec and annotations files)
    filenames = [
        os.path.splitext(os.path.basename(filepath))[0]
        for filepath in ann_im_fullpath
    ]

    # Loop through files and collect spectra for each class
    data = []
    for file in filenames:
        # Progression update
        print('Processing file: ' + file)

        # Load hyperspectral file
        hyspec_file = os.path.join(hyspec_dir, file + '.bip.hdr')
        (im_cube, wl, rgb_ind,
         metadata) = hyspec_io.load_envi_image(hyspec_file)

        # Create non-zero mask
        nonzero_mask = ~np.all(im_cube == 0, axis=2)

        # Open annotation file
        annotation_file = os.path.join(annotation_dir, file + '.png')
        annotation_mask = skimage.io.imread(annotation_file) * nonzero_mask

        # Create "local" dictionary for collecting data from current file
        data_dict = {
            'hyspec_file': hyspec_file,
            'annotation_file': annotation_file,
            'nonzero_mask': nonzero_mask,
            'annotation_mask': annotation_mask,
            'spectra': {}
        }  # Empty dict, to be filled

        # Collect spectra for each class
        for (class_name, class_ind) in class_dict.items():
            spec = im_cube[annotation_mask == class_ind]
            data_dict['spectra'][class_name] = spec

        # Append dictionary to file list
        data.append(data_dict)

    # Return
    return data
def envi_rgb_render(input_dir,
                    output_dir=None,
                    limits=None,
                    recursive_src=False,
                    inpaint=True,
                    closing_rad=3):
    """ Create RGB renderings of hyperspectral files and save as PNG

    # Required arguments:
    input_dir:      Directory contating input image files.
                    All files will be included in the batch.

    # Optional arguments:
    output_dir:     Directory for saving output image files. If None, files are
                    saved in input directory.
    limits:         Limits for stretch, see misc.absolute_stretch
                    If not specified, each image will be stretched to its
                    (2,92) percentiles, separatey for each R,G,B band
    recursive_src:  Whether to search the input_dir recursively
    inpaint:        Interpolate (inpaint) for pixels inside image
    closing_rad:    Radius used for morphology "closing" used to determine which
                    pixels will be inpainted. The larger the value, the more
                    pixels are included.

    """

    # Find input files
    input_files = misc.file_pattern_search(input_dir,
                                           '*.hdr',
                                           recursive=recursive_src)
    print('Found ' + str(len(input_files)) + ' images in input folder.')

    # Make output file paths
    if output_dir is None:
        output_dir = input_dir

    output_files = misc.build_newdir_filepath(input_files,
                                              output_dir,
                                              new_ext='.png')

    # Iterate over batch
    for input_file, output_file in zip(input_files, output_files):
        # Load data
        print('Loading input file ' + input_file)
        (im, wl, rgb_ind, metadata) = hyspec_io.load_envi_image(input_file)

        # Extrct RGB
        im_rgb = im[:, :, rgb_ind]

        # Stretch
        if limits is None:
            im_rgb_sc = image_render.percentile_stretch(im_rgb)
        else:
            im_rgb_sc = image_render.absolute_stretch(im_rgb, limits)

        # Convert to 8-bit int
        im_rgb_sc = np.uint8(im_rgb_sc * 255)

        if inpaint:
            zero_mask = ~np.all(im_rgb_sc == 0, axis=2)
            mask_closed = skimage.morphology.binary_closing(
                zero_mask, skimage.morphology.disk(closing_rad))
            mask_diff = (zero_mask != mask_closed)
            im_rgb_sc = preprocess.inpaint_masked(im_rgb_sc, mask_diff)

        # Save
        print('Saving RGB render of hyperspectral file as ' + output_file)
        skimage.io.imsave(output_file, im_rgb_sc)