Giter VIP home page Giter VIP logo

bigstream's Introduction

BigStream


warp certificate

BigStream is a library of tools for 3D registration including images too large to fit into memory and/or too large to register in a single (multi-threaded) process. BigStream can automate chunking of the alignment problem into overlapping blocks, distributes the blocks to independent workers running in parallel, and stitches the results into a single smooth transform. BigStream includes global affine, piecewise affine, and piecewise deformable alignments; it also includes tools for finding feature points of interest, applying transforms, and inverting transforms. The tools can be used individually to construct custom workflows, but pipelines are also provided for specific alignment problems.

Installation


pip install bigstream

Updates


BigStream has just undergone a major change to prepare for releasing v1.0.0. Improvements include:

  • Better utilization of dask primitives for faster and more robust distribution
  • More accurate linear blending of transform blocks for smoother transforms
  • A single python function to run the easi-fish registration pipeline directly
  • New alignment functions: feature point ransac affine, random affine search
  • Full access to the SimpleITK ImageRegistrationMethod options for almost all alignments
  • Better source code design providing easy modular access at many points in the funciton hierarchy

Development wise, bigstream has reached a milestone in stability and I can now focus on documenting, teaching, and maintaining the package instead of building core functionality.

Branches


The master branch is the most up to date version. With minimal modification it can be used in any distributed environment supported by dask-jobqueue.

The prototype branch is a record of the first implementation, built using a different software stack. Rather than DASK, it handles blocking, distribution, and stiching manually. The primary workflow can be seen in the stream.sh script. This version was built specifically for LSF clusters and minimal modification of the submit function in stream.sh would be required for using this version on other clusters.

Tutorials and Tutorial Data


For those interested in using the modular components of BigStream, ipython notebooks are provided walking you through the components that make up a pipeline. For example, here is the tutorial for the multifish_registration_pipeline. Included in the repository are several datasets useful for testing and demonstrating functionality in the tutorials.

Issues


Please use the github issue tracker on this page for issues of any kind.

Usage Examples


The tutorials are a more in depth way to learn the package, but here are some usage examples.

Bigstream is flexible toolkit that can be used in many different ways. I'll discuss some of them in order of "largest" (pipelines that chain together many steps) to "smallest" (individual functions).

Running the easi-fish registration pipeline:

from bigstream.application_pipelines import easifish_registration_pipeline

# load all the input data
fix_lowres = """ load a lowres version of your fixed image """
fix_highres = """ (lazy, e.g. zarr) load a highres version of your fixed image """
mov_lowres = """ load a lowres version of your moving image """
mov_highres = """ (lazy, e.g. zarr) load a highres version of your moving image """
fix_lowres_spacing = """ voxel spacing of lowres fixed image """
fix_highres_spacing = """ voxel spacing of highres fixed image """
mov_lowres_spacing = """ voxel spacing of lowres moving image """
mov_highres_spacing = """ voxel spacing of highres moving images """
blocksize = [128, 128, 128]  # size of individual alignment blocks in voxels
write_directory = './somewhere_to_save_transforms_and_images'

affine, deform, aligned = easifish_registration_pipeline(
    fix_lowres, fix_highres, mov_lowres, mov_highres,
    fix_lowres_spacing, fix_highres_spacing,
    mov_lowres_spacing, mov_highres_spacing,
    blocksize=blocksize,
    write_directory=write_directory,
)

This pipeline runs 4 steps:

  • global affine based on feature point ransac
  • global affine refinement based on gradient descent on image intensities
  • local affine based on feature point ransac
  • local deform based on gradient descent on image intensities

These four steps can be customized using these optional parameters to the pipeline:

global_ransac_kwargs
global_affine_kwargs
local_ransac_kwargs
local_deform_kwargs

See the docstring for easifish_registration_pipeline for more details.

Running custom alignment pipelines

from bigstream.align import alignment_pipeline
from bigstream.piecewise_align import distributed_piecewise_alignment_pipeline
from bigstream.transform import apply_transform
from bigstream.piecewise_transform import distributed_apply_transform

# load all the input data
fix_lowres = """ load a lowres version of your fixed image """
fix_highres = """ (lazy, e.g. zarr) load a highres version of your fixed image """
mov_lowres = """ load a lowres version of your moving image """
mov_highres = """ (lazy, e.g. zarr) load a highres version of your moving image """
fix_lowres_spacing = """ voxel spacing of lowres fixed image """
fix_highres_spacing = """ voxel spacing of highres fixed image """
mov_lowres_spacing = """ voxel spacing of lowres moving image """
mov_highres_spacing = """ voxel spacing of highres moving images """
blocksize = [128, 128, 128]  # size of individual alignment blocks in voxels
write_directory = './somewhere_to_save_transforms_and_images'

# construct a global alignment pipeline
rigid_kwargs = {}  # see bigstream.align.affine_align docstring for options
affine_kwargs = {} # see bigstream.align.affine_align docstring for options
deform_kwargs = {} # see bigstream.align.deformable_align docstring for options
steps = [('rigid', rigid_kwargs), ('affine', affine_kwargs), ('deform', deform_kwargs)]

# run the alignment
global_transform = alignment_pipeline(
    fix_lowres, mov_lowres,
    fix_lowres_spacing,
    mov_lowres_spacing,
    steps=steps,
)

# apply transform
global_aligned = apply_transform(
    fix_lowres, mov_lowres,
    fix_lowres_spacing, mov_lowres_spacing,
    transform_list=[global_transform,],
)

# construct a local alignment pipeline to refine global result
rigid_kwargs = {}  # see bigstream.align.affine_align docstring for options
affine_kwargs = {} # see bigstream.align.affine_align docstring for options
deform_kwargs = {} # see bigstream.align.deformable_align docstring for options
steps = [('rigid', rigid_kwargs), ('affine', affine_kwargs), ('deform', deform_kwargs)]
blocksize = [128, 128, 128]

# run the alignment
local_transform = distributed_piecewise_alignment_pipeline(
    fix_highres, mov_highres
    fix_highres_spacing, mov_highres_spacing,
    steps=steps,
    blocksize=blocksize,
    static_transform_list=[global_transform,]
    write_path='./deform.zarr',
    cluster_kwargs={## params to control your cluster},
)

# apply the transforms
local_aligned = distributed_apply_transform(
    fix_highres, mov_highres,
    fix_highres_spacing, mov_highres_spacing,
    transform_list=[global_transform, local_transform],
    blocksize=blocksize,
    write_path='./deformed.zarr',
    cluster_kwargs={## params to control your cluster},
)

For details, see the docstrings for:

  • bigstream.align.alignment_pipeline
  • bigstream.piecewise_align.distributed_piecewise_alignment_pipeline
  • bigstream.transform.apply_transform
  • bigstream.piecewise_transform.distributed_apply_transform

Using individual functions

from bigstream.align import affine_align
from bigstream.transform import apply_transform

# load all the input data
fix = """ load your fixed image """
mov = """ load your moving image """
fix_spacing = """ voxel spacing of fixed image """
mov_spacing = """ voxel spacing of moving image """

# run an affine alignment
affine = affine_align(
    fix, mov,
    fix_spacing, mov_spacing,
    # TONS of customization and efficiency options available here
)

# apply transform
aligned = apply_transform(
    fix, mov,
    fix_spacing, mov_spacing,
    transform_list=[affine,],  # this list can be arbitrarily long
)

The docstrings for the functions in bigstream.align show all the configurable options available. Any alignment function has full access to the SimpleITK ImageRegistrationMethod API.

bigstream's People

Contributors

gfleishman avatar mansouralawi avatar orena1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

bigstream's Issues

timing analysis on the RnR-ExM challenge

Hi bigstream team,

Thank you for releasing the code, and congrats on winning the first place in the RnR-ExM challenge.

Do you have the code for the results or timing analysis of the method for the dataset?

Providing spot coordinates to `ransac_affine`

I'm just looking for a little guidance/clarification on the fix_spots and mov_spots arguments of this function in the case where, e.g., objects are already at known locations due to segmentation.

Am I correct that these just need to be arrays of shape n_spots x 4 with the columns being x, y, z, and something to sort with? When I attempt this, I get an error in the _stats call in pairwise_correlation, and I'm not sure if it is due to the coordinates I am providing or something else.

get_spot_context is not suited for images with different dimentation

https://github.com/GFleishman/bigstream/blob/3cd2e4b217639d09b5e2dd0e169a7c210d9cacef/bigstream/features.py#L26

correct me if I am wrong, but get_spot_context, get the pixel content of each spot, these values are then correlated pixel-by-pixel. But if one image is in a different pixel dimensions, this correlation does not make much sense? right?

I assume a solution will be to inter the radius, so it will be the same in both fix & mov and than interp the pixel values so the dimensions will be the same.

Thanks

Feature request - refactor example notebooks so all parameters are automatically findable

Many current bigstream users are working with Jupyter notebooks given to them by Greg (me). Parameters for various steps in their registration pipeline are defined throughout the notebook. First, an updated standard notebook would be very valuable. Second formatting all parameters in such a way that they can easily be dumped into a json or yaml file would help interface with automated tools that want to wrap or call bigstream, like NextFlow.

Transforming coordinates with saved affine matrix does not match transformed volume

I wish to transform coordinates (z,y,x) in one volume with two consecutive affine matrice. I also have a volume transformed with the same two affine matrices. However, I realized the transformed points do not appear in the position I expected. I wonder what is wrong with my code.

Here is the code for computing the affine matrix.

original_spacing = [0.4, 0.1625, 0.1625]
factors = (2, 4, 4)
new_spacing = original_spacing * np.array(factors)
fix_downsampled = downsample(fix_vol, factors)
move_downsampled = downsample(mov_vol, factors)      

ransac_kwargs = {'blob_sizes': [6,10]}

affine_kwargs = { 'metric' : 'MMI',
                        'optimizer':'LBFGSB',
                        'alignment_spacing': 1,
                        'shrink_factors': ( 4, 2, 1),
                        'smooth_sigmas': ( 0., 0., 0.),
                    }

steps = [('ransac', ransac_kwargs)]

affine = alignment_pipeline(fix_downsampled, move_downsampled, new_spacing, new_spacing, steps)
affine2 = alignment_pipeline(fix_vol, mov_vol, original_spacing, original_spacing, steps=[('affine', affine_kwargs)],static_transform_list=[affine])

os.makedirs(os.path.dirname(affine_filename),exist_ok=True)
with open(affine_filename,'wb') as f:
    pickle.dump([affine,affine2],f)
affine_list = [affine,affine2]

Here is the code for tranforming the volume.

affine_filename = f'{output_path}/affine_two_steps.pkl'
with open(affine_filename,'rb') as f:
        affine_list = pickle.load(f)

mov_h5_new = mov_h5.replace('_raw.h5','.h5')
print(mov_h5_new)
if os.path.exists(mov_h5_new):
    os.remove(mov_h5_new)
    
with h5py.File(mov_h5_new, "w") as f_new:
    for channel in ['405','488','561','594','640']:
        print(channel,flush = True)
        with h5py.File(mov_h5, "r") as f:
            print(f.keys())
            mov_vol = f[channel][:]
            aligned_vol = apply_transform(
                fix_vol, mov_vol,
                original_spacing, original_spacing,
                transform_list=affine_list,
            )
            if channel in f_new.keys():
                del f_new[channel]
            f_new.create_dataset(channel, aligned_vol.shape, dtype = aligned_vol.dtype, data=aligned_vol)
print(f'{mov_h5_new} finish transforming other channels',flush = True)  

Here is the code for transforming the coordinates:

spacing = [0.4, 0.1625, 0.1625]
original_spacing = np.array(spacing)

transformed_coords = apply_transform_to_coordinates(
    coordinates = original_coords,
    transform_list = affine_list,
    transform_spacing = original_spacing,
    transform_origin = None,
)

Here is the results. The expected points should appear in the following location:

transformed [[  20 1868   51]
 [  20 1887   73]
 [  20 1899   80]
 ...
 [ 436 2000  853]
 [ 436 2023 1001]
 [ 436 2042  917]]

Yet it now appears like this:

current transformed [[ -13.8649482   531.65490952  923.63328138]
 [ -13.89809115  580.32768386  977.55101852]
 [  -4.96404728  924.18309313  357.72738699]
 ...
 [ 446.53994182 1935.10420533 1131.60909333]
 [ 451.14326026 1943.64447165  637.97002858]
 [ 444.92508985 1965.00453948 1338.77464399]]

Bigstream deformable registration

Hi Greg,

I have successfully run your big stream registration in the pipeline. It works well, and most of the cells can found their correspondence in the moving rounds. It is awesome!

Although Bigstream works well with the cell-level resolution, recently, I am wondering can we apply it to the single-molecule resolution level for aligning the FISH spots (now spots in the aligned images have 2-5 pixels shift). Here are my ideas: 1) use the full resolution images (but not the downsampled ones) to do the affine and deformable registration; 2) adjust the align threshold from 2.0 to 1.0 micro, or even smaller; 3) if the above methods are not applicable, do the registration with the aligned FISH channel but not with DAPI channel.

Now I used the full resolution images (but not the downsampled ones) to do the affine and deformable registration but it looks like the default images need to be scale 2? I am wondering can we update the pipeline and add the options for the high-resolution images?

I would be really appreciated it if you have suggestions. Thank you so much!

Best,
Zhenggang

Issues with tile-wise registration when two image volumes have large offset (bigstream_prototype)

I ran into an issue with the tile-wise feature selection step (spot) when there were large areas from two image volumes that were not overlapping. This caused the spot.py to fail, generate the following error and output no pickle files for the moving_spots:

Traceback (most recent call last):
  File “/bigstream-prototype/bigstream/spots.py”, line 148, in <module>
    pruned_spots = prune_blobs(sortedSpots, overlap, min_distance)[:,:-2].astype(np.int)
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed

Related issues that need to be fixed are:

  • Make spots robust to finding 0 spots
  • Make interpolate affines robust to all identity matrices
  • Make while loop more robust to issues with the 0 tile (or just not dependent on any specific tile)

Thank you so much!

problem loading tutorial data

Hi,

I have trouble loading the example data for the tutorial. I already update the path in the notebook to point towards the resource folder

fix_path = '/mnt/sda/git/bigstream/resources/fix.n5'
mov_path = '/mnt/sda/git/bigstream/resources/mov.n5'

But when I try to load the ow res dapi sections I get this error message


KeyError Traceback (most recent call last)
in
4 # get pointers to the low res scale level
5 # still just pointers, no data loaded into memory yet
----> 6 fix_lowres = fix_zarr['/dapi/lowres']
7 mov_lowres = mov_zarr['/dapi/lowres']
8

~/anaconda3/envs/bigstream/lib/python3.8/site-packages/zarr/hierarchy.py in getitem(self, item)
347 synchronizer=self._synchronizer)
348 else:
--> 349 raise KeyError(item)
350
351 def setitem(self, item, value):

KeyError: '/dapi/lowres'

Thanks,
Michael

adding option to not gaussian filter transforms in motion_correct

Hey Greg,

Thanks for the great repo!

When using the pipeline to register functional data, we expect the transforms to have occasional large deltas (when the live specimen tries to move). Under such circumstances, smoothing the transform with a Gaussian filter does more harm than good. I can set the sigma to be very small but that feels suboptimal - a median filter would be more fitting for this case, or simply the option to have no filtering at all.
I think this is where it is hard coded:
https://github.com/GFleishman/bigstream/blob/f8f003e483fb3e31c9601f8a643a94af524343ea/bigstream/motion_correct.py#L180

Would it be possible to expose this option to users when you have a second? (or tell me that I've missed an existing functionality :) ).

Thanks in advance!

Best,

Ginny

syntax error in PyPI version

when installing bigstream via pip install bigstream, a syntax error is thrown every time one tries to reach the bigstream package.

SyntaxError: expected ':' (piecewise_align.py, line 480)

when investigating the line in question (piecewise_align.py:480) it seems PyPI has a different version of bigstream than currently on github. Manually adding the : to line 480 resolves the issue.

PyPI piecewise_align.py:470-489






def old()                  # <--- this is line 480

    # set working copies of moving data
    if static_transform_list is not None:
        current_moving = apply_transform(
            fix, mov, fix_spacing, mov_spacing,
            transform_list=static_transform_list,
        )
        current_moving_mask = None

Local affine aligments

I am trying to run the tutorial, however I get these errors which I cannot find a way to solve them. I would sincerely appreciate any help. I am using the values and blocksize that were provided with the tutorial


ValueError Traceback (most recent call last)
Input In [21], in <cell line: 3>()
1 # Note use of mov_lowres_aligned as moving image rather than mov_lowres_data
2 # Note also that fix_lowres_spacing is used as the "moving" voxel spacing here
----> 3 local_affines = affine.prepare_piecewise_ransac_affine(
4 fix_lowres_data, mov_lowres_aligned,
5 fix_lowres_spacing, fix_lowres_spacing,
6 min_radius=6, max_radius=20, match_threshold=0.75,
7 blocksize=[128,]*3,
8 )
11 # not a numpy array
12 print(type(local_affines))

File ~/envsternson/lib/python3.8/site-packages/bigstream/affine.py:111, in prepare_piecewise_ransac_affine(fix, mov, fix_spacing, mov_spacing, min_radius, max_radius, match_threshold, blocksize, **kwargs)
109 # wrap images as dask arrays
110 fix_da = da.from_array(fix, chunks=blocksize)
--> 111 mov_da = da.from_array(mov, chunks=blocksize)
113 # wrap affine function
114 def wrapped_ransac_affine(x, y, block_info=None):
115
116 # compute affine

File ~/envsternson/lib/python3.8/site-packages/dask/array/core.py:3335, in from_array(x, chunks, name, lock, asarray, fancy, getitem, meta, inline_array)
3189 """Create dask array from something that looks like an array.
3190
3191 Input must have a .shape, .ndim, .dtype and support numpy-style slicing.
(...)
3332 >>> a = da.from_array(x, chunks=((67, 33), (6,)))
3333 """
3334 if isinstance(x, Array):
-> 3335 raise ValueError(
3336 "Array is already a dask array. Use 'asarray' or " "'rechunk' instead."
3337 )
3338 elif is_dask_collection(x):
3339 warnings.warn(
3340 "Passing an object to dask.array.from_array which is already a "
3341 "Dask collection. This can lead to unexpected behavior."
3342 )

ValueError: Array is already a dask array. Use 'asarray' or 'rechunk' instead.

code bug

I ran the script by the data of myself. the N5 parameter is {"compression":{"type":"gzip","useZlib":false,"level":-1},"pixelResolution":[0.18026115154068303,0.18026115154068303,0.9994],"downsamplingFactors":[1,1,1],"blockSize":[64,64,5],"dataType":"uint8","dimensions":[3904,3884,14]}, there alway is an error in the _method.py, line 80,
def _count_reduce_items(arr, axis, keepdims=False, where=True):
# fast-path for the default case
if where is True:
# no boolean mask given, calculate items according to axis
if axis is None:
axis = tuple(range(arr.ndim))
elif not isinstance(axis, tuple):
axis = (axis,)
items = 1
for ax in axis: #edit by gaoxinwei
items *= arr.shape[mu.normalize_axis_index(ax, arr.ndim)]
items = nt.intp(items)
the axis is 0, so in the ax in axis, it stopped.
Could you help me the find the reason? thank you ~

the main script as follow:

import numpy as np
import zarr, tifffile
from bigstream.align import alignment_pipeline
from bigstream.transform import apply_transform

file paths to tutorial data

replace the capitalized text below with the path to your copy of the bigstream repository

fix_path = 'D:/bigatream/r1.n5'
mov_path = 'D:/bigatream/r2.n5'

create Zarr file objects

fix_zarr = zarr.open(store=zarr.N5Store(fix_path), mode='r')
mov_zarr = zarr.open(store=zarr.N5Store(mov_path), mode='r')

get pointers to the low res scale level

still just pointers, no data loaded into memory yet

fix_lowres = fix_zarr['/lowres']
mov_lowres = mov_zarr['/lowres']

we need the voxel spacings for the low res data sets

we can compute them from the low res data set metadata

fix_meta = fix_lowres.attrs.asdict()
mov_meta = mov_lowres.attrs.asdict()
fix_lowres_spacing = np.array(fix_meta['pixelResolution']) * fix_meta['downsamplingFactors']
mov_lowres_spacing = np.array(mov_meta['pixelResolution']) * mov_meta['downsamplingFactors']
fix_lowres_spacing = fix_lowres_spacing[::-1] # put in zyx order to be consistent with image data
mov_lowres_spacing = mov_lowres_spacing[::-1]

read small image data into memory as numpy arrays

fix_lowres_data = fix_lowres[...]
mov_lowres_data = mov_lowres[...]

sanity check: print the voxel spacings and lowres dataset shapes

print(fix_lowres_spacing, mov_lowres_spacing)
print(fix_lowres_data.shape, mov_lowres_data.shape)

get pointers to the high res scale level

fix_highres = fix_zarr['/highres']
mov_highres = mov_zarr['/highres']

we need the voxel spacings for the high res data sets

we can compute them from the high res data set metadata

fix_meta = fix_highres.attrs.asdict()
mov_meta = mov_highres.attrs.asdict()
fix_highres_spacing = np.array(fix_meta['pixelResolution']) * fix_meta['downsamplingFactors']
mov_highres_spacing = np.array(mov_meta['pixelResolution']) * mov_meta['downsamplingFactors']
fix_highres_spacing = fix_highres_spacing[::-1]
mov_highres_spacing = mov_highres_spacing[::-1]

sanity check: print the voxel spacings and lowres dataset shapes

print(fix_highres_spacing, mov_highres_spacing)
print(fix_highres.shape, mov_highres.shape)

define arguments for the feature point and ransac stage (you'll understand these later)

ransac_kwargs = {'blob_sizes':[6, 20]}
#ransac_kwargs = {'blob_sizes':[6, 20]}

define arguments for the gradient descent stage (you'll understand these later)

affine_kwargs = {
'shrink_factors':(2,),
'smooth_sigmas':(2.5,),
'optimizer_args':{
'learningRate':0.25,
'minStep':0.,
'numberOfIterations':400,
},
}

define the alignment steps

steps = [('ransac', ransac_kwargs), ('affine', affine_kwargs)]

execute the alignment

affine = alignment_pipeline(
fix_lowres_data, mov_lowres_data,
fix_lowres_spacing, mov_lowres_spacing,
steps,
)

resample the moving image data using the transform you found

aligned = apply_transform(
fix_lowres_data, mov_lowres_data,
fix_lowres_spacing, mov_lowres_spacing,
transform_list=[affine,],
)

write results

np.savetxt('./affine.mat', affine)
tifffile.imsave('./affine_lowres.tiff', aligned)

load precomputed result (handy to use later if you've already run the cell)

affine = np.loadtxt('./affine.mat')

Defaults dictionaries for all optimizers and metrics

Some optimizer and metric choices in configure_irm require arguments. Currently if you choose an optimizer or metric but do not supply the required arguments through the corresponding kwargs dictionary, which is quite common because they are two separate parameters and the requirements are not documented in bigstream itself, they are in the SimpleITK.IRM documentation, the system of course throws an error for a missing argument.

This can be solved by defining a dictionary in configure_irm that saves a set of valid default parameters for each optimizer or metric choice. There are too many of these to document properly, so documentation will still be referred to the SimpleITK.IRM documentation, but at least the system will always have a valid set of parameters defined.

grid pattern misalignment after local affines

I followed the example bigstream_intro_tutorial jupyter notebook with the default parameters.
The registration looks fine after global affine. However, weird grid pattern misalignment appeared after local affines.
This happens even if I define local affines filled with identity matrices.
I am not familiar with dask, but I suspect the merging of overlapping regions between chunks is causing the problem.

global affine
global_affine

global + local affines
total_affine

A tutorial error

When I run the tutorial, affine alignment functions, I got this error.
But I am not sure what is wrong.
Can I ask you to suggest some possible problems?
I did not change any code and just run the provided jupyter code.
This was reproducible on my laptop and a remote cluster.

Best,

Error message:

Getting key points
FIXED image: found 74 key points
MOVING image: found 184 key points

TypeError Traceback (most recent call last)
TypeError: only size-1 arrays can be converted to Python scalars

The above exception was the direct cause of the following exception:

ValueError Traceback (most recent call last)
in
3
4 # see below for explanation of parameters
----> 5 global_affine = affine.ransac_affine(
6 fix_lowres_data, mov_lowres_data,
7 fix_lowres_spacing, mov_lowres_spacing,

~/.local/lib/python3.8/site-packages/bigstream/affine.py in ransac_affine(fix, mov, fix_spacing, mov_spacing, min_radius, max_radius, match_threshold, cc_radius, nspots, align_threshold, num_sigma_max, verbose, fix_spots, mov_spots, default, **kwargs)
73
74 # get point correspondences
---> 75 correlations = features.pairwise_correlation(
76 fix_spots, mov_spots,
77 )

~/.local/lib/python3.8/site-packages/bigstream/features.py in pairwise_correlation(A, B)
59
60 # get means and std for all contexts, center contexts
---> 61 a_mean, a_std = _stats(a_con)
62 b_mean, b_std = _stats(b_con)
63 a_con = a_con - a_mean[..., None]

~/.local/lib/python3.8/site-packages/bigstream/features.py in _stats(arr)
43
44 # compute mean and standard deviation along columns
---> 45 arr = arr.astype(np.float64)
46 means = np.mean(arr, axis=1)
47 sqr_means = np.mean(np.square(arr), axis=1)

ValueError: setting an array element with a sequence.

`NameError` due to missing `psutil` import in `bigstream.utility`

While using the bigstream package, I encountered a NameError suggesting that psutil was not defined. This error was raised when invoking the get_number_of_cores function in the utility.py module of the bigstream package.

Error Traceback:
File ".../align/align.py", line 278, in execute_volumetric_alignment_bigstream
affine = affine_align(
File ".../site-packages/bigstream/align.py", line 852, in affine_align
irm = configure_irm(**kwargs)
File ".../site-packages/bigstream/configure_irm.py", line 135, in configure_irm
ncores = ut.get_number_of_cores()
File ".../site-packages/bigstream/utility.py", line 603, in get_number_of_cores
ncores = psutil.cpu_count(logical=False)
NameError: name 'psutil' is not defined

It seems that the psutil module is not imported in the utility.py file, leading to this error.

To resolve this issue, a simple import statement for psutil should be added to the top of the utility.py module.

I'll be creating a PR to address this issue. Let me know if there are any additional details or steps required!

global RANSAC affine -- insufficient fixed spots found

The images I am working with is a 3D sample imaged by cyto-DAPI. Individual cells were clearly visible in both moving and fixed images. We wanted to match those cells from two imaging rounds.

I have a question about the first step of the registration -- global RANSAC affine. I often get “insufficient fixed spots found”. To deal with it,

  • shall I tweak the “blob_sizes” to get more spots? By reading your code, that seems to be the only parameter that controls how many blobs were detected.
  • should the “blob_sizes” set to be the number of voxels spanned by half the size of a cell (radius)?
  • what would be a good range of “blob_sizes”? I am using “s4 resolution - 1.89,1.84,1.84 um spacing (pre-expansion)”.

Any suggestions / directions would be appreciated.

Misalignment of high res image.

Hi

I am facing an issue when applying the affine transormation onto the high_res image.
I am using the alignment_pipeline function to run the globa alignment.
The result affine matrix seems to be good, since the the transformed mov_low_res image generated by apply_transform using the affine matrix, produces a image roughly aligned to the fix_low_res image.

However, when I apply the same matrix to the highres, it produces a shifted image.
(The images shown below have fix:green, mov:red. The highres images are down sampled after the transformation.)

Montage

Does anyone have an idea why this is happening?

Global RANSAC affine -- difference between return default and skip?

What is the difference between a failed RANSAC global affine that returns default vs simply skipping that step?

I thought they would be the same, as a failed step returns an identity affine matrix. However, I found the results are very different: a failed RANSAC that returns default followed by a global affine for some reason generate results that are much more sensible than skipping the RANSAC and directly do the whole-image gradient-descent based alignment.

Does this make sense at all? Did I miss anything? Any comments and suggestions would be appreciated! Thanks!

Two feature-requests: virtual masking, and origins for distributed functions

Feature proposal 1: Add virtual masking options

Currently, fix_mask and mov_mask must either be None or a numpy array containing ones and zeros. That is, you have to explicitly create your own mask outside of bigstream and then provide it. I would consider allowing fix_mask and mov_mask to also be a tuple of arbitrary length containing ints or floats. In such a case, the function would consider any value in that tuple to be background, and create masks internally where appropriate. Going one step further I could allow fix_mask and mov_mask to be a function which takes an array as input and returns an array of bools. In that case, bigstream would apply that function internally at the appropriate times to create masks.

For your application, you could then just do mov_mask=(0,) to mask out all your padded data. Internally this would be much easier to work with than a large array equal in size to the image itself.

Feature proposal 2: Add image origins to the distributed_piecewise_alignment_pipeline

Right now it sounds like when you convert from 2D to 3D you are simply padding planes of zeros on top of and below the real data. You would do this such that the final shape of the padded "faked 3D" image is the same as the fixed image shape, and you would put the correct number of planes above and below such that the position of the real data plane was as close as possible to where you think it needs to register into the fixed 3D volume. Is that correct?

If you were doing this with the in-memory non-distributed function I would suggest that you use a mov_origin. All the alignment functions in align.py take this argument. Here it is for the alignment_pipeline. The documentation is obviously pretty sparse so I don't blame anyone for not understanding how this could help them. Basically the origins let you specify how the fixed and moving image voxel grids should be positioned relative to one another.

So, for your application, instead of padding with a really large number of planes above and below the real data plane, you would pad with just a few planes above and below, and then define a mov_origin that was equal to the offset of the moving data (0, 0, 0) voxel from the fixed data (0, 0, 0) voxel. Origins are in physical units, so you would need to take the voxel spacings into account.

However - origins are currently not available for the distributed pipelines because they complicate the blocking process. However I think this is solvable and I may be willing to work on it in the future.

can't find function dog_ransac_affine

Hi,

I'm trying to run the global affine on the test dataset using the tutorial code:

now we'll use bigstream

from bigstream import affine

see next section for explanation of the parameters

global_affine = affine.dog_ransac_affine(
fix_lowres_data, mov_lowres_data, fix_lowres_spacing, mov_lowres_spacing,
cc_radius=8, nspots=2000, match_threshold=0.75, align_threshold=2.5,
)

But when I run it it tells me the function does not exist. I checked and it's not there but there is this function:

ransac_affine

Am I supposed to use this function? If so I need to set min_radius and max_radius. Any suggestions for good starting values?

Thanks,
Michael

rerunning the same code block in jupyter notebook

I found that rerunning the same code (e.g. global affine alignment and transformation) in a Jupyter notebook generated different results.

It looks like the new alignment code still somehow remembers the previous alignment. Is this the case? Below is an example code. If you rerun this repeatedly in a jupyter notebook, the new cost function/metric would not start decreasing from the beginning (~0 cross correlation), but it seemed to remember where it was last time.

Could you clarify this? Thanks a lot!

# define alignment steps
ransac_kwargs = {
    'blob_sizes':[2, 8],
    'cc_radius':12,
    'match_threshold':0.4,
    'nspots':10000,
}

affine_kwargs = {
    'alignment_spacing':4.0,
    'shrink_factors':(1,),
    'smooth_sigmas':(4.,),
    'optimizer_args':{
        'learningRate':0.25,
        'minStep':0.,
        'numberOfIterations':400,
    },
    'metric':'C',
}


steps = [
    ('ransac', ransac_kwargs,),
    ('affine', affine_kwargs,),
]

mov = r2
fix = r1

# align
affine = alignment_pipeline(
    fix, mov,
    fix_spacing_s4,
    mov_spacing_s4,
    steps,
    # fix_mask=fix_mask,
    # mov_mask=mov_mask,
)

# apply affine only
affine_aligned = apply_transform(
    fix, mov,
    fix_spacing_s4, mov_spacing_s4,
    transform_list=[affine,],
)

# write results
np.savetxt(outdir+'/affine_r2tor1.mat', affine)
tifffile.imwrite(outdir+'/affine_r2tor1.tiff', affine_aligned)

easifish tutorial: issue with unknown kwargs

when trying to run the easi-fish tutorial, I ran into a runtime error in easifish_registration_pipeline (application_pipelines.py:230):

TypeError: Server.__init__() got an unexpected keyword argument 'ncpus'

The error gets thrown at line 230.

 # if no cluster was given, make one then run on it
    if cluster is None:
        with cluster_constructor(**{**c, **cluster_kwargs}) as cluster:    # <--- this is line 230
            deform = alignment(cluster)
            aligned = resample(cluster)

It seems c is the problem. When commenting out the 'ncpus':1, I get the same error with the next kwarg threads, and then with min_workers, and then with max_workers.
config seems to be fine.

    c = {'ncpus':1,
         'threads':1,
         'min_workers':10,
         'max_workers':100,
         'config':{
             'distributed.worker.memory.target':0.9,
             'distributed.worker.memory.spill':0.9,
             'distributed.worker.memory.pause':0.9,
         },

commenting out / removing the items ncpus, threads, min_workers, max_workers or removing c from line 230 resolves the issue.

Is that a known Issue? how important are these kwargs?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.