├── MAINTAINERS.md ├── CHANGELOG.md ├── Scripts └── MultiOtsu.py ├── LICENSE ├── README.md ├── CONTRIBUTING.md └── Jupyter Notebooks └── DigitalRock.ipynb /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | Matheus Esteves Ferreira - m.estevesf@ibm.com 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.0.1] - 2019-02-15 8 | 9 | ### Added 10 | - Added a changelog 11 | 12 | [unreleased]: https://github.com/ibm/repo-template/compare/v0.0.1...HEAD 13 | [0.0.1]: https://github.com/ibm/repo-template/releases/tag/v0.0.1 14 | -------------------------------------------------------------------------------- /Scripts/MultiOtsu.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | from skimage.filters import threshold_multiotsu 4 | 5 | image_path = sys.argv[1] 6 | N = int(sys.argv[2]) 7 | 8 | volume = open(image_path,"rb") 9 | volume = np.fromfile(volume, dtype=np.uint8).reshape([N,N,N]) 10 | 11 | thr_multiotsu = threshold_multiotsu(volume) 12 | binary = volume >= thr_multiotsu[0] 13 | 14 | porosity = 1-np.mean(binary) 15 | 16 | print('Computed Sample Porosity {}'.format(porosity)) 17 | 18 | binary.tofile(image_path.replace('.raw', '') +'-normalized-MultilevelOtsu.raw') 19 | 20 | print('Image saved to: {}'.format(image_path.replace('.raw', '') +'-normalized-MultilevelOtsu.raw')) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause "New" or "Revised" License 2 | https://opensource.org/licenses/BSD-3-Clause 3 | 4 | --- 5 | Copyright (c) 2022, IBM. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are 10 | met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above 16 | copyright notice, this list of conditions and the following 17 | disclaimer in the documentation and/or other materials provided 18 | with the distribution. 19 | 20 | * Neither the name of the NumPy Developers nor the names of any 21 | contributors may be used to endorse or promote products derived 22 | from this software without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # microCT-Dataset 3 | 4 | 5 | 6 | 7 | 8 | ## Scope 9 | 10 | This repository relates to the publication with title: 11 | 12 | "Full scale, microscopically resolved tomographies of sandstone and carbonate rocks augmented by experimental porosity and permeability values of the same samples" by Matheus Esteves Ferreira et al. 13 | 14 | Available at: [https://doi.org/10.1038/s41597-023-02259-z](https://www.nature.com/articles/s41597-023-02259-z) 15 | 16 | and the corresponding dataset repository archived at: DOI: [10.25452/figshare.plus.21375565](https://plus.figshare.com/articles/dataset/Full_scale_microscopically_resolved_tomographies_of_sandstone_and_carbonate_rocks_augmented_by_experimental_porosity_and_permeability_values/21375565) 17 | 18 | 19 | ## Usage 20 | 21 | This repository comprises the following Jupyter Notebook for for visualizing and processing the micro tomography data part of a dataset being externalized in data centric publication. 22 | 23 | DigitalRock.ipynb 24 | 25 | This notebook uses the data in the repository in [Figshare](https://plus.figshare.com/articles/dataset/Full_scale_microscopically_resolved_tomographies_of_sandstone_and_carbonate_rocks_augmented_by_experimental_porosity_and_permeability_values/21375565) 26 | 27 | --- 28 | 29 | * [LICENSE](LICENSE) 30 | * [README.md](README.md) 31 | * [CONTRIBUTING.md](CONTRIBUTING.md) 32 | * [MAINTAINERS.md](MAINTAINERS.md) 33 | 34 | 35 | ## License 36 | 37 | All source files must include a Copyright and License header. The SPDX license header is 38 | preferred because it can be easily scanned. 39 | 40 | If you would like to see the detailed LICENSE click [here](LICENSE). 41 | 42 | ```text 43 | # 44 | # Copyright 2020- IBM Inc. All rights reserved 45 | # SPDX-License-Identifier: BSD-3-Clause 46 | # 47 | ``` 48 | ## Authors 49 | 50 | - Matheus Esteves Ferreira 51 | - Mariana Rodrigues Del Grande 52 | - Rodrigo Neumann 53 | - Jaione Tirapu Azpiroz 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing In General 2 | Our project welcomes external contributions. If you have an itch, please feel 3 | free to scratch it. 4 | 5 | To contribute code or documentation, please submit a **FIXME** [pull request](https://github.com/ibm/repo-template/pulls). 6 | 7 | A good way to familiarize yourself with the codebase and contribution process is 8 | to look for and tackle low-hanging fruit in the **FIXME** [issue tracker](https://github.com/ibm/repo-template/issues). 9 | Before embarking on a more ambitious contribution, please quickly [get in touch](#communication) with us. 10 | 11 | **Note: We appreciate your effort, and want to avoid a situation where a contribution 12 | requires extensive rework (by you or by us), sits in backlog for a long time, or 13 | cannot be accepted at all!** 14 | 15 | ### Proposing new features 16 | 17 | If you would like to implement a new feature, please **FIXME** [raise an issue](https://github.com/ibm/repo-template/issues) 18 | before sending a pull request so the feature can be discussed. This is to avoid 19 | you wasting your valuable time working on a feature that the project developers 20 | are not interested in accepting into the code base. 21 | 22 | ### Fixing bugs 23 | 24 | If you would like to fix a bug, please **FIXME** [raise an issue](https://github.com/ibm/repo-template/issues) before sending a 25 | pull request so it can be tracked. 26 | 27 | ### Merge approval 28 | 29 | The project maintainers use LGTM (Looks Good To Me) in comments on the code 30 | review to indicate acceptance. A change requires LGTMs from two of the 31 | maintainers of each component affected. 32 | 33 | For a list of the maintainers, see the [MAINTAINERS.md](MAINTAINERS.md) page. 34 | 35 | ## Legal 36 | 37 | Each source file must include a license header for the Apache 38 | Software License 2.0. Using the SPDX format is the simplest approach. 39 | e.g. 40 | 41 | ``` 42 | /* 43 | Copyright All Rights Reserved. 44 | 45 | SPDX-License-Identifier: Apache-2.0 46 | */ 47 | ``` 48 | 49 | We have tried to make it as easy as possible to make contributions. This 50 | applies to how we handle the legal aspects of contribution. We use the 51 | same approach - the [Developer's Certificate of Origin 1.1 (DCO)](https://github.com/hyperledger/fabric/blob/master/docs/source/DCO1.1.txt) - that the Linux® Kernel [community](https://elinux.org/Developer_Certificate_Of_Origin) 52 | uses to manage code contributions. 53 | 54 | We simply ask that when submitting a patch for review, the developer 55 | must include a sign-off statement in the commit message. 56 | 57 | Here is an example Signed-off-by line, which indicates that the 58 | submitter accepts the DCO: 59 | 60 | ``` 61 | Signed-off-by: John Doe 62 | ``` 63 | 64 | You can include this automatically when you commit a change to your 65 | local git repository using the following command: 66 | 67 | ``` 68 | git commit -s 69 | ``` 70 | 71 | ## Communication 72 | **FIXME** Please feel free to connect with us on our [Slack channel](link). 73 | 74 | ## Setup 75 | **FIXME** Please add any special setup instructions for your project to help the developer 76 | become productive quickly. 77 | 78 | ## Testing 79 | **FIXME** Please provide information that helps the developer test any changes they make 80 | before submitting. 81 | 82 | ## Coding style guidelines 83 | **FIXME** Optional, but recommended: please share any specific style guidelines you might 84 | have for your project. 85 | -------------------------------------------------------------------------------- /Jupyter Notebooks/DigitalRock.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "699f06ed-7a64-4aca-b35f-d659382b1c67", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%matplotlib widget" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "id": "0a789dec-cb34-46d6-9826-3f50cb125f5f", 16 | "metadata": {}, 17 | "source": [ 18 | "# MicroCT Image Processing" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "dfa3624f-9433-4181-8587-75c7d32c0114", 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "#\n", 29 | "# Copyright 2020- IBM Inc. All rights reserved\n", 30 | "# SPDX-License-Identifier: BSD-3-Clause\n", 31 | "#" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "id": "12bb5b3d-f887-43a7-a140-6166d9bad7af", 37 | "metadata": {}, 38 | "source": [ 39 | "## About this Notebook" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "id": "eef50e6d-47a1-4550-af61-97ef2df4f3b0", 45 | "metadata": {}, 46 | "source": [ 47 | "This companion notebook is published along a data-centric journal publication [1] related to a published Open-Source dataset on cabonate and sandstone rock tomographies [2] and aims to provide readers and users of the dataset with a way to visualize the data, as well as to the algorithms used for processing of the microCT image data.\n", 48 | "\n", 49 | "This notebook also demonstrates the difference between using the Otsu and multi-Otsu algorithms for determining the porosity of the samples. Further information about these algorithms and their effect on the porosity estimates can be found in [1]." 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "id": "ad63213d-eea5-4199-81f1-98b6f3642336", 55 | "metadata": {}, 56 | "source": [ 57 | "### References " 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "id": "bbd6b01d-36f2-4700-b6b5-121d3ae319ce", 63 | "metadata": {}, 64 | "source": [ 65 | "[1] Add reference to paper\n", 66 | "\n", 67 | "[2] 10.25452/figshare.plus.21375565" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "21b07ae7-cb77-45d7-a38c-d48b0914e5df", 73 | "metadata": { 74 | "tags": [] 75 | }, 76 | "source": [ 77 | "## Importing Libraries" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 3, 83 | "id": "10b56b89-40dc-42e1-b399-f2327f282d87", 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "import numpy as np\n", 88 | "import matplotlib as mpl\n", 89 | "import matplotlib.pyplot as plt\n", 90 | "import ipywidgets as widgets\n", 91 | "from skimage.filters import threshold_multiotsu, threshold_otsu" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "id": "a50d3998-5584-4c7f-bac0-b8967d2368b4", 97 | "metadata": { 98 | "tags": [] 99 | }, 100 | "source": [ 101 | "### Digital Rock Class" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "id": "66a0cf06-eb26-4193-afd0-379045b263bd", 107 | "metadata": {}, 108 | "source": [ 109 | "The digital rock class contains the algorithm used for contrast filtering of the rock samples. An instance to this class takes as input the path of the raw NxNxN cube file and the number of pixels on each size of the cube, N.\n", 110 | "\n", 111 | "It is important to note, however, that this code won't work as is for the full paralellpided files, as it expects cubic volumes. This can be easily circonvented, but may be unusable due to memory requirements.\n", 112 | "\n", 113 | "The equalize function carries out the contrast enhancement filter as described on [1]. As default, the filter cuts off the histogram at the 99.8 percentile, but this can be changed in the \"clip\" variable." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 4, 119 | "id": "22eab84e-a80f-495d-9e91-9fc41c694210", 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "class digitalRock():\n", 124 | " def __init__(self, path, N):\n", 125 | " self.N = N\n", 126 | " self.volume = np.fromfile(path, dtype=np.uint8).reshape([N,N,N])\n", 127 | " \n", 128 | " def multp(self, A):\n", 129 | " return np.uint8((A*self.scale)*255)\n", 130 | " \n", 131 | " def equalize(self, clip=99.8):\n", 132 | " self.filter_thr = int(np.percentile(self.volume, clip))\n", 133 | " clipped_volume = np.clip(self.volume, 0, self.filter_thr)\n", 134 | " self.minInt = clipped_volume.min()\n", 135 | " self.maxInt = clipped_volume.max()\n", 136 | " self.scale = 1/(self.maxInt-self.minInt)\n", 137 | " \n", 138 | " clipped_volume -= self.minInt\n", 139 | " filtered = np.array(list(map(self.multp,clipped_volume)))\n", 140 | " \n", 141 | " return filtered" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "id": "c2aa2052-fd4f-469e-966a-c37da3108af5", 147 | "metadata": { 148 | "tags": [] 149 | }, 150 | "source": [ 151 | "### Visualizations" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 5, 157 | "id": "0b46e0ff-ae6c-4e72-bc9d-e71270b36616", 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "def plot_slices(img, N=2500, scale=True):\n", 162 | " @widgets.interact(Zslice=(0,N-1,1))\n", 163 | " def update(Zslice = 0):\n", 164 | " plt.figure()\n", 165 | " plt.xlabel('X')\n", 166 | " plt.ylabel('Y')\n", 167 | " plt.title('Z={}'.format(Zslice))\n", 168 | " if scale==True:\n", 169 | " plt.imshow(img[:,:,Zslice], vmin=0, vmax=255)\n", 170 | " else:\n", 171 | " plt.imshow(img[:,:,Zslice])\n", 172 | " plt.colorbar()\n", 173 | " plt.show()" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 6, 179 | "id": "721b6e14-d7a8-4bca-9b19-223bdcb09538", 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "def plot_hist(img, N=2500):\n", 184 | " @widgets.interact(Zslice=(0,N-1,1))\n", 185 | " def update(Zslice = 0):\n", 186 | " plt.figure()\n", 187 | " plt.title('Z={}'.format(Zslice))\n", 188 | " plt.hist(img[:,:,Zslice].ravel(), bins=255, range=[0,255])\n", 189 | " plt.show()" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "id": "071cd56b-ce60-482c-985a-c87159e4b013", 195 | "metadata": { 196 | "tags": [] 197 | }, 198 | "source": [ 199 | " ## Loading Data " 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 7, 205 | "id": "f94eb13b-38b0-4dfd-8c6e-37e1898a0a9f", 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "filename = 'kocurek_5a_2p25um_ir_rec_2500x2500x2500_grayscale_filtered_ROI-1.raw'\n", 210 | "N=2500" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 15, 216 | "id": "f90e7a29-1a86-431b-acd6-3d387ecb6b25", 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "rock = digitalRock(filename, N)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "id": "69610cf2-fcf0-4fe5-b9c4-eb3081c04929", 226 | "metadata": {}, 227 | "source": [ 228 | "### Selecting an Image Subset" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "id": "914c24cd-6694-43ee-86bd-1aab106774bc", 234 | "metadata": {}, 235 | "source": [ 236 | "Due to the large memory requirements for loading the full microCT images in this work, the next cell can be used to select a subset of the original raw volume (given by N). In case you prefer to visualize a subset of the image, commenting the next cell will be enough. It is important to note that the memory requirements can be up to 8x the image size (due to conversions between uint8 and float 64 by numpy)." 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "id": "c5106a01-3520-4353-9312-f982543f7818", 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "# N = 1000\n", 247 | "# rock.volume = rock.volume[:N, :N,:N]" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "id": "208330e8-d576-4f77-bafc-49eacc34b7ee", 253 | "metadata": { 254 | "tags": [] 255 | }, 256 | "source": [ 257 | "## Data Visualization" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 21, 263 | "id": "e2cad146-69f3-47ce-9939-da9892fefe20", 264 | "metadata": {}, 265 | "outputs": [ 266 | { 267 | "data": { 268 | "application/vnd.jupyter.widget-view+json": { 269 | "model_id": "fb3c45e657244868b6d7b54ffef2d1b6", 270 | "version_major": 2, 271 | "version_minor": 0 272 | }, 273 | "text/plain": [ 274 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 275 | ] 276 | }, 277 | "metadata": {}, 278 | "output_type": "display_data" 279 | } 280 | ], 281 | "source": [ 282 | "plot_slices(rock.volume, N=N)" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 22, 288 | "id": "de1d5a0a-161d-43fe-99d3-770944d90992", 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "data": { 293 | "application/vnd.jupyter.widget-view+json": { 294 | "model_id": "1cf03a0f92d24cdcbfef161c7175a2d9", 295 | "version_major": 2, 296 | "version_minor": 0 297 | }, 298 | "text/plain": [ 299 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 300 | ] 301 | }, 302 | "metadata": {}, 303 | "output_type": "display_data" 304 | } 305 | ], 306 | "source": [ 307 | "plot_hist(rock.volume, N=N)" 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "id": "75649023-d4dc-4490-a800-99039c2e3626", 313 | "metadata": { 314 | "tags": [] 315 | }, 316 | "source": [ 317 | "## Contrast Enhancement Filter" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 23, 323 | "id": "aa280875-9c82-43b2-90d6-b47294930228", 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [ 327 | "filtered = rock.equalize()" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "id": "c179b978-9435-4fd3-9a77-93d612170533", 333 | "metadata": { 334 | "tags": [] 335 | }, 336 | "source": [ 337 | "### Before" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 24, 343 | "id": "b46d8e2f-2929-4962-a3fb-9e886af12961", 344 | "metadata": {}, 345 | "outputs": [ 346 | { 347 | "data": { 348 | "application/vnd.jupyter.widget-view+json": { 349 | "model_id": "806029ea6fef4ec1bf8e775f233970e0", 350 | "version_major": 2, 351 | "version_minor": 0 352 | }, 353 | "text/plain": [ 354 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 355 | ] 356 | }, 357 | "metadata": {}, 358 | "output_type": "display_data" 359 | } 360 | ], 361 | "source": [ 362 | "plot_slices(rock.volume, N=N)" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 25, 368 | "id": "5a59df68-86cf-4129-a35a-681be8047f01", 369 | "metadata": {}, 370 | "outputs": [ 371 | { 372 | "data": { 373 | "application/vnd.jupyter.widget-view+json": { 374 | "model_id": "32935def74be4b6aa2b717f0712da250", 375 | "version_major": 2, 376 | "version_minor": 0 377 | }, 378 | "text/plain": [ 379 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 380 | ] 381 | }, 382 | "metadata": {}, 383 | "output_type": "display_data" 384 | } 385 | ], 386 | "source": [ 387 | "plot_hist(rock.volume, N=N)" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "id": "24964298-8a20-465a-a06a-1f3cb452f21c", 393 | "metadata": { 394 | "tags": [] 395 | }, 396 | "source": [ 397 | "### After" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 26, 403 | "id": "d7a26237-9fbf-4d50-9fa0-3f0078101f22", 404 | "metadata": {}, 405 | "outputs": [ 406 | { 407 | "data": { 408 | "application/vnd.jupyter.widget-view+json": { 409 | "model_id": "934ddc57686d406685e45d0f59aac71b", 410 | "version_major": 2, 411 | "version_minor": 0 412 | }, 413 | "text/plain": [ 414 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 415 | ] 416 | }, 417 | "metadata": {}, 418 | "output_type": "display_data" 419 | } 420 | ], 421 | "source": [ 422 | "plot_slices(filtered, N=N)" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 27, 428 | "id": "a6fe8f97-ee50-4c31-8c54-1c4e5fd5d5bd", 429 | "metadata": {}, 430 | "outputs": [ 431 | { 432 | "data": { 433 | "application/vnd.jupyter.widget-view+json": { 434 | "model_id": "3bef080985b34fa99c86d7a7045aba87", 435 | "version_major": 2, 436 | "version_minor": 0 437 | }, 438 | "text/plain": [ 439 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 440 | ] 441 | }, 442 | "metadata": {}, 443 | "output_type": "display_data" 444 | } 445 | ], 446 | "source": [ 447 | "plot_hist(filtered, N=N)" 448 | ] 449 | }, 450 | { 451 | "cell_type": "markdown", 452 | "id": "5fc95107-53ec-4503-8473-4d119170cc00", 453 | "metadata": { 454 | "tags": [] 455 | }, 456 | "source": [ 457 | "## Anisotropic Diffusion" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "id": "20bff4ab-e744-43ce-af49-fe625be6cc78", 463 | "metadata": {}, 464 | "source": [ 465 | "As discussed in the methods section [1], the images generated from the last step were later processed by an anisotropic diffusion filter implemented into the CT analyser software (Bruker, version 1.20.8.0). The filter was set to 3D space, the type used was Privilege high contrast edges (Perona-Malik), the number of iterations set to 5 and the gradient threshold set to 10. The user defined integration constant was left unchecked. This step reduces the amount of image noise that is inherent in any digital imaging technique." 466 | ] 467 | }, 468 | { 469 | "cell_type": "markdown", 470 | "id": "e97055f8-f42b-4879-bdc3-884e9c928b7e", 471 | "metadata": { 472 | "tags": [] 473 | }, 474 | "source": [ 475 | "## Image Segmentation" 476 | ] 477 | }, 478 | { 479 | "cell_type": "markdown", 480 | "id": "1ebf17d6-0e26-45b6-a10a-067ed815da40", 481 | "metadata": { 482 | "tags": [] 483 | }, 484 | "source": [ 485 | "### Otsu" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": 28, 491 | "id": "3440c8ae-2185-49c1-96ff-f701a04e78b7", 492 | "metadata": {}, 493 | "outputs": [], 494 | "source": [ 495 | "thr_otsu = threshold_otsu(filtered)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": 29, 501 | "id": "d47a1383-7b51-4244-8b26-336b4b63283e", 502 | "metadata": {}, 503 | "outputs": [], 504 | "source": [ 505 | "binary_otsu = filtered >= thr_otsu" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": 30, 511 | "id": "7fc11636-4844-4b87-b0ea-8414cfcfd784", 512 | "metadata": {}, 513 | "outputs": [ 514 | { 515 | "data": { 516 | "application/vnd.jupyter.widget-view+json": { 517 | "model_id": "f3c043cb3b444e0ea64b9bcbd8b9d28f", 518 | "version_major": 2, 519 | "version_minor": 0 520 | }, 521 | "text/plain": [ 522 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 523 | ] 524 | }, 525 | "metadata": {}, 526 | "output_type": "display_data" 527 | } 528 | ], 529 | "source": [ 530 | "plot_slices(binary_otsu, N=N, scale=False)" 531 | ] 532 | }, 533 | { 534 | "cell_type": "markdown", 535 | "id": "c50e5d8c-f35d-4607-92b5-c2cc163c2405", 536 | "metadata": { 537 | "tags": [] 538 | }, 539 | "source": [ 540 | "### Multilevel Otsu" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 31, 546 | "id": "4d77690e-1ee1-49e7-8e4c-e0044c00d8c4", 547 | "metadata": {}, 548 | "outputs": [], 549 | "source": [ 550 | "thr_multiotsu = threshold_multiotsu(filtered)" 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": 32, 556 | "id": "80508bfc-2604-4c68-8b5a-972966d1b85b", 557 | "metadata": {}, 558 | "outputs": [], 559 | "source": [ 560 | "regions = np.digitize(filtered, bins=thr_multiotsu)" 561 | ] 562 | }, 563 | { 564 | "cell_type": "code", 565 | "execution_count": 33, 566 | "id": "e06644f5-2921-420f-87b7-ea468c2c1aed", 567 | "metadata": {}, 568 | "outputs": [ 569 | { 570 | "data": { 571 | "application/vnd.jupyter.widget-view+json": { 572 | "model_id": "8fe9539865e74f70bf98bd0a8afc1bb9", 573 | "version_major": 2, 574 | "version_minor": 0 575 | }, 576 | "text/plain": [ 577 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 578 | ] 579 | }, 580 | "metadata": {}, 581 | "output_type": "display_data" 582 | } 583 | ], 584 | "source": [ 585 | "plot_slices(regions, N=N, scale=False)" 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": 34, 591 | "id": "e345fab2-e8f9-4c8b-b5f3-4934d7e139ee", 592 | "metadata": {}, 593 | "outputs": [ 594 | { 595 | "data": { 596 | "text/plain": [ 597 | "array([107, 142])" 598 | ] 599 | }, 600 | "execution_count": 34, 601 | "metadata": {}, 602 | "output_type": "execute_result" 603 | } 604 | ], 605 | "source": [ 606 | "thr_multiotsu" 607 | ] 608 | }, 609 | { 610 | "cell_type": "code", 611 | "execution_count": 45, 612 | "id": "2c2d746f-23c3-4906-b082-abe1b17af8ba", 613 | "metadata": {}, 614 | "outputs": [], 615 | "source": [ 616 | "binary_multiotsu = filtered >= thr_multiotsu[0]" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": 36, 622 | "id": "fe0e7dbc-810e-41a8-b91b-0d10320c4a48", 623 | "metadata": {}, 624 | "outputs": [ 625 | { 626 | "data": { 627 | "application/vnd.jupyter.widget-view+json": { 628 | "model_id": "d97cf60428a547769b2c6c156131a92c", 629 | "version_major": 2, 630 | "version_minor": 0 631 | }, 632 | "text/plain": [ 633 | "interactive(children=(IntSlider(value=0, description='Zslice', max=2499), Output()), _dom_classes=('widget-int…" 634 | ] 635 | }, 636 | "metadata": {}, 637 | "output_type": "display_data" 638 | } 639 | ], 640 | "source": [ 641 | "plot_slices(binary_multiotsu, N=N, scale=False)" 642 | ] 643 | }, 644 | { 645 | "cell_type": "markdown", 646 | "id": "ac522d16-e274-4469-a47c-7338a1a10665", 647 | "metadata": {}, 648 | "source": [ 649 | "### Calculating Porosity" 650 | ] 651 | }, 652 | { 653 | "cell_type": "code", 654 | "execution_count": 37, 655 | "id": "092d7946-99fe-4010-ab64-64a2c4124812", 656 | "metadata": {}, 657 | "outputs": [ 658 | { 659 | "name": "stdout", 660 | "output_type": "stream", 661 | "text": [ 662 | "Calculated Porosity for Otsu = 32.0 %\n" 663 | ] 664 | } 665 | ], 666 | "source": [ 667 | "porosity_otsu = 1-np.mean(binary_otsu)\n", 668 | "print('Calculated Porosity for Otsu = {} %'.format(np.round(porosity_otsu, 2)*100))" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 38, 674 | "id": "2205bb7a-b12a-413d-a120-21fd787dfa29", 675 | "metadata": {}, 676 | "outputs": [ 677 | { 678 | "name": "stdout", 679 | "output_type": "stream", 680 | "text": [ 681 | "Calculated Porosity for MultiOtsu = 9.0 %\n" 682 | ] 683 | } 684 | ], 685 | "source": [ 686 | "porosity_multiotsu = 1-np.mean(binary_multiotsu)\n", 687 | "print('Calculated Porosity for MultiOtsu = {} %'.format(np.round(porosity_multiotsu, 2)*100))" 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "id": "59acf955-8617-40ce-9c74-13c3ea31862a", 693 | "metadata": {}, 694 | "source": [ 695 | "## Data Export" 696 | ] 697 | }, 698 | { 699 | "cell_type": "markdown", 700 | "id": "419d3633-61c4-471a-b115-68adc570250b", 701 | "metadata": {}, 702 | "source": [ 703 | "If you wish to save the filtered data, just execute the code below for saving the new image to the folder" 704 | ] 705 | }, 706 | { 707 | "cell_type": "code", 708 | "execution_count": null, 709 | "id": "096abe25-40a1-4205-9a07-b13dae2d39e3", 710 | "metadata": {}, 711 | "outputs": [], 712 | "source": [ 713 | "# filtered.tofile(workdir + filename.replace('grayscale', 'binary'))\n" 714 | ] 715 | } 716 | ], 717 | "metadata": { 718 | "kernelspec": { 719 | "display_name": "Python 3 (ipykernel)", 720 | "language": "python", 721 | "name": "python3" 722 | }, 723 | "language_info": { 724 | "codemirror_mode": { 725 | "name": "ipython", 726 | "version": 3 727 | }, 728 | "file_extension": ".py", 729 | "mimetype": "text/x-python", 730 | "name": "python", 731 | "nbconvert_exporter": "python", 732 | "pygments_lexer": "ipython3", 733 | "version": "3.7.13" 734 | } 735 | }, 736 | "nbformat": 4, 737 | "nbformat_minor": 5 738 | } 739 | --------------------------------------------------------------------------------