{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc29cbfb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import shapeworks as sw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b8d6dd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a7b7d8d",
   "metadata": {},
   "source": [
    "## shapeworks Image from numpy array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "209ead06",
   "metadata": {},
   "outputs": [],
   "source": [
    "dims = (1,3,2) # NOTE: numpy dims are specified in z, y, x order\n",
    "farr = np.ndarray(dims, dtype=np.float32)\n",
    "\n",
    "ival = 10; jval = 50; kval = 1.75\n",
    "for i in range(0, farr.shape[2]):\n",
    "    for j in range(0, farr.shape[1]):\n",
    "        for k in range(0, farr.shape[0]):\n",
    "            farr[k][j][i] = ival*(i/farr.shape[2]) + jval*(j/farr.shape[1]) + kval/farr.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8b06334",
   "metadata": {},
   "outputs": [],
   "source": [
    "farr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ea8d87a",
   "metadata": {},
   "outputs": [],
   "source": [
    "farr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3d2a6bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "farr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab6d58be",
   "metadata": {},
   "outputs": [],
   "source": [
    "farrimg = sw.Image(farr)\n",
    "farrimg  # NOTE: sw.Image dims are specified in x, y, z order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2077e4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "farrimg.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93eb3964",
   "metadata": {},
   "source": [
    "### While the numpy can still look at the memory, it no longer has ownership:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d48240b",
   "metadata": {},
   "outputs": [],
   "source": [
    "farr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b44489e",
   "metadata": {},
   "outputs": [],
   "source": [
    "farrimg += 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "826ee344",
   "metadata": {},
   "outputs": [],
   "source": [
    "farrimg.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8bbf563",
   "metadata": {},
   "outputs": [],
   "source": [
    "farr.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "212de79a",
   "metadata": {},
   "source": [
    "### ...so the safest thing to do now is let the array go out of scope: \n",
    "- having used a temporary during Image construction: `img = sw.Image(np.array(arr))`\n",
    "- variable replacement after Image construction: `arr = np.zeros(1)`\n",
    "- explicit deletion after Image construction: `del arr`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ec1d063",
   "metadata": {},
   "outputs": [],
   "source": [
    "del farr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0463b4ae",
   "metadata": {},
   "source": [
    "## Only dtype.float32 arrays can be used to initialize an image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94b60fa8",
   "metadata": {},
   "outputs": [],
   "source": [
    "dims = (12,3,21)\n",
    "darr = np.ndarray(dims, dtype=np.float64)\n",
    "\n",
    "ival = 10; jval = 50; kval = 1.75\n",
    "for k in range(0, dims[0]):\n",
    "    for j in range(0, dims[1]):\n",
    "        for i in range(0, dims[2]):\n",
    "            darr[k][j][i] = ival*(i/darr.shape[2]) + jval*(j/darr.shape[1]) + kval/darr.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09c53fe0",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92411614",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45b56f43",
   "metadata": {},
   "outputs": [],
   "source": [
    "# note: this try/catch is only used so the notebook runs to completion; not typically necessary\n",
    "try:\n",
    "    darrimg = sw.Image(darr) # Throws an exception because dtype must be same as Image's pixel type\n",
    "except Exception as ex:\n",
    "    print(ex)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f65cb85c",
   "metadata": {},
   "outputs": [],
   "source": [
    "darrimg = sw.Image(np.array(darr, dtype=np.float32)) # Makes a copy of the array when passsed\n",
    "darrimg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e90c5fc3",
   "metadata": {},
   "source": [
    "## _No unnecessary copies and no memory leaks!_"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ba7815d",
   "metadata": {},
   "source": [
    "### The numpy array still owns its data since a copy was passed to create the Image.\n",
    "### Ownership of the copy's memory was taken by the image before it went out of scope."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd71186d",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "558e7d28",
   "metadata": {},
   "outputs": [],
   "source": [
    "darrimg.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6e83302",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec137f2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "darrimg += 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cd17d3a",
   "metadata": {},
   "outputs": [],
   "source": [
    "darrimg.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ca67194",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5756d536",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr *= 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3dfda2f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "darrimg.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "efe635a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "darr.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "617d6524",
   "metadata": {},
   "source": [
    "### Now for the other way around.\n",
    "## numpy array from shapeworks Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe79e3e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "ellipsoid_path = \"../../../Testing/data/1x2x2.nrrd\"\n",
    "femur_path = \"../../../Testing/data/femur.nrrd\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9a1dc92",
   "metadata": {},
   "outputs": [],
   "source": [
    "img = sw.Image(ellipsoid_path)\n",
    "img"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f340e8a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = img.toArray()\n",
    "arr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40c6ac0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3cbd2b91",
   "metadata": {},
   "outputs": [],
   "source": [
    "img.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b481d02a",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.shape  # remember, numpy dims are zyx and Image dims are xyz "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ff67e4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "img.dims()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf678031",
   "metadata": {},
   "source": [
    "### The numpy array references the memory of the current Image and can change it:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a146bae",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr += 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f9abfde",
   "metadata": {},
   "outputs": [],
   "source": [
    "img.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "845eb756",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "683eda1b",
   "metadata": {},
   "source": [
    "### ...but it still doesn't have ownership.\n",
    "### Since many Image operations reallocate internally, it's still safest to let it go out of scope as shown above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "055471dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4ed2684",
   "metadata": {},
   "outputs": [],
   "source": [
    "del arr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "621fde40",
   "metadata": {},
   "source": [
    "## If a copy is needed, pass `copy=True` to `toArray()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "edae0511",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = img.toArray(copy=True)\n",
    "arr.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1049f118",
   "metadata": {},
   "source": [
    "### This can be useful when the array is created from a temporary Image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "baac7dce",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = sw.Image(ellipsoid_path).toArray(copy=True)\n",
    "arr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ca419df",
   "metadata": {},
   "outputs": [],
   "source": [
    "def use_arr(arr):\n",
    "    return arr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fb26868",
   "metadata": {},
   "outputs": [],
   "source": [
    "use_arr(sw.Image(ellipsoid_path).toArray(copy=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9da595d8",
   "metadata": {},
   "source": [
    "## viewing the image using pyvista\n",
    "### One common reason to get the Image's array is for viewing using pyvista using the `for_viewing` argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e83a798",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pyvista as pv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bec8e2dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "pv.set_jupyter_backend(backend=\"ipyvtklink\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8920d158",
   "metadata": {},
   "outputs": [],
   "source": [
    "#help(pv.Plotter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73bbb755",
   "metadata": {},
   "outputs": [],
   "source": [
    "plotter = pv.Plotter(shape    = (1, 1),\n",
    "                     notebook = True,\n",
    "                     border   = True)\n",
    "plotter.add_axes()\n",
    "plotter.add_bounding_box()\n",
    "#plotter.show_bounds()  # for some reason extremely slow on osx\n",
    "#plotter.show_grid()    # for some reason extremely slow on osx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a300ded3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: pyvisya-wrapped vtk images require 'F' ordering to prevent copying\n",
    "arr = img.toArray(for_viewing = True) # 'F' is `for_viewing`\n",
    "arr.flags"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b3e8e6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.flags"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52159b2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# sw2vtkImage takes care of this for us\n",
    "vtkimg = sw.sw2vtkImage(img, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3f753a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "vol = plotter.add_volume(vtkimg, shade=True, show_scalar_bar=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f13c6977",
   "metadata": {},
   "outputs": [],
   "source": [
    "plotter.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aed72728",
   "metadata": {},
   "source": [
    "## Finally, we can `assign` a numpy array to an existing Image\n",
    "### This retains the Image's origin, scale, and coordsys."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29d0dde7",
   "metadata": {},
   "outputs": [],
   "source": [
    "plotter = pv.Plotter(shape    = (1, 1),\n",
    "                     notebook = True,\n",
    "                     border   = True)\n",
    "plotter.add_axes()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4afd3400",
   "metadata": {},
   "outputs": [],
   "source": [
    "img1 = sw.Image(femur_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb62973b",
   "metadata": {},
   "outputs": [],
   "source": [
    "img1.setSpacing((1.5, 0.75, 1))  # set spacing to show that it's preserved on both copy and assign"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbf52525",
   "metadata": {},
   "outputs": [],
   "source": [
    "img2 = sw.Image(img1)  # make a copy to be processed by a scipy Python filter (spacing preserved)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3d9aa9f",
   "metadata": {},
   "source": [
    "### Let's use a scipy operation on the image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb70d24a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy import ndimage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b8b7122",
   "metadata": {},
   "outputs": [],
   "source": [
    "ck = ndimage.gaussian_filter(img2.toArray(), 12.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3025c12",
   "metadata": {},
   "source": [
    "### The return from this filter is the right size and type, but it's a copy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bf0a728",
   "metadata": {},
   "outputs": [],
   "source": [
    "ck.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19baf60a",
   "metadata": {},
   "outputs": [],
   "source": [
    "ck.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38dce8ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "ck.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccd0bd0e",
   "metadata": {},
   "source": [
    "### Let's assign it back to Image so we can retain Image's origin, scale, and coordsys:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b86ba6cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "img2.assign(ck)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "875bbb87",
   "metadata": {},
   "outputs": [],
   "source": [
    "# notice numpy array ownership has been transferred to Image\n",
    "ck.flags['OWNDATA']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0bbbf2b",
   "metadata": {},
   "source": [
    "### Now we can look at it again in the plotter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2569e5e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "plotter.add_volume(sw.sw2vtkImage(img2), shade=True, show_scalar_bar=True)\n",
    "\n",
    "plotter.add_volume(sw.sw2vtkImage(img1), shade=True, show_scalar_bar=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c4cb1c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "plotter.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.8"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
