Skip to content

Instantly share code, notes, and snippets.

@Feyn-Man
Created October 29, 2020 20:24
Show Gist options
  • Save Feyn-Man/7209d3e8055549444880b85396a20d8a to your computer and use it in GitHub Desktop.
Save Feyn-Man/7209d3e8055549444880b85396a20d8a to your computer and use it in GitHub Desktop.
Contour_extraction_numpy_mask_array.ipynb
Display the source blob
Display the rendered blob
Raw
{
"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