예제 #1
0
    def cull(self, dir_root, fextension):
        
        # Good recording code ranges:
        good_rec_id_rngs = [range(50000, 70001),
                            range(170000, 180001),
                            ] 
        
        # Get all audio file paths relative
        # to dir_root:
        
        pattern = f'*{fextension}' 
        wav_paths = Utils.find_in_dir_tree(dir_root, 
                                           pattern=pattern)
                                           
        
        #*********
        # wav_paths = ['/foo/bar/AM01_20190711_049999.wav', # no
        #   		   '/foo/bar/AM01_20190711_050000.wav', # yes
        #   		   '/foo/bar/AM01_20190711_070000.wav', # yes
        #   		   '/foo/bar/AM01_20190711_070001.wav', # no
        #   		   '/foo/bar/AM01_20190711_169999.wav', # no
        #   		   '/foo/bar/AM01_20190711_170000.wav', # yes
        #   		   '/foo/bar/AM01_20190711_170001.wav', # no
        # ]
        # #********* 
        # Get just the filename without parents
        # and extension:
        to_delete = []
        for aud_path in wav_paths:
            ser_num = self.extract_ser_num(aud_path)
            if   ser_num in good_rec_id_rngs[0] \
              or ser_num in good_rec_id_rngs[1]:
                continue
            else:
                to_delete.append(aud_path)

        print(f"Examined {len(wav_paths)} {pattern} files...")
        if len(to_delete) > 0:
            
            if Utils.user_confirm(f"List the {len(to_delete)} bad files? (n/Y)", default='Y'):
                for fpath in to_delete:
                    print(f"{os.path.getsize(fpath)} bytes: {fpath}")

            if Utils.user_confirm(f"Delete {len(to_delete)} aud files? (N/y):", default='N'):
                num_deleted = 0
                for fname in to_delete:
                    try:
                        os.remove(fname)
                    except Exception as e:
                        print(f"Could not delete {fname}: {repr(e)}")
                    else:
                        num_deleted += 1
                        
                print(f"Removed {num_deleted} files")
            else:
                print('Canceling')
        else:
            print('No files are out of good recorder serial number ranges')
예제 #2
0
 def cull_spectro_paths(cls,
                        species_or_recorder_name,
                        dst_dir,
                        rec_paths,
                        overwrite_policy=WhenAlreadyDone.ASK):
     #******* DISABLED ************
     # method analogous to cull_rec_paths() in create_spectrograms()
     # Currently below is just a copy from create_spectrograms().
     # If we end up needing culling, update this body
     return rec_paths
     #******* DISABLED ************
     # NEVER REACHED
     new_rec_paths = []
     for aud_fname in rec_paths:
         fname_stem = Path(aud_fname).stem
         dst_path = os.path.join(dst_dir, species_or_recorder_name,
                                 f"{fname_stem}.png")
         if not os.path.exists(dst_path):
             # Destination spectrogram does not exist;
             # keep this audio file in the to-do list:
             new_rec_paths.append(aud_fname)
             continue
         if overwrite_policy == WhenAlreadyDone.OVERWRITE:
             os.remove(dst_path)
             new_rec_paths.append(aud_fname)
             continue
         if overwrite_policy == WhenAlreadyDone.SKIP:
             # Don't even assign audio file to a worker,
             # since its spectro already exists:
             continue
         if overwrite_policy == WhenAlreadyDone.ASK:
             if Utils.user_confirm(
                     f"Spectrogram for {dst_path} exists; overwrite?"):
                 os.remove(dst_path)
                 new_rec_paths.append(aud_fname)
                 continue
     return new_rec_paths
예제 #3
0
    # Enforce args to set_info or add_info being
    # equal length, i.e. having 'names' and 'values'
    # as pairs:
    if (setting and len(info_to_set) % 2 != 0) \
       or (adding and len(info_to_add) % 2 != 0):
        print(
            "Info entries must be pairs of keys and values; length is odd numbered here"
        )
        sys.exit(1)

    # Safety precaution just for setting
    # (and thereby overwriting) metadata:

    if setting and not args.force:
        if not Utils.user_confirm(
                "Really want to overwrite png file metadata? (N/y)",
                default='n'):
            print("Canceling")
            sys.exit(0)

    if args.printout:
        print("Metadata before:")
        PNGMetadataManipulator.extract_metadata(args.snippet_src,
                                                show=args.show,
                                                printout=args.printout)
        print("")

    # Setting info_to_set:
    if args.outfile is None:
        # Overwrite the input file,
        # i.e. add metadata in place:
예제 #4
0
    def chop_one_spectro_file(
        cls,
        spectro_fname,
        out_dir,
        species_name,
        window_len=5,
        skip_size=2,
        original_duration=None,
        overwrite_policy=WhenAlreadyDone.ASK,
    ):
        """
        Generates window_len second spectrogram snippets
        from spectrograms files of arbitrary length. 
        
        To compute the number of time slices to extract
        for each snippet, the time_slices of the spectrogram time
        slices in fractional seconds must be known. The time_slices
        can be approximated if the play length of the underlying
        audio is known (even if the precise fft settings are unavailable).
        
        If the given .png file contains metadata with a 'duration' 
        key, then the corresponding value is used as the duration of 
        the original audio file in fractional seconds. This metadata
        will be present if the .png file was created with the 
        SoundProcessor.create_spectrogram(). 
        
        To enable use of spectrogram images created elsewhere, callers
        can instead supply original_duration in fractional seconds.
        
        For now, if neither the embedded metadata, nor the original_duration
        is supplied, a ValueError is raised. 
    
        :param spectro_fname: full path to spectrogram file to chop
        :type spectro_fname: str
        :param out_dir: root directory under which spectrogram
            snippets will be saved (in different subdirs)
        :type out_dir: str
        :param species_name: name of species to embed in the 
            metadata of this snippet, and use for determining
            subdirectory where to place the snippet
        :type species_name: str
        :param window_len: number of seconds to be covered by each snippet
        :type window_len: int
        :param skip_size: number of seconds to shift right in 
            time for the start of each chop
        :type skip_size: int
        :param original_duration:
        :raise ValueError: if neither embedded duration metadata is found
            in the given file, nor original_duration is provided
        """

        # Read the spectrogram, getting an np array:
        spectro_arr, metadata = SoundProcessor.load_spectrogram(spectro_fname)
        duration = metadata.get('duration', None)

        if duration is None:
            if original_duration is None:
                raise ValueError(
                    f"Time duration of original recording cannot be determined for {spectro_fname}"
                )
            else:
                duration = float(original_duration)
        else:
            duration = float(duration)

        # If original file is already at or below
        # the single window length, it's a snippet
        # in itself. Copy it to the output with an
        # appropriate snippet name to match the other
        # snippets: wall start time is zero:

        if duration < window_len:
            # No partial snippets
            return
        # Note: Also have sample rate ('sr') and species ('label')
        # in the metadata, but don't need those here.

        _freq_bands, time_slices = spectro_arr.shape
        # Time in fractions of second
        # per spectrogram column:
        twidth = duration / time_slices

        # Integer of duration (which is in seconds):
        time_dur_int = int(np.ceil(duration))
        time_upper_bound = 1 + time_dur_int - skip_size

        # Caller specifies skip_size and window
        # length in *seconds*. Convert to spectrogram
        # time slices (with rounding error):

        samples_win_len = int(window_len // twidth)
        # Does samples_win_len satisfy the
        # minimum spectrogram snippet width for
        # pretrained models?
        samples_win_len = max(cls.MIN_SNIPPET_WIDTH, samples_win_len)

        time_true_each_snippet = samples_win_len * twidth

        samples_skip_size = int(skip_size // twidth)
        samples_upper_bound = int(time_upper_bound // twidth)

        assert (samples_upper_bound <= time_slices)

        for _snip_num, samples_start_idx in enumerate(
                range(0, samples_upper_bound, samples_skip_size)):

            # Absolute start time of this snippet
            # within the entire spectrogram:
            wall_start_time = samples_start_idx * twidth
            # Create a name for the snippet file:
            snippet_path = cls.create_snippet_fpath(spectro_fname,
                                                    round(wall_start_time),
                                                    out_dir)

            spectro_done = os.path.exists(snippet_path)

            if spectro_done:
                if overwrite_policy == WhenAlreadyDone.SKIP:
                    # Next snippet:
                    continue
                elif overwrite_policy == WhenAlreadyDone.ASK:
                    if not Utils.user_confirm(
                            f"Snippet {Path(snippet_path).stem} exists, overwrite?",
                            default='N'):
                        continue

            # Chop: All rows, columns from current
            #       window start for window lenth samples:
            snippet_data = spectro_arr[:, samples_start_idx:samples_start_idx +
                                       samples_win_len]
            _num_rows, num_cols = snippet_data.shape
            if num_cols < samples_win_len:
                # Leave that little spectrogram
                # snippet leftover for Elijah:
                break

            snippet_info = metadata.copy()
            # Add the
            snippet_info['duration(secs)'] = samples_win_len * twidth
            snippet_info['start_time(secs)'] = wall_start_time
            snippet_info['end_time(secs)'] = wall_start_time + (
                samples_win_len * twidth)
            snippet_info['species'] = species_name
            SoundProcessor.save_image(snippet_data, snippet_path, snippet_info)
        return time_true_each_snippet