Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize cell detection #398

Conversation

matham
Copy link
Contributor

@matham matham commented Apr 10, 2024

Description

What is this PR

  • Bug fix
  • Addition of a new feature
  • Other

Why is this PR needed?

Our lab has whole cleared brain light sheet images. We previously used Imaris for cell counting but wanted to test open source approaches. When I ran it on our brain (1500 image stack), it seemed to not finish even after a day. Looking closer I saw that cluster splitting is where it stalled because it took too long. Hence I started optimizing the code and ended up with this PR...

What does this PR do?

  1. It jits more of the cell detection code
  2. It optimizes structures to be more efficient
  3. Updated split_cells to use multiprocessing, which helps when there are a fair number of them.

How has this PR been tested?

I ran the pipeline upto and including the initial cell detection that generates the points of 300k cells on the whole brain. Then, before the splitting stage I dumped the cell coordinates and parameters used by the pipeline to initialize VolumeFilter and then tested with a small subset of those cells using the following script to make sure the number of cells didn't change with each commit.

I also used this code for profiling:

import time
import multiprocessing
import numpy as np
from numba import types, typed, set_num_threads
import cProfile
import pstats
from pstats import SortKey

from brainglobe_utils.cells.cells import Cell
from cellfinder.core.detect.filters.volume.structure_splitting import split_cells
from cellfinder.core.detect.filters.volume.volume_filter import VolumeFilter, sphere_volume


if __name__ == "__main__":
    # set_num_threads(6)
    data = np.load(r"D:\code\cellcount\cellcount\volume_cells.npz", allow_pickle=False)
    unknowns = [(name, data[str(name)]) for name in data["unknowns"]]
    artifacts = [(name, data[str(name)]) for name in data["artifacts"]]
    to_split = [(name, data[str(name)]) for name in data["to_split"]]

    filter = VolumeFilter(
        soma_diameter=7,
        soma_size_spread_factor=1.4,
        n_planes=1555,
        n_locks_release=59,
        save_planes=False,
        plane_directory=None,
        start_plane=1555,
        max_cluster_size=1348,
        outlier_keep=False,
        artifact_keep=False,
        setup_params=(np.zeros((3848, 3222), dtype=np.uint32), 7, 1, 3, 0.6, 0)
    )

    # this is used for the earlier commits
    # maps = filter.cell_detector.coords_maps
    # for key, value in to_split[:1]:
    #     maps[key] = value
    # cells = filter.get_results()
    #
    # for key, value in to_split[:10]:
    #     maps[key] = value

    # use this for final commit
    mp_ctx = multiprocessing.get_context("spawn")
    with mp_ctx.Pool(10) as worker_pool:
        for key, value in to_split[:1]:
            filter.cell_detector.add_points(key, value)
        cells = filter.get_results(worker_pool)
        filter.cell_detector.coords_maps.clear()

        for key, value in to_split[:20]:
            filter.cell_detector.add_points(key, value)

        ts = time.perf_counter()
        # cProfile.run("cells = filter.get_results(worker_pool)", "stats_main10")
        cells = filter.get_results(worker_pool)
        # cells = filter.get_results()
        print(f"Elapsed = {time.perf_counter() - ts}, num cells = {len(cells)}")
        # p = pstats.Stats('stats_main10')
        # p.sort_stats(SortKey.TIME).print_stats()

Is this a breaking change?

I don't think so. Like I changed process to not call directly get_results and get_results needs a worker pool arg. But these are really all internal API, not user side API.

Checklist:

  • The code has been tested locally
  • Tests have been added to cover all new functionality (unit & integration)
  • The documentation has been updated to reflect any changes
  • The code has been formatted with pre-commit

Changes

I'm going to list the changes in the order of the commits. There's two measures, the duration measured using time and cprofile data. And I tested it with 10 clusters.

The duration for main branch:

Elapsed = 19.57954730000347, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:34: NumbaTypeSafetyWarning: unsafe cast from int32 to uint64. Precision may be lost.
  d[key] = value
D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:39: NumbaTypeSafetyWarning: unsafe cast from int64 to uint64. Precision may be lost.
  return d[key]
<string>:3: NumbaTypeSafetyWarning: unsafe cast from uint64 to int64. Precision may be lost.
Elapsed = 23.919210099964403, num cells = 86
Tue Apr  9 22:20:08 2024    stats_main10

         232428 function calls (232427 primitive calls) in 0.392 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.086    0.000    0.086    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:266(_walk)
     8080    0.083    0.000    0.104    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1129(roll)
      100    0.029    0.000   23.896    0.239 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
     4340    0.022    0.000    0.128    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:132(append)
     4140    0.014    0.000    0.100    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:170(walk)
     4440    0.013    0.000    0.013    0.000 {method 'astype' of 'numpy.ndarray' objects}
     2400    0.011    0.000    0.033    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:101(_mean)
     4240    0.011    0.000   23.542    0.006 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
     8080    0.010    0.000    0.015    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1330(normalize_axis_tuple)
     2766    0.008    0.000    0.008    0.000 {method 'reduce' of 'numpy.ufunc' objects}
       10    0.007    0.001    0.015    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     2100    0.006    0.000    0.008    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:6(get_2d_bins)
     8554    0.006    0.000    0.006    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
      100    0.005    0.000    0.011    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:6(make_sphere)
     4220    0.004    0.000    0.004    0.000 {built-in method numpy.array}
      100    0.004    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:147(__getitem__)
     4274    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
     2400    0.004    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:67(_count_reduce_items)
       10    0.004    0.000   23.900    2.390 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4140    0.003    0.000    0.008    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:163(get_middle_plane)
     8080    0.003    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1380(<listcomp>)
     8080    0.003    0.000    0.003    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1216(<dictcomp>)
     4150    0.003    0.000    0.003    0.000 {method 'copy' of 'numpy.ndarray' objects}
      100    0.003    0.000    0.003    0.000 <string>:2(method)
     2310    0.002    0.000    0.002    0.000 {built-in method numpy.zeros}
      100    0.002    0.000    0.068    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:20(__init__)
     8680    0.002    0.000    0.002    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:125(ready)
    12580    0.002    0.000    0.002    0.000 {built-in method numpy.core._multiarray_umath.normalize_axis_index}
      400    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:139(atleast_3d)
      400    0.002    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:659(dstack)
      300    0.002    0.000    0.042    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:108(<listcomp>)
     4800    0.002    0.000    0.003    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_ufunc_config.py:452(_no_nep50_warning)
    12880    0.002    0.000    0.002    0.000 {built-in method numpy.asanyarray}
     5600    0.002    0.000    0.004    0.000 {built-in method builtins.next}
     2400    0.002    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\contextlib.py:102(__init__)
     2400    0.001    0.000    0.035    0.000 {method 'mean' of 'numpy.ndarray' objects}
      100    0.001    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
     2100    0.001    0.000    0.040    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:58(binned_mean_2d)
      100    0.001    0.000    0.052    0.001 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:76(bin_mean_3d)
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:139(__exit__)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:279(helper)
     8180    0.001    0.000    0.001    0.000 {method 'items' of 'dict' objects}
     8080    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:85(empty_like)
      100    0.001    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:130(__enter__)
     8080    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1125(_roll_dispatcher)
     8080    0.001    0.000    0.001    0.000 {built-in method _operator.index}
     2100    0.001    0.000    0.001    0.000 {method 'reshape' of 'numpy.ndarray' objects}
     6160    0.001    0.000    0.001    0.000 {built-in method builtins.isinstance}
      100    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
     4500    0.001    0.000    0.001    0.000 {built-in method math.ceil}
     4800    0.001    0.000    0.001    0.000 {built-in method builtins.issubclass}
     2400    0.000    0.000    0.000    0.000 {method 'set' of '_contextvars.ContextVar' objects}
     2400    0.000    0.000    0.000    0.000 {method 'reset' of '_contextvars.ContextVar' objects}
     2400    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
      300    0.000    0.000    0.000    0.000 {built-in method numpy.arange}
     4344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
      100    0.000    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
       10    0.000    0.000   23.916    2.392 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
      310    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:71(_wrapreduction)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2177(sum)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
        1    0.000    0.000   23.917   23.917 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
      400    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:655(_dstack_dispatcher)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:207(_arrays_for_stack_dispatcher)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:29(<listcomp>)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
     2300    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
       20    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:170(<listcomp>)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
  922/921    0.000    0.000    0.000    0.000 {built-in method builtins.len}
      100    0.000    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
      400    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:72(<dictcomp>)
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
      400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
      100    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:153(concatenate)
        1    0.000    0.000   23.917   23.917 {built-in method builtins.exec}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      300    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:49(get_structure_centre)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:135(_atleast_3d_dispatcher)
      201    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
      100    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2172(_sum_dispatcher)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:669(result_type)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       20    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
        2    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        1    0.000    0.000   23.917   23.917 <string>:1(<module>)
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
        2    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        2    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:202(sphere_volume)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        2    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        2    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFC2BFFC920}
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Replaced accumulation of points in coords_maps from stacking each new point on the previous array to using a list of tuples, and each new point is simply added to the list. Also, fixed the types so that we don't get the warnings about type conversions:

Elapsed = 1.2765582000138238, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 2.038256699917838, num cells = 86
Tue Apr  9 22:32:48 2024    stats_main10

         232321 function calls (232320 primitive calls) in 0.534 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.124    0.000    0.124    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:266(_walk)
     8080    0.118    0.000    0.144    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1129(roll)
      100    0.033    0.000    2.006    0.020 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
     4340    0.031    0.000    0.177    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:132(append)
     2400    0.017    0.000    0.053    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:101(_mean)
     4140    0.017    0.000    0.141    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:170(walk)
     2766    0.015    0.000    0.015    0.000 {method 'reduce' of 'numpy.ufunc' objects}
       10    0.012    0.001    0.024    0.002 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     4241    0.012    0.000    1.519    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
     8080    0.012    0.000    0.018    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1330(normalize_axis_tuple)
     4440    0.011    0.000    0.011    0.000 {method 'astype' of 'numpy.ndarray' objects}
     2100    0.009    0.000    0.014    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:6(get_2d_bins)
     8554    0.009    0.000    0.009    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
     4220    0.008    0.000    0.008    0.000 {built-in method numpy.array}
      100    0.007    0.000    0.014    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:6(make_sphere)
     2400    0.006    0.000    0.008    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:67(_count_reduce_items)
     4140    0.005    0.000    0.013    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:163(get_middle_plane)
      100    0.005    0.000    0.006    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:147(__getitem__)
     4150    0.004    0.000    0.004    0.000 {method 'copy' of 'numpy.ndarray' objects}
     8080    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1216(<dictcomp>)
     2310    0.004    0.000    0.004    0.000 {built-in method numpy.zeros}
       10    0.004    0.000    2.010    0.201 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4273    0.003    0.000    0.003    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
     8080    0.003    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1380(<listcomp>)
      300    0.003    0.000    0.068    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:108(<listcomp>)
      100    0.003    0.000    0.102    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:20(__init__)
      400    0.003    0.000    0.003    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:139(atleast_3d)
      400    0.003    0.000    0.006    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:659(dstack)
    12580    0.003    0.000    0.003    0.000 {built-in method numpy.core._multiarray_umath.normalize_axis_index}
     8680    0.002    0.000    0.002    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:125(ready)
     4800    0.002    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_ufunc_config.py:452(_no_nep50_warning)
     2400    0.002    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:102(__init__)
     5600    0.002    0.000    0.006    0.000 {built-in method builtins.next}
    12880    0.002    0.000    0.002    0.000 {built-in method numpy.asanyarray}
     2400    0.002    0.000    0.055    0.000 {method 'mean' of 'numpy.ndarray' objects}
     2400    0.002    0.000    0.005    0.000 C:\Prog\Python\Python310\lib\contextlib.py:139(__exit__)
     8554    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
     2100    0.002    0.000    0.065    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:58(binned_mean_2d)
      100    0.002    0.000    0.083    0.001 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:76(bin_mean_3d)
      100    0.002    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
     2400    0.001    0.000    0.004    0.000 C:\Prog\Python\Python310\lib\contextlib.py:279(helper)
     2100    0.001    0.000    0.001    0.000 {method 'reshape' of 'numpy.ndarray' objects}
     2400    0.001    0.000    0.004    0.000 C:\Prog\Python\Python310\lib\contextlib.py:130(__enter__)
     8180    0.001    0.000    0.001    0.000 {method 'items' of 'dict' objects}
     8080    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:85(empty_like)
     8080    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:1125(_roll_dispatcher)
     8080    0.001    0.000    0.001    0.000 {built-in method _operator.index}
      100    0.001    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
     6159    0.001    0.000    0.001    0.000 {built-in method builtins.isinstance}
     4500    0.001    0.000    0.001    0.000 {built-in method math.ceil}
     4800    0.001    0.000    0.001    0.000 {built-in method builtins.issubclass}
     2400    0.001    0.000    0.001    0.000 {method 'set' of '_contextvars.ContextVar' objects}
     2400    0.001    0.000    0.001    0.000 {method 'reset' of '_contextvars.ContextVar' objects}
      100    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
      100    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
     4344    0.001    0.000    0.001    0.000 {method 'append' of 'list' objects}
      300    0.000    0.000    0.000    0.000 {built-in method numpy.arange}
     2400    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
      100    0.000    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
       10    0.000    0.000    2.036    0.204 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
      310    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
        1    0.000    0.000    2.037    2.037 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:71(_wrapreduction)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2177(sum)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:207(_arrays_for_stack_dispatcher)
     2300    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:29(<listcomp>)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
      400    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:655(_dstack_dispatcher)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:170(<listcomp>)
  922/921    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      400    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
      100    0.000    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
       20    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
       10    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:72(<dictcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:153(concatenate)
      100    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
      300    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
        1    0.000    0.000    0.000    0.000 <string>:2(method)
      400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
      120    0.000    0.000    0.001    0.000 {method 'min' of 'numpy.ndarray' objects}
        1    0.000    0.000    2.037    2.037 {built-in method builtins.exec}
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:135(_atleast_3d_dispatcher)
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
      201    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
      120    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
      100    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2172(_sum_dispatcher)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
       20    0.000    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:669(result_type)
        1    0.000    0.000    2.037    2.037 <string>:1(<module>)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFC2BFFC920}

Switched to using fortran layout for volume block because we constantly rotate (roll) the last axis - the z-axis, and fortran layout is column major order which is faster for rotating that axis.

Elapsed = 1.227499600034207, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 1.2761926999082789, num cells = 86
Tue Apr  9 22:37:51 2024    stats_main10

         151521 function calls (151520 primitive calls) in 0.224 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.078    0.000    0.078    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:266(_walk)
     4340    0.019    0.000    0.020    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:135(append)
      100    0.017    0.000    1.260    0.013 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
     2400    0.009    0.000    0.029    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:101(_mean)
     4140    0.009    0.000    0.087    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:170(walk)
     2766    0.007    0.000    0.007    0.000 {method 'reduce' of 'numpy.ufunc' objects}
     4241    0.006    0.000    1.059    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
     4440    0.006    0.000    0.006    0.000 {method 'astype' of 'numpy.ndarray' objects}
       10    0.005    0.001    0.011    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     2100    0.005    0.000    0.007    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:6(get_2d_bins)
     8554    0.005    0.000    0.005    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
      100    0.004    0.000    0.008    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:6(make_sphere)
     4150    0.003    0.000    0.003    0.000 {method 'copy' of 'numpy.ndarray' objects}
      100    0.003    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:147(__getitem__)
     2400    0.003    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:67(_count_reduce_items)
     4140    0.003    0.000    0.005    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:163(get_middle_plane)
     4220    0.002    0.000    0.002    0.000 {built-in method numpy.array}
       10    0.002    0.000    1.262    0.126 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
      400    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:139(atleast_3d)
      100    0.002    0.000    0.058    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:20(__init__)
     4273    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
      400    0.002    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:659(dstack)
     2310    0.002    0.000    0.002    0.000 {built-in method numpy.zeros}
     4800    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_ufunc_config.py:452(_no_nep50_warning)
      300    0.001    0.000    0.037    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:108(<listcomp>)
     2400    0.001    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\contextlib.py:102(__init__)
     5600    0.001    0.000    0.004    0.000 {built-in method builtins.next}
     2100    0.001    0.000    0.036    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:58(binned_mean_2d)
     8680    0.001    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:128(ready)
      100    0.001    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
     2400    0.001    0.000    0.031    0.000 {method 'mean' of 'numpy.ndarray' objects}
      100    0.001    0.000    0.046    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\array_operations.py:76(bin_mean_3d)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:139(__exit__)
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:279(helper)
     2400    0.001    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\contextlib.py:130(__enter__)
      100    0.001    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
     2100    0.001    0.000    0.001    0.000 {method 'reshape' of 'numpy.ndarray' objects}
     6159    0.001    0.000    0.001    0.000 {built-in method builtins.isinstance}
     4500    0.001    0.000    0.001    0.000 {built-in method numpy.core._multiarray_umath.normalize_axis_index}
     4500    0.001    0.000    0.001    0.000 {built-in method math.ceil}
     4800    0.001    0.000    0.001    0.000 {built-in method builtins.issubclass}
     4800    0.001    0.000    0.001    0.000 {built-in method numpy.asanyarray}
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
     2400    0.000    0.000    0.000    0.000 {method 'set' of '_contextvars.ContextVar' objects}
      100    0.000    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
     4344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
     2400    0.000    0.000    0.000    0.000 {method 'reset' of '_contextvars.ContextVar' objects}
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
     2400    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
      300    0.000    0.000    0.000    0.000 {built-in method numpy.arange}
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
       10    0.000    0.000    1.274    0.127 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
      310    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:71(_wrapreduction)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:207(_arrays_for_stack_dispatcher)
        1    0.000    0.000    1.275    1.275 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
     2300    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\tools\geometry.py:29(<listcomp>)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2177(sum)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\shape_base.py:655(_dstack_dispatcher)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\lib\index_tricks.py:170(<listcomp>)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
  922/921    0.000    0.000    0.000    0.000 {built-in method builtins.len}
      400    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
      100    0.000    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:72(<dictcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:153(concatenate)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
      400    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\shape_base.py:135(_atleast_3d_dispatcher)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
      100    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
      300    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
        1    0.000    0.000    1.275    1.275 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 <string>:2(method)
      100    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
      201    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2172(_sum_dispatcher)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      100    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    1.275    1.275 <string>:1(<module>)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:669(result_type)
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFC2C14C920}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)

Used an lru-cache for the computing the kernel.

Elapsed = 1.1849509000312537, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 1.2268906999379396, num cells = 86
Tue Apr  9 22:40:04 2024    stats_main10

         74721 function calls (74720 primitive calls) in 0.167 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.079    0.000    0.079    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:275(_walk)
     4340    0.019    0.000    0.019    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:144(append)
      100    0.017    0.000    1.211    0.012 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
     4140    0.009    0.000    0.088    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:179(walk)
     4241    0.006    0.000    1.067    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
       10    0.005    0.001    0.011    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     4340    0.005    0.000    0.005    0.000 {method 'astype' of 'numpy.ndarray' objects}
     8554    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
     4150    0.003    0.000    0.003    0.000 {method 'copy' of 'numpy.ndarray' objects}
     4220    0.002    0.000    0.002    0.000 {built-in method numpy.array}
     4140    0.002    0.000    0.005    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:172(get_middle_plane)
       10    0.002    0.000    1.213    0.121 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4273    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
      100    0.001    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
     8680    0.001    0.000    0.001    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:137(ready)
      100    0.001    0.000    0.002    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\ball_filter.py:65(__init__)
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
      366    0.001    0.000    0.001    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      100    0.001    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
      110    0.000    0.000    0.000    0.000 {built-in method numpy.zeros}
      100    0.000    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
       10    0.000    0.000    1.225    0.123 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:71(_wrapreduction)
      100    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2177(sum)
        1    0.000    0.000    1.226    1.226 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
      310    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
     2300    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
      800    0.000    0.000    0.000    0.000 {built-in method builtins.next}
     1344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      100    0.000    0.000    0.002    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:72(<dictcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
      359    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
      300    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
      100    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
      400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
      100    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
      201    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
        1    0.000    0.000    1.226    1.226 {built-in method builtins.exec}
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
        1    0.000    0.000    0.000    0.000 <string>:2(method)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
  122/121    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      100    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
      100    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\fromnumeric.py:2172(_sum_dispatcher)
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
        1    0.000    0.000    1.226    1.226 <string>:1(<module>)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFC2C14C920}

@matham
Copy link
Contributor Author

matham commented Apr 10, 2024

Had to split text because of length limitation:

jitted the ball filter code. Main difference is in the cProfile timing, meaning more happens underneath python, which is ultimately good.

Elapsed = 1.1832596000749618, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 1.2050180999794975, num cells = 86
Tue Apr  9 22:42:46 2024    stats_main10

         77940 function calls (77939 primitive calls) in 0.136 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.069    0.000    0.069    0.000 <string>:2(method)
    16861    0.018    0.000    1.158    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
      100    0.016    0.000    1.189    0.012 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
       10    0.005    0.001    0.011    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     4340    0.005    0.000    0.005    0.000 {method 'astype' of 'numpy.ndarray' objects}
     8554    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
      200    0.002    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
     8513    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
       10    0.002    0.000    1.191    0.119 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4150    0.002    0.000    0.002    0.000 {method 'copy' of 'numpy.ndarray' objects}
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
      200    0.001    0.000    0.008    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
     8000    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      266    0.000    0.000    0.000    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      110    0.000    0.000    0.000    0.000 {built-in method numpy.zeros}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
       10    0.000    0.000    1.203    0.120 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
     3344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
     2000    0.000    0.000    0.000    0.000 {built-in method builtins.next}
        1    0.000    0.000    1.204    1.204 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
     2400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
       80    0.000    0.000    0.000    0.000 {built-in method numpy.array}
      200    0.000    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
      600    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
      110    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
      600    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
      401    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
      200    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
      200    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
      200    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
        1    0.000    0.000    1.204    1.204 {built-in method builtins.exec}
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
  122/121    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      259    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
        1    0.000    0.000    1.204    1.204 <string>:1(<module>)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFBFD3BC920}

Like for the fortran optimization above, numba doesn't support specifying array order so we can't use fortran, instead I switched the internal volume to having the z-axis first, which is row-major so again rolling the z-axis should be faster. The effect is small, not not insignificant.

Elapsed = 1.1676770999329165, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 1.2028532000258565, num cells = 86
Tue Apr  9 22:45:46 2024    stats_main10

         77940 function calls (77939 primitive calls) in 0.136 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.069    0.000    0.069    0.000 <string>:2(method)
    16861    0.019    0.000    1.156    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
      100    0.016    0.000    1.187    0.012 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
       10    0.005    0.001    0.011    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     4340    0.005    0.000    0.005    0.000 {method 'astype' of 'numpy.ndarray' objects}
     8554    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
     8513    0.002    0.000    0.002    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
      200    0.002    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
       10    0.002    0.000    1.189    0.119 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4150    0.002    0.000    0.002    0.000 {method 'copy' of 'numpy.ndarray' objects}
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
      200    0.001    0.000    0.008    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
     8000    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      266    0.000    0.000    0.000    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      110    0.000    0.000    0.000    0.000 {built-in method numpy.zeros}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
       10    0.000    0.000    1.201    0.120 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
     3344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
     2000    0.000    0.000    0.000    0.000 {built-in method builtins.next}
        1    0.000    0.000    1.202    1.202 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
     2400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
       80    0.000    0.000    0.000    0.000 {built-in method numpy.array}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      200    0.000    0.000    0.004    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
      110    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      600    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
      600    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      200    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
      401    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
      200    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
      200    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
        1    0.000    0.000    1.202    1.202 {built-in method builtins.exec}
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
      259    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
  122/121    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
        1    0.000    0.000    1.202    1.202 <string>:1(<module>)
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFBFD3BC920}

Unrolling the recusrion. No effect, but nice nontheless in case the recursion does go deeper than normal.

Elapsed = 1.1694583999924362, num cells = 86

With cProfile
D:\code\cellcount\Scripts\python.exe D:\code\cellcount\cellcount\__init__.py 
Elapsed = 1.1998373001115397, num cells = 86
Tue Apr  9 22:48:23 2024    stats_main10

         77940 function calls (77939 primitive calls) in 0.137 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     4140    0.069    0.000    0.069    0.000 <string>:2(method)
    16861    0.019    0.000    1.152    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\boxing.py:59(wrapper)
      100    0.016    0.000    1.184    0.012 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:45(ball_filter_imgs)
       10    0.005    0.001    0.011    0.001 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:23(coords_to_volume)
     4340    0.005    0.000    0.005    0.000 {method 'astype' of 'numpy.ndarray' objects}
     8554    0.004    0.000    0.004    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:685(__init__)
     8513    0.003    0.000    0.003    0.000 D:\code\cellcount\lib\site-packages\numba\core\serialize.py:30(_numba_unpickle)
      200    0.002    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\inspect.py:3050(_bind)
       10    0.002    0.000    1.186    0.119 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:109(iterative_ball_filter)
     4150    0.002    0.000    0.002    0.000 {method 'copy' of 'numpy.ndarray' objects}
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2787(args)
     8554    0.001    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\numpy\core\getlimits.py:709(max)
      200    0.001    0.000    0.008    0.000 D:\code\cellcount\lib\site-packages\numba\experimental\jitclass\base.py:119(__call__)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2840(apply_defaults)
      200    0.001    0.000    0.001    0.000 C:\Prog\Python\Python310\lib\inspect.py:2810(kwargs)
     8000    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2699(kind)
      266    0.000    0.000    0.000    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      110    0.000    0.000    0.000    0.000 {built-in method numpy.zeros}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\numeric.py:136(ones)
       10    0.000    0.000    1.198    0.120 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:164(split_cells)
       86    0.000    0.000    0.001    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:25(__init__)
     3344    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    1.199    1.199 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:137(get_results)
     2000    0.000    0.000    0.000    0.000 {built-in method builtins.next}
     2400    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2687(name)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:146(check_centre_in_cuboid)
       80    0.000    0.000    0.000    0.000 {built-in method numpy.array}
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:59(_sanitize_position)
      100    0.000    0.000    0.000    0.000 <string>:2(ctor)
      200    0.000    0.000    0.003    0.000 C:\Prog\Python\Python310\lib\inspect.py:3181(bind)
      110    0.000    0.000    0.000    0.000 {built-in method numpy.empty}
      600    0.000    0.000    0.000    0.000 {method 'items' of 'mappingproxy' objects}
      800    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:3006(parameters)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:176(__getitem__)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:223(<listcomp>)
      600    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
      200    0.000    0.000    0.000    0.000 {method 'values' of 'mappingproxy' objects}
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_detection.py:54(get_structure_centre)
      401    0.000    0.000    0.000    0.000 {built-in method builtins.iter}
      200    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\inspect.py:2779(__init__)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\brainglobe_utils\cells\cells.py:37(<listcomp>)
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:19(<listcomp>)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:37(_getitem)
       86    0.000    0.000    0.000    0.000 {method 'all' of 'numpy.ndarray' objects}
      120    0.000    0.000    0.000    0.000 {method 'min' of 'numpy.ndarray' objects}
      200    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\core\dispatcher.py:858(__get__)
        1    0.000    0.000    1.199    1.199 {built-in method builtins.exec}
      120    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:43(_amin)
       86    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:61(_all)
      259    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
  122/121    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       11    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:909(__iter__)
      258    0.000    0.000    0.000    0.000 {built-in method math.isnan}
      100    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\multiarray.py:1080(copyto)
       60    0.000    0.000    0.000    0.000 {method 'max' of 'numpy.ndarray' objects}
       20    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:17(get_shape)
      172    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       60    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numpy\core\_methods.py:39(_amax)
        1    0.000    0.000    1.199    1.199 <string>:1(<module>)
       10    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       12    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:166(_typed)
       10    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\structure_splitting.py:28(<listcomp>)
       10    0.000    0.000    0.000    0.000 {method 'index' of 'list' objects}
       20    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
       24    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:160(_numba_type_)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:198(__len__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:192(__iter__)
        3    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1724(isEnabledFor)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:88(__new__)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:62(_iter)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:77(_from_meminfo_ptr)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:107(__init__)
        2    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1455(debug)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\cellfinder\cellfinder\core\detect\filters\volume\volume_filter.py:201(sphere_volume)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:149(_parse_arg)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\logging\__init__.py:1467(info)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:840(items)
        1    0.000    0.000    0.000    0.000 D:\code\cellcount\lib\site-packages\numba\typed\typeddict.py:27(_length)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:862(__init__)
        1    0.000    0.000    0.000    0.000 C:\Prog\Python\Python310\lib\_collections_abc.py:865(__len__)
        1    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x00007FFC2C14C920}

Now, for the original problem of splitting clusters being so slow and from the terminal it wasn't clear why it was stuck. I parallelized splitting clusters and also added a progress bar.

As expected, for very few clusters (10 as above) on 16 cores, it slows down:
Elapsed = 20.479029599926434, num cells = 86

But, if we use 100 clusters now it goes from prior commit being:
Elapsed = 54.93395039997995, num cells = 840
to this commit being:
Elapsed = 40.44253879995085, num cells = 840.

For us with many cores and a few thouasand clusters in a whole brain it saves a lot. I did notice looking at the task manager that it uses all cores, even when spacifying a fair number of blank cores. I think though internally somehow it must distribute reading the volume data or something across all cores because there were lots of kernel thread acitiviy.


Next, I used numba's @njit(parallel=True) to parallelize walk of the 3D filter, but I parameterized and made it only do that for the full images. Here's why.

When we first process the images to find the cells, the system uses many cores to filter each slice and then passing it to the 3D filter which in the main thread calls walk for each filtered slice. So for each walk call, it process a full original image which has many pixels and will benefit from parallelization by numba. AND it happens only in the main thread. Further more, looking at the CPU usage, the 2D filter threads stall because walk, (in the main thread) is slower than 2D filtering happening in other threads, which wait for the main thread to tell it to filter the next slice in 2D. Meaning we have most cores (n_ball_procs - ball_z_size to be exact) doing nothing in this stage. So it's safe to parallelize walk across those cores.

On the other hand, during cluster splitting. walk gets a bunch of clusters, which is each quite small likely. So paralelizing them will likey be just overhead and no gain. Even worse, splitting happens under already paralleization from the last commit, so each thread is already being used. Hence, we don't parallelize.

That's why I made paralle=False the default in walk and only enabled it in the intial 3D filtering.

So, what does this gain? Now we have to compare the full cell detection stage.

Brain one:

Previous commit - 25:26 for initial processing. 22 sec for splitting.
This commit - 12:19 for initial processing. 24 sec for splitting.

With the previous commit
(lightsheet) matte@rta:~/cellfinder$ brainmapper -s "/media/matte/Data/whole_brains_src/20240212/MF1_79M_W_BS_561.tif" -b "/media/matte/Data/whole_brains_src/20240212/MF1_79M_W_BS_488.tif" -o "/media/matte/Data2/whole_brains_dst/MF1_79M_W_BS_20240212" -v 4.5 4.06 4.06 --orientation "sar" --n-free-cpus 12 --soma-diameter 30 --ball-xy-size 6 --ball-z-size 15 --threshold 10 --atlas allen_mouse_25um
2024-04-09 23:47:55.825699: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  SSE4.1 SSE4.2 AVX AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2024-04-09 23:47:57 PM INFO     2024-04-09 23:47:57 PM - INFO - MainProcess fancylog.py:309 - Starting logging                          fancylog.py:309
                       INFO     2024-04-09 23:47:57 PM - INFO - MainProcess fancylog.py:310 - Multiprocessing-logging module found.     fancylog.py:310
                                Logging from all processes                                                                                             
                       WARNING  2024-04-09 23:47:57 PM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. prep.py:262
                                Skipping.                                                                                                              
2024-04-09 23:47:58 PM INFO     2024-04-09 23:47:58 PM - INFO - MainProcess main.py:74 - Skipping registration                               main.py:74
                       WARNING  2024-04-09 23:47:58 PM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. prep.py:262
                                Skipping.                                                                                                              
                       INFO     2024-04-09 23:47:58 PM - INFO - MainProcess main.py:121 - Detecting cell candidates                         main.py:121
Processing planes:   0%|                                                                                                      | 0/1437 [00:00<?, ?it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|███████████████████████████████████████████████████████████████████████████████████████▏   | 1377/1437 [24:35<01:03,  1.06s/it]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|███████████████████████████████████████████████████████████████████████████████████████▎   | 1378/1437 [24:35<01:00,  1.02s/it]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 1437/1437 [25:26<00:00,  1.06s/it]
2024-04-10 00:17:26 AM INFO     2024-04-10 00:17:26 AM - INFO - MainProcess volume_filter.py:139 - Splitting cell clusters and     volume_filter.py:139
                                writing results                                                                                                        
Splitting cell clusters: 100%|████████████████████████████████████████████████████████████████████████████████████| 7438/7438 [00:22<00:00, 335.70it/s]
Detection complete - all planes done in : 0:28:29.547782
After this commit
2024-04-10 04:30:44 AM INFO     2024-04-10 04:30:44 AM - INFO - MainProcess main.py:100 - brainreg completed. Results can be found here: /media/matte/Data2/whole_brains_dst/MF1_79M_W_BS_20240212/registration                                                        main.py:100
                       WARNING  2024-04-10 04:30:44 AM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. Skipping.                                                                                                                  prep.py:262
                       INFO     2024-04-10 04:30:44 AM - INFO - MainProcess main.py:121 - Detecting cell candidates                                                                                                                                                    main.py:121
Processing planes:   0%|                                                                                                                                                                                                                                 | 0/1437 [00:00<?, ?it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:   0%|▎                                                                                                                                                                                                                      | 2/1437 [00:06<1:09:26,  2.90s/it]<string>:3: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
Processing planes:  96%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎        | 1379/1437 [11:54<00:25,  2.23it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌        | 1380/1437 [11:54<00:24,  2.32it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1437/1437 [12:19<00:00,  1.94it/s]
2024-04-10 04:46:33 AM INFO     2024-04-10 04:46:33 AM - INFO - MainProcess volume_filter.py:142 - Splitting cell clusters and writing results                                                                                                                volume_filter.py:142
Splitting cell clusters:   0%|                                                                                                                                                                                                                           | 0/7438 [00:00<?, ?it/s]<string>:3: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
Splitting cell clusters: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7438/7438 [00:24<00:00, 302.02it/s]
Detection complete - all planes done in : 0:15:55.721739

And for another brain

Previous commit - 26:05 for initial processing. 29 sec for splitting.
This commit - 15:33 for initial processing. 32 sec for splitting.

With the previous commit
(lightsheet) matte@rta:~/cellfinder$ brainmapper -s "/media/matte/Data/whole_brains_src/20240213/MF1_84F_W_BS_561.tif" -b "/media/matte/Data/whole_brains_src/20240213/MF1_84F_W_BS_488.tif" -o "/media/matte/Data2/whole_brains_dst/MF1_84F_W_BS_20240213" -v 4.5 4.06 4.06 --orientation "sar" --n-free-cpus 12 --soma-diameter 30 --ball-xy-size 6 --ball-z-size 15 --threshold 10 --atlas allen_mouse_25um
2024-04-10 00:24:00.168337: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  SSE4.1 SSE4.2 AVX AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2024-04-10 00:24:01 AM INFO     2024-04-10 00:24:01 AM - INFO - MainProcess fancylog.py:309 - Starting logging                          fancylog.py:309
                       INFO     2024-04-10 00:24:01 AM - INFO - MainProcess fancylog.py:310 - Multiprocessing-logging module found.     fancylog.py:310
                                Logging from all processes                                                                                             
                       WARNING  2024-04-10 00:24:01 AM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. prep.py:262
                                Skipping.                                                                                                              
2024-04-10 00:24:02 AM INFO     2024-04-10 00:24:02 AM - INFO - MainProcess main.py:74 - Skipping registration                               main.py:74
2024-04-10 00:24:03 AM WARNING  2024-04-10 00:24:03 AM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. prep.py:262
                                Skipping.                                                                                                              
                       INFO     2024-04-10 00:24:03 AM - INFO - MainProcess main.py:121 - Detecting cell candidates                         main.py:121
Processing planes:   0%|                                                                                                      | 0/1555 [00:00<?, ?it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|███████████████████████████████████████████████████████████████████████████████████████▍   | 1495/1555 [25:16<00:59,  1.00it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|███████████████████████████████████████████████████████████████████████████████████████▌   | 1496/1555 [25:17<00:56,  1.05it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 1555/1555 [26:05<00:00,  1.01s/it]
2024-04-10 00:52:49 AM INFO     2024-04-10 00:52:49 AM - INFO - MainProcess volume_filter.py:139 - Splitting cell clusters and     volume_filter.py:139
                                writing results                                                                                                        
Splitting cell clusters: 100%|████████████████████████████████████████████████████████████████████████████████████| 5536/5536 [00:29<00:00, 186.05it/s]
Detection complete - all planes done in : 0:28:26.591511
After this commit
2024-04-10 02:54:31 AM INFO     2024-04-10 02:54:31 AM - INFO - MainProcess main.py:100 - brainreg completed. Results can be found here: /media/matte/Data2/whole_brains_dst/MF1_84F_W_BS_20240213/registration                                                        main.py:100
                       WARNING  2024-04-10 02:54:31 AM - WARNING - MainProcess prep.py:262 - Registered atlas exists, assuming already run. Skipping.                                                                                                                  prep.py:262
                       INFO     2024-04-10 02:54:31 AM - INFO - MainProcess main.py:121 - Detecting cell candidates                                                                                                                                                    main.py:121
Processing planes:   0%|                                                                                                                                                                                                                                 | 0/1555 [00:00<?, ?it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:   0%|▎                                                                                                                                                                                                                      | 2/1555 [00:08<1:34:57,  3.67s/it]<string>:3: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
Processing planes:  96%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉        | 1496/1555 [15:04<00:32,  1.82it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes:  96%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏       | 1498/1555 [15:05<00:28,  2.01it/s]/home/matte/cellfinder/cellfinder/core/detect/filters/plane/classical_filter.py:41: RuntimeWarning: invalid value encountered in divide
  filtered_img /= filtered_img.max()
Processing planes: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1555/1555 [15:33<00:00,  1.67it/s]
2024-04-10 03:12:50 AM INFO     2024-04-10 03:12:50 AM - INFO - MainProcess volume_filter.py:142 - Splitting cell clusters and writing results                                                                                                                volume_filter.py:142
Splitting cell clusters:   0%|                                                                                                                                                                                                                           | 0/5536 [00:00<?, ?it/s]<string>:3: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
Splitting cell clusters: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5536/5536 [00:32<00:00, 172.70it/s]
Detection complete - all planes done in : 0:18:32.235402

@adamltyson adamltyson requested a review from a team April 10, 2024 15:29
@adamltyson
Copy link
Member

Hi @matham, thanks for your PRs! Someone will hopefully be able to take a look at them soon, but they won't be trivial to review.

I wonder if you would like to join our develop chat over on Zulip (you can sign in with GitHub)? It would be useful to know more about your use cases and how we can help.

@matham
Copy link
Contributor Author

matham commented Apr 10, 2024

Ok, I'll join zulip later! And apologies for the giant PR, which I know is hard to review. I initially thought of splitting it up, but the changes depend on each other so went with one PR for now.

Copy link
Member

@alessandrofelder alessandrofelder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @matham

Thanks a lot for this 🤩 - the changes, as far as I understand them, look really helpful to me. And the commit-by-commit explanation really helped the review!

I have two questions, mostly so I am sure I understand the intention entirely (so we can maintain going forwards). Was wondering whether you could help explain?

cellfinder/core/detect/detect.py Outdated Show resolved Hide resolved
matham and others added 4 commits April 14, 2024 01:34
Co-authored-by: Alessandro Felder <alessandrofelder@users.noreply.github.com>
…am/cellfinder into numba_optim_parallel_split_numba_mp
@matham
Copy link
Contributor Author

matham commented Apr 14, 2024

Added a fix in last commit for num_threads that I found while testing napari.

@alessandrofelder alessandrofelder changed the base branch from main to matham/optimise-cell-detection April 25, 2024 10:14
@alessandrofelder
Copy link
Member

Thanks @matham - this is great and I am happy with the changes.

I'm going to merge this into a separate dev branch to enable me to install it more easily to run end-to-end tests on our HPC (pip installing from you dev-branch installs as version 0.1.dev+... which is incompatible with some other brainglobe tools)

@alessandrofelder alessandrofelder merged commit 27ccf25 into brainglobe:matham/optimise-cell-detection Apr 25, 2024
14 of 16 checks passed
adamltyson pushed a commit that referenced this pull request May 3, 2024
* Replace coord map values with numba list/tuple for optim.

* Switch to fortran layout for faster update of last dim.

* Cache kernel.

* jit ball filter.

* Put z as first axis to speed z rolling (row-major memory).

* Unroll recursion (no perf impact either way).

* Parallelize cell cluster splitting.

* Parallelize walking for full images.

* Cleanup docs and pep8 etc.

* Add pre-commit fixes.

* Fix parallel always being selected and numba function 1st class warning.

* Run hook.

* Older python needs Union instead of |.

* Accept review suggestion.



* Address review changes.

* num_threads must be an int.

---------

Co-authored-by: Matt Einhorn <matt@einhorn.dev>
@matham matham deleted the numba_optim_parallel_split_numba_mp branch May 3, 2024 20:25
IgorTatarnikov added a commit that referenced this pull request May 10, 2024
* replace tensorflow Tensor with keras tensor

* add case for TF prep in prep_model_weights

* add different backends to pyproject.toml

* add backend configuration to cellfinder init file. tests passing with jax locally

* define extra dependencies for cellfinder with different backends. run tox with TF backend

* run tox using TF and JAX backend

* install TF in brainmapper environment before running tests in CI

* add backends check to cellfinder init file

* clean up comments

* fix tf-nightly import check

* specify TF backend in include guard check

* clarify comment

* remove 'backend' from dependencies specifications

* Apply suggestions from code review

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

* PyTorch runs utilizing multiple cores

* PyTorch fix with default models

* Tests run on every push for now

* Run test on torch backend only

* Fixed guard test to set torch as KERAS_BACKEND

* KERAS_BACKEND env variable set directly in test_include_guard.yaml

* Run test on python 3.11

* Remove tf-nightly from __init__ version check

* Added 3.11 to legacy tox config

* Changed legacy tox config for real this time

* Don't set the wrong max_processing value

* Torch is now set as the default backend

* Tests only run with torch, updated comments

* Unpinned torch version

* Add codecov token (#403)

* add codecov token

* generate xml coverage report

* add timeout to testing jobs

* Allow turning off classification or detection in GUI (#402)

* Allow turning off classification or detection in GUI.

* Fix test.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Refactor to fix code analysis errors.

* Ensure array is always 2d.

* Apply suggestions from code review

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

* Support single z-stack tif file for input (#397)

* Support single z-stack tif file for input.

* Fix commit hook.

* Apply review suggestions.

* Remove modular asv benchmarks (#406)

* remove modular asv benchmarks

* recover old structure

* remove asv-specific lines from gitignore and manifest

* prune benchmarks

* Adapt CI so it covers both new and old Macs, and installs required additional dependencies on M1 (#408)

* naive attempt at adapting to silicon mac CI

* run include guard test on Silicon CI

* double-check hdf5 is needed

* Optimize cell detection (#398) (#407)

* Replace coord map values with numba list/tuple for optim.

* Switch to fortran layout for faster update of last dim.

* Cache kernel.

* jit ball filter.

* Put z as first axis to speed z rolling (row-major memory).

* Unroll recursion (no perf impact either way).

* Parallelize cell cluster splitting.

* Parallelize walking for full images.

* Cleanup docs and pep8 etc.

* Add pre-commit fixes.

* Fix parallel always being selected and numba function 1st class warning.

* Run hook.

* Older python needs Union instead of |.

* Accept review suggestion.



* Address review changes.

* num_threads must be an int.

---------

Co-authored-by: Matt Einhorn <matt@einhorn.dev>

* [pre-commit.ci] pre-commit autoupdate (#412)

updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](pre-commit/pre-commit-hooks@v4.5.0...v4.6.0)
- [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.4.3](astral-sh/ruff-pre-commit@v0.3.5...v0.4.3)
- [github.com/psf/black: 24.3.0 → 24.4.2](psf/black@24.3.0...24.4.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Simplify model download (#414)

* Simplify model download

* Update model cache

* Remove jax and tf tests

* Standardise the data types for inputs to all be float32

* Force torch to use CPU on arm based macOS during tests

* Added PYTORCH_MPS_HIGH_WATERMARK_RATION env variable

* Set env variables in test setup

* Try to set the default device to cpu in the test itself

* Add device call to Conv3D to force cpu

* Revert changes, request one cpu left free

* Revers the numb cores, don't use arm based mac runner

* Merged main, removed torch flags on cellfinder install for guards and brainmapper

* Lowercase Torch

* Change cache directory

---------

Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>
Co-authored-by: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com>
Co-authored-by: Matt Einhorn <matt@einhorn.dev>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alessandro Felder <alessandrofelder@users.noreply.github.com>
Co-authored-by: Adam Tyson <code@adamltyson.com>
IgorTatarnikov added a commit that referenced this pull request May 28, 2024
* remove pytest-lazy-fixture as dev dependency and skip test (with WG temp fix)

* Test Keras is present (#374)

* check if Keras present

* change TF to Keras in CI

* remove comment

* change dependencies in pyproject.toml for Keras 3.0

* Migrate to Keras 3.0 with TF backend (#373)

* remove pytest-lazy-fixture as dev dependency and skip test (with WG temp fix)

* change tensorflow dependency for cellfinder

* replace keras imports from tensorflow to just keras imports

* add keras import and reorder

* add keras and TF 2.16 to pyproject.toml

* comment out TF version check for now

* change checkpoint filename for compliance with keras 3. remove use_multiprocessing=False from fit() as it is no longer an input. test_train() passing

* add multiprocessing parameters to cube generator constructor and remove from fit() signature (keras3 change)

* apply temp garbage collector fix

* skip troublesome test

* skip running tests on CI on windows

* remove commented out TF check

* clean commented out code. Explicitly pass use_multiprocessing=False (as before)

* remove str conversion before model.save

* raise test_detection error for sonarcloud happy

* skip running tests on windows on CI

* remove filename comment and small edits

* Replace TF references in comments and warning messages (#378)

* change some old references to TF for the import check

* change TF cached model to Keras

* Cellfinder with Keras 3.0 and jax backend (#379)

* replace tensorflow Tensor with keras tensor

* add case for TF prep in prep_model_weights

* add different backends to pyproject.toml

* add backend configuration to cellfinder init file. tests passing with jax locally

* define extra dependencies for cellfinder with different backends. run tox with TF backend

* run tox using TF and JAX backend

* install TF in brainmapper environment before running tests in CI

* add backends check to cellfinder init file

* clean up comments

* fix tf-nightly import check

* specify TF backend in include guard check

* clarify comment

* remove 'backend' from dependencies specifications

* Apply suggestions from code review

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

---------

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

* Run cellfinder with JAX in Windows tests in CI (#382)

* use jax backend in brainmapper tests in CI

* skip TF backend on windows

* fix pip install cellfinder for brainmapper CI tests

* add keras env variable for brainmapper CLI tests

* fix prep_model_weights

* It/keras3 pytorch (#396)

* replace tensorflow Tensor with keras tensor

* add case for TF prep in prep_model_weights

* add different backends to pyproject.toml

* add backend configuration to cellfinder init file. tests passing with jax locally

* define extra dependencies for cellfinder with different backends. run tox with TF backend

* run tox using TF and JAX backend

* install TF in brainmapper environment before running tests in CI

* add backends check to cellfinder init file

* clean up comments

* fix tf-nightly import check

* specify TF backend in include guard check

* clarify comment

* remove 'backend' from dependencies specifications

* Apply suggestions from code review

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

* PyTorch runs utilizing multiple cores

* PyTorch fix with default models

* Tests run on every push for now

* Run test on torch backend only

* Fixed guard test to set torch as KERAS_BACKEND

* KERAS_BACKEND env variable set directly in test_include_guard.yaml

* Run test on python 3.11

* Remove tf-nightly from __init__ version check

* Added 3.11 to legacy tox config

* Changed legacy tox config for real this time

* Don't set the wrong max_processing value

* Torch is now set as the default backend

* Tests only run with torch, updated comments

* Unpinned torch version

* Add codecov token (#403)

* add codecov token

* generate xml coverage report

* add timeout to testing jobs

* Allow turning off classification or detection in GUI (#402)

* Allow turning off classification or detection in GUI.

* Fix test.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Refactor to fix code analysis errors.

* Ensure array is always 2d.

* Apply suggestions from code review

Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>

* Support single z-stack tif file for input (#397)

* Support single z-stack tif file for input.

* Fix commit hook.

* Apply review suggestions.

* Remove modular asv benchmarks (#406)

* remove modular asv benchmarks

* recover old structure

* remove asv-specific lines from gitignore and manifest

* prune benchmarks

* Adapt CI so it covers both new and old Macs, and installs required additional dependencies on M1 (#408)

* naive attempt at adapting to silicon mac CI

* run include guard test on Silicon CI

* double-check hdf5 is needed

* Optimize cell detection (#398) (#407)

* Replace coord map values with numba list/tuple for optim.

* Switch to fortran layout for faster update of last dim.

* Cache kernel.

* jit ball filter.

* Put z as first axis to speed z rolling (row-major memory).

* Unroll recursion (no perf impact either way).

* Parallelize cell cluster splitting.

* Parallelize walking for full images.

* Cleanup docs and pep8 etc.

* Add pre-commit fixes.

* Fix parallel always being selected and numba function 1st class warning.

* Run hook.

* Older python needs Union instead of |.

* Accept review suggestion.



* Address review changes.

* num_threads must be an int.

---------

Co-authored-by: Matt Einhorn <matt@einhorn.dev>

* [pre-commit.ci] pre-commit autoupdate (#412)

updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](pre-commit/pre-commit-hooks@v4.5.0...v4.6.0)
- [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.4.3](astral-sh/ruff-pre-commit@v0.3.5...v0.4.3)
- [github.com/psf/black: 24.3.0 → 24.4.2](psf/black@24.3.0...24.4.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Simplify model download (#414)

* Simplify model download

* Update model cache

* Remove jax and tf tests

* Standardise the data types for inputs to all be float32

* Force torch to use CPU on arm based macOS during tests

* Added PYTORCH_MPS_HIGH_WATERMARK_RATION env variable

* Set env variables in test setup

* Try to set the default device to cpu in the test itself

* Add device call to Conv3D to force cpu

* Revert changes, request one cpu left free

* Revers the numb cores, don't use arm based mac runner

* Merged main, removed torch flags on cellfinder install for guards and brainmapper

* Lowercase Torch

* Change cache directory

---------

Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>
Co-authored-by: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com>
Co-authored-by: Matt Einhorn <matt@einhorn.dev>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alessandro Felder <alessandrofelder@users.noreply.github.com>
Co-authored-by: Adam Tyson <code@adamltyson.com>

* Set pooling padding to valid by default on all MaxPooling3D layers

* Removed tf error suppression and other tf related functions

* Force torch to use cpu device when CELLFINDER_TEST_DEVICE env variable set to cpu

* Added nev variable to test step

* Use the GITHUB ACTIONS environemntal variable instead

* Added docstring for fixture setting device to cpu on arm based mac

* Revert changes to no_free_cpus being fixture, and default param

* Fixed typo in test_and_deploy.yml

* Set multiprocessing to false for the data generators

* Update all cache steps to match

* Remove reference to TF

* Make sure tests can run locally when GITHUB_ACTIONS env variable is missing2

* Removed warning when backend is not configured

* Set the label tensor to be float32 to ensure compatibility with mps

* Always set KERAS_BACKEND to torch on init

* Remove code in __init__ checking for if backend is installed

---------

Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>
Co-authored-by: Kimberly Meechan <24316371+K-Meech@users.noreply.github.com>
Co-authored-by: Matt Einhorn <matt@einhorn.dev>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alessandro Felder <alessandrofelder@users.noreply.github.com>
Co-authored-by: Adam Tyson <code@adamltyson.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants