{"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":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport cv2\nimport matplotlib.pyplot as plt\n\nimport os\nimport sys\nimport gc\nfrom tqdm import tqdm\nimport seaborn as sns\n\nfrom time import sleep\n\nfrom openslide import OpenSlide\nfrom collections import defaultdict\nfrom PIL import Image\n\nimport csv","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Initializion of required variables here.**","metadata":{}},{"cell_type":"code","source":"# initialize variables here\nis_resize_image = True\nsave_image_size = (256, 256)\ntarget_image_size = (256, 256)\n\nmax_slices = 256\n\nresized_image_path = {'train': \"./train/\", 'test':\"./test/\"}\n\ninput_images_path = \"../input/minivggnet-using-3x3-kernel/train/\"\ntest_images_path = \"../input/minivggnet-using-3x3-kernel/test/\"\n\nsave_model_path = \"../input/minivggnet-using-3x3-kernel/model/minivggnet_strip_ai.hdf5\"\n\nif is_resize_image:\n    input_images_path = \"./train/\"\n    test_images_path = \"./test/\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Functions**","metadata":{}},{"cell_type":"code","source":"def mask_image(bgr_region, mask):\n    masked = bgr_region * np.dstack([mask, mask, mask])\n    return masked\n\ndef complement_image(image):\n    complement = 255 - image\n    return complement","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def filter_image_canny(bgr_region, grey_region):\n    #remove green pen marks\n    # remove blue pen marks\n    # remove ren pen marks\n    \n    canny = cv2.Canny(grey_region, threshold1=0, threshold2=25)\n    filtered_image = mask_image(bgr_region, canny)\n    return filtered_image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import skimage.filters as sk_filters\nimport skimage.color as sk_color\nimport skimage.segmentation as sk_segmentation\n\ndef filter_image_kmeans_seg(bgr_region, grey_region):\n    #remove green pen marks\n    # remove blue pen marks\n    # remove ren pen marks\n    \n    \n    otsu_thresh_value = sk_filters.threshold_otsu(complement_image(grey_region))\n    otsu = (complement_image(grey_region) > otsu_thresh_value)\n    otsu_masked = mask_image(bgr_region, otsu) \n    labels = sk_segmentation.slic(otsu_masked, start_label=1, compactness=10, n_segments=3000)\n    filtered_image = sk_color.label2rgb(labels, otsu_masked, bg_label=0, kind='avg')\n    return filtered_image\n\ndef filter_image_otsu(bgr_region, grey_region):\n    #remove green pen marks\n    # remove blue pen marks\n    # remove ren pen marks\n    \n    (T, threshInv) = cv2.threshold(grey_region, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) \n    filtered_image = cv2.bitwise_and(bgr_region, bgr_region, mask=threshInv)\n    return filtered_image\n\ndef filter_image_entropy(bgr_region, grey_region, neighborhood=9, threshold=5):\n    #remove green pen marks\n    # remove blue pen marks\n    # remove ren pen marks\n    \n    entropy = sk_filters.rank.entropy(grey_region, np.ones((neighborhood, neighborhood))) > threshold\n    filtered_image = mask_image(bgr_region, entropy)\n    return filtered_image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CheckStdevPreprocessor:\n    def __init__(self, min_stdev=20, verbose=False):\n        self.min_stdev = min_stdev\n        self.verbose =  verbose\n        \n    # image in numpy array\n    def preprocess(self, image):     \n        std_dev = np.std(image.ravel())  # remove less than 20 std dev\n        if std_dev > self.min_stdev:\n            if self.verbose:\n                print(\"[INFO] STD: {:.02f}\".format(std_dev))\n            return True\n        else:\n            return False","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CheckContoursPreprocessor:\n    def __init__(self, min_contours=40, kernel_size=(11,11), verbose=False):\n        self.min_contours = min_contours\n        self.kernel_size = kernel_size\n        self.verbose =  verbose\n    \n    # image in numpy array\n    def preprocess(self, image):     \n        blurred = cv2.GaussianBlur(image, self.kernel_size, 0)\n        edged = cv2.Canny(blurred, threshold1=100, threshold2=200)\n        # version 3.x\n        cnts, hierarchy= cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, \n                                          cv2.CHAIN_APPROX_SIMPLE)\n        if cnts is not None and len(cnts) > self.min_contours:\n            if self.verbose:\n                print(\"[INFO] Edges {}\".format(len(cnts)))\n            return True\n        else:\n            return False","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CheckPeaksPreprocessor:\n    def __init__(self, min_peaks=10, verbose=False):\n        self.min_peaks = min_peaks\n        self.verbose =  verbose\n    \n    # image in numpy array\n    def preprocess(self, image):    \n            #image_df = pd.DataFrame({\"pixel\": image.ravel()})\n            #print(image_df.head())\n            #print(image_df[\"pixel\"].value_counts().head())\n            #peaks, _ = find_peaks(image_df, width=10)\n            #print(len(peaks)\n        return False","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Load training and testing dataset**","metadata":{}},{"cell_type":"code","source":"# load the images and labels\ntrain_data = pd.read_csv(\"../input/mayo-clinic-strip-ai/train.csv\")\ntest_data = pd.read_csv(\"../input/mayo-clinic-strip-ai/test.csv\")\n\nclass_labels = [str(x) for x in np.unique(train_data['label'])]\n\nprint(train_data.head())\nprint(class_labels)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Query image properties and save it to the dataframe.**","metadata":{}},{"cell_type":"code","source":"image_prop = defaultdict(list)\n\nfor i in range(len(train_data)):\n    image_id = train_data[\"image_id\"][i]\n    image_path = \"../input/mayo-clinic-strip-ai/train/\" + image_id + \".tif\"\n    image_slide = OpenSlide(image_path)\n    image_prop['image_id'].append(image_id)\n    image_prop['width'].append(image_slide.dimensions[0])\n    image_prop['height'].append(image_slide.dimensions[1])\n    image_prop['size'].append(round(os.path.getsize(image_path) / 1e6, 2))\n    if image_slide.dimensions[0] > image_slide.dimensions[1]:\n        aspect_ratio = image_slide.dimensions[0] / image_slide.dimensions[1]\n    else:\n        aspect_ratio = image_slide.dimensions[1] / image_slide.dimensions[0]\n    image_prop['aspect_ratio'].append(round(aspect_ratio,1))\n    image_prop['pixels'].append(image_slide.dimensions[0] * image_slide.dimensions[1])\n\ndel image_slide\n\nimage_data = pd.DataFrame(image_prop)\n\ndel image_prop\n\nimage_data.sort_values(by='image_id', inplace=True)\nimage_data.reset_index(inplace=True, drop=True)\nimage_data.head()\n\ntrain_data = train_data.merge(image_data, on='image_id')\ntrain_data.sort_values([\"size\"], ascending=False).head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Slices image into fixed spatial dimension**","metadata":{}},{"cell_type":"code","source":"def slice_image(slide, size=(1024,1024), slices=None, min_std=20, image_id=\"image_id\", path=\"\", \n                preprocessors=None, patient_id=\"patient_id\", image_label=None, other_info=None,\n                max_slices = 1e6, debug_resizing=False):\n    \n    hits = 0\n    \n    path = str(path).strip()\n    if len(path):\n        try:\n            os.mkdir(path)\n            os.mkdir(os.path.join(path, \"orig/\"))\n            os.mkdir(os.path.join(path, \"zeros\"))\n            os.mkdir(os.path.join(path, \"filtered/\"))\n            if image_label is not None:\n                os.mkdir(os.path.join(path, \"filtered/\", image_label))\n        except:\n            pass  \n    if slices is None:\n        sl = list()\n        sl.append(np.ceil(slide.dimensions[0] / size[0]).astype(int))\n        sl.append(np.ceil(slide.dimensions[1] / size[1]).astype(int))\n        slices = tuple(sl)\n    \n    if debug_resizing:\n        print(\"[DEBUG] Number of slices: x={}, y={}\".format(slices[0], slices[1]))\n        \n    image_prop = defaultdict(list)\n    for i in range(slices[0]):\n        \n        if len(image_prop['image_slice_id']) >= max_slices:\n            break\n            \n        for j in range(slices[1]):\n            \n            if len(image_prop['image_slice_id']) >= max_slices:\n                break\n                \n            region = slide.read_region((i*size[0], j*size[1]), 0, size).convert(\"RGB\")\n            region = np.array(region)  # convert PIL RGB to np array\n            region = cv2.cvtColor(region, cv2.COLOR_RGB2BGR) # convert to RGB to BGR\n            grey = cv2.cvtColor(region, cv2.COLOR_BGR2GRAY) # convert RGB to gray\n            \n            # check to see if our preprocessors are not None\n            if preprocessors is not None:\n            # loop over the preprocessors and apply each to\n            # the image\n                is_valid = False\n                for p in preprocessors:\n                    is_valid = p.preprocess(grey)\n                    if is_valid is False:\n                        break\n                        \n                if is_valid:\n                    \n                    hits += 1\n                    \n                    image_slice_id = \"{}.{:03d}.{:03d}\".format(image_id,i,j)\n                    image_prop['image_id'].append(image_id)\n                    image_prop['image_slice_id'].append(image_slice_id)\n                    image_prop['patient_id'].append(patient_id)\n                    \n                    if other_info is not None:\n                        for desciption, value in other_info.items():\n                            image_prop[desciption].append(value)\n                    \n                    # save to disk  {image_id}.{x position}.{y position}.jpg\n                    if debug_resizing:\n                        image_slice_id_path = os.path.join(path, \"orig/\", \"{}.jpg\".format(image_slice_id) )\n                        cv2.imwrite(image_slice_id_path, region)\n                    \n                    filtered_image = filter_image_otsu(region, grey)\n                    if image_label is not None:\n                        image_slice_id_path = os.path.join(path, \"filtered/\", \"{}/\".format(image_label), \"{}.jpg\".format(image_slice_id) )\n                    else:\n                        image_slice_id_path = os.path.join(path, \"filtered/\", \"{}.jpg\".format(image_slice_id) )\n                    cv2.imwrite(image_slice_id_path, filtered_image)\n                    \n                    if debug_resizing:\n                        print(\"[DEBUG] With Preprocessor: saved {}-{}\".format(i, j))\n                    \n                if is_valid and debug_resizing: \n                    fig, axes = plt.subplots(1,2, figsize=(6,4))\n                    axes[0].imshow(region);\n                    sns.histplot(grey.ravel(), ax=axes[1])\n                    plt.show()\n            # preprocessors are None        \n            else:\n                image_slice_id = \"{}.{:03d}.{:03d}\".format(image_id,i,j)\n                image_prop['image_id'].append(image_id)\n                image_prop['image_slice_id'].append(image_slice_id)\n                image_prop['patient_id'].append(patient_id)\n                \n                if other_info is not None:\n                        for desciption, value in other_info.items():\n                            image_prop[desciption].append(value)\n                            \n                # save to disk  {image_id}.{x position}.{y position}.jpg\n                if debug_resizing:\n                    image_slice_id_path = os.path.join(path, \"orig/\", \"{}.jpg\".format(image_slice_id) )\n                    cv2.imwrite(image_slice_id_path, region)\n\n                filtered_image = filter_image_otsu(region, grey)\n                if image_label is not None:\n                    image_slice_id_path = os.path.join(path, \"filtered/\", \"{}/\".format(image_label), \"{}.jpg\".format(image_slice_id) )\n                else:\n                    image_slice_id_path = os.path.join(path, \"filtered/\", \"{}.jpg\".format(image_slice_id) )\n                cv2.imwrite(image_slice_id_path, filtered_image)\n                \n                if debug_resizing:\n                    print(\"[DEBUG] No Preprocessor: saved {}-{}\".format(i, j))\n    if hits == 0 and preprocessors is not None:\n        if debug_resizing:\n            print(\"[DEBUG] No sliced image found for {}\".format(image_id))\n            \n        image_slice_id = \"{}.non.non\".format(image_id)\n        image_prop['image_id'].append(image_id)\n        image_prop['image_slice_id'].append(image_slice_id)\n        image_prop['patient_id'].append(patient_id)\n\n        if other_info is not None:\n            for desciption, value in other_info.items():\n                image_prop[desciption].append(value)\n\n        # save to disk  {image_id}.non.non.jpg\n        region = np.zeros((size[0],size[1],3), np.uint8)\n        \n        # ignore image with zero result if image_label is defined\n        if image_label is not None:\n            image_slice_id_path = os.path.join(path, \"zeros/\", \"{}.jpg\".format(image_slice_id) )\n            cv2.imwrite(image_slice_id_path, region)\n            return None\n        else:\n            image_slice_id_path = os.path.join(path, \"filtered/\", \"{}.jpg\".format(image_slice_id) )\n            cv2.imwrite(image_slice_id_path, region)\n            return image_prop\n        \n    return image_prop","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Resize Dataset**","metadata":{}},{"cell_type":"code","source":"def resize_images(data, input_images_dir, output_path, size=None, group_by_label=False,\n              preprocessors=None, details=None, max_slices = 1e6, debug_resizing=False):\n\n    image_slices = defaultdict(list)\n    image_slices['image_id'] = []\n    image_slices['image_slice_id'] = []\n    image_slices['patient_id'] =[]\n    \n    image_prop = defaultdict(list)\n    \n    with tqdm(total=len(data.index)) as pbar: \n        for row in data.itertuples():\n            image_id = row.image_id\n            patient_id = row.patient_id\n            image_label = None\n            if group_by_label:\n                image_label = row.label\n            \n            other_info = {}\n            if details is not None:\n                for i in details:\n                    other_info[i] = getattr(row, i)\n            \n            image_path = os.path.join(input_images_dir, \"{}.tif\".format(image_id))\n            slide = OpenSlide(image_path)\n            \n            if size is not None:\n                image_prop = slice_image(slide, size=size, path=output_path, image_id=image_id, patient_id=patient_id,\n                                         other_info=other_info, image_label=image_label,\n                                         preprocessors=preprocessors, max_slices = max_slices, debug_resizing=debug_resizing)\n                if image_prop is not None:\n                    image_slices['image_id'].extend(image_prop['image_id'])\n                    image_slices['image_slice_id'].extend(image_prop['image_slice_id'])\n                    image_slices['patient_id'].extend(image_prop['patient_id'])\n                \n                    for desciption, value in other_info.items():\n                        image_slices[desciption].extend(image_prop[desciption])\n                \n            elif size is None and slide.dimensions[0] > slide.dimensions[1]:  # width is greater than height \n                number_slice_w = np.ceil(slide.dimensions[0] / 8192.0).astype(int)\n                width = np.ceil(slide.dimensions[0]/ number_slice_w).astype(int)\n                height = np.ceil(width / (slide.dimensions[0] / slide.dimensions[1]) ).astype(int)\n                number_slice_h = np.ceil(slide.dimensions[1] / height).astype(int)\n                size = (width, height)\n                slices = (number_slice_w, number_slice_h)\n                image_prop = slice_image(slide, size=size, path=output_path, image_id=image_id, patient_id=patient_id,\n                                         other_info=other_info, image_label=image_label, slices = slices,\n                                         preprocessors=preprocessors, max_slices = max_slices, debug_resizing=debug_resizing)\n                if image_prop is not None:\n                    image_slices['image_id'].extend(image_prop['image_id'])\n                    image_slices['image_slice_id'].extend(image_prop['image_slice_id'])\n                    image_slices['patient_id'].extend(image_prop['patient_id'])\n                \n                    for desciption, value in other_info.items():\n                        image_slices[desciption].extend(image_prop[desciption])\n                \n            elif size is None and slide.dimensions[1] > slide.dimensions[0] :\n                number_slice_h = np.ceil(slide.dimensions[1] / 8192.0).astype(int)\n                height = np.ceil(slide.dimensions[1] / number_slice_h).astype(int)\n                width = np.ceil(height / (slide.dimensions[1] / slide.dimensions[0])).astype(int)\n                number_slice_w = np.ceil(slide.dimensions[0]/ width).astype(int)\n                size = (width, height)\n                slices = (number_slice_w, number_slice_h)\n                image_prop = slice_image(slide, size=size, path=output_path, image_id=image_id, patient_id=patient_id,\n                                         other_info=other_info, image_label=image_label, slices = slices,\n                                         preprocessors=preprocessors, max_slices = max_slices, debug_resizing=debug_resizing)\n                \n                if image_prop is not None:\n                    image_slices['image_id'].extend(image_prop['image_id'])\n                    image_slices['image_slice_id'].extend(image_prop['image_slice_id'])\n                    image_slices['patient_id'].extend(image_prop['patient_id'])\n                \n                    for desciption, value in other_info.items():\n                        image_slices[desciption].extend(image_prop[desciption])\n           \n            pbar.update(1)\n            if image_prop is not None:\n                pbar.write('[INFO] processed: {} with slices of {}'.format(row.image_id, \n                                                                           len(image_prop['image_slice_id'])))\n            else:\n                pbar.write('[INFO] processed: {} with 1 zero-filled slice'.format(row.image_id))\n    return image_slices","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Train data**","metadata":{}},{"cell_type":"code","source":"# For debugging only\nif False:\n    #train_data = train_data.loc[train_data['pixels'] >= 3115812450].sort_values(by=['label'], ascending=False)  # get the biggest sizes\n    train_data = train_data.loc[train_data['image_id'].isin([\"5adc4c_0\", \"280c26_0\", \"00c058_0\",\"59441e_0\"])]  # selected images\n    train_data = train_data.head(4)\n    \n    #mask = train_data[\"image_id\"].isin([\"5adc4c_0\", \"7b9aaa_0\", \"e26a04_0\"])\n    #train_data = train_data[mask]\n    print(train_data.head())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# initialize the image processor\ncstdev = CheckStdevPreprocessor(verbose=False)\nccont = CheckContoursPreprocessor(verbose=False)\n\ntry:\n    os.mkdir(\"./train/\")\n    os.mkdir(os.path.join(\"./train/\", \"orig/\"))\n    os.mkdir(os.path.join(\"./train/\", \"zeros/\"))\n    os.mkdir(os.path.join(\"./train/\", \"filtered/\"))\n    os.mkdir(os.path.join(\"./train/\", \"filtered/CE/\"))\n    os.mkdir(os.path.join(\"./train/\", \"filtered/LAA/\"))\nexcept:\n    pass \n\ntrain_image_slices = resize_images(train_data, \"../input/mayo-clinic-strip-ai/train/\", \"./train/\", \n                                   size=save_image_size, \n                                   preprocessors=[cstdev, ccont], max_slices = max_slices, \n                                   details=['label'], group_by_label=True, debug_resizing=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Save the details of sliced training datasets**","metadata":{}},{"cell_type":"code","source":"with open(\"./sliced_train.csv\", 'w') as outfile:\n    writerfile = csv.writer(outfile)\n    writerfile.writerow(train_image_slices.keys())\n    writerfile.writerows(zip(*train_image_slices.values()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Test Data**","metadata":{}},{"cell_type":"code","source":"# initialize the image processor\ncstdev = CheckStdevPreprocessor(verbose=False)\nccont = CheckContoursPreprocessor(verbose=False)\n\ntry:\n    os.mkdir(test_images_path)\n    os.mkdir(os.path.join(\"./test/\", \"orig/\"))\n    os.mkdir(os.path.join(\"./test/\", \"filtered/\"))\nexcept:\n    pass \n\ntest_image_slices = resize_images(test_data, \"../input/mayo-clinic-strip-ai/test/\", \"./test/\", \n                                  size=save_image_size, \n                                  preprocessors=[cstdev, ccont], max_slices = max_slices, \n                                  details=None, group_by_label=False, debug_resizing=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open(\"./sliced_test.csv\", 'w') as outfile:\n    writerfile = csv.writer(outfile)\n    writerfile.writerow(test_image_slices.keys())\n    writerfile.writerows(zip(*test_image_slices.values()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if False:\n    \n    sliced_train_filtered = pd.read_csv(\"./sliced_train.csv\")\n    print(sliced_train_filtered.head(2))\n    print(sliced_train_filtered.tail(2))\n    \n    img_orig = cv2.imread(\"./train/filtered/CE/b07b42_0.004.046.jpg\")\n    img_filtered = cv2.imread(\"./train/filtered/CE/b07b42_0.004.046.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()\n    \n    img_orig = cv2.imread(\"./train/orig/b894f4_0.024.053.jpg\")\n    img_filtered = cv2.imread(\"./train/filtered/b894f4_0.024.053.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()\n\n    sliced_train_filtered = pd.read_csv(\"./sliced_test.csv\")\n    print(sliced_train_filtered.head(2))\n    print(sliced_train_filtered.tail(2))\n\n    img_orig = cv2.imread(\"./test/orig/b07b42_0.004.046.jpg\")\n    img_filtered = cv2.imread(\"./test/filtered/CE/b07b42_0.004.046.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()\n    \n    img_orig = cv2.imread(\"./test/orig/01adc5_0.020.037.jpg\")\n    img_filtered = cv2.imread(\"./test/filtered/01adc5_0.020.037.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if False:\n    img_orig = cv2.imread(\"./test/orig/006388_0.012.185.jpg\")\n    img_filtered = cv2.imread(\"./test/filtered/006388_0.012.185.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()\n    \n    img_orig = cv2.imread(\"./test/orig/01adc5_0.020.037.jpg\")\n    img_filtered = cv2.imread(\"./test/filtered/01adc5_0.020.037.jpg\")\n\n    fig, axes = plt.subplots(1,2, figsize=(18,12))\n    axes[0].imshow(img_orig)\n    axes[1].imshow(img_filtered)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}