{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#### This notebook draws the gray dots from the annotation file","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport json\nimport torch\nimport os\nfrom PIL import Image, ImageDraw","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ann_path = \"/kaggle/input/benetech-making-graphs-accessible/train/annotations/\"\ntest_ann_path = \"/kaggle/input/benetech-making-graphs-accessible/test/annotations\"","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.498819Z","iopub.execute_input":"2023-04-24T12:53:43.499715Z","iopub.status.idle":"2023-04-24T12:53:43.510498Z","shell.execute_reply.started":"2023-04-24T12:53:43.499659Z","shell.execute_reply":"2023-04-24T12:53:43.509418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef show_image(pilim : Image):\n    plt.imshow(np.array(pilim))\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.511981Z","iopub.execute_input":"2023-04-24T12:53:43.512672Z","iopub.status.idle":"2023-04-24T12:53:43.522445Z","shell.execute_reply.started":"2023-04-24T12:53:43.512634Z","shell.execute_reply":"2023-04-24T12:53:43.521471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image = Image.open(\"/kaggle/input/benetech-making-graphs-accessible/train/images/ff42f8f92719.jpg\").convert('RGB') # ff42f8f92719 0000ae6cbdb1\nwith open(\"/kaggle/input/benetech-making-graphs-accessible/train/annotations/ff42f8f92719.json\") as f:\n    test_annotation = json.load(f)\n\nprint(test_annotation)\nshow_image(test_image)","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.524934Z","iopub.execute_input":"2023-04-24T12:53:43.525329Z","iopub.status.idle":"2023-04-24T12:53:43.805280Z","shell.execute_reply.started":"2023-04-24T12:53:43.525291Z","shell.execute_reply":"2023-04-24T12:53:43.803840Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Plotting the annotations","metadata":{}},{"cell_type":"code","source":"test_annotation.keys()","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.806825Z","iopub.execute_input":"2023-04-24T12:53:43.807885Z","iopub.status.idle":"2023-04-24T12:53:43.815940Z","shell.execute_reply.started":"2023-04-24T12:53:43.807843Z","shell.execute_reply":"2023-04-24T12:53:43.814606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_annotation['axes']","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.817347Z","iopub.execute_input":"2023-04-24T12:53:43.817743Z","iopub.status.idle":"2023-04-24T12:53:43.832105Z","shell.execute_reply.started":"2023-04-24T12:53:43.817710Z","shell.execute_reply":"2023-04-24T12:53:43.830608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if test_annotation['axes']['x-axis']['values-type'] == 'numerical':\n    x_min_cord_id, x_max_cord_id = test_annotation['axes']['x-axis']['ticks'][0]['id'], test_annotation['axes']['x-axis']['ticks'][-1]['id']\n    y_min_cord_id, y_max_cord_id = test_annotation['axes']['y-axis']['ticks'][0]['id'], test_annotation['axes']['y-axis']['ticks'][-1]['id']\n\n    x_min_cord, x_max_cord = test_annotation['axes']['x-axis']['ticks'][0]['tick_pt']['x'], test_annotation['axes']['x-axis']['ticks'][-1]['tick_pt']['x']\n    y_min_cord, y_max_cord = test_annotation['axes']['y-axis']['ticks'][0]['tick_pt']['y'], test_annotation['axes']['y-axis']['ticks'][-1]['tick_pt']['y']\n\n    x_min_value, x_max_value = float(test_annotation['text'][x_min_cord_id]['text']), float(test_annotation['text'][x_max_cord_id]['text'])\n    # Note: y MIN cord will match y MAX value -> The value decreases from top to bottom\n    y_min_value, y_max_value = float(test_annotation['text'][y_max_cord_id]['text']), float(test_annotation['text'][y_min_cord_id]['text'])\n\n    print(f\"({x_min_cord}, {x_max_cord}) -> {x_min_value}, {x_max_value}\")\n    print(f\"({y_min_cord}, {y_max_cord}) -> {y_min_value}, {y_max_value}\")\n\n    x_unit_len = (x_max_cord - x_min_cord) / (x_max_value - x_min_value)\n    y_unit_len =  (y_max_cord - y_min_cord) / (y_max_value - y_min_value)\n\n    print(x_unit_len, y_unit_len)\n","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.833958Z","iopub.execute_input":"2023-04-24T12:53:43.834422Z","iopub.status.idle":"2023-04-24T12:53:43.849706Z","shell.execute_reply.started":"2023-04-24T12:53:43.834383Z","shell.execute_reply":"2023-04-24T12:53:43.848496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_annotation['text']","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.851848Z","iopub.execute_input":"2023-04-24T12:53:43.852219Z","iopub.status.idle":"2023-04-24T12:53:43.877718Z","shell.execute_reply.started":"2023-04-24T12:53:43.852185Z","shell.execute_reply":"2023-04-24T12:53:43.876236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# BBox\nbbox_dict = test_annotation['plot-bb']\nheight, width, bbox_x, bbox_y = bbox_dict.values()\ndraw = ImageDraw.Draw(test_image)\ndraw.rectangle(((bbox_x, bbox_y), (bbox_x + width, bbox_y + height)))\n\n# texts\ntexts = test_annotation['text']\ndata_series_labels = list(map(lambda x: x['x'], test_annotation['data-series']))\ntick_label_record = {} # For plotting data series later\nrole_mapping = {\n    \"chart_title\": (255, 0, 0),\n    \"axis_title\": (0, 255, 0),\n    \"tick_label\": (0, 0, 255)\n}\nfor text in texts:\n    x0, x1, x2, x3, y0, y1, y2, y3 = text['polygon'].values()\n    draw.rectangle(((x0, y0), (x1, y2)), outline=role_mapping[text['role']])\n    if text['text'] in data_series_labels:\n        tick_label_record[text['text']] = (x1 + x0) // 2\n\n# axes\nr = 2\nfor axis in  test_annotation['axes'].values():\n    for tick in axis['ticks']:\n        x, y = tick['tick_pt'].values()\n        draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(0, 0, 0))\n\n# data series\ndataseries = test_annotation['data-series']\n\ny_min_cord_id, y_max_cord_id = test_annotation['axes']['y-axis']['ticks'][0]['id'], test_annotation['axes']['y-axis']['ticks'][-1]['id']\ny_min_cord, y_max_cord = test_annotation['axes']['y-axis']['ticks'][0]['tick_pt']['y'], test_annotation['axes']['y-axis']['ticks'][-1]['tick_pt']['y']\n# Note: MIN cord will match MAX value -> The value decreases from top to bottom\ny_min_value, y_max_value = float(test_annotation['text'][y_max_cord_id]['text']), float(test_annotation['text'][y_min_cord_id]['text'])\ny_unit_len =  (y_max_cord - y_min_cord) / (y_max_value - y_min_value)\n\nif test_annotation['axes']['x-axis']['values-type'] == 'numerical':\n    x_min_cord_id, x_max_cord_id = test_annotation['axes']['x-axis']['ticks'][0]['id'], test_annotation['axes']['x-axis']['ticks'][-1]['id']\n    x_min_cord, x_max_cord = test_annotation['axes']['x-axis']['ticks'][0]['tick_pt']['x'], test_annotation['axes']['x-axis']['ticks'][-1]['tick_pt']['x']\n    # Similar to y_min_value and y_max_value\n    x_min_value, x_max_value = float(test_annotation['text'][x_max_cord_id]['text']), float(test_annotation['text'][x_min_cord_id]['text'])\n    x_unit_len = (x_max_cord - x_min_cord) / (x_max_value - x_min_value)\n\n    for entry in dataseries:\n        x_val, y_val = entry.values()\n        x = x_min_cord + (x_max_value - x_val) * x_unit_len\n        y = y_min_cord + (y_max_value - y_val) * y_unit_len\n        draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(112, 112, 112))\n\nelse:\n    # Categorical\n    print(\"cate\")\n    x_ticks = list(map(lambda ele: ele['tick_pt']['x'], test_annotation['axes']['x-axis']['ticks']))\n    for entry, x in zip(dataseries, x_ticks):\n        y_val = entry['y']\n        y = y_min_cord + (y_max_value - y_val) * y_unit_len\n        draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(112, 112, 112))\n\nshow_image(test_image)","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:43.881956Z","iopub.execute_input":"2023-04-24T12:53:43.882545Z","iopub.status.idle":"2023-04-24T12:53:44.170793Z","shell.execute_reply.started":"2023-04-24T12:53:43.882495Z","shell.execute_reply":"2023-04-24T12:53:44.169832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"... and an abstracted function for you to play around :)","metadata":{}},{"cell_type":"code","source":"def draw_annotations(image: Image, annotation:dict) -> None:\n    draw = ImageDraw.Draw(image)\n    \n    # BBox\n    bbox_dict = annotation['plot-bb']\n    height, width, bbox_x, bbox_y = bbox_dict.values()\n    draw.rectangle(((bbox_x, bbox_y), (bbox_x + width, bbox_y + height)))\n\n    # texts\n    texts = annotation['text']\n    data_series_labels = list(map(lambda x: x['x'], annotation['data-series']))\n    tick_label_record = {} # For plotting data series later\n    role_mapping = {\n        \"chart_title\": (255, 0, 0),\n        \"axis_title\": (0, 255, 0),\n        \"tick_label\": (0, 0, 255)\n    }\n    for text in texts:\n        x0, x1, x2, x3, y0, y1, y2, y3 = text['polygon'].values()\n        draw.rectangle(((x0, y0), (x1, y2)), outline=role_mapping[text['role']])\n        if text['text'] in data_series_labels:\n            tick_label_record[text['text']] = (x1 + x0) // 2\n\n    # axes\n    r = 2\n    for axis in  annotation['axes'].values():\n        for tick in axis['ticks']:\n            x, y = tick['tick_pt'].values()\n            draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(0, 0, 0))\n\n    # data series\n    dataseries = annotation['data-series']\n\n    y_min_cord_id, y_max_cord_id = annotation['axes']['y-axis']['ticks'][0]['id'], annotation['axes']['y-axis']['ticks'][-1]['id']\n    y_min_cord, y_max_cord = annotation['axes']['y-axis']['ticks'][0]['tick_pt']['y'], annotation['axes']['y-axis']['ticks'][-1]['tick_pt']['y']\n    # Note: MIN cord will match MAX value -> The value decreases from top to bottom\n    y_min_value, y_max_value = float(annotation['text'][y_max_cord_id]['text']), float(annotation['text'][y_min_cord_id]['text'])\n    y_unit_len =  (y_max_cord - y_min_cord) / (y_max_value - y_min_value)\n\n    if annotation['axes']['x-axis']['values-type'] == 'numerical':\n        print('numerical')\n        x_min_cord_id, x_max_cord_id = annotation['axes']['x-axis']['ticks'][0]['id'], annotation['axes']['x-axis']['ticks'][-1]['id']\n        x_min_cord, x_max_cord = annotation['axes']['x-axis']['ticks'][0]['tick_pt']['x'], annotation['axes']['x-axis']['ticks'][-1]['tick_pt']['x']\n        # Similar to y_min_value and y_max_value\n        x_min_value, x_max_value = float(annotation['text'][x_max_cord_id]['text']), float(annotation['text'][x_min_cord_id]['text'])\n        x_unit_len = (x_max_cord - x_min_cord) / (x_max_value - x_min_value)\n\n        for entry in dataseries:\n            x_val, y_val = entry.values()\n            x = x_min_cord + (x_max_value - x_val) * x_unit_len\n            y = y_min_cord + (y_max_value - y_val) * y_unit_len\n            draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(112, 112, 112))\n\n    else:\n        # Categorical\n        x_ticks = list(map(lambda ele: ele['tick_pt']['x'], annotation['axes']['x-axis']['ticks']))\n        for entry, x in zip(dataseries, x_ticks):\n            y_val = entry['y']\n            y = y_min_cord + (y_max_value - y_val) * y_unit_len\n            draw.ellipse(((x - r, y - r), (x + r, y + r)), fill=(112, 112, 112))","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:44.172486Z","iopub.execute_input":"2023-04-24T12:53:44.173046Z","iopub.status.idle":"2023-04-24T12:53:44.192987Z","shell.execute_reply.started":"2023-04-24T12:53:44.173008Z","shell.execute_reply":"2023-04-24T12:53:44.191547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Test","metadata":{}},{"cell_type":"code","source":"# TODO: Change the number of samples, using 10 here\nfor img_name in os.listdir(\"/kaggle/input/benetech-making-graphs-accessible/train/images/\")[:50]:\n    try:\n        name_without_ext = img_name.split(\".\")[0]\n        im = Image.open(f\"/kaggle/input/benetech-making-graphs-accessible/train/images/{img_name}\").convert(\"RGB\")\n        with open(f\"/kaggle/input/benetech-making-graphs-accessible/train/annotations/{name_without_ext}.json\") as f:\n            test_annotation = json.load(f)\n        draw_annotations(im, test_annotation)\n        show_image(im)\n    except TypeError:\n        im.show()\n    except ValueError as e:\n        print(e)\n        im.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-24T12:53:44.194247Z","iopub.execute_input":"2023-04-24T12:53:44.194585Z","iopub.status.idle":"2023-04-24T12:53:56.188768Z","shell.execute_reply.started":"2023-04-24T12:53:44.194555Z","shell.execute_reply":"2023-04-24T12:53:56.187330Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We see that not all the data points are labelled with gray dots, could be a bug or the dataset :\")","metadata":{}}]}