Skip to content

Commit 3770677

Browse files
author
Laura Murphy
committed
bioio
1 parent 05c244c commit 3770677

1 file changed

Lines changed: 39 additions & 94 deletions

File tree

episodes/01-opening-and-checking-an-image.md

Lines changed: 39 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -255,89 +255,62 @@ plt.show()
255255

256256
## Proprietary formats
257257

258-
Images can come in many formats, but for many of the common ones such as TIFF,
259-
PNG and BMP, skimage is smart enough to know how to read each one.
258+
Modern microscopes save files in vendor-specific formats (like .czi, .nd2, or .lif). While skimage can handle standard TIF(F)s, these complex files contain vital metadata, for example, pixel spacing information, that we need for accurate analysis.
260259

261-
Some image formats are associated with specific instruments or equipment and
262-
require specialised packages to open. Depending on your system, these may
263-
already be available via `import` the same as any other Python package. If
264-
not, then these should be installed into whatever Python instance you are
265-
using.
266260

267-
If using JupyterHub or JupyterLab, go to 'New' -> 'Terminal'. This will open
268-
a shell session in a new browser tab, where you can run `pip install` commands.
269-
270-
### Carl Zeiss .czi
271-
272-
Images in .czi format can be opened with the `czifile` library. In the Terminal
273-
you opened above:
261+
### The Universal Adapter: BIOIO
274262

275-
$ pip install czifile
263+
Instead of installing a different library for every microscope brand, we recommend BIOIO. It acts as a consistent interface for almost any biological image format, allowing you to use the same commands regardless of the file source.
276264

277-
### Nikon .nd2
278-
279-
$ pip install nd2
280-
281-
### Imaris .ims
265+
If using JupyterHub or JupyterLab, go to 'New' -> 'Terminal'. This will open
266+
a shell session in a new browser tab, where you can run `pip install` commands.
282267

283-
$ pip install imaris-ims-file-reader
268+
::: callout
269+
### Advanced: Scaling up with Dask
270+
Once you are a bit more established as a Python user, the integration of BIOIO with Dask becomes a major advantage. It allows for "Lazy Loading," where the computer only reads the specific pixels you ask for. This is essential for analyzing massive datasets that are larger than your computer's RAM.
271+
:::
284272

285-
### Leica .lif
273+
:::: challenge
274+
## Exercise 4: Reading with BIOIO
286275

287-
$ pip install readlif
276+
Load the bioio package and use it to read the test file 'data/Ersi_organoid_WT2.nd2'.
288277

278+
1. Use print(img.dims) to check the dimensions. What does each letter represent?
279+
2. Use get_image_data to extract a single 2D frame for the first channel (C=0) at the middle Z-slice (Z=13).
289280

290-
::::::::::::::::::::::::::::::::::::: challenge
291-
## Exercise 4: Proprietary formats
281+
::: solution
282+
```python
283+
from bioio import BioImage
292284

293-
Load the [nd2](https://www.talleylambert.com/nd2/#installation) package and use it to read
294-
the test file 'Ersi_organoid_WT2.nd2'. Install it if you need to.
285+
# Load the image object
286+
img = BioImage('data/Ersi_organoid_WT2.nd2')
295287

296-
What axes are present in the image? Which look most likely to be the X and
297-
Y axes? Use `imshow` to display a single frame from the image.
288+
# Check dimensions explicitly
289+
print(img.dims)
290+
# Returns: Dimensions [T: 1, C: 3, Z: 27, Y: 512, X: 512]
298291

299-
:::::::::::::::::::::::: solution
300-
If not already present, the nd2 package can be installed in the Terminal with
301-
`pip install nd2`. According to its linked documentation, it has
302-
an `imread` function that works in a similar way to the one in skimage, and returns a numpy
303-
multidimensional array that we can work with the same way we have before:
292+
# Grab a specific slice (Channel 0, Z-slice 13)
293+
pixel_data = img.get_image_data("YX", C=0, Z=13)
304294

305-
```python
306-
import nd2
307-
image = nd2.imread('data/Ersi_organoid_WT2.nd2')
308-
image.shape
309-
# returns: (27, 3, 512, 512)
310-
```
311-
312-
`img.shape` shows that there are four axes, `(27, 3, 512, 512)`. The latter two numbers look like
313-
the X and Y axes, while the second number looks like a number of colour channels. The first number
314-
looks like either a time series or a Z axis. We can show a single frame with:
295+
import matplotlib.pyplot as plt
296+
plt.imshow(pixel_data)
315297

316-
```python
317-
plt.imshow(image[0, 0, :, :])
318298
```
319299

320300
![](fig/1_6_nd2_image_frame.jpg){alt='ND2 image'}
321301

322302
:::::::::::::::::::::::::::::::::
323303
::::::::::::::::::::::::::::::::::::::::::::::::
324304

325-
As discussed above in exercise 4, it may be difficult to distinguish a time series from a Z axis.
326-
You may also notice that here the X/Y axes are the latter two numbers, but in other examples
327-
above, the X/Y axes were the first two. This demonstrates the diversity and general lack of
328-
consistency in image formatting, and how it's usually a good idea to find out as much as you can
329-
about the image from documentation and metadata before processing it.
330-
331305
## Altering the lookup table
332306

333-
Now that we've been able to open an image, we can start to explore it and display
334-
it in different ways.
307+
Now that we have extracted a specific 2D slice of data using BIOIO, we can explore different ways to display it.
335308

336-
The `imshow()` function can take extra arguments in addition to the image to display. One
337-
of these is called `cmap`, which can apply alternate lookup tables (a.k.a. colour maps):
309+
The imshow() function can take an argument called cmap, which applies a "Lookup Table" (LUT). This maps the numerical intensity values in your image to specific colors on your screen.
338310

339311
```python
340-
plt.imshow(image[0, 0, :, :], cmap='viridis')
312+
# Using the pixel_data we extracted from the BIOIO object earlier
313+
plt.imshow(pixel_data, cmap='viridis')
341314
```
342315

343316
Skimage uses lookup tables from the plotting library matplotlib. A list of available tables
@@ -427,45 +400,17 @@ position 0 (the start).
427400

428401
### Pixel size
429402

430-
Pixels are an approximation of an object - knowing that something is 50 pixels long and
431-
50 pixels wide doesn't tell us anything about its actual size. To make any real-world
432-
measurements on the image, we need the image's **pixel size**.
433-
434-
To get this, it is necessary to read the image's **metadata**. For this we need a different
435-
library, imageio. There are a couple of different places we can look:
436-
403+
To make real-world measurements, we need the image's pixel size. While older methods required digging through complex metadata dictionaries, BIOIO makes this simple:
437404
```python
438-
import imageio
439-
meta = imageio.v3.immeta('data/FluorescentCells_3channel.tif')
440-
props = imageio.v3.improps('data/FluorescentCells_3channel.tif')
441-
print('meta:', meta)
442-
print('props:', props)
405+
# Access the physical pixel sizes directly from the bioio object
406+
print(img.physical_pixel_sizes)
407+
# Returns physical sizes (e.g., X=0.1, Y=0.1, Z=0.5) usually in micrometres.
443408
```
444409

445-
`immeta()` gives us a dict summarising the image, and `improps` gives an object with the
446-
property 'spacing'. However, there is no guarantee that wither of these will contain any
447-
information on pixel size, and the BioimageBook notes that
448-
[these numbers can be misleading](https://bioimagebook.github.io/chapters/1-concepts/5-pixel_size/python.html#imageio)
449-
and require interpretation and cross-checking.
450-
451-
Even if `immeta()` does return a key called 'unit', the value may be returned as escaped Unicode:
452-
453-
'unit': '\\u00B5m'
454-
455-
This can be un-escaped with:
456-
457-
>>> m['unit'].encode().decode('unicode-escape')
458-
μm
459-
460-
Here, this would indicate that the image is to be mesaured in micrometres. Combining this
461-
with other information that may be available, e.g. `improps().spacing`, will help you figure
462-
out the pixel size.
463-
464410
::::::::::::::::::::::::::::::::::::: keypoints
465-
- Common image formats can usually all be loaded in the same way with skimage
466-
- Specialised proprietary formats may require specialised libraries
467-
- Basic metrics of an image include histogram, shape and max/min pixel values
468-
- These metrics can help tell us how the miage should be analysed
469-
- Lookup tables can change how a single-channel image is rendered
470-
- An RGB image contains 3 channels for red, green and blue
411+
Standard image formats (TIFF, PNG) can be loaded as NumPy arrays using skimage.
412+
Proprietary microscope formats are best handled by BIOIO to preserve dimensions and metadata.
413+
Basic metrics like histograms, shape, and pixel ranges help determine the best analysis strategy.
414+
Lookup tables (LUTs) change how data is rendered visually but do not change the underlying pixel values.
415+
Lazy loading (via Dask in BIOIO) allows you to explore massive datasets without overwhelming your computer's memory.
471416
::::::::::::::::::::::::::::::::::::::::::::::::

0 commit comments

Comments
 (0)