diff --git a/chunkflow/__init__.py b/chunkflow/__init__.py index 599ac820..a54fe2b6 100644 --- a/chunkflow/__init__.py +++ b/chunkflow/__init__.py @@ -2,5 +2,5 @@ # -*- coding: utf-8 -*- # Using Green Threads -import gevent.monkey -gevent.monkey.patch_all(thread=False) +# import gevent.monkey +# gevent.monkey.patch_all(thread=False) diff --git a/chunkflow/chunk/base.py b/chunkflow/chunk/base.py index f8ca242e..af57e210 100755 --- a/chunkflow/chunk/base.py +++ b/chunkflow/chunk/base.py @@ -289,6 +289,9 @@ def to_h5(self, file_name: str, with_offset: bool=True, f.create_dataset('/unique_nonzeros', data = unique) return file_name + def __len__(self): + return len(self.array) + def __array__(self): return self.array @@ -550,13 +553,18 @@ def add_overlap(self, other): overlap_slices = self._get_overlap_slices(other.slices) self.array[overlap_slices] += other.array[overlap_slices] - def cutout(self, slices: tuple): + def cutout(self, x: Union[tuple, BoundingBox, Bbox]): """ cutout a region of interest from this chunk :param slices: the global slices of region of interest :return: another chunk of region of interest """ + if isinstance(x, BoundingBox) or isinstance(x, Bbox): + slices = x.to_slices() + else: + slices = x + if len(slices) == self.ndim - 1: slices = (slice(0, self.shape[0]), ) + slices internalSlices = self._get_internal_slices(slices) diff --git a/chunkflow/lib/bounding_boxes.py b/chunkflow/lib/bounding_boxes.py index ccca9656..7b5621e7 100644 --- a/chunkflow/lib/bounding_boxes.py +++ b/chunkflow/lib/bounding_boxes.py @@ -1,23 +1,64 @@ +# support the class method with parameter type of itself +from __future__ import annotations + import logging import os -from itertools import product -from collections import UserList +from collections import UserList, namedtuple from math import ceil from typing import Union from copy import deepcopy import numpy as np -import h5py +import h5py +from numpy.lib.arraysetops import isin from cloudvolume import CloudVolume from cloudvolume.lib import Vec, Bbox +class Cartesian(namedtuple('Cartesian', ['z', 'y', 'x'])): + """Cartesian coordinate or offset.""" + __slots__ = () + + def __sub__(self, offset: Union[Cartesian, int]): + """subtract to another voxel coordinate + + Args: + offset (Cartesian, int): another voxel coordinate + """ + if isinstance(offset, int): + offset = (offset, offset, offset) + return Cartesian(*[x-o for x, o in zip(self, offset)]) + + def __add__(self, offset: Union[Cartesian, int]): + """add another coordinate + + Args: + offset (Cartesian, int): offset + """ + if isinstance(offset, int): + offset = (offset, offset, offset) + return Cartesian(*[x+o for x, o in zip(self, offset)]) + + @property + def vec(self): + return Vec(*self) + + class BoundingBox(Bbox): def __init__(self, min_corner: list, max_corner: list, dtype=None, voxel_size: tuple = None): super().__init__(min_corner, max_corner, dtype=dtype) self._voxel_size = voxel_size + @classmethod + def from_corners(cls, minpt: Cartesian, maxpt: Cartesian): + if isinstance(minpt, Cartesian): + minpt = minpt.vec + + if isinstance(maxpt, Cartesian): + maxpt = maxpt.vec + return cls(minpt, maxpt) + @classmethod def from_bbox(cls, bbox: Bbox, voxel_size: tuple = None): return cls(bbox.minpt, bbox.maxpt, voxel_size=voxel_size) @@ -32,6 +73,24 @@ def from_list(cls, x: list): bbox = Bbox.from_list(x) return cls.from_bbox(bbox) + @classmethod + def from_points(cls, x: np.ndarray): + bbox = Bbox.from_points(x) + return cls.from_bbox(bbox) + + @classmethod + def from_center(cls, center: Cartesian, extent: int): + """Create bounding box from center and extent + + Args: + center (Cartesian): center coordinate + extent (int): the range to extent, like radius + """ + minpt = center - extent + maxpt = center - extent + return cls.from_corners(minpt, maxpt) + + def clone(self): bbox = Bbox(self.minpt, self.maxpt, dtype=self.dtype) bbox = bbox.clone() @@ -48,7 +107,7 @@ def adjust(self, size: Union[int, tuple, list, Vec]): self.maxpt += size return self - def union(self, bbox2: Union[BoundingBox, Bbox]): + def union(self, bbox2): """Merge another bounding box Args: diff --git a/chunkflow/lib/synapses.py b/chunkflow/lib/synapses.py index 6e5692b0..0dd921c0 100644 --- a/chunkflow/lib/synapses.py +++ b/chunkflow/lib/synapses.py @@ -46,9 +46,10 @@ def __init__(self, pre: np.ndarray, pre_confidence: np.ndarray = None, np.testing.assert_array_less(0, resolution) self.resolution = resolution - self.pre = pre + # unsigned integer will have minus issues + self.pre = pre.astype(np.int32) self.pre_confidence = pre_confidence - self.post = post + self.post = post.astype(np.int32) self.post_confidence = post_confidence @classmethod @@ -217,8 +218,7 @@ def distances_from_pre_to_post(self): for post_idx in range(self.post_num): post = self.post[post_idx, 1:] pre_idx = self.post[post_idx, 0] - pre = self.pre[pre_idx] + pre = self.pre[pre_idx, :] distances[post_idx] = np.linalg.norm(pre - post) return distances - diff --git a/tests/__init__.py b/tests/__init__.py index 599ac820..2b732193 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -2,5 +2,5 @@ # -*- coding: utf-8 -*- # Using Green Threads -import gevent.monkey -gevent.monkey.patch_all(thread=False) +# # import gevent.monkey +# gevent.monkey.patch_all(thread=False) diff --git a/tests/lib/test_bounding_box.py b/tests/lib/test_bounding_box.py index 6483d243..5aed613a 100644 --- a/tests/lib/test_bounding_box.py +++ b/tests/lib/test_bounding_box.py @@ -1,8 +1,18 @@ -from cloudvolume.lib import Bbox +from cloudvolume.lib import Bbox, Vec -from chunkflow.lib.bounding_boxes import BoundingBox +from chunkflow.lib.bounding_boxes import BoundingBox, Cartesian +def test_cartesian(): + ct = Cartesian(1,2,3) + ct += 2 + ct == Cartesian(3,4,5) + + ct -= 2 + ct == Cartesian(1,2,3) + + ct.vec == Vec(1,2,3) + def test_bounding_box(): bbox = Bbox.from_delta((1,3,2), (64, 32, 8)) bbox = BoundingBox.from_bbox(bbox) @@ -10,4 +20,11 @@ def test_bounding_box(): bbox = bbox.clone() assert isinstance(bbox, BoundingBox) + minpt = Cartesian(1,2,3) + maxpt = Cartesian(2,3,4) + bbox = BoundingBox.from_corners(minpt, maxpt) + + bbox = BoundingBox.from_center(Cartesian(1,2,3), 3) + bbox == BoundingBox.from_list([-2, -1, 0, 4, 5, 6]) + \ No newline at end of file