{"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":"# RSNA-2022 : [beginner] Detect breast area - OpenCV - connectedComponents","metadata":{"id":"6LF5_Ea6qRFQ"}},{"cell_type":"markdown","source":"**[Change Log]**\n- Ver.0 : 1st notebook.","metadata":{}},{"cell_type":"markdown","source":"# Objective\nThis notebook tries to describe the <font color=red>breast area detection in simple and comfortable way to read</font> for the [RSNA Screening Mammography Breast Cancer Detection](https://www.kaggle.com/competitions/rsna-breast-cancer-detection), so some codes might seem redundant but that is the concept.\n\nThis notebook is inspirated by the https://www.kaggle.com/code/vslaykovsky/rsna-cut-off-empty-space-from-images/data as an example of breast area detection. \n\nThe key contents are;\n1. Read DICOM file by **pydicom**\n2. Get breast area by **Opencv - connectedComponents**\n3. I. Show rectangle for breast area by **Opencv - rectangle & putText**\n4. Create png file\n5. II. Show rectangle for breast area by **Opencv - rectangle & putText**","metadata":{}},{"cell_type":"code","source":"# Import library\n\n## Basic ##\n%matplotlib inline\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\nimport cv2\nimport glob\nfrom tqdm.notebook import tqdm\n\n## dicom file relevant\n!pip install -qU pylibjpeg\nimport pylibjpeg\nimport pydicom\n\n## Setting for matplotlib\nplt.rcParams['font.size'] = 14\nplt.rcParams['figure.figsize'] = (6,6)\nplt.rcParams['axes.grid'] = True\n","metadata":{"id":"EBjRX49eqRFd","execution":{"iopub.status.busy":"2023-01-09T05:31:04.013601Z","iopub.execute_input":"2023-01-09T05:31:04.01421Z","iopub.status.idle":"2023-01-09T05:31:19.914588Z","shell.execute_reply.started":"2023-01-09T05:31:04.014099Z","shell.execute_reply":"2023-01-09T05:31:19.912988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preparation","metadata":{"id":"obUglB5x18Lk"}},{"cell_type":"markdown","source":"## csv data","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/test.csv')\ntest_df","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:31:37.544259Z","iopub.execute_input":"2023-01-09T05:31:37.544808Z","iopub.status.idle":"2023-01-09T05:31:37.58635Z","shell.execute_reply.started":"2023-01-09T05:31:37.544765Z","shell.execute_reply":"2023-01-09T05:31:37.585041Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train data\ntrain_df = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/train.csv')\ntrain_df.head()","metadata":{"id":"-utQupbe6dYb","execution":{"iopub.status.busy":"2023-01-09T05:31:52.309996Z","iopub.execute_input":"2023-01-09T05:31:52.310443Z","iopub.status.idle":"2023-01-09T05:31:52.44624Z","shell.execute_reply.started":"2023-01-09T05:31:52.310398Z","shell.execute_reply":"2023-01-09T05:31:52.444677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(test_df.shape)\nprint(train_df.shape)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:32:30.758706Z","iopub.execute_input":"2023-01-09T05:32:30.759147Z","iopub.status.idle":"2023-01-09T05:32:30.766718Z","shell.execute_reply.started":"2023-01-09T05:32:30.759114Z","shell.execute_reply":"2023-01-09T05:32:30.765173Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train data with cancer == 1\ntrain_cancer_df = train_df[train_df['cancer'] == 1]\ntrain_cancer_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:33:15.044534Z","iopub.execute_input":"2023-01-09T05:33:15.044975Z","iopub.status.idle":"2023-01-09T05:33:15.072379Z","shell.execute_reply.started":"2023-01-09T05:33:15.044943Z","shell.execute_reply":"2023-01-09T05:33:15.071387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# path to image data\nfname = glob.glob('/kaggle/input/rsna-breast-cancer-detection/train_images/*/*.dcm')[0]","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:33:58.186511Z","iopub.execute_input":"2023-01-09T05:33:58.186905Z","iopub.status.idle":"2023-01-09T05:34:53.327934Z","shell.execute_reply.started":"2023-01-09T05:33:58.186874Z","shell.execute_reply":"2023-01-09T05:34:53.326283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fname","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:35:53.338587Z","iopub.execute_input":"2023-01-09T05:35:53.339026Z","iopub.status.idle":"2023-01-09T05:35:53.345919Z","shell.execute_reply.started":"2023-01-09T05:35:53.338995Z","shell.execute_reply":"2023-01-09T05:35:53.344975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here patient ID is last second one","metadata":{}},{"cell_type":"code","source":"fname.split('/')[-2]","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:36:28.748435Z","iopub.execute_input":"2023-01-09T05:36:28.749056Z","iopub.status.idle":"2023-01-09T05:36:28.759669Z","shell.execute_reply.started":"2023-01-09T05:36:28.749008Z","shell.execute_reply":"2023-01-09T05:36:28.758009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fname.split('/')[-1][:-2]","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:38:26.008225Z","iopub.execute_input":"2023-01-09T05:38:26.0087Z","iopub.status.idle":"2023-01-09T05:38:26.016349Z","shell.execute_reply.started":"2023-01-09T05:38:26.00865Z","shell.execute_reply":"2023-01-09T05:38:26.0154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fname.split('/')[-1][:-4]","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:37:45.092408Z","iopub.execute_input":"2023-01-09T05:37:45.092937Z","iopub.status.idle":"2023-01-09T05:37:45.101653Z","shell.execute_reply.started":"2023-01-09T05:37:45.092901Z","shell.execute_reply":"2023-01-09T05:37:45.100257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Image ID is first one from last","metadata":{}},{"cell_type":"markdown","source":"As described in the [EDA](https://www.kaggle.com/code/masatakaitakura/eda-for-beginner-rsna-mammography-breast-cancer), mammography image have a \"Blank area\", so we use the <font color=red>**OpenCV - connectedComponents** </font>and show the breast area by rectangles.","metadata":{}},{"cell_type":"code","source":"pydicom.dcmread(fname)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:38:54.033429Z","iopub.execute_input":"2023-01-09T05:38:54.033975Z","iopub.status.idle":"2023-01-09T05:38:54.118433Z","shell.execute_reply.started":"2023-01-09T05:38:54.033937Z","shell.execute_reply":"2023-01-09T05:38:54.117258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pydicom.dcmread(fname).pixel_array","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:44:31.318569Z","iopub.execute_input":"2023-01-09T05:44:31.319026Z","iopub.status.idle":"2023-01-09T05:44:31.992445Z","shell.execute_reply.started":"2023-01-09T05:44:31.318989Z","shell.execute_reply":"2023-01-09T05:44:31.990946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1. DICOM image data","metadata":{}},{"cell_type":"code","source":"# Read DICOM file\nsize=512\n\n# obtain ids\npatient_id = fname.split('/')[-2]\nimage_id = fname.split('/')[-1][:-4]\n\n# image file\ndicom = pydicom.dcmread(fname)\nimg = dicom.pixel_array\n# normalize image\nimg = (img - img.min()) / (img.max() - img.min())\nif dicom.PhotometricInterpretation == \"MONOCHROME1\":\n    img = 1 - img\nimg = cv2.resize(img, (size, size))","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:44:46.203314Z","iopub.execute_input":"2023-01-09T05:44:46.203763Z","iopub.status.idle":"2023-01-09T05:44:46.950719Z","shell.execute_reply.started":"2023-01-09T05:44:46.203726Z","shell.execute_reply":"2023-01-09T05:44:46.949762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# show image\nfig, ax = plt.subplots(1, 1)\nax.imshow(img)\n\nimg.shape","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:44:53.103302Z","iopub.execute_input":"2023-01-09T05:44:53.103812Z","iopub.status.idle":"2023-01-09T05:44:53.476439Z","shell.execute_reply.started":"2023-01-09T05:44:53.103769Z","shell.execute_reply":"2023-01-09T05:44:53.475132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Get breast area by Opencv - connectedComponents\nAs shown above image, the mammography image contain the \"blank\" area. To focus the breast for the training, we create the fit image by using **<font color=red>cv2.connectedcomponent</font>**.","metadata":{}},{"cell_type":"markdown","source":"The image file's contrast is described in pixel_array with 0 to 1 integer.   \n**(Dark) 0 <--> 1 (Blight)**","metadata":{}},{"cell_type":"code","source":"print(f'max: {img.max()}, min: {img.min()}')","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:46:43.429143Z","iopub.execute_input":"2023-01-09T05:46:43.429595Z","iopub.status.idle":"2023-01-09T05:46:43.438228Z","shell.execute_reply.started":"2023-01-09T05:46:43.429549Z","shell.execute_reply":"2023-01-09T05:46:43.436705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can obtain the blight area by the following comparison operator and obtain the boolean equation. \n- False : darker than criteria\n- True : lighter tahn criteria","metadata":{}},{"cell_type":"code","source":"# obtain the blight area\nimg > 0.05","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:46:59.64418Z","iopub.execute_input":"2023-01-09T05:46:59.644574Z","iopub.status.idle":"2023-01-09T05:46:59.653201Z","shell.execute_reply.started":"2023-01-09T05:46:59.644545Z","shell.execute_reply":"2023-01-09T05:46:59.651797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next, convert the boolean to **np.array** and show the image.","metadata":{}},{"cell_type":"code","source":"# convert boolean to np\nimg_01 = (img > 0.05).astype(np.uint8)[:, :]\nprint(img_01)\n\nplt.imshow(img_01)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:47:10.143437Z","iopub.execute_input":"2023-01-09T05:47:10.144131Z","iopub.status.idle":"2023-01-09T05:47:10.434332Z","shell.execute_reply.started":"2023-01-09T05:47:10.144084Z","shell.execute_reply":"2023-01-09T05:47:10.432974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The [connectedComponentsWithStats](https://docs.opencv.org/4.6.0/d3/dc0/group__imgproc__shape.html#ga5ed7784614678adccb699c70fb841075) computes \n- the connected components labeled image of boolean image\n- a statistics output for each label","metadata":{}},{"cell_type":"code","source":"# label the regions of non-empty pixels\nretval, labels, stats, centroids = cv2.connectedComponentsWithStats(image=(img > 0.05).astype(np.uint8)[:, :], connectivity=8, ltype=cv2.CV_32S)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:47:34.208722Z","iopub.execute_input":"2023-01-09T05:47:34.209115Z","iopub.status.idle":"2023-01-09T05:47:34.224423Z","shell.execute_reply.started":"2023-01-09T05:47:34.209085Z","shell.execute_reply":"2023-01-09T05:47:34.222758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The followings are specific outputs.\n- retval:\nReturns the number of labels. Note that the background is also counted as one.\n- labels:\nOutput labeling image\n- stats:\nIt stores the region information for each label.\n  **[region top left x coordinate, region top left y coordinate, region width, region height, area]** Area is the number of pixels in the region.\n-centroids:\nCentroid information for each label is stored.","metadata":{}},{"cell_type":"code","source":"print(f'retval: {retval}\\n\\nlabels: {labels}\\nlabel shape={labels.shape}\\n\\nstats={stats}\\n\\ncentroids={centroids}')","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:48:00.968996Z","iopub.execute_input":"2023-01-09T05:48:00.969434Z","iopub.status.idle":"2023-01-09T05:48:00.979377Z","shell.execute_reply.started":"2023-01-09T05:48:00.969403Z","shell.execute_reply":"2023-01-09T05:48:00.977914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The `labels` can show the colored grouping as shown below.\n\n### Findings\n- some image includes the letter of [laterality] and [view], those area are around 10 - 80.","metadata":{}},{"cell_type":"code","source":"plt.imshow(labels)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:48:28.76381Z","iopub.execute_input":"2023-01-09T05:48:28.764217Z","iopub.status.idle":"2023-01-09T05:48:29.058101Z","shell.execute_reply.started":"2023-01-09T05:48:28.764186Z","shell.execute_reply":"2023-01-09T05:48:29.05688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(0,retval): # retval[0] is background\n    x, y, width, height, area = stats[i]\n    if area > 10: # judge if the area is larger than the value\n        print(f'index-{i}: area={area}')","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:48:38.350521Z","iopub.execute_input":"2023-01-09T05:48:38.351013Z","iopub.status.idle":"2023-01-09T05:48:38.359333Z","shell.execute_reply.started":"2023-01-09T05:48:38.350978Z","shell.execute_reply":"2023-01-09T05:48:38.357899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3.I. Show rectangle for breast area by Opencv - rectangle & putText\nNext, to identify the each detected areas, let's add rectangle and text to each.  \nNote: To avoid the change of original `img`, copy the image and create `img_rect`.","metadata":{}},{"cell_type":"code","source":"# set img_rect\nimport copy\nimg_rect = copy.copy(img)\n# plt.imshow(img_rect)\n\ni = 0\n\nfor i in range(1,retval): # retval[0] = background\n    x, y, width, height, area = stats[i] \n\n    if area > 10: # detect more than 10 pixcel area\n        cv2.rectangle(img_rect,\n                      pt1=(x,y),\n                      pt2=(x+width,y+height),\n                      color=(0,0,255),\n                      thickness=5)\n        cv2.putText(img_rect, f\"[{i}]:{area}\", (x, y-10), cv2.FONT_HERSHEY_PLAIN, 1, (255, 0, 0), 1, cv2.LINE_AA)\nplt.imshow(img_rect)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:49:06.793858Z","iopub.execute_input":"2023-01-09T05:49:06.794315Z","iopub.status.idle":"2023-01-09T05:49:07.091198Z","shell.execute_reply.started":"2023-01-09T05:49:06.794284Z","shell.execute_reply":"2023-01-09T05:49:07.089779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I could not show the OpenCV - rectangle on the image which is directly created from DICOM file, so I create png file from DICOM file and use for OpenCV image.","metadata":{}},{"cell_type":"markdown","source":"# 4. Create png file","metadata":{}},{"cell_type":"code","source":"train_images = glob.glob(\"/kaggle/input/rsna-breast-cancer-detection/train_images/*/*.dcm\")\n\nlen(train_images)  # 54706","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:49:18.828663Z","iopub.execute_input":"2023-01-09T05:49:18.829095Z","iopub.status.idle":"2023-01-09T05:49:27.514272Z","shell.execute_reply.started":"2023-01-09T05:49:18.829063Z","shell.execute_reply":"2023-01-09T05:49:27.513491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SAVE_FOLDER = \"png/\"\nSIZE = 512\nEXTENSION = \"png\"\n\nos.makedirs(SAVE_FOLDER, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:49:30.36304Z","iopub.execute_input":"2023-01-09T05:49:30.363494Z","iopub.status.idle":"2023-01-09T05:49:30.370502Z","shell.execute_reply.started":"2023-01-09T05:49:30.363458Z","shell.execute_reply":"2023-01-09T05:49:30.369043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create and save png file\ndef process(f, size=512, save_folder=\"png/\", extension=\"png\"):\n    patient = f.split('/')[-2]\n    image = f.split('/')[-1][:-4]\n\n    dicom = pydicom.dcmread(f)\n    img = dicom.pixel_array\n\n    img = (img - img.min()) / (img.max() - img.min())\n\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        img = 1 - img\n\n    img = cv2.resize(img, (size, size))\n\n    cv2.imwrite(save_folder + f\"{patient}_{image}.{extension}\", (img * 255).astype(np.uint8))","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:50:34.403976Z","iopub.execute_input":"2023-01-09T05:50:34.404407Z","iopub.status.idle":"2023-01-09T05:50:34.413484Z","shell.execute_reply.started":"2023-01-09T05:50:34.404372Z","shell.execute_reply":"2023-01-09T05:50:34.412033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for f in tqdm(train_images[:6]): #if you want to process all, remove [:25]\n    process(f, save_folder=SAVE_FOLDER)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:50:39.197787Z","iopub.execute_input":"2023-01-09T05:50:39.198184Z","iopub.status.idle":"2023-01-09T05:50:49.306993Z","shell.execute_reply.started":"2023-01-09T05:50:39.198151Z","shell.execute_reply":"2023-01-09T05:50:49.305171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Confirm the created png file","metadata":{}},{"cell_type":"code","source":"png_images = glob.glob('/kaggle/working/png/*.png')\n\nlen(png_images)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:50:59.969396Z","iopub.execute_input":"2023-01-09T05:50:59.970043Z","iopub.status.idle":"2023-01-09T05:50:59.980243Z","shell.execute_reply.started":"2023-01-09T05:50:59.969995Z","shell.execute_reply":"2023-01-09T05:50:59.97876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot result\nrow = 2\ncol = len(png_images)//row\n\nfig, axes = plt.subplots(row, col, figsize=(20,20))\n    \nfor idx, image in enumerate(png_images):\n    frame = cv2.imread(image)\n    i = idx // col\n    j = idx % col\n    axes[i, j].imshow(frame)\n    axes[i, j].set_title(image.split('/')[4]) # change the number with the file path\n\nplt.subplots_adjust(wspace=0.2, hspace=-0.4)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:51:04.828099Z","iopub.execute_input":"2023-01-09T05:51:04.828566Z","iopub.status.idle":"2023-01-09T05:51:06.096069Z","shell.execute_reply.started":"2023-01-09T05:51:04.828532Z","shell.execute_reply":"2023-01-09T05:51:06.094636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we can prepare the png files by specify the train_images dcm file path.","metadata":{}},{"cell_type":"markdown","source":"# 5.II. Show rectangle for breast area by Opencv - rectangle & putText","metadata":{}},{"cell_type":"code","source":"# path to image data (already set at the begining)\nfname = glob.glob('/kaggle/input/rsna-breast-cancer-detection/train_images/*/*.dcm')[0]\nfname","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:51:53.243733Z","iopub.execute_input":"2023-01-09T05:51:53.244224Z","iopub.status.idle":"2023-01-09T05:52:03.022797Z","shell.execute_reply.started":"2023-01-09T05:51:53.244185Z","shell.execute_reply":"2023-01-09T05:52:03.021497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# prepare png file\nprocess(fname)\n\n# read png file\npng_path = '/kaggle/working/png/' + fname.split('/')[5] + '_' + fname.split('/')[6][:-4] + '.png'\npng = cv2.imread(png_path) # png for display\npng_gray = cv2.imread(png_path, cv2.IMREAD_GRAYSCALE) # grayscale png for labelling\n\n# label the regions of non-empty pixels\nretval, labels, stats, centroids = cv2.connectedComponentsWithStats(image=(png_gray > 0.05).astype(np.uint8)[:, :], connectivity=8, ltype=cv2.CV_32S)\n\n# show rectangle\ni = 0\n\nfor i in range(1,retval): # retval[0] = background\n    x, y, width, height, area = stats[i] \n\n    if area > 10: # detect more than 10 pixcel area\n        cv2.rectangle(png,\n                      pt1=(x,y),\n                      pt2=(x+width,y+height),\n                      color=(255,0,0),\n                      thickness=5)\n        cv2.putText(png, f\"[{i}]area:{area}\", (x, y+100), cv2.FONT_HERSHEY_PLAIN, 1.5, (255, 0, 0), 2, cv2.LINE_AA)\nplt.imshow(png)","metadata":{"execution":{"iopub.status.busy":"2023-01-09T05:52:06.628763Z","iopub.execute_input":"2023-01-09T05:52:06.629192Z","iopub.status.idle":"2023-01-09T05:52:07.650921Z","shell.execute_reply.started":"2023-01-09T05:52:06.629154Z","shell.execute_reply":"2023-01-09T05:52:07.649629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we can display the rectangle and text on the png image successfully.","metadata":{}},{"cell_type":"markdown","source":"Thank you very much for reading, I hope this notebook helps you!\n\n**If you enjoyed the notebook, please <font color=red>upvote!</font> 🙏 Thank you, appreciate your support!**","metadata":{}}]}