def prepare_html_subpages(images_html,output_dir,options=None):
    
    if options is None:
            options = PostProcessingOptions()
            
    # Count items in each category
    image_counts = {}
    for res, array in images_html.items():
        image_counts[res] = len(array)

    # Optionally sort by filename before writing to html    
    if options.sort_html_by_filename:                
        images_html_sorted = {}
        for res, array in images_html.items():
            sorted_array = sorted(array, key=lambda x: x['filename'])
            images_html_sorted[res] = sorted_array        
        images_html = images_html_sorted
        
    # Write the individual HTML files
    for res, array in images_html.items():
        write_html_image_list(
            filename=os.path.join(output_dir, '{}.html'.format(res)), 
            images=array,
            options={
                'headerHtml': '<h1>{}</h1>'.format(res.upper())
            })
    
    return image_counts
def prepare_html_subpages(images_html, output_dir, options=None):
    """
    Write out a series of html image lists, e.g. the fp/tp/fn/tn pages.

    image_html is a dictionary mapping an html page name (e.g. "fp") to a list
    of image structs friendly to write_html_image_list
    """
    if options is None:
            options = PostProcessingOptions()

    # Count items in each category
    image_counts = {}
    for res, array in images_html.items():
        image_counts[res] = len(array)

    # Optionally sort by filename before writing to html
    if options.sort_html_by_filename:
        images_html_sorted = {}
        for res, array in images_html.items():
            sorted_array = sorted(array, key=lambda x: x['filename'])
            images_html_sorted[res] = sorted_array
        images_html = images_html_sorted

    # Write the individual HTML files
    for res, array in images_html.items():
        write_html_image_list(
            filename=os.path.join(output_dir, '{}.html'.format(res)),
            images=array,
            options={
                'headerHtml': '<h1>{}</h1>'.format(res.upper())
            })

    return image_counts
Пример #3
0
class DbVizOptions:

    # Set to None to visualize all images
    num_to_visualize = None

    # Target size for rendering; set either dimension to -1 to preserve aspect ratio
    viz_size = (675, -1)
    htmlOptions = write_html_image_list()
    sort_by_filename = True
    trim_to_images_with_bboxes = False
    random_seed = 0  # None
    add_search_links = False
    include_filename_links = False

    # These are mutually exclusive; both are category names, not IDs
    classes_to_exclude = None
    classes_to_include = None

    # Special tag used to say "show me all images with multiple categories"
    multiple_categories_tag = '*multiple*'

    # We sometimes flatten image directories by replacing a path separator with
    # another character.  Leave blank for the typical case where this isn't necessary.
    pathsep_replacement = ''  # '~'

    # Control rendering parallelization
    parallelize_rendering_n_cores = 100
    parallelize_rendering = False
Пример #4
0
class BboxDbVizOptions:
    
    # Set to None to visualize all images
    num_to_visualize = None
    
    # Target size for rendering; set either dimension to -1 to preserve aspect ratio
    viz_size = (675, -1)
    htmlOptions = write_html_image_list()
    sort_by_filename = True
    trim_to_images_with_bboxes = False
    random_seed = 0; # None

    # We sometimes flatten image directories by replacing a path separator with 
    # another character.  Leave blank for the typical case where this isn't necessary.
    pathsep_replacement = '' # '~'
Пример #5
0
def processImages(db_path,
                  output_dir,
                  image_base_dir,
                  options=None,
                  bbox_db=None):
    """
    Writes images and html to output_dir to visualize the annotations in the json file
    db_path.
    
    Returns the html filename and the bbox database.
    """

    if options is None:
        options = DbVizOptions()

    print(options.__dict__)

    os.makedirs(os.path.join(output_dir, 'rendered_images'), exist_ok=True)
    assert (os.path.isfile(db_path))
    assert (os.path.isdir(image_base_dir))

    if bbox_db is None:
        print('Loading the database...')
        bbox_db = json.load(open(db_path))
        print('...done')

    annotations = bbox_db['annotations']
    images = bbox_db['images']
    categories = bbox_db['categories']

    # Optionally remove all images without bounding boxes, *before* sampling
    if options.trim_to_images_with_bboxes:

        bHasBbox = [False] * len(annotations)
        for iAnn, ann in enumerate(annotations):
            if 'bbox' in ann:
                assert isinstance(ann['bbox'], list)
                bHasBbox[iAnn] = True
        annotationsWithBboxes = list(compress(annotations, bHasBbox))

        imageIDsWithBboxes = [x['image_id'] for x in annotationsWithBboxes]
        imageIDsWithBboxes = set(imageIDsWithBboxes)

        bImageHasBbox = [False] * len(images)
        for iImage, image in enumerate(images):
            imageID = image['id']
            if imageID in imageIDsWithBboxes:
                bImageHasBbox[iImage] = True
        imagesWithBboxes = list(compress(images, bImageHasBbox))
        images = imagesWithBboxes

    # put the annotations in a dataframe so we can select all annotations for a given image
    df_anno = pd.DataFrame(annotations)
    df_img = pd.DataFrame(images)

    # construct label map
    label_map = {}
    for cat in categories:
        label_map[int(cat['id'])] = cat['name']

    # take a sample of images
    if options.num_to_visualize is not None:
        df_img = df_img.sample(n=options.num_to_visualize,
                               random_state=options.random_seed)

    images_html = []

    # iImage = 0
    for iImage in tqdm(range(len(df_img))):

        img_id = df_img.iloc[iImage]['id']
        img_relative_path = df_img.iloc[iImage]['file_name']
        img_path = os.path.join(
            image_base_dir,
            imageFilenameToPath(img_relative_path, image_base_dir))

        if not os.path.exists(img_path):
            print('Image {} cannot be found'.format(img_path))
            continue

        annos_i = df_anno.loc[df_anno['image_id'] ==
                              img_id, :]  # all annotations on this image

        try:
            originalImage = vis_utils.open_image(img_path)
            original_size = originalImage.size
            image = vis_utils.resize_image(originalImage, options.viz_size[0],
                                           options.viz_size[1])
        except Exception as e:
            print('Image {} failed to open. Error: {}'.format(img_path, e))
            continue

        bboxes = []
        boxClasses = []

        # All the class labels we've seen for this image (with out without bboxes)
        imageCategories = set()

        annotationLevelForImage = ''

        # Iterate over annotations for this image
        # iAnn = 0; anno = annos_i.iloc[iAnn]
        for iAnn, anno in annos_i.iterrows():

            if 'sequence_level_annotation' in anno:
                bSequenceLevelAnnotation = anno['sequence_level_annotation']
                if bSequenceLevelAnnotation:
                    annLevel = 'sequence'
                else:
                    annLevel = 'image'
                if annotationLevelForImage == '':
                    annotationLevelForImage = annLevel
                elif annotationLevelForImage != annLevel:
                    annotationLevelForImage = 'mixed'

            categoryID = anno['category_id']
            categoryName = label_map[categoryID]
            if options.add_search_links:
                categoryName = categoryName.replace('"', '')
                categoryName = '<a href="https://www.bing.com/images/search?q={}">{}</a>'.format(
                    categoryName, categoryName)
            imageCategories.add(categoryName)

            if 'bbox' in anno:
                bbox = anno['bbox']
                if isinstance(bbox, float):
                    assert math.isnan(
                        bbox
                    ), "I shouldn't see a bbox that's neither a box nor NaN"
                    continue
                bboxes.append(bbox)
                boxClasses.append(anno['category_id'])

        imageClasses = ', '.join(imageCategories)

        # render bounding boxes in-place
        vis_utils.render_db_bounding_boxes(bboxes, boxClasses, image,
                                           original_size, label_map)

        file_name = '{}_gtbbox.jpg'.format(img_id.lower().split('.jpg')[0])
        file_name = file_name.replace('/', '~')
        image.save(os.path.join(output_dir, 'rendered_images', file_name))

        labelLevelString = ''
        if len(annotationLevelForImage) > 0:
            labelLevelString = ' (annotation level: {})'.format(
                annotationLevelForImage)

        images_html.append({
            'filename':
            '{}/{}'.format('rendered_images', file_name),
            'title':
            '{}<br/>{}, number of boxes: {}, class labels: {}{}'.format(
                img_relative_path, img_id, len(bboxes), imageClasses,
                labelLevelString),
            'textStyle':
            'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5'
        })

    # ...for each image

    if options.sort_by_filename:
        images_html = sorted(images_html, key=lambda x: x['filename'])

    htmlOutputFile = os.path.join(output_dir, 'index.html')

    htmlOptions = options.htmlOptions
    htmlOptions['headerHtml'] = '<h1>Sample annotations from {}</h1>'.format(
        db_path)
    write_html_image_list(filename=htmlOutputFile,
                          images=images_html,
                          options=htmlOptions)

    print('Visualized {} images, wrote results to {}'.format(
        len(images_html), htmlOutputFile))

    return htmlOutputFile, bbox_db
def render_images_for_directory(iDir, directoryHtmlFiles, suspiciousDetections,
                                options):
    nDirs = len(directoryHtmlFiles)

    if options.pbar is not None:
        options.pbar.update()

    if options.debugMaxRenderDir > 0 and iDir > options.debugMaxRenderDir:
        return None

    dirName = 'dir{:0>4d}'.format(iDir)

    # suspiciousDetectionsThisDir is a list of DetectionLocation objects
    suspiciousDetectionsThisDir = suspiciousDetections[iDir]

    if len(suspiciousDetectionsThisDir) == 0:
        return None

    timeStr = datetime.now().strftime('%H:%M:%S')
    print('Processing directory {} of {} ({})'.format(iDir, nDirs, timeStr))

    dirBaseDir = os.path.join(options.outputBase, dirName)
    os.makedirs(dirBaseDir, exist_ok=True)

    directoryDetectionHtmlFiles = []
    directoryDetectionImageInfo = []

    # For each problematic detection in this directory
    #
    # iDetection = 0; detection = suspiciousDetectionsThisDir[iDetection];
    nDetections = len(suspiciousDetectionsThisDir)
    bPrintedMissingImageWarning = False

    for iDetection, detection in enumerate(suspiciousDetectionsThisDir):

        if options.debugMaxRenderDetection > 0 and iDetection > options.debugMaxRenderDetection:
            break

        nInstances = len(detection.instances)
        print('Processing detection {} of {} ({} instances)'.format(
            iDetection, nDetections, nInstances))
        detectionName = 'detection{:0>4d}'.format(iDetection)
        detectionBaseDir = os.path.join(dirBaseDir, detectionName)
        os.makedirs(detectionBaseDir, exist_ok=True)

        # _ = pretty_print_object(detection)
        assert (nInstances >= options.occurrenceThreshold)

        imageInfo = []

        # Render images

        # iInstance = 0; instance = detection.instances[iInstance]
        for iInstance, instance in enumerate(detection.instances):

            if options.debugMaxRenderInstance >= 0 and iInstance >= options.debugMaxRenderInstance:
                break

            imageRelativeFilename = 'image{:0>4d}.jpg'.format(iInstance)
            imageOutputFilename = os.path.join(detectionBaseDir,
                                               imageRelativeFilename)
            thisImageInfo = {}
            thisImageInfo['filename'] = imageRelativeFilename
            confidence = instance.bbox[4]
            confidenceStr = '{:.2f}'.format(confidence)
            t = confidenceStr + ' (' + instance.filename + ')'
            thisImageInfo['title'] = t
            imageInfo.append(thisImageInfo)

            inputFileName = os.path.join(options.imageBase, instance.filename)
            if not os.path.isfile(inputFileName):
                if options.bPrintMissingImageWarnings:
                    if (options.missingImageWarningType
                            == 'all') or (not bPrintedMissingImageWarning):
                        print('Warning: could not find file {}'.format(
                            inputFileName))
                        bPrintedMissingImageWarning = True
            else:
                render_bounding_box(instance.bbox, inputFileName,
                                    imageOutputFilename, 15)

        # ...for each instance

        # Write html for this detection
        detectionHtmlFile = os.path.join(detectionBaseDir, 'index.html')

        htmlOptions = write_html_image_list.write_html_image_list()
        htmlOptions['defaultImageStyle'] = 'max-width:650px;'
        write_html_image_list.write_html_image_list(detectionHtmlFile,
                                                    imageInfo, htmlOptions)

        thisDirectoryImageInfo = {}
        directoryDetectionHtmlFiles.append(detectionHtmlFile)

        # Use the first image from this detection (arbitrary) as the canonical example
        # that we'll render for the directory-level page.
        thisDirectoryImageInfo['filename'] = os.path.join(
            detectionName, imageInfo[0]['filename'])
        detectionHtmlFileRelative = os.path.relpath(detectionHtmlFile,
                                                    dirBaseDir)
        title = '<a href="{}">{}</a>'.format(detectionHtmlFileRelative,
                                             detectionName)
        thisDirectoryImageInfo['title'] = title
        directoryDetectionImageInfo.append(thisDirectoryImageInfo)

    # ...for each detection

    # Write the html file for this directory
    directoryHtmlFile = os.path.join(dirBaseDir, 'index.html')

    htmlOptions = write_html_image_list.write_html_image_list()
    htmlOptions['defaultImageStyle'] = 'max-width:650px;'
    write_html_image_list.write_html_image_list(directoryHtmlFile,
                                                directoryDetectionImageInfo,
                                                htmlOptions)

    return directoryHtmlFile
Пример #7
0
def process_images(db_path,output_dir,image_base_dir,options=None):
    """
    Writes images and html to output_dir to visualize the annotations in the json file
    db_path.
    
    db_path can also be a previously-loaded database.
    
    Returns the html filename and the database:
        
    return htmlOutputFile,image_db
    """    
    
    if options is None:
        options = DbVizOptions()
    
    print(options.__dict__)
    
    os.makedirs(os.path.join(output_dir, 'rendered_images'), exist_ok=True)
    assert(os.path.isdir(image_base_dir))
    
    if isinstance(db_path,str):
        assert(os.path.isfile(db_path))    
        print('Loading database from {}...'.format(db_path))
        image_db = json.load(open(db_path))
        print('...done')
    elif isinstance(db_path,dict):
        print('Using previously-loaded DB')
        image_db = db_path
    else:
        raise ValueError('Illegal dictionary or filename')    
        
    annotations = image_db['annotations']
    images = image_db['images']
    categories = image_db['categories']
    
    # Optionally remove all images without bounding boxes, *before* sampling
    if options.trim_to_images_with_bboxes:
        
        bHasBbox = [False] * len(annotations)
        for iAnn,ann in enumerate(annotations):
            if 'bbox' in ann:
                assert isinstance(ann['bbox'],list)
                bHasBbox[iAnn] = True
        annotationsWithBboxes = list(compress(annotations, bHasBbox))
        
        imageIDsWithBboxes = [x['image_id'] for x in annotationsWithBboxes]
        imageIDsWithBboxes = set(imageIDsWithBboxes)
        
        bImageHasBbox = [False] * len(images)
        for iImage,image in enumerate(images):
            imageID = image['id']
            if imageID in imageIDsWithBboxes:
                bImageHasBbox[iImage] = True
        imagesWithBboxes = list(compress(images, bImageHasBbox))
        images = imagesWithBboxes
                
    # Optionally remove images with specific labels, *before* sampling
    if options.classes_to_exclude is not None:
     
        print('Indexing database')
        indexed_db = IndexedJsonDb(image_db)
        bValidClass = [True] * len(images)        
        for iImage,image in enumerate(images):
            classes = indexed_db.get_classes_for_image(image)
            for excludedClass in options.classes_to_exclude:
                if excludedClass in classes:
                   bValidClass[iImage] = False
                   break
               
        imagesWithValidClasses = list(compress(images, bValidClass))
        images = imagesWithValidClasses    
    
    # Put the annotations in a dataframe so we can select all annotations for a given image
    print('Creating data frames')
    df_anno = pd.DataFrame(annotations)
    df_img = pd.DataFrame(images)
    
    # Construct label map
    label_map = {}
    for cat in categories:
        label_map[int(cat['id'])] = cat['name']
    
    # Take a sample of images
    if options.num_to_visualize is not None:
        df_img = df_img.sample(n=options.num_to_visualize,random_state=options.random_seed)
    
    images_html = []
    
    # Set of dicts representing inputs to render_db_bounding_boxes:
    #
    # bboxes, boxClasses, image_path
    rendering_info = []
    
    print('Preparing rendering list')
    # iImage = 0
    for iImage in tqdm(range(len(df_img))):
        
        img_id = df_img.iloc[iImage]['id']
        img_relative_path = df_img.iloc[iImage]['file_name']
        img_path = os.path.join(image_base_dir, image_filename_to_path(img_relative_path, image_base_dir))
    
        annos_i = df_anno.loc[df_anno['image_id'] == img_id, :]  # all annotations on this image
    
        bboxes = []
        boxClasses = []
        
        # All the class labels we've seen for this image (with out without bboxes)
        imageCategories = set()
        
        annotationLevelForImage = ''
        
        # Iterate over annotations for this image
        # iAnn = 0; anno = annos_i.iloc[iAnn]
        for iAnn,anno in annos_i.iterrows():
        
            if 'sequence_level_annotation' in anno:
                bSequenceLevelAnnotation = anno['sequence_level_annotation']
                if bSequenceLevelAnnotation:
                    annLevel = 'sequence'
                else:
                    annLevel = 'image'
                if annotationLevelForImage == '':
                    annotationLevelForImage = annLevel
                elif annotationLevelForImage != annLevel:
                    annotationLevelForImage = 'mixed'
                    
            categoryID = anno['category_id']
            categoryName = label_map[categoryID]
            if options.add_search_links:
                categoryName = categoryName.replace('"','')
                categoryName = '<a href="https://www.bing.com/images/search?q={}">{}</a>'.format(categoryName,categoryName)
            imageCategories.add(categoryName)
            
            if 'bbox' in anno:
                bbox = anno['bbox']        
                if isinstance(bbox,float):
                    assert math.isnan(bbox), "I shouldn't see a bbox that's neither a box nor NaN"
                    continue
                bboxes.append(bbox)
                boxClasses.append(anno['category_id'])
        
        imageClasses = ', '.join(imageCategories)
                
        file_name = '{}_gtbbox.jpg'.format(img_id.lower().split('.jpg')[0])
        file_name = file_name.replace('/', '~')
        
        rendering_info.append({'bboxes':bboxes, 'boxClasses':boxClasses, 'img_path':img_path,
                               'output_file_name':file_name})
                
        labelLevelString = ''
        if len(annotationLevelForImage) > 0:
            labelLevelString = ' (annotation level: {})'.format(annotationLevelForImage)
            
        # We're adding html for an image before we render it, so it's possible this image will
        # fail to render.  For applications where this script is being used to debua a database
        # (the common case?), this is useful behavior, for other applications, this is annoying.
        #
        # TODO: optionally write html only for images where rendering succeeded
        images_html.append({
            'filename': '{}/{}'.format('rendered_images', file_name),
            'title': '{}<br/>{}, number of boxes: {}, class labels: {}{}'.format(img_relative_path,img_id, len(bboxes), imageClasses, labelLevelString),
            'textStyle': 'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5'
        })
    
    # ...for each image

    def render_image_info(rendering_info):
        
        img_path = rendering_info['img_path']
        bboxes = rendering_info['bboxes']
        bboxClasses = rendering_info['boxClasses']
        output_file_name = rendering_info['output_file_name']
        
        if not os.path.exists(img_path):
            print('Image {} cannot be found'.format(img_path))
            return
            
        try:
            original_image = vis_utils.open_image(img_path)
            original_size = original_image.size
            image = vis_utils.resize_image(original_image, options.viz_size[0], options.viz_size[1])
        except Exception as e:
            print('Image {} failed to open. Error: {}'.format(img_path, e))
            return
            
        vis_utils.render_db_bounding_boxes(boxes=bboxes, classes=bboxClasses,
                                           image=image, original_size=original_size,
                                           label_map=label_map)
        image.save(os.path.join(output_dir, 'rendered_images', output_file_name))
    
    # ...def render_image_info
    
    print('Rendering images')
    start_time = time.time()
    if options.parallelize_rendering:
        if options.parallelize_rendering_n_cores is None:
            pool = ThreadPool()
        else:
            print('Rendering images with {} workers'.format(options.parallelize_rendering_n_cores))
            pool = ThreadPool(options.parallelize_rendering_n_cores)
            tqdm(pool.imap(render_image_info, rendering_info), total=len(rendering_info))
    else:
        for file_info in tqdm(rendering_info):        
            render_image_info(file_info)
    elapsed = time.time() - start_time
    
    print('Rendered {} images in {}'.format(len(rendering_info),humanfriendly.format_timespan(elapsed)))
        
    if options.sort_by_filename:    
        images_html = sorted(images_html, key=lambda x: x['filename'])
        
    htmlOutputFile = os.path.join(output_dir, 'index.html')
    
    htmlOptions = options.htmlOptions
    if isinstance(db_path,str):
        htmlOptions['headerHtml'] = '<h1>Sample annotations from {}</h1>'.format(db_path)
    else:
        htmlOptions['headerHtml'] = '<h1>Sample annotations</h1>'
    write_html_image_list(
            filename=htmlOutputFile,
            images=images_html,
            options=htmlOptions)

    print('Visualized {} images, wrote results to {}'.format(len(images_html),htmlOutputFile))
    
    return htmlOutputFile,image_db
Пример #8
0
def visualize_sequences(datasets_table, sequences, args):
    num_images = 0

    images_html = []
    rendering_info = []

    for seq in sequences:
        if 'images' not in seq:
            continue

        # dataset and seq_id are required fields
        dataset_name = seq['dataset']
        seq_id = seq['seq_id']

        # sort the images in the sequence

        images_in_seq = sorted(seq['images'], key=lambda x: x['frame_num']) if len(seq['images']) > 1 else seq['images']

        for im in images_in_seq:
            if args.trim_to_images_bboxes_labeled and 'bbox' not in im:
                continue

            num_images += 1

            blob_path = MegadbUtils.get_full_path(datasets_table, dataset_name, im['file'])
            frame_num = im.get('frame_num', -1)
            im_class = im.get('class', None)
            if im_class is None:  # if no class label on the image, show the class label on the sequence
                im_class = seq.get('class', [])

            rendering = {}
            rendering['blob_service'] = MegadbUtils.get_blob_service(datasets_table, dataset_name)
            rendering['container_name'] = datasets_table[dataset_name]['container']
            rendering['blob_path'] = blob_path
            rendering['bbox'] = im.get('bbox', [])

            annotated_img_name = 'anno_' + blob_path.replace('/', args.pathsep_replacement).replace('\\', args.pathsep_replacement)
            rendering['annotated_img_name'] = annotated_img_name

            rendering_info.append(rendering)

            images_html.append({
                'filename': 'rendered_images/{}'.format(annotated_img_name),
                'title': 'Seq ID: {}. Frame number: {}<br/> Image file: {}<br/> number of boxes: {}, image class labels: {}'.format(seq_id, frame_num, blob_path, len(rendering['bbox']), im_class),
                'textStyle': 'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5'
            })

        if num_images >= args.num_to_visualize:
            print('num_images visualized is {}'.format(num_images))
            break

    # pool = ThreadPool()
    render_image_info_partial = partial(render_image_info, args=args)
    # print('len of rendering_info', len(rendering_info))
    # tqdm(pool.imap_unordered(render_image_info_partial, rendering_info), total=len(rendering_info))

    for rendering in tqdm(rendering_info):
        render_image_info_partial(rendering)

    print('Making HTML...')

    html_path = os.path.join(args.output_dir, 'index.html')
    # options = write_html_image_list()
    # options['headerHtml']
    write_html_image_list(
        filename=html_path,
        images=images_html
    )
def renderImagesForDirectory(iDir, directoryHtmlFiles, suspiciousDetections,
                             options):

    nDirs = len(directoryHtmlFiles)

    if options.pbar is not None:
        options.pbar.update()

    if options.debugMaxRenderDir > 0 and iDir > options.debugMaxRenderDir:
        return None

    dirName = 'dir{:0>4d}'.format(iDir)

    # suspiciousDetectionsThisDir is a list of DetectionLocation objects
    suspiciousDetectionsThisDir = suspiciousDetections[iDir]

    if len(suspiciousDetectionsThisDir) == 0:
        return None

    timeStr = datetime.now().strftime('%H:%M:%S')
    print('Processing directory {} of {} ({})'.format(iDir, nDirs, timeStr))

    dirBaseDir = os.path.join(options.outputBase, dirName)
    os.makedirs(dirBaseDir, exist_ok=True)

    directoryDetectionHtmlFiles = []
    directoryDetectionExampleImages = []
    directoryDetectionTitles = []

    # For each problematic detection in this directory
    #
    # iDetection = 0; detection = suspiciousDetectionsThisDir[iDetection];
    nDetections = len(suspiciousDetectionsThisDir)
    for iDetection, detection in enumerate(suspiciousDetectionsThisDir):

        if options.debugMaxRenderDetection > 0 and iDetection > options.debugMaxRenderDetection:
            break

        nInstances = len(detection.instances)
        print('Processing detection {} of {} ({} instances)'.format(
            iDetection, nDetections, nInstances))
        detectionName = 'detection{:0>4d}'.format(iDetection)
        detectionBaseDir = os.path.join(dirBaseDir, detectionName)
        os.makedirs(detectionBaseDir, exist_ok=True)

        # _ = prettyPrintObject(detection)
        assert (nInstances >= options.occurrenceThreshold)

        imageFileNames = []
        titles = []

        # Render images

        # iInstance = 0; instance = detection.instances[iInstance]
        for iInstance, instance in enumerate(detection.instances):

            if options.debugMaxRenderInstance > 9 and iInstance > options.debugMaxRenderInstance:
                break

            imageRelativeFilename = 'image{:0>4d}.jpg'.format(iInstance)
            imageOutputFilename = os.path.join(detectionBaseDir,
                                               imageRelativeFilename)
            imageFileNames.append(imageRelativeFilename)
            confidence = instance.bbox[4]
            confidenceStr = '{:.2f}'.format(confidence)
            t = confidenceStr + ' (' + instance.filename + ')'
            titles.append(t)

            inputFileName = os.path.join(options.imageBase, instance.filename)
            if not os.path.isfile(inputFileName):
                print('Warning: could not find file {}'.format(inputFileName))
            else:
                # render_bounding_box(instance.bbox,1,None,inputFileName,imageOutputFilename,0,10)
                render_bounding_box(instance.bbox, inputFileName,
                                    imageOutputFilename, 15)

        # ...for each instance

        # Write html for this detection
        detectionHtmlFile = os.path.join(detectionBaseDir, 'index.html')

        htmlOptions = write_html_image_list.write_html_image_list()
        htmlOptions['imageStyle'] = 'max-width:700px;'
        write_html_image_list.write_html_image_list(detectionHtmlFile,
                                                    imageFileNames, titles,
                                                    htmlOptions)

        directoryDetectionHtmlFiles.append(detectionHtmlFile)
        directoryDetectionExampleImages.append(
            os.path.join(detectionName, imageFileNames[0]))
        detectionHtmlFileRelative = os.path.relpath(detectionHtmlFile,
                                                    dirBaseDir)
        title = '<a href="{}">{}</a>'.format(detectionHtmlFileRelative,
                                             detectionName)
        directoryDetectionTitles.append(title)

    # ...for each detection

    # Write the html file for this directory
    directoryHtmlFile = os.path.join(dirBaseDir, 'index.html')

    write_html_image_list.write_html_image_list(
        directoryHtmlFile, directoryDetectionExampleImages,
        directoryDetectionTitles, htmlOptions)

    return directoryHtmlFile
Пример #10
0
    if len(annos_i) > 0:
        bboxes = list(annos_i.loc[:, 'bbox'])
        classes = list(annos_i.loc[:, 'category_id'])
        vis_utils.render_iMerit_boxes(bboxes, classes, image,
                                      label_map)  # image changed in place

    file_name = '{}_gtbbox.jpg'.format(img_name.lower().split('.jpg')[0])
    image.save(os.path.join(output_dir, 'rendered_images', file_name))

    images_html.append({
        'filename':
        '{}/{}'.format('rendered_images', file_name),
        'title':
        '{}, number of boxes: {}'.format(img_name, len(annos_i)),
        'textStyle':
        'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5'
    })

#%% Write to HTML

images_html = sorted(images_html, key=lambda x: x['filename'])
write_html_image_list(
    filename=os.path.join(output_dir, 'index.html'),
    images=images_html,
    options={
        'headerHtml':
        '<h1>Sample annotations from {}</h1>'.format(incoming_annotation_path)
    })

print('Visualized {} images.'.format(len(images_html)))
Пример #11
0
def _compare_batch_results(options,output_index,pairwise_options):
        
    assert options.pairwise_options is None
    
    random.seed(options.random_seed)

    ##%% Validate inputs
    
    assert os.path.isfile(pairwise_options.results_filename_a)
    assert os.path.isfile(pairwise_options.results_filename_b)
    assert os.path.isdir(options.image_folder)
    os.makedirs(options.output_folder,exist_ok=True)
    
    
    ##%% Load both result sets
    
    with open(pairwise_options.results_filename_a,'r') as f:
        results_a = json.load(f)
    
    with open(pairwise_options.results_filename_b,'r') as f:
        results_b = json.load(f)
        
    assert results_a['detection_categories'] == detection_categories
    assert results_b['detection_categories'] == detection_categories
    
    if pairwise_options.results_description_a is None:
        if 'detector' not in results_a['info']:
            print('No model metadata supplied for results-A, assuming MDv4')
            pairwise_options.results_description_a = 'MDv4 (assumed)'
        else:            
            pairwise_options.results_description_a = results_a['info']['detector']
    
    if pairwise_options.results_description_b is None:
        if 'detector' not in results_b['info']:
            print('No model metadata supplied for results-B, assuming MDv4')
            pairwise_options.results_description_b = 'MDv4 (assumed)'
        else:            
            pairwise_options.results_description_b = results_b['info']['detector']
    
    images_a = results_a['images']
    images_b = results_b['images']
    
    filename_to_image_a = {im['file']:im for im in images_a}
    filename_to_image_b = {im['file']:im for im in images_b}
    
    
    ##%% Make sure they represent the same set of images
    
    filenames_a = [im['file'] for im in images_a]
    filenames_b_set = set([im['file'] for im in images_b])
    
    assert len(images_a) == len(images_b)
    assert len(filenames_a) == len(images_a)
    assert len(filenames_b_set) == len(images_b)
    for fn in filenames_a:
        assert fn in filenames_b_set
    
    
    ##%% Find differences
    
    # Each of these maps a filename to a two-element list (the image in set A, the image in set B)
    #
    # Right now, we only handle a very simple notion of class transition, where the detection
    # of maximum confidence changes class *and* both images have an above-threshold detection.
    common_detections = {}
    common_non_detections = {}
    detections_a_only = {}
    detections_b_only = {}
    class_transitions = {}
    
    # fn = filenames_a[0]
    for fn in tqdm(filenames_a):
    
        im_a = filename_to_image_a[fn]
        im_b = filename_to_image_b[fn]
        
        categories_above_threshold_a = set()

        if not 'detections' in im_a:
            assert 'failure' in im_a and im_a['failure'] is not None
            continue
        
        if not 'detections' in im_b:
            assert 'failure' in im_b and im_b['failure'] is not None
            continue
        
        invalid_category_error = False
        
        # det = im_a['detections'][0]
        for det in im_a['detections']:
            
            category_id = det['category']
            
            if category_id not in detection_categories:
                print('Warning: unexpected category {} for model A on file {}'.format(category_id,fn))
                invalid_category_error = True
                break
                
            conf = det['conf']
            
            if conf >= pairwise_options.detection_thresholds_a[detection_categories[category_id]]:
                categories_above_threshold_a.add(category_id)
                            
        if invalid_category_error:
            continue
        
        categories_above_threshold_b = set()
        
        for det in im_b['detections']:
            
            if category_id not in detection_categories:
                print('Warning: unexpected category {} for model B on file {}'.format(category_id,fn))
                invalid_category_error = True
                break
            
            category_id = det['category']
            conf = det['conf']
            
            if conf >= pairwise_options.detection_thresholds_b[detection_categories[category_id]]:
                categories_above_threshold_b.add(category_id)
                            
        if invalid_category_error:
            continue
        
        im_pair = (im_a,im_b)
        
        detection_a = (len(categories_above_threshold_a) > 0)
        detection_b = (len(categories_above_threshold_b) > 0)
                
        if detection_a and detection_b:            
            if categories_above_threshold_a == categories_above_threshold_b:
                common_detections[fn] = im_pair
            else:
                class_transitions[fn] = im_pair
        elif (not detection_a) and (not detection_b):
            common_non_detections[fn] = im_pair
        elif detection_a and (not detection_b):
            detections_a_only[fn] = im_pair
        else:
            assert detection_b and (not detection_a)
            detections_b_only[fn] = im_pair
            
    # ...for each filename
    
    print('Of {} files:\n{} common detections\n{} common non-detections\n{} A only\n{} B only\n{} class transitions'.format(
        len(filenames_a),len(common_detections),
        len(common_non_detections),len(detections_a_only),
        len(detections_b_only),len(class_transitions)))
        
    
    ##%% Sample and plot differences
    
    from multiprocessing.pool import ThreadPool
    
    if options.n_rendering_threads > 1:
       print('Rendering images with {} workers'.format(options.n_rendering_threads))
       pool = ThreadPool(options.n_rendering_threads)    
    
    
    categories_to_image_pairs = {
        'common_detections':common_detections,
        'common_non_detections':common_non_detections,
        'detections_a_only':detections_a_only,
        'detections_b_only':detections_b_only,
        'class_transitions':class_transitions
    }
    
    categories_to_page_titles = {
        'common_detections':'Detections common to both models',
        'common_non_detections':'Non-detections common to both models',
        'detections_a_only':'Detections reported by model A only',
        'detections_b_only':'Detections reported by model B only',
        'class_transitions':'Detections reported as different classes by models A and B'
    }

    local_output_folder = os.path.join(options.output_folder,'cmp_' + \
                                       str(output_index).zfill(3))

    def render_detection_comparisons(category,image_pairs,image_filenames):
        
        print('Rendering detections for category {}'.format(category))
                
        category_folder = os.path.join(local_output_folder,category)
        os.makedirs(category_folder,exist_ok=True)
        
        # Render two sets of results (i.e., a comparison) for a single
        # image.
        def render_image_pair(fn):
            
            input_image_path = os.path.join(options.image_folder,fn)
            assert os.path.isfile(input_image_path), 'Image {} does not exist'.format(input_image_path)
            
            im = visualization_utils.open_image(input_image_path)
            image_pair = image_pairs[fn]
            detections_a = image_pair[0]['detections']
            detections_b = image_pair[1]['detections']
            
            """
            def render_detection_bounding_boxes(detections, image,
                                                label_map={},
                                                classification_label_map={},
                                                confidence_threshold=0.8, thickness=4, expansion=0,
                                                classification_confidence_threshold=0.3,
                                                max_classifications=3,
                                                colormap=COLORS):
            """
            if options.target_width is not None:
                im = visualization_utils.resize_image(im, options.target_width)
                
            visualization_utils.render_detection_bounding_boxes(detections_a,im,
                                                                confidence_threshold=pairwise_options.rendering_confidence_threshold_a,
                                                                thickness=4,expansion=0,
                                                                colormap=options.colormap_a,
                                                                textalign=visualization_utils.TEXTALIGN_LEFT)
            visualization_utils.render_detection_bounding_boxes(detections_b,im,
                                                                confidence_threshold=pairwise_options.rendering_confidence_threshold_b,
                                                                thickness=2,expansion=0,
                                                                colormap=options.colormap_b,
                                                                textalign=visualization_utils.TEXTALIGN_RIGHT)
        
            output_image_fn = path_utils.flatten_path(fn)
            output_image_path = os.path.join(category_folder,output_image_fn)
            im.save(output_image_path)           
            return output_image_path
        
        # ...def render_image_pair()
        
        # fn = image_filenames[0]
        if options.n_rendering_threads <= 1:
            output_image_paths = []
            for fn in tqdm(image_filenames):        
                output_image_paths.append(render_image_pair(fn))
        else:
            output_image_paths = list(tqdm(pool.imap(render_image_pair, image_filenames), total=len(image_filenames)))
        
        return output_image_paths
    
    # ...def render_detection_comparisons()
    
    # For each category, generate comparison images and the 
    # comparison HTML page.
    #
    # category = 'common_detections'
    for category in categories_to_image_pairs.keys():
        
        # Choose detection pairs we're going to render for this category
        image_pairs = categories_to_image_pairs[category]
        image_filenames = list(image_pairs.keys())
        if len(image_filenames) > options.max_images_per_category:
            print('Sampling {} of {} image pairs for category {}'.format(
                options.max_images_per_category,
                len(image_filenames),
                category))
            image_filenames = random.sample(image_filenames,
                                            options.max_images_per_category)
        assert len(image_filenames) <= options.max_images_per_category

        input_image_absolute_paths = [os.path.join(options.image_folder,fn) for fn in image_filenames]
        
        category_image_output_paths = render_detection_comparisons(category,
                                                            image_pairs,image_filenames)
        
        category_html_filename = os.path.join(local_output_folder,
                                              category + '.html')
        category_image_output_paths_relative = [os.path.relpath(s,local_output_folder) \
                                         for s in category_image_output_paths]
        
        image_info = []
        
        assert len(category_image_output_paths_relative) == len(input_image_absolute_paths)
        
        import urllib
        for i_fn,fn in enumerate(category_image_output_paths_relative): 
            info = {
                'filename': fn,
                'title': fn,
                'textStyle': 'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5',
                'linkTarget': urllib.parse.quote(input_image_absolute_paths[i_fn])
            }
            image_info.append(info)
    
        category_page_header_string = '<h1>{}</h1>'.format(categories_to_page_titles[category])
        category_page_header_string += '<p style="font-weight:bold;">\n'
        category_page_header_string += 'Model A: {}<br/>\n'.format(pairwise_options.results_description_a)
        category_page_header_string += 'Model B: {}'.format(pairwise_options.results_description_b)
        category_page_header_string += '</p>\n'
        
        category_page_header_string += '<p>\n'
        category_page_header_string += 'Detection thresholds for A ({}):\n{}<br/>'.format(
            pairwise_options.results_description_a,str(pairwise_options.detection_thresholds_a))
        category_page_header_string += 'Detection thresholds for B ({}):\n{}<br/>'.format(
            pairwise_options.results_description_b,str(pairwise_options.detection_thresholds_b))
        category_page_header_string += 'Rendering threshold for A ({}):\n{}<br/>'.format(
            pairwise_options.results_description_a,str(pairwise_options.rendering_confidence_threshold_a))
        category_page_header_string += 'Rendering threshold for B ({}):\n{}<br/>'.format(
            pairwise_options.results_description_b,str(pairwise_options.rendering_confidence_threshold_b))
        category_page_header_string += '</p>\n'        
        
        write_html_image_list(
            category_html_filename,
            images=image_info,
            options={
                'headerHtml': category_page_header_string
            })
        
    # ...for each category
    
    
    ##%% Write the top-level HTML file content

    html_output_string  = ''
    
    html_output_string += '<p>Comparing <b>{}</b> (A, red) to <b>{}</b> (B, blue)</p>'.format(
        pairwise_options.results_description_a,pairwise_options.results_description_b)
    html_output_string += '<div class="contentdiv">\n'
    html_output_string += 'Detection thresholds for {}:\n{}<br/>'.format(
        pairwise_options.results_description_a,str(pairwise_options.detection_thresholds_a))
    html_output_string += 'Detection thresholds for {}:\n{}<br/>'.format(
        pairwise_options.results_description_b,str(pairwise_options.detection_thresholds_b))
    html_output_string += 'Rendering threshold for {}:\n{}<br/>'.format(
        pairwise_options.results_description_a,str(pairwise_options.rendering_confidence_threshold_a))
    html_output_string += 'Rendering threshold for {}:\n{}<br/>'.format(
        pairwise_options.results_description_b,str(pairwise_options.rendering_confidence_threshold_b))
    
    html_output_string += '<br/>'
    
    html_output_string += 'Rendering a maximum of {} images per category<br/>'.format(options.max_images_per_category)
    
    html_output_string += '<br/>'
    
    html_output_string += ('Of {} total files:<br/><br/><div style="margin-left:15px;">{} common detections<br/>{} common non-detections<br/>{} A only<br/>{} B only<br/>{} class transitions</div><br/>'.format(
        len(filenames_a),len(common_detections),
        len(common_non_detections),len(detections_a_only),
        len(detections_b_only),len(class_transitions)))
    
    html_output_string += 'Comparison pages:<br/><br/>\n'
    html_output_string += '<div style="margin-left:15px;">\n'
        
    comparison_path_relative = os.path.relpath(local_output_folder,options.output_folder)    
    for category in categories_to_image_pairs.keys():
        category_html_filename = os.path.join(comparison_path_relative,category + '.html')
        html_output_string += '<a href="{}">{}</a><br/>\n'.format(
            category_html_filename,category)
    
    html_output_string += '</div>\n'
    html_output_string += '</div>\n'
    
    return html_output_string
Пример #12
0
    targetWidth = 600
    wpercent = (targetWidth / float(img.size[0]))
    hsize = int((float(img.size[1]) * float(wpercent)))
    imgResized = img.resize((targetWidth, hsize), Image.ANTIALIAS)
    return imgResized


outputDir = r'd:\temp\ocrTest'
os.makedirs(outputDir, exist_ok=True)

outputSummaryFile = os.path.join(outputDir, 'summary.html')

htmlImageList = []
htmlTitleList = []

options = write_html_image_list.write_html_image_list()

for iImage, regionSet in enumerate(imageRegions):

    # Add image name and resized image
    image = resizeImage(images[iImage])
    fn = os.path.join(outputDir, 'img_{}_base.png'.format(iImage))
    image.save(fn)

    title = 'Image: {}'.format(ntpath.basename(imageFileNames[iImage]))
    htmlImageList.append({'filename': fn, 'title': title})

    bPrintedDate = False

    # Add results and individual region images
    for iRegion, region in enumerate(regionSet):
def visualize_incoming_annotations(args):
    print('Connecting to MegaDB to get the datasets table...')
    megadb_utils = MegadbUtils()
    datasets_table = megadb_utils.get_datasets_table()

    print('Loading the MegaDB entries...')
    with open(args.megadb_entries) as f:
        sequences = json.load(f)
    print(f'Total number of sequences: {len(sequences)}')
    dataset_seq_images = defaultdict(dict)
    for seq in sequences:
        dataset_seq_images[seq['dataset']][seq['seq_id']] = seq['images']

    print('Loading incoming annotation entries...')
    incoming = IndexedJsonDb(args.incoming_annotation)
    print(
        f'Number of images in this annotation file: {len(incoming.image_id_to_image)}'
    )

    if args.num_to_visualize != -1 and args.num_to_visualize <= len(
            incoming.image_id_to_image):
        incoming_id_to_anno = sample(
            list(incoming.image_id_to_annotations.items()),
            args.num_to_visualize)
    else:
        incoming_id_to_anno = incoming.image_id_to_annotations.items()

    # The file_name field in the incoming json looks like alka_squirrels.seq2020_05_07_25C.frame119221.jpg
    # we need to use the dataset, sequence and frame info to find the actual path in blob storage
    # using the sequences
    images_html = []
    for image_id, annotations in tqdm(incoming_id_to_anno):
        if args.trim_to_images_bboxes_labeled and annotations[0][
                'category_id'] == 5:
            # category_id 5 is No Object Visible
            continue

        anno_file_name = incoming.image_id_to_image[image_id]['file_name']
        parts = anno_file_name.split('.')
        dataset_name = parts[0]
        seq_id = parts[1].split('seq')[1]
        frame_num = int(parts[2].split('frame')[1])

        im_rel_path = get_image_rel_path(dataset_seq_images, dataset_name,
                                         seq_id, frame_num)
        if im_rel_path is None:
            print(f'Not found in megadb entries: dataset {dataset_name},'
                  f' seq_id {seq_id}, frame_num {frame_num}')
            continue

        im_full_path = megadb_utils.get_full_path(datasets_table, dataset_name,
                                                  im_rel_path)

        # download the image
        container_client = megadb_utils.get_storage_client(
            datasets_table, dataset_name)
        downloader = container_client.download_blob(im_full_path)
        image_file = io.BytesIO()
        blob_props = downloader.download_to_stream(image_file)
        image = vis_utils.open_image(image_file)

        boxes = [anno['bbox'] for anno in annotations]
        classes = [anno['category_id'] for anno in annotations]

        vis_utils.render_iMerit_boxes(boxes,
                                      classes,
                                      image,
                                      label_map=incoming.cat_id_to_name)

        file_name = '{}_gtbbox.jpg'.format(
            os.path.splitext(anno_file_name)[0].replace('/', '~'))
        image = vis_utils.resize_image(image, args.output_image_width)
        image.save(os.path.join(args.output_dir, 'rendered_images', file_name))

        images_html.append({
            'filename':
            '{}/{}'.format('rendered_images', file_name),
            'title':
            '{}, number of boxes: {}'.format(
                anno_file_name, len([b for b in boxes if len(b) > 0])),
            'textStyle':
            'font-family:verdana,arial,calibri;font-size:80%;text-align:left;margin-top:20;margin-bottom:5'
        })

    # Write to HTML
    images_html = sorted(images_html, key=lambda x: x['filename'])
    write_html_image_list(filename=os.path.join(args.output_dir, 'index.html'),
                          images=images_html,
                          options={
                              'headerHtml':
                              '<h1>Sample annotations from {}</h1>'.format(
                                  args.incoming_annotation)
                          })

    print('Visualized {} images.'.format(len(images_html)))