Exemple #1
0
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)
    )
Exemple #2
0
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&param2=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)
    )
Exemple #3
0
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)
    )