xp1632 / VPE_IP

0 stars 0 forks source link

PyImageJ in JupyterLab_Installation_and_Image_dims_Tech_details #62

Open xp1632 opened 4 months ago

xp1632 commented 4 months ago

Installation of PyImageJ by mamba

https://py.imagej.net/en/latest/Install.html

  1. Install mamba in conda by: conda install mamba

  2. Install Miniforge3: https://github.com/conda-forge/miniforge#miniforge3

  3. Install PyImageJ via: mamba create -n pyimagej -c conda-forge pyimagej openjdk=11

  4. activate PyImageJ environment in mamba activate pyimagej

  5. To test if pyimagej is installed, we run python -c "import imagej; ij = imagej.init('2.14.0'); print(ij.getVersion())" (in original guide it says : python -c 'import imagej; ij = imagej.init("2.14.0"); print(ij.getVersion())', however the double quote and single quote here would lead to syntax error) image

  6. in pyimageJ environment, we jupyter lab

xp1632 commented 4 months ago

Trouble shooting of cannot find imageJ module in jupyter

The error message suggests that the Jupyter Lab is not able to find the imagej module. This could be because Jupyter Lab is not using the correct Python interpreter from the pyimagej environment.

Here are a few steps to troubleshoot this issue:

  1. Check the Python interpreter in Jupyter Lab: In a new Jupyter Lab cell, run the following command:

    import sys
    print(sys.executable)

    This command prints the path to the Python interpreter that Jupyter Lab is using. Make sure that this path points to the Python interpreter in the pyimagej environment.

  2. Install the ipykernel package in the pyimagej environment: The ipykernel package allows Jupyter to create Python kernels for different environments. You can install it using the following command:

    mamba install -n pyimagej ipykernel
  3. Create a new Jupyter kernel for the pyimagej environment: After installing ipykernel, you can create a new Jupyter kernel for the pyimagej environment using the following command:

    python -m ipykernel install --user --name pyimagej --display-name "Python (pyimagej)"

    This command creates a new Jupyter kernel named "Python (pyimagej)".

  4. Select the new Jupyter kernel in Jupyter Lab: After creating the new Jupyter kernel, you need to select it in Jupyter Lab. You can do this by clicking on the kernel name in the top right corner of Jupyter Lab and selecting "Python (pyimagej)" from the dropdown menu.

After following these steps, try running your code again in Jupyter Lab. It should now be able to find the imagej module.

xp1632 commented 4 months ago

Caused by different python interpreters:

image image

The Python interpreters are different when we run commands in cmd pyimagej env and the jupyter lab we run in it


  1. mamba activate pyimagej
  2. mamba install ipykernel
  3. python -m ipykernel install --user --name pyimagej --display-name "Python (pyimagej)"
  4. Select the new Jupyter kernel in Jupyter Lab

Install Pyimagej in base env?

xp1632 commented 4 months ago

Installation of PyimageJ done

image

xp1632 commented 4 months ago

PyimageJ mechanism:

PyImageJ is built on the scyjava library, which is built on JPype and jgo. These libraries enable us to import any available Java class into our Python program, and use its full API, just as we could from within a Java program.


xp1632 commented 4 months ago

import imagej java classes in python:

- Here's an example of

import imagej

# initialize ImageJ
ij = imagej.init('sc.fiji:fiji:2.14.0')
print(f"ImageJ version: {ij.getVersion()}")

# load test image
dataset = ij.io().open('test_image.tif')

# display test image (see the Working with Images for more info)
ij.py.show(dataset)

# import HyperSphereShape and create radius of 5
HyperSphereShape = jimport('net.imglib2.algorithm.neighborhood.HyperSphereShape')
radius = HyperSphereShape(5)

# apply filter
result = ij.dataset().create(dataset)
ij.op().filter().mean(result, dataset, radius)

# show image
ij.py.show(result)

image

image

xp1632 commented 4 months ago

Convenient methods of PyImageJ

Ref: Doc5 ij.py. function more information
show Show an image 06-Working-with-Images
to_java Convert data from Python to Java 03-Sending-Data-to-Java
from_java Convert data from Java to Python 04-Retrieving-Data-from-Java
run_macro Run an original ImageJ macro 07-Running-Macros-Scripts-and-Plugins
run_script Run an ImageJ script (supported languages) 07-Running-Macros-Scripts-and-Plugins
run_plugin Run a plugin 07-Running-Macros-Scripts-and-Plugins
initialize_numpy_image Create a new numpy image in the same shape as input image 06-Working-with-Images
sync_image Synchronize data between ImageJ and ImageJ2 data structures --
active_dataset Get the active image as a Dataset --
active_xarray Get a copy of the active image as an xarray.DataArray --
active_imageplus Get the ImagePlus from the current window --
xp1632 commented 4 months ago

Warning messages for ImageJ1's RoiManager/WindowsManager

Ref: Doc5

xp1632 commented 4 months ago

Rest plan of notebook topics:

xp1632 commented 4 months ago

Images data types in PyImageJ:


Two ways of Open Images:


Or we could read image by python package skimage:

# Import an image with scikit-image.
# NB: 4D (X, Y, Channel, Z) confocal image of a HeLa cell in metaphase.
import skimage
url = 'https://media.imagej.net/pyimagej/4d/metaphase_hela_cell.tif'
img = skimage.io.imread(url)

# get microtubule slice
# NB: The dimension shape of `img` is (pln, row, col, ch) or (Z, Y, X, Channel).
mt = img[0, :, :, 2]

# show image
ij.py.show(mt)

Two ways of Display images:

image

xp1632 commented 4 months ago

images can also be displayed with ipywidgets, napari, itkwidgets

xp1632 commented 4 months ago

Details about image data dims:

Ref: Doc6.6



image image


def dump_info(image):
    """A handy function to print details of an image object."""
    name = image.name if hasattr(image, 'name') else None # xarray
    if name is None and hasattr(image, 'getName'): name = image.getName() # Dataset
    if name is None and hasattr(image, 'getTitle'): name = image.getTitle() # ImagePlus
    print(f" name: {name or 'N/A'}")
    print(f" type: {type(image)}")
    print(f"dtype: {image.dtype if hasattr(image, 'dtype') else 'N/A'}")
    print(f"shape: {image.shape}")
    print(f" dims: {image.dims if hasattr(image, 'dims') else 'N/A'}")

dump_info(dataset2)

image

# convert the ImageJ image (Java) to an xarray.DataArray (Python)
xarr = ij.py.from_java(dataset2)

# dump info on xarray.DataArray result from ij.py.from_java()
dump_info(xarr)

image


xp1632 commented 4 months ago

Conversion: Python <----> Java:

Python type Java type Notes
numpy.ndarray net.imglib2.img.Img An Img is both a RandomAccessibleInterval and an IterableInterval.
xarray.DataArray net.imagej.Dataset A Dataset is an Img with additional metadata including dimensional axis labels.
int Integer, Long, or BigInteger Destination Java type depends on magnitude of the integer.
float Float, Double, or BigDecimal Destination Java type depends on magnitude and precision of the value.
str String
bool boolean
dict LinkedHashMap Converted recursively (i.e., elements of the dict are converted too).
set LinkedHashSet Converted recursively (i.e., elements of the set are converted too).
list ArrayList Converted recursively (i.e., elements of the list are converted too).

Passing data from Python to Java:

image


Python List --> Java List:

Ref:Doc3

# create lists
python_list = [1, 2, 3, 4]
java_list = ij.py.to_java(python_list)

# modify one list
python_list[0] = 4

# check list contents
print(f"python_list: {python_list}\njava_list: {java_list}")

image


xp1632 commented 4 months ago

5.2 Numpy array --> Java RandomAccessibleInterval

import numpy as np

# get numpy array and list
test_arr = np.array([[5, 12], [21, 32]])
test_list = [1, 2, 4, 8, 16, 32, 64]

# convert array and list to Java
jarr = ij.py.to_java(test_arr)
jlist = ij.py.to_java(python_list)

print(type(jarr))
print(type(jlist))

image

xp1632 commented 4 months ago

5.3 Passing Image from Python to Java


Take away:

- We need to pay attention that:

  1. Modifying the java object we get from to_java() method also modifies its linked python object
  2. Image Data dimensions has different orders: ImaeJ2(XYCZ), scikit(ZCYX)

# Import an image with scikit-image.
# NB: 4D (X, Y, Channel, Z) confocal image of a HeLa cell in metaphase.
import skimage

url = 'https://media.imagej.net/pyimagej/4d/metaphase_hela_cell.tif'
img = skimage.io.imread(url)
# get microtubule slice
# NB: The dimension shape of `img` is (pln, row, col, ch) or (Z, Y, X, Channel).
mt = img[0, :, :, 2]

# show image
ij.py.show(mt)

image

result = np.zeros(mt.shape)
# these sigmas will be nice for the larger sections
sigma1 = 2.5
sigma2 = 0.5
# note the use of to_java on img and result to turn the numpy images into RAIs
ij.op().filter().dog(ij.py.to_java(result), ij.py.to_java(mt), sigma1, sigma2)
# purple highlights the edges of the vessels, green highlights the centers
ij.py.show(result, cmap = 'viridis')

image

xp1632 commented 4 months ago

ndarray and xarray:

xp1632 commented 4 months ago

5.4 Conversion Java -> Python

- To translate a Java image into Python, use:

p_colony_img = ij.py.from_java(colony_img) ij.py.show(p_colony_img,cmap='gray') print(f"cell_colony type: {type(colony_img)}") print(f"xr_colony type: {type(p_colony_img)}")

![image](https://github.com/Max-ChenFei/VPE_IP/assets/8528052/553f0495-1a4d-4166-92f0-b81bf17a3091)

- And we apply a `op` filter to this image by `ij.op().filter()` 
- 

HyperSphereShape = jimport('net.imglib2.algorithm.neighborhood.HyperSphereShape') radius = HyperSphereShape(2) ij.op().help("filter.variance")

apply filter

result = ij.dataset().create(colony_img) ij.op().filter().mean(result, colony_img, radius) ij.py.show(result,cmap='gray')

![image](https://github.com/Max-ChenFei/VPE_IP/assets/8528052/17b2daab-e236-49e4-ac66-d87124d7c548)

- Then we use `otsu thresholding` segmentation on it :

thresholded = ij.op().run("threshold.otsu", result) ij.py.show(thresholded,cmap='gray')


![image](https://github.com/Max-ChenFei/VPE_IP/assets/8528052/d71405eb-649d-4502-b006-4367b5167f17)
xp1632 commented 4 months ago

Dims order in image conversion:

#64

-As we stated in Issue 64, different dim orders of different image type would be automatically reordered, base on different situation or if dim_order is specified

xp1632 commented 4 months ago

8. Slicing and cropping java/python images


Here are some quick examples using the test_timeseries.tif 4D dataset with dimensions (X, Y, Channel, Time):

Syntax Result Shape
image the image without any slicing (250, 250, 3, 15)
image[:, :, 0, :] channel 0 (blue - nucleus) (250, 250, 15)
image[:, :, -1, :] last channel (green - viral proteins) (250, 250, 15)
image[100:200, 100:200,: ,:] cropped 100 x 100 4D image, centered (100, 100, 3, 15)

image

image

image

image

xp1632 commented 4 months ago

Create Numpy array of zeros in images shape

xp1632 commented 4 months ago

Run ImageJ1 marcos by ij.py.run_macro:

macro = """
#@ String name
#@ int age
#@ String city
#@output Object greeting
greeting = "Hello " + name + ". You are " + age + " years old, and live in " + city + "."
"""
args = {
    'name': 'Chuckles',
    'age': 26,
    'city': 'Saarbruecken'
}
result = ij.py.run_macro(macro, args)
print(result.getOutput('greeting'))
xp1632 commented 4 months ago

Run ImageJ Scripts by ij.py.run_script:


compute_stats_script = """
#@ OpService ops
#@ net.imglib2.RandomAccessibleInterval image
#@output stats

statNames = new org.scijava.table.GenericColumn("Statistic")
statValues = new org.scijava.table.DoubleColumn("Value")
addRow = (n, v) -> { statNames.add(n); statValues.add(v.getRealDouble()) }

addRow("geometricMean", ops.stats().geometricMean(image))
addRow("harmonicMean", ops.stats().harmonicMean(image))
addRow("kurtosis", ops.stats().kurtosis(image))
addRow("max", ops.stats().max(image))
addRow("mean", ops.stats().mean(image))
addRow("median", ops.stats().median(image))
addRow("min", ops.stats().min(image))
addRow("moment1AboutMean", ops.stats().moment1AboutMean(image))
addRow("moment2AboutMean", ops.stats().moment2AboutMean(image))
addRow("moment3AboutMean", ops.stats().moment3AboutMean(image))
addRow("moment4AboutMean", ops.stats().moment4AboutMean(image))
addRow("size", ops.stats().size(image))
addRow("skewness", ops.stats().skewness(image))
addRow("stdDev", ops.stats().stdDev(image))
addRow("sum", ops.stats().sum(image))
addRow("sumOfInverses", ops.stats().sumOfInverses(image))
addRow("sumOfLogs", ops.stats().sumOfLogs(image))
addRow("sumOfSquares", ops.stats().sumOfSquares(image))
addRow("variance", ops.stats().variance(image))

stats = new org.scijava.table.DefaultGenericTable()
stats.add(statNames)
stats.add(statValues)
"""

# load a sample image
image = ij.io().open('sample-data/test_image.tif')
args = {"image":image}
result = ij.py.run_script("Groovy", compute_stats_script, args)

# convert SciJava Table to pandas DataFrame
df = ij.py.from_java(result.getOutput("stats"))
print(df)
xp1632 commented 3 months ago

Run ImageJ Ops (including both examples from ImageJ and PyImageJ)


- Two ways of calling Op:


- Hint for parameters



- We should pay attention that:

  1. Some parameters for Op, we need to convert it to java format by PyImageJ's ij.py.jargs() image

Besides converting python parameters via Jargs(), calling op in ImageJ and PyImageJ are the same

here's a quick op code snippet:

import imagej

# initialize ImageJ
ij = imagej.init()
print(f"ImageJ version: {ij.getVersion()}")
[op for op in ij.op().ops() if "tube" in op]
ij.op().help("filter.tubeness")
# run tubeness op with ij.op().run()
def tubeness_1(image, sigma, calibration=[]):
    return ij.op().run("filter.tubeness", ij.py.jargs(image, sigma, calibration))

image_1 = ij.io().open('sample-data/test_still.tif')
ij.py.show(tubeness_1(image_1, sigma=10))