# Array passing without copying

In [ ]:

Copied!

```
import shapeworks as sw
```

import shapeworks as sw

In [ ]:

Copied!

```
import numpy as np
```

import numpy as np

## shapeworks Image from numpy array¶

In [ ]:

Copied!

```
dims = (1,3,2) # NOTE: numpy dims are specified in z, y, x order
farr = np.ndarray(dims, dtype=np.float32)
ival = 10; jval = 50; kval = 1.75
for i in range(0, farr.shape[2]):
for j in range(0, farr.shape[1]):
for k in range(0, farr.shape[0]):
farr[k][j][i] = ival*(i/farr.shape[2]) + jval*(j/farr.shape[1]) + kval/farr.shape[0]
```

dims = (1,3,2) # NOTE: numpy dims are specified in z, y, x order
farr = np.ndarray(dims, dtype=np.float32)
ival = 10; jval = 50; kval = 1.75
for i in range(0, farr.shape[2]):
for j in range(0, farr.shape[1]):
for k in range(0, farr.shape[0]):
farr[k][j][i] = ival*(i/farr.shape[2]) + jval*(j/farr.shape[1]) + kval/farr.shape[0]

In [ ]:

Copied!

```
farr.mean()
```

farr.mean()

In [ ]:

Copied!

```
farr.dtype
```

farr.dtype

In [ ]:

Copied!

```
farr.flags['OWNDATA']
```

farr.flags['OWNDATA']

In [ ]:

Copied!

```
farrimg = sw.Image(farr)
farrimg # NOTE: sw.Image dims are specified in x, y, z order
```

farrimg = sw.Image(farr)
farrimg # NOTE: sw.Image dims are specified in x, y, z order

In [ ]:

Copied!

```
farrimg.mean()
```

farrimg.mean()

### While the numpy can still look at the memory, it no longer has ownership:¶

In [ ]:

Copied!

```
farr.flags['OWNDATA']
```

farr.flags['OWNDATA']

In [ ]:

Copied!

```
farrimg += 100
```

farrimg += 100

In [ ]:

Copied!

```
farrimg.mean()
```

farrimg.mean()

In [ ]:

Copied!

```
farr.mean()
```

farr.mean()

### ...so the safest thing to do now is let the array go out of scope:¶

- having used a temporary during Image construction:
`img = sw.Image(np.array(arr))`

- variable replacement after Image construction:
`arr = np.zeros(1)`

- explicit deletion after Image construction:
`del arr`

In [ ]:

Copied!

```
del farr
```

del farr

## Only dtype.float32 arrays can be used to initialize an image:¶

In [ ]:

Copied!

```
dims = (12,3,21)
darr = np.ndarray(dims, dtype=np.float64)
ival = 10; jval = 50; kval = 1.75
for k in range(0, dims[0]):
for j in range(0, dims[1]):
for i in range(0, dims[2]):
darr[k][j][i] = ival*(i/darr.shape[2]) + jval*(j/darr.shape[1]) + kval/darr.shape[0]
```

dims = (12,3,21)
darr = np.ndarray(dims, dtype=np.float64)
ival = 10; jval = 50; kval = 1.75
for k in range(0, dims[0]):
for j in range(0, dims[1]):
for i in range(0, dims[2]):
darr[k][j][i] = ival*(i/darr.shape[2]) + jval*(j/darr.shape[1]) + kval/darr.shape[0]

In [ ]:

Copied!

```
darr.dtype
```

darr.dtype

In [ ]:

Copied!

```
darr.flags['OWNDATA']
```

darr.flags['OWNDATA']

In [ ]:

Copied!

```
# note: this try/catch is only used so the notebook runs to completion; not typically necessary
try:
darrimg = sw.Image(darr) # Throws an exception because dtype must be same as Image's pixel type
except Exception as ex:
print(ex)
```

# note: this try/catch is only used so the notebook runs to completion; not typically necessary
try:
darrimg = sw.Image(darr) # Throws an exception because dtype must be same as Image's pixel type
except Exception as ex:
print(ex)

In [ ]:

Copied!

```
darrimg = sw.Image(np.array(darr, dtype=np.float32)) # Makes a copy of the array when passsed
darrimg
```

darrimg = sw.Image(np.array(darr, dtype=np.float32)) # Makes a copy of the array when passsed
darrimg

*No unnecessary copies and no memory leaks!*¶

In [ ]:

Copied!

```
darr.flags['OWNDATA']
```

darr.flags['OWNDATA']

In [ ]:

Copied!

```
darrimg.mean()
```

darrimg.mean()

In [ ]:

Copied!

```
darr.mean()
```

darr.mean()

In [ ]:

Copied!

```
darrimg += 50
```

darrimg += 50

In [ ]:

Copied!

```
darrimg.mean()
```

darrimg.mean()

In [ ]:

Copied!

```
darr.mean()
```

darr.mean()

In [ ]:

Copied!

```
darr *= 10
```

darr *= 10

In [ ]:

Copied!

```
darrimg.mean()
```

darrimg.mean()

In [ ]:

Copied!

```
darr.mean()
```

darr.mean()

In [ ]:

Copied!

```
ellipsoid_path = "../../../Testing/data/1x2x2.nrrd"
femur_path = "../../../Testing/data/femur.nrrd"
```

ellipsoid_path = "../../../Testing/data/1x2x2.nrrd"
femur_path = "../../../Testing/data/femur.nrrd"

In [ ]:

Copied!

```
img = sw.Image(ellipsoid_path)
img
```

img = sw.Image(ellipsoid_path)
img

In [ ]:

Copied!

```
arr = img.toArray()
arr.dtype
```

arr = img.toArray()
arr.dtype

In [ ]:

Copied!

```
arr.mean()
```

arr.mean()

In [ ]:

Copied!

```
img.mean()
```

img.mean()

In [ ]:

Copied!

```
arr.shape # remember, numpy dims are zyx and Image dims are xyz
```

arr.shape # remember, numpy dims are zyx and Image dims are xyz

In [ ]:

Copied!

```
img.dims()
```

img.dims()

### The numpy array references the memory of the current Image and can change it:¶

In [ ]:

Copied!

```
arr += 100
```

arr += 100

In [ ]:

Copied!

```
img.mean()
```

img.mean()

In [ ]:

Copied!

```
arr.mean()
```

arr.mean()

In [ ]:

Copied!

```
arr.flags['OWNDATA']
```

arr.flags['OWNDATA']

In [ ]:

Copied!

```
del arr
```

del arr

## If a copy is needed, pass `copy=True`

to `toArray()`

¶

In [ ]:

Copied!

```
arr = img.toArray(copy=True)
arr.flags['OWNDATA']
```

arr = img.toArray(copy=True)
arr.flags['OWNDATA']

### This can be useful when the array is created from a temporary Image:¶

In [ ]:

Copied!

```
arr = sw.Image(ellipsoid_path).toArray(copy=True)
arr.mean()
```

arr = sw.Image(ellipsoid_path).toArray(copy=True)
arr.mean()

In [ ]:

Copied!

```
def use_arr(arr):
return arr.mean()
```

def use_arr(arr):
return arr.mean()

In [ ]:

Copied!

```
use_arr(sw.Image(ellipsoid_path).toArray(copy=True))
```

use_arr(sw.Image(ellipsoid_path).toArray(copy=True))

In [ ]:

Copied!

```
import pyvista as pv
```

import pyvista as pv

In [ ]:

Copied!

```
pv.set_jupyter_backend(backend="ipyvtklink")
```

pv.set_jupyter_backend(backend="ipyvtklink")

In [ ]:

Copied!

```
#help(pv.Plotter)
```

#help(pv.Plotter)

In [ ]:

Copied!

```
plotter = pv.Plotter(shape = (1, 1),
notebook = True,
border = True)
plotter.add_axes()
plotter.add_bounding_box()
#plotter.show_bounds() # for some reason extremely slow on osx
#plotter.show_grid() # for some reason extremely slow on osx
```

plotter = pv.Plotter(shape = (1, 1),
notebook = True,
border = True)
plotter.add_axes()
plotter.add_bounding_box()
#plotter.show_bounds() # for some reason extremely slow on osx
#plotter.show_grid() # for some reason extremely slow on osx

In [ ]:

Copied!

```
# NOTE: pyvisya-wrapped vtk images require 'F' ordering to prevent copying
arr = img.toArray(for_viewing = True) # 'F' is `for_viewing`
arr.flags
```

# NOTE: pyvisya-wrapped vtk images require 'F' ordering to prevent copying
arr = img.toArray(for_viewing = True) # 'F' is `for_viewing`
arr.flags

In [ ]:

Copied!

```
arr.flags
```

arr.flags

In [ ]:

Copied!

```
# sw2vtkImage takes care of this for us
vtkimg = sw.sw2vtkImage(img, verbose=True)
```

# sw2vtkImage takes care of this for us
vtkimg = sw.sw2vtkImage(img, verbose=True)

In [ ]:

Copied!

```
vol = plotter.add_volume(vtkimg, shade=True, show_scalar_bar=True)
```

vol = plotter.add_volume(vtkimg, shade=True, show_scalar_bar=True)

In [ ]:

Copied!

```
plotter.show()
```

plotter.show()

In [ ]:

Copied!

```
plotter = pv.Plotter(shape = (1, 1),
notebook = True,
border = True)
plotter.add_axes()
```

plotter = pv.Plotter(shape = (1, 1),
notebook = True,
border = True)
plotter.add_axes()

In [ ]:

Copied!

```
img1 = sw.Image(femur_path)
```

img1 = sw.Image(femur_path)

In [ ]:

Copied!

```
img1.setSpacing((1.5, 0.75, 1)) # set spacing to show that it's preserved on both copy and assign
```

img1.setSpacing((1.5, 0.75, 1)) # set spacing to show that it's preserved on both copy and assign

In [ ]:

Copied!

```
img2 = sw.Image(img1) # make a copy to be processed by a scipy Python filter (spacing preserved)
```

img2 = sw.Image(img1) # make a copy to be processed by a scipy Python filter (spacing preserved)

### Let's use a scipy operation on the image:¶

In [ ]:

Copied!

```
from scipy import ndimage
```

from scipy import ndimage

In [ ]:

Copied!

```
ck = ndimage.gaussian_filter(img2.toArray(), 12.0)
```

ck = ndimage.gaussian_filter(img2.toArray(), 12.0)

### The return from this filter is the right size and type, but it's a copy:¶

In [ ]:

Copied!

```
ck.shape
```

ck.shape

In [ ]:

Copied!

```
ck.dtype
```

ck.dtype

In [ ]:

Copied!

```
ck.flags['OWNDATA']
```

ck.flags['OWNDATA']

### Let's assign it back to Image so we can retain Image's origin, scale, and coordsys:¶

In [ ]:

Copied!

```
img2.assign(ck)
```

img2.assign(ck)

In [ ]:

Copied!

```
# notice numpy array ownership has been transferred to Image
ck.flags['OWNDATA']
```

# notice numpy array ownership has been transferred to Image
ck.flags['OWNDATA']

### Now we can look at it again in the plotter:¶

In [ ]:

Copied!

```
plotter.add_volume(sw.sw2vtkImage(img2), shade=True, show_scalar_bar=True)
plotter.add_volume(sw.sw2vtkImage(img1), shade=True, show_scalar_bar=True)
```

plotter.add_volume(sw.sw2vtkImage(img2), shade=True, show_scalar_bar=True)
plotter.add_volume(sw.sw2vtkImage(img1), shade=True, show_scalar_bar=True)

In [ ]:

Copied!

```
plotter.show()
```

plotter.show()