Example #1
0
        def aux(rootdir, level=0):
            pad = ' ' * level
            dirpaths, filepaths = collect_dirs_and_files(rootdir)
            filepaths_by_ext = sort_files_by_ext(filepaths)
            if filepaths_by_ext:
                for ext, filepaths in sorted(filepaths_by_ext.items()):
                    seq = FileSequence.load(filepaths)
                    if seq:
                        range_str = ', '.join(
                            ['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                        print('%s %s | %4d | %s' %
                              (pad, ext.ljust(8), len(filepaths), range_str))
                    else:
                        print('%s %s | %4d ' %
                              (pad, ext.ljust(8), len(filepaths)))
            elif not dirpaths:
                print('%s (empty)' % pad)

            for dirpath in dirpaths:
                print('%s %s:' % (pad, os.path.basename(dirpath)))
                aux(dirpath, level + 1)
Example #2
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(
            os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        filepaths_by_ext = sort_files_by_ext(filepaths)
        cr2_files = filepaths_by_ext.get('.cr2', [])
        if cr2_files:
            seq = FileSequence.load(cr2_files)
            if seq:
                range_str = ', '.join(
                    ['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                print('%d image files found: %s' % (len(cr2_files), range_str))
            else:
                print('%d image files found.' % len(cr2_files))
        else:
            raise RuntimeError('No .cr2 image files found')

        print('Setting exposure delta to %+0.2f EV...' % args.ev_delta)
        print('')
        changed_count = 0
        unchanged_count = 0
        for image_filepath in sorted(cr2_files):
            old_ev_delta = read_param(image_filepath, 'ev_delta', 0.0)
            if old_ev_delta == args.ev_delta:
                unchanged_count += 1
            else:
                print('%s: %+0.2f EV --> %+0.2f EV' %
                      (os.path.basename(image_filepath), old_ev_delta,
                       args.ev_delta))
                write_param(image_filepath, 'ev_delta', args.ev_delta)
                changed_count += 1
        print('')
        print('Updated params for %d images; left %d images unchanged.' %
              (changed_count, unchanged_count))
        if changed_count:
            print('Run fma dt-apply to apply changes to .xmp files.')
Example #3
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        xmp_files = sort_files_by_ext(filepaths).get('.cr2.xmp', [])
        if xmp_files:
            seq = FileSequence.load(xmp_files)
            if seq:
                range_str = ', '.join(['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                print('%d sidecar files found: %s' % (len(xmp_files), range_str))
            else:
                print('%d sidecar files found.' % len(xmp_files))

            if not args.force:
                print('')
                print('These existing sidecar files contain edits made in darktable.')
                print('Clearing these files will DELETE these previously-made edits.')
                print('If you\'re sure you want to do that, re-run with -f.')
                return
        else:
            print('No .xmp sidecar files found.')
            return

        print('')
        print('Deleting %d sidecar files...' % len(xmp_files))
        num_deleted = 0
        for filepath in xmp_files:
            print('DELETE %s' % os.path.basename(filepath))
            os.remove(filepath)
            num_deleted += 1

        print('')
        print('Deleted %d files.' % num_deleted)

        print('Regenerating .xmp sidecar files...')
        regenerate_xmps(images_dir)
Example #4
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        filepaths_by_ext = sort_files_by_ext(filepaths)
        cr2_files = filepaths_by_ext.get('.cr2', [])
        if cr2_files:
            seq = FileSequence.load(cr2_files)
            if seq:
                range_str = ', '.join(['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                print('%d image files found: %s' % (len(cr2_files), range_str))
            else:
                print('%d image files found.' % len(cr2_files))
        else:
            raise RuntimeError('No .cr2 image files found')
        print('')

        print('Launching darktable...')
        run_darktable([images_dir])
Example #5
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(
            os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        cr2_files = sort_files_by_ext(filepaths).get('.cr2', [])
        seq = FileSequence.load(cr2_files) if cr2_files else None
        if not seq:
            raise RuntimeError('No .cr2 image sequence found')

        print('Generating image cache...')
        generate_cache(images_dir)

        tmp_dirpath = os.path.join(images_dir, '.temp-sides')
        with temporary_directory(tmp_dirpath):

            os.startfile(tmp_dirpath)

            backs_dirpath = os.path.join(tmp_dirpath, 'backs')
            os.makedirs(backs_dirpath)

            cr2_filepath_lookup = {}
            for image_filepath, cached_filepath in image_iterator(images_dir):
                short_filename = os.path.basename(cached_filepath)
                cr2_filepath_lookup[short_filename] = image_filepath

                img = cv2.imread(cached_filepath)
                img = cv2.resize(img, (img.shape[1] // 4, img.shape[0] // 4))

                is_back = None
                image_params = read_params(image_filepath)
                if image_params.get('side') in ('front', 'back'):
                    is_back = image_params.get('side') == 'back'
                    print('%s -- loaded -- %s' %
                          (short_filename, 'back' if is_back else 'FRONT'))

                if is_back is None:
                    corner_size_factor = image_params.get(
                        'crop_corner_size_factor', CORNER_SIZE_FACTOR)
                    key_range_h = image_params.get('crop_key_range_h',
                                                   KEY_RANGE_HSV[0])
                    key_range_s = image_params.get('crop_key_range_S',
                                                   KEY_RANGE_HSV[1])
                    key_range_v = image_params.get('crop_key_range_v',
                                                   KEY_RANGE_HSV[2])
                    erosion_size = image_params.get('crop_erosion_size',
                                                    EROSION_SIZE)
                    dilation_size = image_params.get('crop_dilation_size',
                                                     DILATION_SIZE)
                    mask = get_background_mask(
                        img, corner_size_factor,
                        [key_range_h, key_range_s, key_range_v], erosion_size,
                        dilation_size)
                    alpha = cv2.erode(~mask,
                                      np.ones((5, 5), np.uint8),
                                      iterations=5)

                    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                    res = cv2.adaptiveThreshold(gray, 255,
                                                cv2.ADAPTIVE_THRESH_MEAN_C,
                                                cv2.THRESH_BINARY_INV, 7, 5.0)
                    res = cv2.bitwise_and(res, res, mask=alpha)
                    percentage = np.count_nonzero(res) / np.count_nonzero(
                        alpha)

                    is_back = percentage < args.threshold
                    print('%s -- % 5.1f%% -- %s' %
                          (short_filename, percentage * 100.0,
                           'back' if is_back else 'FRONT'))

                dst_dirpath = backs_dirpath if is_back else tmp_dirpath
                dst_filepath = os.path.join(dst_dirpath, short_filename)
                cv2.imwrite(dst_filepath, img)

            print('')
            print(
                'Please check the .temp-sides directory and move any back images to backs folder.'
            )
            print(
                'Then check the backs folder and make sure it doesn\'t contain any front images.'
            )
            input('Once all images are sorted, press enter to proceed.')

            for filename in os.listdir(tmp_dirpath):
                cr2_filepath = cr2_filepath_lookup.get(filename)
                if cr2_filepath:
                    print('%s - front' % filename)
                    write_param(cr2_filepath, 'side', 'front')

            for filename in os.listdir(backs_dirpath):
                cr2_filepath = cr2_filepath_lookup.get(filename)
                if cr2_filepath:
                    print('%s - back' % filename)
                    write_param(cr2_filepath, 'side', 'back')
Example #6
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        filepaths_by_ext = sort_files_by_ext(filepaths)
        cr2_files = filepaths_by_ext.get('.cr2', [])
        if cr2_files:
            seq = FileSequence.load(cr2_files)
            if seq:
                range_str = ', '.join(['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                print('%d image files found: %s' % (len(cr2_files), range_str))
            else:
                print('%d image files found.' % len(cr2_files))
        else:
            raise RuntimeError('No .cr2 image files found')

        xmp_files = filepaths_by_ext.get('.cr2.xmp', [])
        if xmp_files:
            seq = FileSequence.load(xmp_files)
            if seq:
                range_str = ', '.join(['(%d-%d)' % (lo, hi) for lo, hi in seq.ranges])
                print('%d sidecar files found: %s' % (len(xmp_files), range_str))
            else:
                print('%d sidecar files found.' % len(xmp_files))

            if not args.force:
                print('')
                print('These existing sidecar files contain edits made in darktable.')
                print('Applying these edits will DELETE these previously-made edits.')
                print('If you\'re sure you want to do that, re-run with -f.')
                return

        print('')
        print('Generating image cache...')
        generate_cache(images_dir)

        print('Regenerating .xmp sidecar files...')
        regenerate_xmps(images_dir)

        for image_filepath, cached_filepath in image_iterator(images_dir):
            iops = []

            image_params = read_params(image_filepath)
            try:
                crop_params = compute_crop_params(cached_filepath, image_params)
                print ('%s -> %r' % (os.path.basename(image_filepath), crop_params))

                iop_clipping = dt_iop_clipping_params_t()
                iop_clipping.crop_auto = 0
                iop_clipping.angle = crop_params['angle']
                iop_clipping.cx = crop_params['cx']
                iop_clipping.cy = crop_params['cy']
                iop_clipping.cw = crop_params['cw']
                iop_clipping.ch = crop_params['ch']
                iops.append(iop_clipping)
            except Exception as exc:
                print('WARNING: Failed to crop %s: %s' % (os.path.basename(image_filepath), exc))

            ev_delta = image_params.get('ev_delta')
            if ev_delta:
                iop_exposure = dt_iop_exposure_params_t()
                iop_exposure.exposure = ev_delta
                iops.append(iop_exposure)

            xmp_filepath = image_filepath + '.xmp'
            if iops:
                edit_xmp(xmp_filepath, iops)
                print('%s: %s' % (os.path.basename(xmp_filepath), ', '.join([iop.operation for iop in iops])))
            else:
                print('%s: <skipped>' % (os.path.basename(xmp_filepath)))

        print('Launching darktable. Reimport all changed .xmp files when prompted.')
        run_darktable([images_dir])
Example #7
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        images_dir = os.path.normpath(os.path.join(args.collection, args.subdir))
        _, filepaths = collect_dirs_and_files(images_dir)
        cr2_files = sort_files_by_ext(filepaths).get('.cr2', [])
        seq = FileSequence.load(cr2_files) if cr2_files else None
        if not seq:
            raise RuntimeError('No .cr2 image sequence found')

        cr2_filepath = seq.pattern % (args.image or seq.ranges[0][0])
        if not os.path.isfile(cr2_filepath):
            raise RuntimeError('No such file: %s' % cr2_filepath)
        cached_filepath = get_cached_image_filepath(cr2_filepath)
        if not os.path.isfile(cached_filepath):
            print('Generating cached image...')
            regenerate_cached_image(cr2_filepath)

        img = cv2.imread(cached_filepath)
        img = cv2.resize(img, (img.shape[1] // 4, img.shape[0] // 4))

        modes = ['original', 'color key', 'corners']
        mode_index = len(modes) - 1

        corner_size_factor = read_param(cr2_filepath, 'crop_corner_size_factor', 0.05)
        key_range_h = read_param(cr2_filepath, 'crop_key_range_h', 4.0)
        key_range_s = read_param(cr2_filepath, 'crop_key_range_s', 25.0)
        key_range_v = read_param(cr2_filepath, 'crop_key_range_v', 25.0)
        erosion_size = read_param(cr2_filepath, 'crop_erosion_size', 5)
        dilation_size = read_param(cr2_filepath, 'crop_dilation_size', 0)

        min_line_length_factor = read_param(cr2_filepath, 'crop_min_line_length_factor', 0.005)
        max_line_gap_factor = read_param(cr2_filepath, 'crop_max_line_gap_factor', 0.001)
        max_inclination_deg = read_param(cr2_filepath, 'crop_max_inclination_deg', 10.0)
        line_exclusion_size_factor = read_param(cr2_filepath, 'crop_line_exclusion_size_factor', 0.1)
        num_clusters = read_param(cr2_filepath, 'crop_num_clusters', 8)
        cluster_merge_threshold_size_factor = read_param(cr2_filepath, 'crop_cluster_merge_threshold_size_factor', 0.025)

        inset_interval = read_param(cr2_filepath, 'crop_inset_interval', 1.0)
        inset_white_threshold = read_param(cr2_filepath, 'crop_inset_white_threshold', 0.0025)
        extra_inset = read_param(cr2_filepath, 'crop_extra_inset', 8.0)

        save_prompted = False

        while True:
            mode = modes[mode_index]

            if mode_index >= modes.index('color key'):
                mask = get_background_mask(img, corner_size_factor, [key_range_h, key_range_s, key_range_v], erosion_size, dilation_size)

            if mode_index >= modes.index('corners'):
                rect_corners = find_rectilinear_corners(mask, min_line_length_factor, max_line_gap_factor, max_inclination_deg, line_exclusion_size_factor, num_clusters, cluster_merge_threshold_size_factor)
                corners = shrink_inside_mask(mask, rect_corners, inset_interval, inset_white_threshold, extra_inset * 0.25) if rect_corners else None

            base = None
            if mode == 'original':
                base = img.copy()
            elif mode == 'color key':
                base = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)
            elif mode == 'corners':
                base = img.copy()
                if corners:
                    top_left = int(corners[Corner.top_left][0]), int(corners[Corner.top_left][1])
                    top_right = int(corners[Corner.top_right][0]), int(corners[Corner.top_right][1])
                    bottom_left = int(corners[Corner.bottom_left][0]), int(corners[Corner.bottom_left][1])
                    bottom_right = int(corners[Corner.bottom_right][0]), int(corners[Corner.bottom_right][1])
                    cv2.line(base, top_left, top_right, (255, 192, 192), 2)
                    cv2.line(base, top_right, bottom_right, (255, 192, 192), 2)
                    cv2.line(base, bottom_right, bottom_left, (255, 192, 192), 2)
                    cv2.line(base, bottom_left, top_left, (255, 192, 192), 2)
            else:
                raise RuntimeError('Unsupported mode: %s' % mode)


            height, width = base.shape[0], base.shape[1]
            long_side = height if height > width else width
            short_side = width if height > width else height
            size = min(short_side // 2, max(1, int(long_side * corner_size_factor)))
            cv2.rectangle(base, (0, 0), (size, size), (255, 192, 128), 1)
            cv2.rectangle(base, (base.shape[1] - size, 0), (base.shape[1], size), (255, 192, 128), 1)
            cv2.rectangle(base, (0, base.shape[0] - size), (size, base.shape[0]), (255, 192, 128), 1)
            cv2.rectangle(base, (base.shape[1] - size, base.shape[0] - size), (base.shape[1], base.shape[0]), (255, 192, 128), 1)

            mode_labels = [('[%s]' % m) if m == mode else m for m in modes]
            controls_label(0, 'MODE (,|.): %s' % ' | '.join(mode_labels), base)

            controls_label(2, 'COLOR KEY:', base)
            float_control(3, corner_size_factor, 'corner_size', base, 'C')
            float_control(4, key_range_h, 'key_range_h', base, 'H')
            float_control(5, key_range_s, 'key_range_s', base, 'S')
            float_control(6, key_range_v, 'key_range_v', base, 'V')
            int_control(7, erosion_size, 'erosion_size', base, 'E')
            int_control(8, dilation_size, 'dilation_size', base, 'D')

            controls_label(10, 'CORNERS:', base)
            float_control(11, min_line_length_factor, 'min_line_length_factor', base, 'L')
            float_control(12, max_line_gap_factor, 'max_line_gap_factor', base, 'G')
            float_control(13, max_inclination_deg, 'max_inclination_deg', base, 'I')
            float_control(14, line_exclusion_size_factor, 'line_exclusion_size_factor', base, 'X')
            int_control(15, num_clusters, 'num_clusters', base, 'N')
            float_control(16, cluster_merge_threshold_size_factor, 'cluster_merge_threshold', base, 'M')
            float_control(17, inset_interval, 'inset_interval', base, 'T')
            float_control(18, inset_white_threshold, 'inset_white_threshold', base, 'W')
            float_control(19, extra_inset, 'extra_inset', base, 'Z')

            if save_prompted:
                draw_text(base, (400, 400), 'Press ENTER to save settings for all images in collection.')
                draw_text(base, (500, 420), '(Press any other key to cancel.)')

            cv2.imshow('image', base)
            key = cv2.waitKeyEx(0)

            if key in (27, ord('q'), ord('Q')):
                break

            if key == 13:
                if save_prompted:
                    num_saved = 0
                    for num in seq.numbers:
                        filepath = seq.pattern % num
                        params = read_params(filepath)
                        params['crop_corner_size_factor'] = corner_size_factor
                        params['crop_key_range_h'] = key_range_h
                        params['crop_key_range_s'] = key_range_s
                        params['crop_key_range_v'] = key_range_v
                        params['crop_erosion_size'] = erosion_size
                        params['crop_dilation_size'] = dilation_size
                        params['crop_min_line_length_factor'] = min_line_length_factor
                        params['crop_max_line_gap_factor'] = max_line_gap_factor
                        params['crop_max_inclination_deg'] = max_inclination_deg
                        params['crop_line_exclusion_size_factor'] = line_exclusion_size_factor
                        params['crop_num_clusters'] = num_clusters
                        params['crop_cluster_merge_threshold_size_factor'] = cluster_merge_threshold_size_factor
                        params['crop_inset_interval'] = inset_interval
                        params['crop_inset_white_threshold'] = inset_white_threshold
                        params['crop_extra_inset'] = extra_inset
                        write_params(filepath, params)
                        num_saved += 1
                    print('Wrote crop params for %d images.' % num_saved)
                    save_prompted = False
                else:
                    save_prompted = True
            else:
                save_prompted = False

            if key == ord(','):
                mode_index = (mode_index - 1) if mode_index > 0 else len(modes) - 1
            elif key == ord('.'):
                mode_index = (mode_index + 1) % len(modes)
            elif is_key(key, 'C'):
                incr = 0.01 if key == ord('c') else -0.01
                corner_size_factor += incr
            elif is_key(key, 'H'):
                incr = 0.1 if key == ord('h') else -0.1
                key_range_h += incr
            elif is_key(key, 'S'):
                incr = 0.1 if key == ord('s') else -0.1
                key_range_s += incr
            elif is_key(key, 'V'):
                incr = 0.1 if key == ord('v') else -0.1
                key_range_v += incr
            elif is_key(key, 'E'):
                incr = 1 if key == ord('e') else -1
                erosion_size += incr
            elif is_key(key, 'D'):
                incr = 1 if key == ord('d') else -1
                dilation_size += incr
            elif is_key(key, 'L'):
                incr = (1.0 if key == ord('l') else -1.0) * 0.001
                min_line_length_factor += incr
            elif is_key(key, 'G'):
                incr = (1.0 if key == ord('g') else -1.0) * 0.0001
                max_line_gap_factor += incr
            elif is_key(key, 'I'):
                incr = (1.0 if key == ord('i') else -1.0) * 1.0
                max_inclination_deg += incr
            elif is_key(key, 'X'):
                incr = (1.0 if key == ord('x') else -1.0) * 0.1
                line_exclusion_size_factor += incr
            elif is_key(key, 'N'):
                incr = (1 if key == ord('n') else -1)
                num_clusters += incr
            elif is_key(key, 'M'):
                incr = (1.0 if key == ord('m') else -1.0) * 0.025
                cluster_merge_threshold_size_factor += incr
            elif is_key(key, 'T'):
                incr = (1.0 if key == ord('t') else -1.0) * 0.1
                inset_interval += incr
            elif is_key(key, 'W'):
                incr = (1.0 if key == ord('w') else -1.0) * 0.001
                inset_white_threshold += incr
            elif is_key(key, 'Z'):
                incr = (1.0 if key == ord('z') else -1.0)
                extra_inset += incr

        cv2.destroyAllWindows()
Example #8
0
    def run(cls, args):
        print('Collection: %s' % os.path.basename(args.collection))
        print('    Subdir: %s' % (args.subdir or '<ROOT>'))
        print('')

        output_dir = os.path.normpath(
            os.path.join(args.collection, args.subdir))
        start_num = 1
        num_prefix = args.subdir[
            0] if args.subdir and args.subdir != 'images' else ''
        pattern = os.path.join(
            output_dir, '%s_%s' %
            (os.path.basename(args.collection), num_prefix)) + '%04d.CR2'

        print("Writing to: %s" % output_dir)
        _, filepaths = collect_dirs_and_files(output_dir)
        existing_cr2_files = sort_files_by_ext(filepaths).get('.cr2')
        if existing_cr2_files:
            print('%d existing images:' % len(existing_cr2_files))
            seq = FileSequence.load(existing_cr2_files)
            for range in seq.ranges:
                print(' - ranging from %d to %d' % range)
                start_num = range[1] + 1
                pattern = seq.pattern
        else:
            print('No existing images.')
        print('')
        prefix = os.path.basename(pattern[:pattern.rfind(r'%')])

        print('Capture will start from:')
        print(' - %s' % (pattern % start_num))
        print('')

        print('Pre-capture checklist:')
        print('')
        print('  1. Arrange lights.')
        print('  2. Disable OIS in lens.')
        print('  3. Ensure that lens zoom is not extreme.')
        print('  4. Ensure that camera height exceeds minimum macro distance.')
        print('  5. Set shooting mode to full manual.')
        print('  6. Find appropriate exposure around f/5.6.')
        print('  7. Take full-frame photo of 18% gray card.')
        print('  8. Set that photo as source for custom white balance.')
        print('  9. Set white balance mode to Custom.')
        print(' 10. Ensure that color-keyable background fills frame.')
        print('')
        if not args.force:
            print('Proceed?')
            choice = input('> ')
            if choice.lower() not in ('y', 'yes'):
                print("Abort.")
                return

        wait_for_device()

        if not os.path.isdir(output_dir):
            os.makedirs(output_dir)
            print("Created: %s" % output_dir)

        close_eos_windows()
        update_eos_config(output_dir, prefix, start_num)
        activate_liveview()

        print(
            "escape: exit | up/down/left/right: capture | backspace: delete last"
        )
        print("Waiting for input...")
        image_num = start_num
        delete_prompted = False
        for command in interactive_process(__shoot_keys__):
            if delete_prompted:
                if command == 'delete':
                    raw_filepath = pattern % (image_num - 1)
                    os.remove(raw_filepath)
                    print('Deleted %s' % raw_filepath)
                    xmp_filepath = raw_filepath + '.xmp'
                    if os.path.isfile(xmp_filepath):
                        os.remove(xmp_filepath)
                        print('Deleted %s' % xmp_filepath)
                    image_num -= 1
                    close_eos_windows()
                    update_eos_config(output_dir, prefix, image_num)
                    activate_liveview()
                else:
                    print('Cancelled delete.')
                delete_prompted = False
            elif command == 'delete':
                raw_filepath = pattern % (image_num - 1)
                if os.path.isfile(raw_filepath):
                    print('Press backspace again to delete %s...' %
                          os.path.basename(raw_filepath))
                    delete_prompted = True
                else:
                    print('No such file: %s' % raw_filepath)
            elif command.startswith('capture'):
                raw_filepath = pattern % image_num
                top_edge = command.replace('capture_', '')
                write_param(raw_filepath, 'top_edge', top_edge)
                print('%s --> %s' % (raw_filepath, top_edge))
                capture_liveview_photo()
                image_num += 1

        print('Finished.')
        close_eos_windows()
        print('Turn camera power off to conserve battery.')