Created
October 29, 2020 20:24
-
-
Save Feyn-Man/7209d3e8055549444880b85396a20d8a to your computer and use it in GitHub Desktop.
Contour_extraction_numpy_mask_array.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"colab": { | |
"name": "Contour_extraction_numpy_mask_array.ipynb", | |
"provenance": [], | |
"collapsed_sections": [], | |
"authorship_tag": "ABX9TyP6+t2NVsWKiIqpO8GXR14i", | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3" | |
} | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/Feyn-Man/7209d3e8055549444880b85396a20d8a/contour_extraction_numpy_mask_array.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "j7zsGt2FduX4" | |
}, | |
"source": [ | |
"# Contour extraction \n", | |
"Extract contour of a binary numpy array mask, using only numpy." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "vR5lBChGpV_t" | |
}, | |
"source": [ | |
"import numpy as np" | |
], | |
"execution_count": 1, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "Tcb5Vilxp6fr" | |
}, | |
"source": [ | |
"img = np.array([[0,0,0,0,0],\n", | |
" [0,0,1,0,0],\n", | |
" [0,1,1,1,1],\n", | |
" [0,1,1,1,0],\n", | |
" [0,0,1,1,0],\n", | |
" [0,0,1,0,0]])" | |
], | |
"execution_count": 2, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "4rko5FdMJXSB" | |
}, | |
"source": [ | |
"def extract_contour(img_arr):\n", | |
" \"\"\"\n", | |
" Extract contour from a full binary mask array.\n", | |
" e.g. \n", | |
" full_mask = np.array([[0,0,0,0,0],\n", | |
" [0,0,1,0,0],\n", | |
" [0,1,1,1,1],\n", | |
" [0,1,1,1,0],\n", | |
" [0,0,1,1,0],\n", | |
" [0,0,1,0,0]])\n", | |
"\n", | |
" extract_contour(full_mask)\n", | |
" > array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 1, 0, 1, 1],\n", | |
" [0, 1, 0, 1, 0],\n", | |
" [0, 0, 1, 1, 0],\n", | |
" [0, 0, 1, 0, 0]])\n", | |
"\n", | |
" \"\"\"\n", | |
"\n", | |
" img_arr = img_arr.astype(int)\n", | |
" \n", | |
" left_projection = (np.add.accumulate(img_arr, 1)==1).astype(int)\n", | |
" left_edge_correction = np.add.accumulate(left_projection, 1)==1\n", | |
" left_border = left_projection*left_edge_correction\n", | |
"\n", | |
" right_projection = (np.add.accumulate(img_arr[:,::-1], 1)==1).astype(int)\n", | |
" right_edge_correction = np.add.accumulate(right_projection, 1)==1\n", | |
" right_border = (right_projection*right_edge_correction)[:,::-1]\n", | |
"\n", | |
" up_projection = (np.add.accumulate(img_arr, 0)==1).astype(int)\n", | |
" up_edge_correction = np.add.accumulate(up_projection, 0)==1\n", | |
" up_border = up_projection*up_edge_correction\n", | |
"\n", | |
" down_projection = (np.add.accumulate(img_arr[::-1,:], 0)==1).astype(int)\n", | |
" down_edge_correction = np.add.accumulate(down_projection, 0)==1\n", | |
" down_border = (down_projection*down_edge_correction)[::-1,:]\n", | |
" \n", | |
"\n", | |
" return left_border | right_border |up_border | down_border" | |
], | |
"execution_count": 3, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "DI_1Plu9OCmL", | |
"outputId": "43eb85d5-a1c4-4a01-de94-1a8ce5ede41e", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"extract_contour(img)" | |
], | |
"execution_count": 4, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 1, 0, 1, 1],\n", | |
" [0, 1, 0, 1, 0],\n", | |
" [0, 0, 1, 1, 0],\n", | |
" [0, 0, 1, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 4 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "tmkmv4EuXW-x" | |
}, | |
"source": [ | |
"## Explanation" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "ncTrwy3-YNrc" | |
}, | |
"source": [ | |
"The add function used with the accumulate sum each element in the array with the previous one" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "BPSNX1dfYePO", | |
"outputId": "1715cc0e-27df-4497-a29f-68278d1ae073", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"np.add.accumulate(img, 1)" | |
], | |
"execution_count": 5, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 1, 1],\n", | |
" [0, 1, 2, 3, 4],\n", | |
" [0, 1, 2, 3, 3],\n", | |
" [0, 0, 1, 2, 2],\n", | |
" [0, 0, 1, 1, 1]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 5 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "XVBnIVnCYjdP" | |
}, | |
"source": [ | |
"To obtain only the contour of the image we need only the first element i.e. the ones" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "t0eyCCZ9YiNs", | |
"outputId": "ab0e171f-7748-464a-ea11-bde1469c1f25", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"left_projection = (np.add.accumulate(img, 1)==1).astype(int)\n", | |
"left_projection" | |
], | |
"execution_count": 6, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 1, 1],\n", | |
" [0, 1, 0, 0, 0],\n", | |
" [0, 1, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 0, 1, 1, 1]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 6 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "Zku5J-vga1O5" | |
}, | |
"source": [ | |
"Now it's necessary to cut the tails of ones in the upper and in the lower part of the image. This happens when there is only one 1 in the row. To accomplish this task we reuse the accumulate add function and select only the ones again." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "-7Q0bamHa0lP", | |
"outputId": "20f48844-3722-494f-8e4a-233fa7a365a1", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"left_edge_correction = np.add.accumulate(left_projection, 1)==1\n", | |
"left_edge_correction" | |
], | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[False, False, False, False, False],\n", | |
" [False, False, True, False, False],\n", | |
" [False, True, True, True, True],\n", | |
" [False, True, True, True, True],\n", | |
" [False, False, True, True, True],\n", | |
" [False, False, True, False, False]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 7 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "C1vEzA73bomK" | |
}, | |
"source": [ | |
"Now we can apply this mask to the first projection and obtain a profile of the left border" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "kDExzwVWboHt", | |
"outputId": "b8c45072-8395-402e-f208-9247f3b06bb7", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"left_border = left_projection*left_edge_correction\n", | |
"left_border" | |
], | |
"execution_count": 8, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 1, 0, 0, 0],\n", | |
" [0, 1, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 0, 1, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 8 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "_oDfCxkUcCk-" | |
}, | |
"source": [ | |
"To do the same on the right border, since the accumulate fucntion acts from left to right, it's necessary to reverse the array ([:,::-1]) and then reverse it again at the end." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "zLDzpT0NWUrJ", | |
"outputId": "32c20b74-a10d-440d-dbff-9d9d1e52f4f5", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"right_projection = (np.add.accumulate(img[:,::-1], 1)==1).astype(int)\n", | |
"right_edge_correction = np.add.accumulate(right_projection, 1)==1\n", | |
"right_border = (right_projection*right_edge_correction)[:,::-1]\n", | |
"right_border" | |
], | |
"execution_count": 9, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 0, 0, 0, 1],\n", | |
" [0, 0, 0, 1, 0],\n", | |
" [0, 0, 0, 1, 0],\n", | |
" [0, 0, 1, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 9 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "CIIwWSagcWuv" | |
}, | |
"source": [ | |
"To obtain a precise border it's necessary to do the same operation on the columns (axis=0 in accumulate function)." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "-J7rrxKmcmCW", | |
"outputId": "0630208f-9674-452e-f21a-eff58809b8f9", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"up_projection = (np.add.accumulate(img, 0)==1).astype(int)\n", | |
"up_edge_correction = np.add.accumulate(up_projection, 0)==1\n", | |
"up_border = up_projection*up_edge_correction\n", | |
"up_border" | |
], | |
"execution_count": 10, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 1, 0, 1, 1],\n", | |
" [0, 0, 0, 0, 0],\n", | |
" [0, 0, 0, 0, 0],\n", | |
" [0, 0, 0, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 10 | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "sEnVYQNXcwk7", | |
"outputId": "06ac486e-6dfc-46d1-ea11-ea1c82011b3c", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"down_projection = (np.add.accumulate(img[::-1,:], 0)==1).astype(int)\n", | |
"down_edge_correction = np.add.accumulate(down_projection, 0)==1\n", | |
"down_border = (down_projection*down_edge_correction)[::-1,:]\n", | |
"down_border" | |
], | |
"execution_count": 11, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 0, 0, 0],\n", | |
" [0, 0, 0, 0, 1],\n", | |
" [0, 1, 0, 0, 0],\n", | |
" [0, 0, 0, 1, 0],\n", | |
" [0, 0, 1, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 11 | |
} | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "KsfAadAjBkbG" | |
}, | |
"source": [ | |
"Putting all together" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "_OLcoEBoBkyW", | |
"outputId": "bd7f8ff8-59a3-437e-d9b4-36384ae2651f", | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
} | |
}, | |
"source": [ | |
"left_border | right_border |up_border | down_border" | |
], | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": [ | |
"array([[0, 0, 0, 0, 0],\n", | |
" [0, 0, 1, 0, 0],\n", | |
" [0, 1, 0, 1, 1],\n", | |
" [0, 1, 0, 1, 0],\n", | |
" [0, 0, 1, 1, 0],\n", | |
" [0, 0, 1, 0, 0]])" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
}, | |
"execution_count": 12 | |
} | |
] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment