<jupyter_start><jupyter_text>Getting the Labels Ready To look for:* **CHOOSE requires action** * CONSTRUCT* CHECK* SAVE  Imports<jupyter_code>%matplotlib inline
from pycocotools.coco import COCO

import json
import numpy as np
import pandas as pd
import skimage.io as io
import matplotlib.pyplot as plt
import pylab
pylab.rcParams['figure.figsize'] = (8.0, 10.0)
# https://stackoverflow.com/questions/18035595/powersets-in-python-using-itertools
from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
def reverse_one_to_many_dict(one_to_many_dict):
    one_to_one_dict = {}
    
    for key, arr in one_to_many_dict.items():
        for a in arr:
            assert a not in one_to_one_dict, "Trying to replace existing key: {}".format(a)
            one_to_one_dict[a] = key
            
    return one_to_one_dict


# sample_dict_good = {
#     'a': [1, 2, 3],
#     'b': [4, 5, 6]
# }
# print(reverse_one_to_many_dict(sample_dict_good)) # all good

# sample_dict_bad = {
#     'a': [1, 2, 3],
#     'b': [3, 5, 6]
# }
# print(reverse_one_to_many_dict(sample_dict_bad)) # supposed to fail with 3<jupyter_output><empty_output><jupyter_text>CHOOSE: Images and Annotations Directories<jupyter_code>dataDir='..'
dataType='val2017'
annFile='{}/annotations/instances_{}.json'.format(dataDir, dataType)
# initialize COCO api for instance annotations
coco=COCO(annFile)<jupyter_output>loading annotations into memory...
Done (t=0.27s)
creating index...
index created!<jupyter_text>**Note**, `cat` and `cats` stand for **category** and **categories** NOT for cat that is like a dog, but a cat.<jupyter_code># display COCO categories and supercategories
cats = coco.loadCats(coco.getCatIds())
nms=[cat['name'] for cat in cats]
print('COCO categories: \n\n{}\n'.format(' '.join(nms)))

nms = set([cat['supercategory'] for cat in cats])
print('COCO supercategories: \n\t{}'.format(' '.join(nms)))<jupyter_output>COCO categories: 

person bicycle car motorcycle airplane bus train truck boat traffic light fire hydrant stop sign parking meter bench bird cat dog horse sheep cow elephant bear zebra giraffe backpack umbrella handbag tie suitcase frisbee skis snowboard sports ball kite baseball bat baseball glove skateboard surfboard tennis racket bottle wine glass cup fork knife spoon bowl banana apple sandwich orange broccoli carrot hot dog pizza donut cake chair couch potted plant bed dining table toilet tv laptop mouse remote keyboard cell phone microwave oven toaster sink refrigerator book clock vase scissors teddy bear hair drier toothbrush

COCO supercategories: 
	vehicle food outdoor animal indoor person accessory furniture electronic appliance kitchen sports<jupyter_text>CHOOSE: categories of Interest (in `./utils/constants.py`)<jupyter_code>%cd ./utils/
from config import CATEGORIES_OF_INTEREST
print(CATEGORIES_OF_INTEREST)
%cd ..<jupyter_output>/Users/gbatch/Google Drive/colab_projects/cocoapi/notebooks/utils
['bird', 'cat', 'dog', 'person']
/Users/gbatch/Google Drive/colab_projects/cocoapi/notebooks<jupyter_text>CONSTRUCT a mapping from the categories of interest to image IDs<jupyter_code>%%time

catNms_to_imgIds = {}
for include_cats_names in powerset(CATEGORIES_OF_INTEREST):
    include_cats_names = tuple(sorted(include_cats_names)) # sort to have the same keys every time
    
    # include_cats_names should be a tuple (immutable) so that we can use it as a key to a dictionary
    exclude_cats_names = set(CATEGORIES_OF_INTEREST) - set(include_cats_names)
    exclude_cats_names = tuple(sorted(exclude_cats_names)) # sort to have the same keys every time
    
    catIds = coco.getCatIds(catNms=include_cats_names)
    exclCatIDs = coco.getCatIds(catNms=exclude_cats_names)
    
    imgIds = coco.getImgIds(catIds=catIds, exclCatIds=exclCatIDs)

    catNms_to_imgIds[include_cats_names] = imgIds

# correct the Ids for the images which do not have any of the categories in the CATEGORIES_OF_INTEREST
# correction needed because
#    include_cats_names = () results in
#    catIds = All Coco categories but the CATEGORIES_OF_INTEREST
#    There are no images with all those categories present
catNms_to_imgIds[()] = coco.getImgIds(exclCatIds=coco.getCatIds(CATEGORIES_OF_INTEREST))


# correct the Ids for the images with all of the CATEGORIES_OF_INTEREST present
# correction needed because
#    exclude_cats_names = () results in
#    exclCatIds = All Coco categories but the CATEGORIES_OF_INTEREST
#    We exclude all the images with any of the exclusion categories present, which is likely to exclude all the images
catNms_to_imgIds[tuple(sorted(CATEGORIES_OF_INTEREST))] = coco.getImgIds(catIds=coco.getCatIds(CATEGORIES_OF_INTEREST))<jupyter_output>CPU times: user 10.2 ms, sys: 148 µs, total: 10.4 ms
Wall time: 10.4 ms<jupyter_text>CHECK that the IDs in disjoint sets are a partition of the original set of images1. $\cup s_i = S$, where $S$ - original set of IDs2. $ s_i \cap s_j = \emptyset, \forall i \neq j$ - indexes of different categories  CHECK that the sets created are disjoint2. $ s_i \cap s_j = \emptyset, \forall i \neq j$ - indexes of different categories<jupyter_code>%%time

for key1 in catNms_to_imgIds:
    for key2 in catNms_to_imgIds:
        if key1 == key2:
            pass
        else:
            s1 = set(catNms_to_imgIds[key1])
            s2 = set(catNms_to_imgIds[key2])
            inters = s1.intersection(s2)
            
            # # for debugging
            # print(key1, len(s1))
            # print(key2, len(s2))
            # print('Intersection:', inters, '\n')
            
            assert len(inters) == 0, "The subsets have to be disjoint"<jupyter_output>CPU times: user 3.47 ms, sys: 47 µs, total: 3.51 ms
Wall time: 3.52 ms<jupyter_text>Calculate how many images are in each disjoint category<jupyter_code>catNms_to_ImgIdsNum = {key: len(value) for (key, value) in catNms_to_imgIds.items()}
catNms_to_ImgIdsNum
summary = {
    'category': [],
    'total': []
}
for key in catNms_to_ImgIdsNum:
    summary['category'].append(key)
    summary['total'].append(catNms_to_ImgIdsNum[key])
    
summary_df = pd.DataFrame(summary)


assert summary_df['total'].sum() == len(set(coco.getImgIds())), "Some images were missed"
summary_df<jupyter_output><empty_output><jupyter_text>CHECK that the union adds up to the original set1. $\cup s_i = S$, where $S$ - original set of IDs<jupyter_code>all_ids = set(coco.getImgIds())
len(all_ids)

my_all_ids = set().union(*[catNms_to_imgIds[key] for key in catNms_to_imgIds])
len(my_all_ids)


# all ids we have now should be at least the subset of the original ones, uncomment the next line to see it work
# my_all_ids.add(1) 
assert my_all_ids.issubset(all_ids), "Got some new ids that were not present in Coco"


# all ids from the original set have to be contained in the union of the disjoint sets of ids
assert all_ids.issubset(my_all_ids), "We are missing some of the images from the original set"


# which pictures have we missed?
print("IDs missed:", all_ids - my_all_ids)
for sample_id in list(all_ids - my_all_ids):
    print(sample_id)
    
    sample_img_info = coco.loadImgs(sample_id)[0]

    print('Fields available:', list(sample_img_info.keys()), '\n')
    for key, value in sample_img_info.items():
        print(key, (13-len(key))*' ', ':', value)
        
    print('\n', '-'*80, '\n')<jupyter_output>IDs missed: set()<jupyter_text>SAVE: `catNms_to_imgIds` in a json file<jupyter_code># can't save .json files with tuples as keys, but can with strings as keys
strCatNms_to_imgIds = {' '.join(key):value for (key, value) in catNms_to_imgIds.items()}

strCatNms_to_ImgIdsNum = {key: len(value) for (key, value) in strCatNms_to_imgIds.items()}
strCatNms_to_ImgIdsNum
with open(f'../my_annotations/strCatNms_to_imgIds_{dataType}.json', 'w') as f:
    json.dump(strCatNms_to_imgIds, f, sort_keys=True)
with open(f'../my_annotations/strCatNms_to_imgIds_{dataType}.json', 'r') as f:
    strCatNms_to_imgIds = json.load(f)
strCatNms_to_ImgIdsNum = {key: len(value) for (key, value) in strCatNms_to_imgIds.items()}
strCatNms_to_ImgIdsNum<jupyter_output><empty_output><jupyter_text>CONSTRUCT a mapping from Categories to Labels`catNms_to_labels`<jupyter_code>CATEGORIES_OF_INTEREST
catNms_to_labels = {}

for cat_name in catNms_to_imgIds:
    multilabel = []
    for c in CATEGORIES_OF_INTEREST:
        multilabel.append(int(c in set(cat_name)))
    
    catNms_to_labels[cat_name] = multilabel

catNms_to_labels<jupyter_output><empty_output><jupyter_text>CONSTRUCT a mapping from Images to Disjoint Categories`imgIds_to_cats`<jupyter_code>imgIds_to_catNms = reverse_one_to_many_dict(catNms_to_imgIds)

# for key in np.random.choice(list(imgIds_to_catNms.keys()), 1000):
#     print(key, imgIds_to_catNms[key])<jupyter_output><empty_output><jupyter_text>CONSTRUCT a mapping from Images to Multi-Labels`imgIds_to_labels`<jupyter_code>imgIds_to_labels = {}

coco.getImgIds()
for imgId in coco.getImgIds():
    catNm = imgIds_to_catNms[imgId]
    imgIds_to_labels[imgId] = catNms_to_labels[catNm]
    
# imgIds_to_labels
len(imgIds_to_labels)<jupyter_output><empty_output><jupyter_text>CHECK: that the numbers for generic categories are the same<jupyter_code>imgIds_to_labels_df = pd.DataFrame(imgIds_to_labels).T
imgIds_to_labels_df
for i, name in enumerate(CATEGORIES_OF_INTEREST):
    print(i, name)
imgIds_to_labels_df.sum(axis=0)
for i, catNm in enumerate(sorted(CATEGORIES_OF_INTEREST)):
    catId = coco.getCatIds(catNms=[catNm])
    imgIds = coco.getImgIds(catIds=catId)
    print(i, len(imgIds))
    
    assert len(imgIds) == imgIds_to_labels_df.sum(axis=0)[i], \
        "Numbers for primary category ({}: '{}') do not match".format(i, CATEGORIES_OF_INTEREST[i])<jupyter_output>0 125
1 184
2 177
3 2693<jupyter_text>SAVE: `imgIds_to_labels` in a json file**Important:** this labels can only be used if the images are resized without cropping. If any cropping is done, some parts of the image will be cropped out and some of the objects can no longer be present in the cropped images.<jupyter_code>import json

with open(f'../my_annotations/imgIds_to_labels_{dataType}.json', 'w') as f:
    json.dump(imgIds_to_labels, f, sort_keys=True)
with open(f'../my_annotations/imgIds_to_labels_{dataType}.json', 'r') as f:
    imgIds_to_labels = json.load(f)<jupyter_output><empty_output><jupyter_text>EXTRACT: the segmentation annotation labels (to be used with cropping)<jupyter_code>CATEGORIES_OF_INTEREST, coco.getCatIds(catNms=CATEGORIES_OF_INTEREST)
catIDs = coco.getCatIds(catNms=CATEGORIES_OF_INTEREST)
disjoint_cat = np.random.choice(np.array(list(catNms_to_imgIds.keys()), dtype=object))
disjoint_cat
sample_img_id = np.random.choice(catNms_to_imgIds[disjoint_cat])
print(sample_img_id, type(sample_img_id)) # does not work when calling coco.loadImgs(sample_img_id)[0]

sample_img_id = int(sample_img_id)
print(sample_img_id, type(sample_img_id))
sample_img_info = coco.loadImgs(sample_img_id)[0]
sample_img = io.imread(sample_img_info['coco_url'])

plt.axis('off')
plt.imshow(sample_img)
plt.show()

type(sample_img), sample_img.shape
# load and display instance annotations for SELECTED categories
plt.imshow(sample_img); plt.axis('off')
annIds = coco.getAnnIds(imgIds=sample_img_info['id'], catIds=catIds, iscrowd=None)
anns = coco.loadAnns(annIds)
coco.showAnns(anns, draw_bbox=True)
print(len(anns), 'objects of selected categories found on this image.')
for ann in anns:
    for key in ann:
        print(key, '\n\t', ann[key])
        print()
    print('-'*115)
# load and display instance annotations for ALL categories
plt.imshow(sample_img); plt.axis('off')
annIds = coco.getAnnIds(imgIds=sample_img_info['id'], iscrowd=None)
anns = coco.loadAnns(annIds)
coco.showAnns(anns, draw_bbox=True)
print(len(anns), 'objects of selected categories found on this image.')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10631395058647367, 'fraction_numerical': 0.01039846934531237, 'mean_word_length': 5.168291431503335, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 55, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '414484', 'license': 'BSD-2-Clause-FreeBSD', 'n_tokens_mistral': 4417, 'n_tokens_neox': 4279, 'n_words': 1084, 'path': 'notebooks/1-data-prep.ipynb', 'repo_name': 'GeorgeBatch/cocoapi', 'size': '12021'}