예제 #1
0
def delete_entry(str_id):
    # For return path
    id = ObjectId(str_id)
    sample = db.get_sample_by_id(id)
    if sample is None:
        set_error('Item to delete not found.')
        return redirect('/')
    dataset_id = sample['dataset_id']
    readonly = db.is_readonly_dataset_id(dataset_id)
    sample_index, sample_count, prev_sample_id, next_sample_id = db.get_sample_index(dataset_id, id)
    if readonly:
        set_error('Dataset is protected.')
    elif db.delete_sample(id):
        set_notice('Item deleted.')
    else:
        set_error('Could not delete item.')
    # Redirect: To next sample in set if it exists. Otherwise to the dataset page.
    if next_sample_id is not None and next_sample_id != id:
        # Return to same kind of page (info/annotation/diff_annotation)
        is_annotation = request.args.get('annotate')
        is_differential = request.args.get('differential')
        if is_annotation:
            suffix = '?differential=1' if is_differential else ''
            return redirect('/annotate/' + str(next_sample_id) + suffix)
        else:
            return redirect('/info/' + str(next_sample_id))
    else:
        return redirect('/dataset/' + str(dataset_id))
예제 #2
0
def compute_stomata_positions(machine_annotation,
                              heatmap_image,
                              plot=False,
                              do_contour=False,
                              do_peaks=True,
                              prob_threshold=default_prob_threshold,
                              prob_area_threshold=default_prob_area_threshold):
    # Load heatmap + image
    heatmap_filename = os.path.join(config.get_server_heatmap_path(),
                                    machine_annotation['heatmap_filename'])
    print 'Counting thresh %f on heatmap %s' % (prob_threshold,
                                                heatmap_filename)
    sample_info = db.get_sample_by_id(machine_annotation['sample_id'])
    # Derive zoom
    data = np.load(heatmap_filename)
    return compute_stomata_positions_on_prob(
        probs=data['probs'],
        scale=data['scale'],
        margin=machine_annotation['margin'],
        sample_size=sample_info['size'],
        heatmap_image=heatmap_image,
        plot=plot,
        do_contour=do_contour,
        do_peaks=do_peaks,
        prob_threshold=prob_threshold,
        prob_area_threshold=prob_area_threshold)
예제 #3
0
def admin_page():
    num_images = db.get_sample_count()
    num_human_annotations = db.get_human_annotation_count()
    datasets = db.get_datasets()
    enqueued = db.get_unprocessed_samples()
    models = db.get_models(details=True)
    model_id_to_name = {m['_id']: m['name'] for m in models}
    secondary_items = db.get_queued_samples()
    enqueued2 = []
    for item in secondary_items:
        model_name = model_id_to_name.get(item['model_id'], '???')
        if 'sample_id' in item:
            target_name = db.get_sample_by_id(item['sample_id'])['filename']
        elif 'validation_model_id' in item:
            target_name = model_id_to_name.get(item['validation_model_id'],
                                               '?!?')
        else:
            target_name = '!!!'
        enqueued2.append((model_name, target_name, str(item['_id'])))
    enqueued2 = sorted(enqueued2, key=lambda item: item[0])
    status = [(status_name, db.get_status(status_id))
              for status_name, status_id in status_ids]
    return render_template('admin.html',
                           num_images=num_images,
                           num_human_annotations=num_human_annotations,
                           datasets=datasets,
                           enqueued=enqueued,
                           status=status,
                           error=pop_last_error(),
                           models=models,
                           enqueued2=enqueued2)
예제 #4
0
def compute_stomata_positions_for_sample(sample_id, plot=False):
    machine_annotations = db.get_machine_annotations(sample_id)
    sample = db.get_sample_by_id(sample_id)
    image_filename = os.path.join(config.server_image_path, sample['filename'])
    for machine_annotation in machine_annotations:
        heatmap_image_filename = os.path.join(
            config.server_heatmap_path,
            machine_annotation['heatmap_image_filename'])
        heatmap_filename = os.path.join(config.server_heatmap_path,
                                        machine_annotation['heatmap_filename'])
        #plot_heatmap(image_filename, heatmap_filename, heatmap_image_filename)
        print heatmap_image_filename
        heatmap_image = imread(heatmap_image_filename)
        positions = compute_stomata_positions(machine_annotation,
                                              heatmap_image,
                                              plot=plot)
예제 #5
0
def annotate(sid):
    # Query info from DB
    sample_id = ObjectId(sid)
    sample_entry = db.get_sample_by_id(sample_id)
    dataset_id = sample_entry['dataset_id']
    name = sample_entry['name']
    readonly = db.is_readonly_dataset_id(dataset_id)
    sample_index, sample_count, prev_sample_id, next_sample_id = db.get_sample_index(
        dataset_id, sample_id)
    # Unknown entry?
    if sample_entry is None:
        return error_redirect('Unknown entry: "%s".' % str(sample_id))
    # Determine data
    image_filename = 'images/' + sample_entry['filename']
    info_string = ''
    # Differential? Then load machine annotations, unless there are human annotations already
    annotations = db.get_human_annotations(sample_id)
    is_differential = request.args.get('differential')
    if is_differential and not annotations:
        annotations = db.get_machine_annotations(sample_id)
    if len(annotations):
        annotations_json = annotations[0]['positions']
        # Machine annotations are in a different format
        if len(annotations_json) and isinstance(annotations_json[0], list):
            annotations_json = [{
                'x': a[0],
                'y': a[1]
            } for a in annotations_json]
    else:
        annotations_json = []
    return render_template("annotate.html",
                           id=sid,
                           image_filename=image_filename,
                           info_string=info_string,
                           error=pop_last_error(),
                           height=sample_entry['size'][1],
                           width=sample_entry['size'][0],
                           annotations=annotations_json,
                           margin=96,
                           dataset_id=str(dataset_id),
                           sample_index=sample_index,
                           sample_count=sample_count,
                           prev_id=str(prev_sample_id),
                           next_id=str(next_sample_id),
                           is_differential=is_differential,
                           readonly=readonly,
                           name=name)
예제 #6
0
def process_image_sample(net, model_id, sample_id, is_primary_model):
    sample = db.get_sample_by_id(sample_id)
    dataset_info = db.get_dataset_by_id(sample['dataset_id'])
    image_zoom_values = default_image_zoom_values.get(dataset_info.get('image_zoom'))
    image_filename = sample['filename']
    set_status('Processing %s...' % image_filename, secondary=not is_primary_model)
    image_filename_full = os.path.join(config.server_image_path, image_filename)
    if not os.path.isfile(image_filename_full):
        db.set_sample_error(sample['_id'], 'File does not exist: "%s".' % image_filename_full)
        return
    basename, ext = os.path.splitext(image_filename)
    if not ext.lower() in config.image_extensions:
        db.set_sample_error(sample['_id'], 'Unknown file extension "%s".' % ext)
        return
    try:
        # Lots of saving and loading here. TODO: Should be optimized to be done all in memory.
        # Determine output file paths
        heatmap_filename = os.path.join(net.name, basename + '_heatmap.npz')
        heatmap_filename_full = os.path.join(config.server_heatmap_path, heatmap_filename)
        if not os.path.isdir(os.path.dirname(heatmap_filename_full)):
            os.makedirs(os.path.dirname(heatmap_filename_full))
        heatmap_image_filename = os.path.join(net.name, basename + '_heatmap.jpg')
        heatmap_image_filename_full = os.path.join(config.server_heatmap_path, heatmap_image_filename)
        # Process image
        data = process_image_file(net, image_filename_full, heatmap_filename_full, scales=image_zoom_values)
        plot_heatmap(image_filename_full, heatmap_filename_full, heatmap_image_filename_full)
        if 'imq_entropy' not in sample:
            imq = get_image_measures(image_filename_full)
            db.set_image_measures(sample['_id'], imq)
        positions = [] # Computed later
        machine_annotation = db.add_machine_annotation(sample['_id'], model_id, heatmap_filename,
                                                       heatmap_image_filename, positions,
                                                       margin=int(net.margin / data['scale']),
                                                       is_primary_model=is_primary_model,
                                                       scale=data['scale'])
        # Count stomata
        heatmap_image = plt.imread(heatmap_image_filename_full)
        positions = compute_stomata_positions(machine_annotation, heatmap_image, plot=False)
        db.update_machine_annotation_positions(sample['_id'], machine_annotation['_id'], positions,
                                               is_primary_model=is_primary_model)
        plt.imsave(heatmap_image_filename_full, heatmap_image)
        print 'Finished record.'
    except:
        error_string = traceback.format_exc()
        db.set_sample_error(sample['_id'], "Processing error:\n" +str(error_string))
예제 #7
0
def save_annotations(sid):
    sample_id = ObjectId(sid)
    sample_entry = db.get_sample_by_id(sample_id)
    # Caution: Load dataset id from DB (not from form), for security of the readonly check!
    # dataset_id = ObjectId(request.form['dataset_id'].strip())
    dataset_id = sample_entry['dataset_id']
    readonly = db.is_readonly_dataset_id(dataset_id)
    if readonly:
        set_error('Dataset is protected.')
        return redirect('/dataset/' + str(dataset_id))
    is_differential = request.args.get('differential')
    if is_differential:
        base_annotations = db.get_machine_annotations(sample_id)
        redirect_params = '?differential=1'
    else:
        base_annotations = None
        redirect_params = ''
    # Save annotations for sample
    annotations = json.loads(request.form['annotations'].strip())
    margin = int(request.form['margin'].strip())
    print 'Saving annotations.', sid, margin, annotations
    db.set_human_annotation(sample_id,
                            get_current_user_id(),
                            annotations,
                            margin,
                            base_annotations=base_annotations)
    # Forward either to info page or to annotation of next un-annotated entry in DB if found
    annotate_next = ("save_and_continue" in request.form)
    if annotate_next:
        next_sample_id = db.get_next_sample_id(dataset_id,
                                               sample_id,
                                               annotated=False)
        if next_sample_id is not None:
            return redirect('/annotate/' + str(next_sample_id) +
                            redirect_params)
        else:
            set_error('No more samples to annotate.')
            return redirect('/dataset/' + str(dataset_id))
    # Nothing
    return redirect('/info/' + sid)
예제 #8
0
def compute_stomata_positions(machine_annotation,
                              heatmap_image,
                              plot=False,
                              do_contour=False,
                              do_peaks=True):
    # Load heatmap + image
    heatmap_filename = os.path.join(config.server_heatmap_path,
                                    machine_annotation['heatmap_filename'])
    sample_info = db.get_sample_by_id(machine_annotation['sample_id'])
    # Derive zoom
    data = np.load(heatmap_filename)
    probs = data['probs']
    scale = data['scale']
    margin = machine_annotation['margin']
    zoom = float(sample_info['size'][0] - 2 * margin) / probs.shape[0]
    positions = []

    if do_contour and do_peaks:
        heatmap_image2 = np.copy(heatmap_image)
    else:
        heatmap_image2 = heatmap_image

    if do_peaks:
        probs_threshed = probs.copy()
        probs_threshed[probs < prob_threshold] = 0.0
        peaks = detect_peaks(probs_threshed)
        peak_coords = np.nonzero(peaks)
        for c in zip(peak_coords[0], peak_coords[1]):
            thresh = probs[c[0], c[1]]
            thresh_p = np.exp(thresh) / (np.exp(thresh) + np.exp(-thresh))
            pos = c
            radius = 3
            pos_zoomed = tuple(int(x * zoom + margin + zoom / 2) for x in pos)
            radius_zoomed = int(radius * zoom)
            positions += [pos_zoomed]
            cv2.circle(heatmap_image2,
                       center=pos_zoomed,
                       radius=radius_zoomed,
                       color=(255, 255, 0),
                       thickness=4)
            font = cv2.FONT_HERSHEY_SIMPLEX
            cv2.putText(heatmap_image2, '%.3f' % thresh_p, pos_zoomed, font,
                        1.0, (127, 255, 0), 2, cv2.LINE_AA)

    if do_contour:
        pthresh = (probs >= prob_threshold).astype(np.uint8).copy()
        all_contours = cv2.findContours(pthresh, cv2.RETR_EXTERNAL,
                                        cv2.CHAIN_APPROX_SIMPLE)[-2]
        large_contours = [
            c for c in all_contours if cv2.contourArea(c) > prob_area_threshold
        ]
        for c in large_contours:
            (pos, radius) = cv2.minEnclosingCircle(c)
            pos_zoomed = tuple(int(x * zoom + margin + zoom / 2)
                               for x in pos)[::-1]
            radius_zoomed = int(radius * zoom)
            positions += [pos_zoomed]
            cv2.circle(heatmap_image,
                       center=pos_zoomed,
                       radius=radius_zoomed,
                       color=(255, 255, 0),
                       thickness=4)

    print 'Found %d stomata' % len(positions)

    if plot:
        f, axarr = plt.subplots(2, 3, figsize=(12, 7))
        if do_contour:
            axarr[0, 0].matshow(probs)
            axarr[0, 1].matshow(pthresh)
            axarr[0, 2].imshow(heatmap_image.transpose((1, 0, 2)))
        if do_peaks:
            axarr[1, 0].matshow(probs_threshed)
            axarr[1, 1].matshow(peaks)
            axarr[1, 2].imshow(heatmap_image2.transpose((1, 0, 2)))
        f.suptitle('%d Stomata' % len(positions))
        plt.figure()
        plt.imshow(heatmap_image2)
        plt.show()
    return positions
예제 #9
0
def show_info(sid):
    # Query info from DB
    sample_id = ObjectId(sid)
    sample_entry = db.get_sample_by_id(sample_id)
    sample_index, sample_count, prev_sample_id, next_sample_id = db.get_sample_index(sample_entry['dataset_id'],
                                                                                     sample_id)
    # Unknown entry?
    if sample_entry is None:
        return error_redirect('Unknown entry: "%s".' % str(sample_id))
    # Allow fixing of machine annotation? Only if not human-annotated and there is a machine annotation with stomata
    can_annotate_diff = (not sample_entry.get('annotated')) and (sample_entry.get('machine_position_count'))
    # Determine data
    refresh = False
    filename = sample_entry['filename']
    name = sample_entry['name']
    dataset_id = sample_entry['dataset_id']
    readonly = db.is_readonly_dataset_id(dataset_id)
    info_table = []
    for info_name, info_key in info_table_entries:
        info_value = sample_entry.get(info_key)
        if info_value is not None:
            info_table.append((info_name, info_value))
    annotations = []
    if sample_entry['error']:
        info_string = Markup('Error: <pre>' + sample_entry['error_string'] + '</pre>')
    elif not sample_entry['processed']:
        info_string = 'Processing...'
        refresh = True
    else:
        info_string = 'Processed.'
    annotation_data = db.get_machine_annotations(sample_id) + db.get_human_annotations(sample_id)
    has_image_output = False
    for ad in annotation_data:
        model_id = ad.get('model_id')
        an = {'info_string': ''}
        if model_id is not None:
            model_data = db.get_model_by_id(model_id)
            if model_data is None:
                an['info_string'] += '??? Unknown model ???'
                an['title'] = 'Unknown'
            else:
                an['title'] = model_data['name']
                an['info_string'] += 'Margin: %d' % (ad.get('margin') or model_data['margin'])
            an['image_filename'] = 'heatmaps/' + ad['heatmap_image_filename']
            has_image_output = True
        else:
            an['title'] = 'By user %s' % ad.get('user_name')
        if 'scale' in ad:
            an['info_string'] += ' - Scale: %.1f' % ad['scale']
        positions = ad['positions']
        if positions is not None:
            an['info_string'] += ' - %d stomata' % len(positions)
        annotations += [an]
    annotations = list(reversed(annotations))
    if not has_image_output:
        annotations += [{'image_filename': 'images/' + filename, 'title': 'Input image', 'info_string': ''}]
    return render_template("info.html", id=sid, name=name, dataset_id=str(dataset_id), info_string=info_string,
                           annotations=annotations, error=pop_last_error(), refresh=refresh,
                           sample_index=sample_index, sample_count=sample_count, prev_id=str(prev_sample_id),
                           next_id=str(next_sample_id), can_annotate_diff=can_annotate_diff, readonly=readonly,
                           info_table=info_table)