Пример #1
0
def project_details(project):
    """
    Show the details for the selected project.
    """
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)

    stats = {}
    for class_name, tags in config['classes'].items():
        stats[class_name] = {}
        for split in SPLITS:
            videos_dir = directories.get_videos_dir(path, split, class_name)
            tags_dir = directories.get_tags_dir(path, split, class_name)
            stats[class_name][split] = {
                'total':
                len(os.listdir(videos_dir)),
                'tagged':
                len(os.listdir(tags_dir)) if os.path.exists(tags_dir) else 0,
            }

    return render_template('project_details.html',
                           config=config,
                           path=path,
                           stats=stats,
                           project=config['name'])
Пример #2
0
def testing_page(project):
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    output_path_prefix = os.path.join(os.path.basename(path), 'output_videos',
                                      '')

    classifiers = []
    checkpoints_path = os.path.join(path, 'checkpoints')
    if os.path.exists(checkpoints_path):
        # Add main checkpoints directory if best_classifier is available
        default_checkpoint = os.path.join(checkpoints_path,
                                          'best_classifier.checkpoint')
        if os.path.exists(default_checkpoint):
            classifiers.append(os.path.join('checkpoints', ''))

        # Add sub-directories of checkpoints directory if best_classifier is available in there
        classifiers.extend([
            os.path.join('checkpoints', d, '')
            for d in os.listdir(checkpoints_path)
            if os.path.isdir(os.path.join(checkpoints_path, d))
            and os.path.exists(
                os.path.join(checkpoints_path, d,
                             'best_classifier.checkpoint'))
        ])

        classifiers = natsorted(classifiers, alg=ns.IC)

    return render_template('testing.html',
                           project=project,
                           path=path,
                           output_path_prefix=output_path_prefix,
                           classifiers=classifiers)
Пример #3
0
def training_page(project):
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    project_config = project_utils.load_project_config(path)
    output_path_prefix = os.path.join(os.path.basename(path), 'checkpoints', '')
    return render_template('training.html', project=project, path=path, models=utils.get_available_backbone_models(),
                           output_path_prefix=output_path_prefix, project_config=project_config)
Пример #4
0
def save_video(project, split, label):
    project = urllib.parse.unquote(project)
    split = urllib.parse.unquote(split)
    label = urllib.parse.unquote(label)
    path = project_utils.lookup_project_path(project)

    # Read given video to a file
    input_stream = request.files['video']
    output_path = os.path.join(path, f'videos_{split}', label)
    temp_file_name = os.path.join(output_path, 'temp_video.webm')
    with open(temp_file_name, 'wb') as temp_file:
        temp_file.write(input_stream.read())

    # Find a video name that is not used yet
    existing_files = set(
        glob.glob(os.path.join(output_path, 'video_[0-9]*.mp4')))
    video_idx = 0
    output_file = os.path.join(output_path, f'video_{video_idx}.mp4')
    while output_file in existing_files:
        video_idx += 1
        output_file = os.path.join(output_path, f'video_{video_idx}.mp4')

    # Convert video to target frame rate and save to output name
    subprocess.call(f'ffmpeg -i "{temp_file_name}" -r 30 "{output_file}"',
                    shell=True)

    # Remove temp video file
    os.remove(temp_file_name)

    return jsonify(success=True)
Пример #5
0
def download_file(project, split, label, video_name, img_file):
    """
    Load an image from the given path.
    """
    dataset_path = project_utils.lookup_project_path(project)
    img_dir = os.path.join(
        directories.get_frames_dir(dataset_path, split, label), video_name)
    return send_from_directory(img_dir, img_file, as_attachment=True)
Пример #6
0
def edit_class(project, class_name):
    """
    Edit the class name and tags for an existing class in the given project.
    """
    project = urllib.parse.unquote(project)
    class_name = urllib.parse.unquote(class_name)
    path = project_utils.lookup_project_path(project)

    # Get new class name and tags
    new_class_name, new_tag1, new_tag2 = utils.get_class_name_and_tags(
        request.form)

    # Update project config
    config = project_utils.load_project_config(path)
    del config['classes'][class_name]
    config['classes'][new_class_name] = [new_tag1, new_tag2]
    project_utils.write_project_config(path, config)

    # Update directory names
    data_dirs = []
    for split in SPLITS:
        data_dirs.extend([
            directories.get_videos_dir(path, split),
            directories.get_frames_dir(path, split),
            directories.get_tags_dir(path, split),
        ])

        # Feature directories follow the format <dataset_dir>/<split>/<model>/<num_layers_to_finetune>/<label>
        features_dir = directories.get_features_dir(path, split)
        if os.path.exists(features_dir):
            model_dirs = [
                os.path.join(features_dir, model_dir)
                for model_dir in os.listdir(features_dir)
            ]
            data_dirs.extend([
                os.path.join(model_dir, tuned_layers)
                for model_dir in model_dirs
                for tuned_layers in os.listdir(model_dir)
            ])

    logreg_dir = directories.get_logreg_dir(path)
    if os.path.exists(logreg_dir):
        data_dirs.extend([
            os.path.join(logreg_dir, model_dir)
            for model_dir in os.listdir(logreg_dir)
        ])

    for base_dir in data_dirs:
        class_dir = os.path.join(base_dir, class_name)

        if os.path.exists(class_dir):
            new_class_dir = os.path.join(base_dir, new_class_name)
            os.rename(class_dir, new_class_dir)

    return redirect(url_for('project_details', project=project))
Пример #7
0
def project_config():
    """
    Provide the config for a given project.
    """
    data = request.json
    name = data['name']
    path = project_utils.lookup_project_path(name)

    # Get config
    config = project_utils.load_project_config(path)
    return jsonify(config)
Пример #8
0
def show_video_list(project, split, label):
    """
    Show the list of videos for the given split, class label and project.
    If the necessary files for annotation haven't been prepared yet, this is done now.
    """
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    split = urllib.parse.unquote(split)
    label = urllib.parse.unquote(label)

    # load feature extractor
    inference_engine, model_config = utils.load_feature_extractor(path)

    videos_dir = directories.get_videos_dir(path, split, label)
    frames_dir = directories.get_frames_dir(path, split, label)
    features_dir = directories.get_features_dir(path,
                                                split,
                                                model_config,
                                                label=label)
    tags_dir = directories.get_tags_dir(path, split, label)
    logreg_dir = directories.get_logreg_dir(path, model_config, label)

    os.makedirs(logreg_dir, exist_ok=True)
    os.makedirs(tags_dir, exist_ok=True)

    # compute the features and frames missing
    compute_frames_and_features(inference_engine=inference_engine,
                                project_path=path,
                                videos_dir=videos_dir,
                                frames_dir=frames_dir,
                                features_dir=features_dir)

    videos = os.listdir(frames_dir)
    videos = natsorted(videos, alg=ns.IC)

    tagged_list = set(os.listdir(tags_dir))
    tagged = [f'{video}.json' in tagged_list for video in videos]

    num_videos = len(videos)
    num_tagged = len(tagged_list)
    num_untagged = num_videos - num_tagged

    video_list = zip(videos, tagged, list(range(len(videos))))
    return render_template('video_list.html',
                           video_list=video_list,
                           split=split,
                           label=label,
                           path=path,
                           project=project,
                           num_videos=num_videos,
                           num_tagged=num_tagged,
                           num_untagged=num_untagged)
Пример #9
0
def edit_tag(project, tag_idx):
    data = request.form
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)
    tags = config['tags']
    new_tag_name = data['newTagName']

    # Update tag name
    tags[tag_idx] = new_tag_name

    project_utils.write_project_config(path, config)
    return redirect(url_for('project_details', project=project))
Пример #10
0
def remove_class(project, class_name):
    """
    Remove the given class from the config file of the given project. No data will be deleted.
    """
    project = urllib.parse.unquote(project)
    class_name = urllib.parse.unquote(class_name)
    path = project_utils.lookup_project_path(project)

    # Update project config
    config = project_utils.load_project_config(path)
    del config['classes'][class_name]
    project_utils.write_project_config(path, config)

    return redirect(url_for("project_details", project=project))
Пример #11
0
def create_tag(project):
    data = request.form
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)
    tag_name = data['newTagName']

    tags = config['tags']
    new_tag_index = config['max_tag_index'] + 1
    tags[new_tag_index] = tag_name
    config['max_tag_index'] = new_tag_index

    project_utils.write_project_config(path, config)
    return redirect(url_for('project_details', project=project))
Пример #12
0
def remove_tag(project, tag_idx):
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)
    tags = config['tags']

    # Remove tag from the overall tags list
    del tags[tag_idx]

    # Remove tag from the classes
    for class_label, class_tags in config['classes'].items():
        if tag_idx in class_tags:
            class_tags.remove(tag_idx)

    project_utils.write_project_config(path, config)
    return redirect(url_for('project_details', project=project))
Пример #13
0
def record_video(project, split, label):
    """
    Display the video recording screen.
    """
    project = urllib.parse.unquote(project)
    split = urllib.parse.unquote(split)
    label = urllib.parse.unquote(label)
    path = project_utils.lookup_project_path(project)

    countdown, recording = project_utils.get_timer_default(path)

    return render_template('video_recording.html',
                           project=project,
                           split=split,
                           label=label,
                           path=path,
                           countdown=countdown,
                           recording=recording)
Пример #14
0
def send_training_logs(msg):
    global train_process
    global queue_train_logs
    global confmat_event

    try:
        while train_process.is_alive():
            try:
                output = queue_train_logs.get(timeout=1)
                emit('training_logs', {'log': output})
            except queue.Empty:
                # No message received during the last second
                pass

        train_process.terminate()
        train_process = None
    except AttributeError:
        # train_process has been cancelled and is None
        pass
    finally:
        queue_train_logs.close()

    project = msg['project']
    output_folder = msg['outputFolder']

    path = project_utils.lookup_project_path(project)
    img_path = os.path.join(path, 'checkpoints', output_folder,
                            'confusion_matrix.png')
    if confmat_event.is_set() and os.path.exists(img_path):
        with open(img_path, 'rb') as f:
            data = f.read()
        img_base64 = base64.b64encode(data).decode('utf-8')
        if img_base64:
            emit('success', {
                'status': 'Complete',
                'img': f'data:image/png;base64,{img_base64}'
            })
        else:
            emit('failed', {'status': 'Failed'})
    else:
        emit('failed', {'status': 'Failed'})
Пример #15
0
def add_class(project):
    """
    Add a new class to the given project.
    """
    data = request.form
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    class_name = data['className']

    # Update project config
    config = project_utils.load_project_config(path)
    config['classes'][class_name] = []
    project_utils.write_project_config(path, config)

    # Setup directory structure
    for split in SPLITS:
        videos_dir = directories.get_videos_dir(path, split, class_name)

        if not os.path.exists(videos_dir):
            os.mkdir(videos_dir)

    return redirect(url_for("project_details", project=project))
Пример #16
0
def project_details(project):
    """
    Show the details for the selected project.
    """
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)

    stats = {}
    for class_name in config['classes']:
        stats[class_name] = {}
        for split in SPLITS:
            videos_dir = directories.get_videos_dir(path, split, class_name)
            tags_dir = directories.get_tags_dir(path, split, class_name)
            stats[class_name][split] = {
                'total': len(os.listdir(videos_dir)),
                'tagged': len(os.listdir(tags_dir)) if os.path.exists(tags_dir) else 0,
                'videos': natsorted([video for video in os.listdir(videos_dir) if video.endswith(VIDEO_EXT)], alg=ns.IC)
            }
    tags = config['tags']
    return render_template('project_details.html', config=config, path=path, stats=stats, project=config['name'],
                           tags=tags)
Пример #17
0
def flip_videos():
    """
    Flip the videos horizontally for given class and
    copy tags of selected original videos for flipped version of it.
    """
    data = request.json
    project = data['projectName']
    path = project_utils.lookup_project_path(project)
    config = project_utils.load_project_config(path)
    counterpart_class_name = str(data['counterpartClassName'])
    original_class_name = str(data['originalClassName'])
    copy_video_tags = data['videosToCopyTags']

    if counterpart_class_name not in config['classes']:
        config['classes'][counterpart_class_name] = config['classes'][original_class_name] \
            if copy_video_tags['train'] or copy_video_tags['valid'] else []
        project_utils.write_project_config(path, config)

    for split in SPLITS:
        videos_path_in = os.path.join(path, f'videos_{split}', original_class_name)
        videos_path_out = os.path.join(path, f'videos_{split}', counterpart_class_name)
        original_tags_path = os.path.join(path, f'tags_{split}', original_class_name)
        counterpart_tags_path = os.path.join(path, f'tags_{split}', counterpart_class_name)

        # Create directory to save flipped videos
        os.makedirs(videos_path_out, exist_ok=True)
        os.makedirs(counterpart_tags_path, exist_ok=True)

        video_list = [video for video in os.listdir(videos_path_in) if video.endswith(VIDEO_EXT)]

        for video in video_list:
            output_video_list = [op_video for op_video in os.listdir(videos_path_out) if op_video.endswith(VIDEO_EXT)]
            print(f'Processing video: {video}')
            if '_flipped' in video:
                flipped_video_name = ''.join(video.split('_flipped'))
            else:
                flipped_video_name = video.split('.')[0] + '_flipped' + VIDEO_EXT

            if flipped_video_name not in output_video_list:
                # Original video as input
                original_video = ffmpeg.input(os.path.join(videos_path_in, video))
                # Do horizontal flip
                flipped_video = ffmpeg.hflip(original_video)
                # Get flipped video output
                flipped_video_output = ffmpeg.output(flipped_video,
                                                     filename=os.path.join(videos_path_out, flipped_video_name))
                # Run to render and save video
                ffmpeg.run(flipped_video_output)

                # Copy tags of original video to flipped video (in train/valid set)
                if video in copy_video_tags[split]:
                    original_tags_file = os.path.join(original_tags_path, video.replace(VIDEO_EXT, '.json'))
                    flipped_tags_file = os.path.join(counterpart_tags_path,
                                                     flipped_video_name.replace(VIDEO_EXT, '.json'))

                    if os.path.exists(original_tags_file):
                        with open(original_tags_file) as f:
                            original_video_tags = json.load(f)
                        original_video_tags['file'] = flipped_video_name
                        with open(flipped_tags_file, 'w') as f:
                            f.write(json.dumps(original_video_tags, indent=2))

    print("Processing complete!")
    return jsonify(status=True, url=url_for("project_details", project=project))
Пример #18
0
def annotate(project, split, label, idx):
    """
    For the given class label, show all frames for annotating the selected video.
    """
    project = urllib.parse.unquote(project)
    path = project_utils.lookup_project_path(project)
    label = urllib.parse.unquote(label)
    split = urllib.parse.unquote(split)

    _, model_config = utils.load_feature_extractor(path)

    frames_dir = directories.get_frames_dir(path, split, label)
    features_dir = directories.get_features_dir(path,
                                                split,
                                                model_config,
                                                label=label)
    tags_dir = directories.get_tags_dir(path, split, label)
    logreg_dir = directories.get_logreg_dir(path, model_config, label)

    videos = os.listdir(frames_dir)
    videos = natsorted(videos, alg=ns.IC)

    # The list of images in the folder
    images = [
        image
        for image in glob.glob(os.path.join(frames_dir, videos[idx], '*'))
        if utils.is_image_file(image)
    ]
    classes = [-1] * len(images)

    # Load logistic regression model if available and assisted tagging is enabled
    if utils.get_project_setting(path, 'assisted_tagging'):
        logreg_path = os.path.join(logreg_dir, 'logreg.joblib')
        features_path = os.path.join(features_dir, f'{videos[idx]}.npy')
        if os.path.isfile(logreg_path) and os.path.isfile(features_path):
            logreg = load(logreg_path)
            features = np.load(features_path).mean(axis=(2, 3))
            classes = list(logreg.predict(features))

    # Natural sort images, so that they are sorted by number
    images = natsorted(images, alg=ns.IC)
    # Extract image file name (without full path) and include class label
    images = [(os.path.basename(image), _class)
              for image, _class in zip(images, classes)]

    # Load existing annotations
    annotations_file = os.path.join(tags_dir, f'{videos[idx]}.json')
    if os.path.exists(annotations_file):
        with open(annotations_file, 'r') as f:
            data = json.load(f)
            annotations = data['time_annotation']
    else:
        # Use "background" label for all frames per default
        annotations = [0] * len(images)

    # Read tags from config
    config = project_utils.load_project_config(path)
    tags = config['classes'][label]

    return render_template('frame_annotation.html',
                           images=images,
                           annotations=annotations,
                           idx=idx,
                           fps=16,
                           n_images=len(images),
                           video_name=videos[idx],
                           project_config=config,
                           split=split,
                           label=label,
                           path=path,
                           tags=tags,
                           project=project,
                           n_videos=len(videos))
Пример #19
0
 def inject_project_config(project):
     path = project_utils.lookup_project_path(project)
     return project_utils.load_project_config(path)