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))
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)
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)
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)
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)
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))
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)
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
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)