def label_main(request, label_id): """ Main page for a particular label """ label = get_object_or_404(Label, id=label_id) sources_with_label = Source.objects.filter(labelset__labels=label).order_by('name') visible_sources_with_label = [s for s in sources_with_label if s.visible_to_user(request.user)] # Differentiate between the sources that the user is part of # and the other public sources. Sort the source list accordingly, too. sources_of_user = Source.get_sources_of_user(request.user) source_types = [] for s in visible_sources_with_label: if s in sources_of_user: source_types.append('mine') else: source_types.append('public') visible_sources_with_label = zip(source_types, visible_sources_with_label) visible_sources_with_label.sort(key=lambda x: x[0]) # Mine first, then public # Example patches. # TODO: don't hardcode the patch path example_annotations = Annotation.objects.filter(label=label, image__source__visibility=Source.VisibilityTypes.PUBLIC).exclude(user=get_robot_user()).order_by('?')[:5] patches = [dict( annotation=a, fullImage=a.image, source=a.image.source, patchPath="data/annotations/" + str(a.id) + ".jpg", row=a.point.row, col=a.point.column, pointNum=a.point.point_number, ) for a in example_annotations] for p in patches: generate_patch_if_doesnt_exist(p['patchPath'], p['annotation']) return render_to_response('annotations/label_main.html', { 'label': label, 'visible_sources_with_label': visible_sources_with_label, 'patches': patches, }, context_instance=RequestContext(request) )
def visualize_source(request, source_id): """ View for browsing through a source's images. """ ITEMS_PER_PAGE = 20 errors = [] source = get_object_or_404(Source, id=source_id) metadata_view_available = request.user.has_perm(Source.PermTypes.EDIT.code, source) # This is used to create a formset out of the metadataForm. I need to do this # in order to pass in the source id. # From http://stackoverflow.com/a/624013 metadataFormSet = formset_factory(MetadataForm) metadataFormSet.form = staticmethod(curry(MetadataForm, source_id=source_id)) # There is a separate form that controls the checkboxes. checkboxFormSet = formset_factory(CheckboxForm) # Based on submitted GET/POST data, find the following: # - Which view mode we're showing # - Which images we're showing (if image/meta mode), or # filtering the patches by (if patch mode) # - Other parameters # Defaults # Search form to show on the page search_form = BrowseSearchForm(source_id, metadata_view_available) # GET args in url format - for constructing prev page/next page links urlArgsStr = '' # View mode to show the Browse page in page_view = 'images' # (Patch mode only) the label we want to see patches of label = '' # (Patch mode only) show patches annotated by human, by machine, or by either one annotated_by = 'human' image_specify_form = ImageSpecifyForm( dict( specify_method='search_keys', specify_str=json.dumps(dict()), # No filters, just get all images ), source=source, ) if request.POST and 'image_specify_form_from_upload' in request.POST: # Coming from the upload page. # Let the user edit the metadata of the images they just uploaded. page_view = 'metadata' # Make the search form's page_view field value accurate. search_form = BrowseSearchForm( source_id, metadata_view_available, initial=dict(page_view=page_view), ) # We expect the upload page to properly fill in an ImageSpecifyForm # with the image ids. image_specify_form = ImageSpecifyForm(request.POST, source=source) # Defaults on everything else elif request.GET: # Search form submitted OR # a URL with GET parameters was entered. submitted_search_form = BrowseSearchForm( source_id, metadata_view_available, request.GET, ) if submitted_search_form.is_valid(): # Have the search form on the page start with the values # submitted in this search. search_form = submitted_search_form # Some search form parameters are used for things other than image # filtering. Get these parameters. # # If any of these parameters are u'', then that parameter wasn't # submitted. Don't let that '' override the default value. if search_form.cleaned_data['page_view'] != u'': page_view = search_form.cleaned_data['page_view'] if search_form.cleaned_data['label'] != u'': label = search_form.cleaned_data['label'] if search_form.cleaned_data['annotated_by'] != u'': annotated_by = search_form.cleaned_data['annotated_by'] # We're going to serialize this cleaned_data and give it # to an ImageSpecifyForm. A bit of cleanup to do first though. # The Label object isn't serializable, so avoid an error by # popping off this key first. search_form.cleaned_data.pop('label') if page_view == 'patches': # If in patch mode, the image_status filter doesn't apply. # Pop off this filter to make sure the ImageSpecifyForm # doesn't use it. search_form.cleaned_data.pop('image_status') # Give the search form data to the ImageSpecifyForm. Note that # with the exception of the above parameters, any parameters # not used for filtering images will just be ignored and won't cause problems. image_specify_form = ImageSpecifyForm( dict( specify_method='search_keys', specify_str=json.dumps(search_form.cleaned_data), ), source=source, ) else: messages.error(request, 'Error: invalid search parameters.') # Use the defaults else: # Just got to this page via a link, with no GET parameters in the URL. # Use the defaults pass image_results = [] delete_form = None download_form = None if image_specify_form.is_valid(): image_results = image_specify_form.get_images() # Create image delete and image download forms if they're supposed # to be displayed on the page. # # Give the forms the same stuff as the image specify form, so they # know which images to operate on. if page_view == 'images': if request.user.has_perm(Source.PermTypes.EDIT.code, source): delete_form = ImageBatchDeleteForm( image_specify_form.data, source=source, ) if request.user.has_perm(Source.PermTypes.VIEW.code, source): download_form = ImageBatchDownloadForm( image_specify_form.data, source=source, ) else: errors.append("Error: something went wrong with this image search.") # Get the search results (all of them, not just on this page). if page_view == 'images' or page_view == 'metadata': # We'll display the images on the page. Sort them by image id # (highest id first). all_items = image_results.order_by('-pk') else: # patches # Get the annotations in the source that: # - contain the label # - meet the "annotated_by" constraint # # And get the annotations in random order. # # Performance consideration: image__in=<queryset> might be slow in # MySQL. If in doubt, test/benchmark it. # https://docs.djangoproject.com/en/1.5/ref/models/querysets/#in annotations = Annotation.objects.filter(source=source, label=label, image__in=image_results).order_by('?') if annotated_by == 'either': # No further filtering needed pass elif annotated_by == 'machine': annotations = annotations.filter(user=get_robot_user()) else: # 'human' annotations = annotations.exclude(user=get_robot_user()) # Placeholder the image patches with the Annotation objects for now. # We'll actually get the patches when we know which page we're showing. all_items = annotations num_of_total_results = len(all_items) if num_of_total_results == 0: # No image results in this search errors.append("No image results.") # If we're in one of the paginated views, find out what the # results on this page are. page_results = None prev_page_link = None next_page_link = None if page_view == 'images' or page_view == 'patches': paginator = Paginator(all_items, ITEMS_PER_PAGE) # Make sure page request is an int. If not, deliver first page. try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 # If page request (9999) is out of range, deliver last page of results. try: page_results = paginator.page(page) except (EmptyPage, InvalidPage): page_results = paginator.page(paginator.num_pages) # If there are previous or next pages, construct links to them. # These links include GET parameters in the form of # ?param1=value1¶m2=value2 etc. if page_results.has_previous(): prev_page_query_args = request.GET.copy() prev_page_query_args.update(dict(page=page_results.previous_page_number())) prev_page_link = '?' + urllib.urlencode(prev_page_query_args) if page_results.has_next(): next_page_query_args = request.GET.copy() next_page_query_args.update(dict(page=page_results.next_page_number())) next_page_link = '?' + urllib.urlencode(next_page_query_args) # Finalize the data-structure that holds this page's results. if page_view == 'patches': # Get an image-patch for each result on the page. # Earlier we placeholdered the image patches with the annotation objects, # so we're iterating over those annotations now. for index, annotation in enumerate(page_results.object_list): # TODO: don't hardcode the patch path # (this might also apply to the label_main view) patchPath = "data/annotations/" + str(annotation.id) + ".jpg" page_results.object_list[index] = dict( fullImage=annotation.image, patchPath=patchPath, row=annotation.point.row, col=annotation.point.column, pointNum=annotation.point.point_number, ) generate_patch_if_doesnt_exist(patchPath, annotation) else: # 'images' for index, image_obj in enumerate(page_results.object_list): page_results.object_list[index] = dict( image_obj=image_obj, ) # If we're showing the metadata form (grid of fields), then prepare that. if page_view == 'metadata': # Get image statuses (needs annotation, etc.) statuses = [] for image in all_items: statuses.append(image.get_annotation_status_str) # Initialize the form set with the existing metadata values. initValues = { 'form-TOTAL_FORMS': '%s' % len(all_items), 'form-INITIAL_FORMS': '%s' % len(all_items), } initValuesMetadata = initValues for i, image in enumerate(all_items): # Location keys keys = image.get_location_value_str_list() for j, key in enumerate(keys): initValuesMetadata['form-%s-key%s' % (i,j+1)] = key # Image id initValuesMetadata['form-%s-image_id' % i] = image.id # Other fields metadata_field_names = ['photo_date', 'height_in_cm', 'latitude', 'longitude', 'depth', 'camera', 'photographer', 'water_quality', 'strobes', 'framing', 'balance'] for metadata_field in metadata_field_names: formset_field_name = 'form-{num}-{field}'.format(num=i, field=metadata_field) initValuesMetadata[formset_field_name] = getattr(image.metadata, metadata_field) metadataForm = metadataFormSet(initValuesMetadata) checkboxForm = checkboxFormSet(initValues) metadataFormWithExtra = zip(metadataForm.forms, checkboxForm.forms, all_items, statuses) selectAllCheckbox = CheckboxForm() else: # Not showing the metadata view. metadataForm = None metadataFormWithExtra = None selectAllCheckbox = None # The destination page when you click on an image/patch thumbnail. if request.user.has_perm(Source.PermTypes.EDIT.code, source): thumbnail_dest_page = 'annotation_tool' else: thumbnail_dest_page = 'image_detail' return render_to_response('visualization/visualize_source.html', { 'source': source, 'searchForm': search_form, 'errors': errors, 'page_results': page_results, 'num_of_total_results': num_of_total_results, 'thumbnail_dest_page': thumbnail_dest_page, 'prev_page_link': prev_page_link, 'next_page_link': next_page_link, 'delete_form': delete_form, 'download_form': download_form, 'has_delete_form': bool(delete_form), 'has_download_form': False, # TODO: Uncomment this once downloading is implemented #'has_download_form': bool(download_form), 'key_list': source.get_key_list(), 'metadataForm': metadataForm, 'selectAllForm': selectAllCheckbox, 'metadataFormWithExtra': metadataFormWithExtra, 'page_view': page_view, }, context_instance=RequestContext(request) )
def visualize_source(request, source_id): """ View for browsing through a source's images. """ IMAGES_PER_PAGE = 20 searchFormErrors = False source = get_object_or_404(Source, id=source_id) urlArgsStr = '' # GET args in url format - for constructing prev page/next page links label = False imageArgs = dict() # Get image search filters, if any if request.GET: #form to select descriptors to sort images form = VisualizationSearchForm(source_id, request.GET) if form.is_valid(): urlArgsStr = image_search_args_to_url_arg_str(form.cleaned_data) label = form.cleaned_data.pop('labels') imageArgs = image_search_args_to_queryset_args(form.cleaned_data, source) else: searchFormErrors = True else: form = VisualizationSearchForm(source_id) # Perform selected actions, if any, on the images previously shown. # Security check: to guard against forged POST data, make sure the # user actually has permission to the action form. if request.POST and request.user.has_perm(Source.PermTypes.ADMIN.code, source): actionForm = ImageBatchActionForm(request.POST) if actionForm.is_valid(): if actionForm.cleaned_data['action'] == 'delete': actionFormImageArgs = simplejson.loads(actionForm.cleaned_data['searchKeys']) imagesToDelete = Image.objects.filter(source=source, **actionFormImageArgs) for img in imagesToDelete: img.delete() # Note that img.delete() just removes the image from the # database. But the image objects are still there in the # imagesToDelete list (they just don't have primary keys anymore). messages.success(request, 'The %d selected images have been deleted.' % len(imagesToDelete)) else: actionForm = ImageBatchActionForm(initial={'searchKeys': simplejson.dumps(imageArgs)}) # Get the search results (all of them, not just on this page) # (This must happen after processing the action form, so that # images can be deleted/modified before we get search results.) errors = [] if searchFormErrors: errors.append("There were errors in the search parameters.") images = [] showPatches = False else: #if user did not specify a label to generate patches for, assume they want to view whole images if not label: showPatches = False allSearchResults = Image.objects.filter(source=source, **imageArgs) if form.is_valid(): try: value = int(form.cleaned_data.pop('image_status')) except ValueError: value = 0 #All images wanted so just return if value: #Else do check for robot classified source options if source.enable_robot_classifier: if value == 1: allSearchResults = allSearchResults.filter(status__annotatedByHuman=False, status__annotatedByRobot=False) elif value == 2: allSearchResults = allSearchResults.filter(status__annotatedByHuman=False, status__annotatedByRobot=True) else: allSearchResults = allSearchResults.filter(status__annotatedByHuman=True) #Else do check for only human annotated source options else: if value == 1: allSearchResults = allSearchResults.filter(status__annotatedByHuman=False) elif value == 2: allSearchResults = allSearchResults.filter(status__annotatedByHuman=True) # Sort the images. # TODO: Stop duplicating this DB-specific extras query; put it in a separate function... # Also, despite the fact that we're dealing with images and not metadatas, selecting photo_date does indeed work. db_extra_select = {'metadata__year': 'YEAR(photo_date)'} # YEAR() is MySQL only, PostgreSQL has different syntax sort_keys = ['metadata__'+k for k in (['year'] + source.get_value_field_list())] allSearchResults = allSearchResults.extra(select=db_extra_select) allSearchResults = allSearchResults.order_by(*sort_keys) else: #since user specified a label, generate patches to show instead of whole images showPatches = True patchArgs = dict([('image__'+k, imageArgs[k]) for k in imageArgs]) #get all annotations for the source that contain the label try: annotator = int(form.cleaned_data.pop('annotator')) except ValueError: annotator = 2 annotations = Annotation.objects.filter(source=source, label=label, **patchArgs).order_by('?') if not annotator: annotations.exclude(user=get_robot_user()) elif annotator == 1: annotations.filter(user=get_robot_user()) # Placeholder the image patches with the annotation objects for now. # We'll actually get the patches when we know which page we're showing. allSearchResults = annotations if not allSearchResults: if request.GET: # No image results in this search errors.append("Sorry, no images matched your query") else: # No image results, and just got to the visualization page errors.append("Sorry, there are no images for this source yet. Please upload some.") paginator = Paginator(allSearchResults, IMAGES_PER_PAGE) # Make sure page request is an int. If not, deliver first page. try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 # If page request (9999) is out of range, deliver last page of results. try: images = paginator.page(page) except (EmptyPage, InvalidPage): images = paginator.page(paginator.num_pages) if showPatches: # Get an image-patch for each result on the page. # Earlier we placeholdered the image patches with the annotation objects, # so we're iterating over those annotations now. for index, annotation in enumerate(images.object_list): patchPath = "data/annotations/" + str(annotation.id) + ".jpg" images.object_list[index] = dict( type="patches", fullImage=annotation.image, patchPath=patchPath, row=annotation.point.row, col=annotation.point.column, pointNum=annotation.point.point_number, ) generate_patch_if_doesnt_exist(patchPath, annotation) else: for index, image_obj in enumerate(images.object_list): images.object_list[index] = dict( type="full_images", image_obj=image_obj, ) return render_to_response('visualization/visualize_source.html', { 'errors': errors, 'searchForm': form, 'source': source, 'images': images, 'showPatches': showPatches, 'searchParamsStr': urlArgsStr, 'actionForm': actionForm, }, context_instance=RequestContext(request) )