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

[WIP] communication protocol with BigDataViewer proofreading client #87

Merged
merged 45 commits into from
Sep 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dd42b56
Clean up gala.morpho imports
jni Jul 2, 2016
dd7c73e
Add compact watershed transform
jni Jul 2, 2016
db6f19a
Add new multiple separation message type
jni Jul 6, 2016
385e72e
Bug fixes on new multi-split protocol
jni Jul 6, 2016
41985a1
Allow null ID service; better config overrides
jni Jul 6, 2016
38ac9fe
Remap output IDs to unique ones from server
jni Jul 6, 2016
86d1f29
Add (passing) test with Solver using ID service
jni Jul 6, 2016
68c14e0
Use daemon threads for solver it server tests
jni Jul 7, 2016
749b3ec
Resolve when idle, and periodically
jni Jul 7, 2016
9a0e612
Fix parameters in compact_watershed
jni Jul 7, 2016
00bb8b7
Add multiscale grid seeding and related functions
jni Jul 7, 2016
921b191
Don't die if you don't recognize a message
jni Jul 7, 2016
f8c17cb
Revert "Use daemon threads for solver it server tests"
jni Jul 7, 2016
f90c563
Normalize labels before plotting data points in decision boundary
jni Jul 8, 2016
934f811
Ravel arrays to prevent shape warnings
jni Jul 8, 2016
4b1b416
Add print statements to server to diagnose ZMQ
jni Jul 8, 2016
5c92b7b
Sleep between bouts of proofreading to allow solver to catch up
jni Jul 8, 2016
4c71401
Send segmentations non-blocking in case proofreader is gone
jni Jul 8, 2016
c311ee8
Learn separations at a fragment level only
jni Jul 8, 2016
a1a282f
Don't replay merge history, use classifier
jni Jul 8, 2016
26ae97c
Starting ID can be consumed by 0 or boundary_body
jni Jul 8, 2016
85f92cd
Check whether original graph has edge between separated fragments
jni Jul 8, 2016
d840ad9
Add more diagnostic communication prints
jni Jul 8, 2016
18d8bcd
Bug fix: expecting wrong key for merges
jni Jul 8, 2016
00ad1cc
Don't re-solve if you only have one kind of label
jni Jul 8, 2016
9565cdf
Add new function to plot seed points on image
jni Jul 8, 2016
f121e14
Add tqdm as progressbar dependency
jni Jul 8, 2016
2d1013b
Bug fix: proofreader also needs merge keyword update
jni Jul 8, 2016
b12c871
Add pipelined compact watershed function
jni Jul 8, 2016
d502470
Remove silly call
jni Jul 8, 2016
07a8b5e
Bug fix: wrong shape of arrays
jni Jul 14, 2016
74ce960
Use skimage compact watershed if available
jni Aug 4, 2016
29dbd0d
Don't inherit from object now that we're Py3-only
jni Sep 19, 2016
29f6a05
Add class docstring for Solver
jni Sep 19, 2016
ccc71e5
Clean up ncut implementation
jni Sep 19, 2016
5982167
Remove MPL annotation finder
jni Sep 19, 2016
0201d81
Misc changes
jni Sep 20, 2016
dd10228
Move to conda-forge for env management
jni Sep 21, 2016
e825d6d
Create env quietly to minimise Travis noise
jni Sep 21, 2016
abbc914
Don't reinstall env packages with pip
jni Sep 21, 2016
b51c100
Update deprecated pytest section in setup.cfg
jni Sep 21, 2016
926df42
Add fix for scipy 0.18 which modifies COO inplace
jni Sep 21, 2016
fcc80a9
Add nose to dependencies
jni Sep 21, 2016
40d9aa3
Bump pytest requirement
jni Sep 21, 2016
cb70694
Remove unused functions and improve testing of evaluate
jni Sep 21, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 2 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,11 @@ install:
- conda update -q conda

# try to create env, but ignore if it fails -- probably cached.
- conda env create || true
- conda env create -q || true
- source activate gala
- conda info -a

# custom package not available from conda
- pip install viridis

# install testing and coverage packages
- pip install pytest pytest-cov
# install coveralls for reporting
- pip install coveralls

# Install gala
Expand Down
44 changes: 23 additions & 21 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
name: gala
channels:
- conda-forge
dependencies:
- python=3.5*
- setuptools=19.6*
- cython=0.23*
- pytest=2.8*
- numpy=1.11*
- nose=1.3*
- numpydoc=0.5*
- h5py=2.6*
- matplotlib=1.5*
- scipy=0.17*
- scikit-learn=0.17*
- scikit-image=0.12*
- networkx=1.10*
- jupyter=1.0*
- pyzmq=15.*
- pip:
- coverage>=4.0
- pytest-cov>=2.2
- viridis>=0.4
- memory_profiler>=0.41
- line_profiler>=1.0
- python>=3.5*
- setuptools>=19.6*
- cython>=0.23*
- pytest>=3*
- nose>=1.3
- numpy>=1.11*
- numpydoc>=0.5*
- h5py>=2.6*
- matplotlib>=1.5*
- scipy>=0.17*
- scikit-learn>=0.17*
- scikit-image>=0.12*
- networkx>=1.10*
- jupyter>=1.0*
- pyzmq>=15.*
- coverage>=4.0
- pytest-cov>=2.2
- viridis>=0.4
- memory_profiler>=0.41
- line_profiler>=1.1
- tqdm>=4.7
40 changes: 28 additions & 12 deletions gala/agglo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from numpy import (array, mean, zeros, zeros_like, where, unique,
newaxis, nonzero, median, float, ones, arange, inf, isnan,
flatnonzero, unravel_index, bincount)
from tqdm import tqdm
import numpy as np
from scipy.stats import sem
from scipy import sparse
Expand Down Expand Up @@ -978,6 +979,8 @@ def agglomerate(self, threshold=0.5, save_history=False):
if self.merge_queue.is_empty():
self.merge_queue = self.build_merge_queue()
history, scores, evaluation = [], [], []
# total merges is number of nodes minus boundary_node minus one.
progress = tqdm(total=self.number_of_nodes() - 2)
while len(self.merge_queue) > 0 and \
self.merge_queue.peek()[0] < threshold:
merge_priority, _, n1, n2 = self.merge_queue.pop()
Expand All @@ -989,6 +992,8 @@ def agglomerate(self, threshold=0.5, save_history=False):
evaluation.append(
(self.number_of_nodes()-1, self.split_vi())
)
progress.update(1)
progress.close()
if save_history:
return history, scores, evaluation

Expand Down Expand Up @@ -2001,12 +2006,20 @@ def write_plaza_json(self, fout, synapsejson=None, offsetz=0):

def ncut(self, num_clusters=10, kmeans_iters=5, sigma=255.0*20, nodes=None,
**kwargs):
"""Run normalized cuts on the current set of superpixels.
Keyword arguments:
num_clusters -- number of clusters to compute
kmeans_iters -- # iterations to run kmeans when clustering
sigma -- sigma value when setting up weight matrix
Return value: None
"""Run normalized cuts on the current set of fragments.

Parameters
----------
num_clusters : int, optional
The desired number of clusters
kmeans_iters : int, optional
The maximum number of iterations for the kmeans clustering
of the Laplacian eigenvectors.
sigma : float, optional
The damping factor on the edge weights. The higher this value,
the closer to 1 (the maximum) edges with large weights will be.
nodes : collection of int, optional
Restrict the ncut to the listed nodes.
"""
if nodes is None:
nodes = self.nodes()
Expand All @@ -2033,21 +2046,24 @@ def cluster_by_labels(self, labels, nodes=None):
self.merge_nodes(node1, node)


def compute_W(self, merge_priority_function, sigma=255.0*20, nodes=None):
""" Computes the weight matrix for clustering"""
def compute_W(self, distance_function, sigma=255.0*20, nodes=None):
"""Compute the weight matrix for n-cut clustering.

See `ncut` for parameters.
"""
if nodes is None:
nodes = array(self.nodes())
n = len(nodes)
nodes2ind = dict(zip(nodes, range(n)))
W = lil_matrix((n,n))
W = lil_matrix((n, n))
for u, v in self.real_edges(nodes):
try:
i, j = nodes2ind[u], nodes2ind[v]
except KeyError:
continue
w = merge_priority_function(self, ((u, v)))
W[i,j] = W[j,i] = np.exp(-w**2/sigma)
return W
w = distance_function(self, (u, v))
W[i, j] = W[j, i] = np.exp(-w**2 / sigma)
return W.tocsr()


def update_frozen_sets(self, n1, n2):
Expand Down
2 changes: 1 addition & 1 deletion gala/agglo2.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def sparse_boundaries(coo_boundaries):
bounds : SparseLOL
A map of edge indices to locations in the volume.
"""
edge_to_idx = coo_boundaries.tocsr()
edge_to_idx = coo_boundaries.copy().tocsr()
# edge_to_idx: CSR matrix that maps each edge to a unique integer
# we don't use the ID 0 so that empty spots can be used to mean "no ID".
edge_to_idx.data = np.arange(1, len(edge_to_idx.data) + 1, dtype=np.int_)
Expand Down
60 changes: 0 additions & 60 deletions gala/annotefinder.py

This file was deleted.

77 changes: 2 additions & 75 deletions gala/evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,79 +42,6 @@ def nzcol(mat, row_idx):
return mat[row_idx].nonzero()[1]


def sparse_min(mat, axis=None):
"""Compute the minimum value in a sparse matrix (optionally over an axis).

This function mimics the numpy.min() API for sparse.CSC or CSR matrices.

Parameters
----------
mat : a scipy.sparse csc or csr matrix
The matrix for which to compute the min.
axis : int in {0, 1}, optional
Compute the minimum over each column (`axis=0`) or over each row
(`axis=1`). By default, compute over entire matrix.

Returns
-------
mn : mat.dtype (if `axis=None`) or np.ndarray of shape (mat.shape[1-axis],)
The minimum value in the array or along an axis.
"""
mn = - sparse_max(-mat, axis)
return mn


def sparse_max(mat, axis=None):
"""Compute the maximum value in a sparse matrix (optionally over an axis).

This function mimics the numpy.max() API for sparse.CSC or CSR matrices.

Parameters
----------
mat : a scipy.sparse csc or csr matrix
The matrix for which to compute the max.
axis : int in {0, 1}, optional
Compute the maximum over each column (`axis=0`) or over each row
(`axis=1`). By default, compute over entire matrix.

Returns
-------
mx : mat.dtype (if `axis=None`) or np.ndarray of shape (mat.shape[1-axis],)
The maximum value in the array or along an axis.
"""
if type(mat) == sparse.csr_matrix:
mat = mat.tocsc()
if axis is None:
mx = np.max(mat.data)
elif axis == 0:
mx = sparse_csr_row_max(mat.T)
elif axis == 1:
mx = sparse_csr_row_max(mat.tocsr())
else:
raise ValueError("Invalid axis %i for matrix (2 dimensional)." % axis)
return mx


def sparse_csr_row_max(csr_mat):
"""Compute maximum over each row of a CSR format sparse matrix.

Parameters
----------
csr_mat : scipy.sparse.csr_matrix
The input matrix.

Returns
-------
mx : np.ndarray of shape `(mat.shape[0],)`
The maximum along every row.
"""
ret = np.zeros(csr_mat.shape[0])
row_diff = np.diff(csr_mat.indptr)
ret[row_diff != 0] = np.maximum.reduceat(csr_mat.data,
csr_mat.indptr[:-1][row_diff > 0])
return ret


def pixel_wise_boundary_precision_recall(pred, gt):
"""Evaluate voxel prediction accuracy against a ground truth.

Expand Down Expand Up @@ -1254,12 +1181,12 @@ def vi_tables(x, y=None, ignore_x=[0], ignore_y=[0]):

# Calculate log conditional probabilities and entropies
lpygx = np.zeros(np.shape(px))
lpygx[nzx] = xlogx(divide_rows(nzpxy, nzpx)).sum(axis=1)
lpygx[nzx] = xlogx(divide_rows(nzpxy, nzpx)).sum(axis=1).ravel()
# \sum_x{p_{y|x} \log{p_{y|x}}}
hygx = -(px*lpygx) # \sum_x{p_x H(Y|X=x)} = H(Y|X)

lpxgy = np.zeros(np.shape(py))
lpxgy[nzy] = xlogx(divide_columns(nzpxy, nzpy)).sum(axis=0)
lpxgy[nzy] = xlogx(divide_columns(nzpxy, nzpy)).sum(axis=0).ravel()
hxgy = -(py*lpxgy)

return [pxy] + list(map(np.asarray, [px, py, hxgy, hygx, lpygx, lpxgy]))
Expand Down