-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstitcher.py
More file actions
145 lines (90 loc) · 3.76 KB
/
stitcher.py
File metadata and controls
145 lines (90 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""
Code provided from Allen Institute
"""
import logging
import operator as op
from collections import defaultdict
from six.moves import reduce
import numpy as np
class Stitcher(object):
def __init__(self, image_dimensions, tiles, channels):
logging.info('image_dimensions: {0}'.format(image_dimensions))
self.image_dimensions = image_dimensions
self.tiles = tiles
self.channels = channels
def run(self, cb=np.array):
slice_image, stitched_indicator = initialize_images(self.image_dimensions, len(self.channels))
missing_tiles = {}
for tile in self.tiles:
if tile.is_missing:
missing_tiles[tile.index] = tile.get_missing_path()
tile.initialize_image()
else:
tile.trim_self()
self.stitch(slice_image, stitched_indicator, tile, cb)
return slice_image, missing_tiles
def stitch(self, slice_image, stitched_indicator, tile, cb=np.array):
region = tile.get_image_region()
current_region = slice_image[region[0], region[1], region[2]]
indicator_region = stitched_indicator[region[0], region[1], region[2]]
stup = (tile.size['row'], tile.size['column'])
blend = get_blend(indicator_region, stup, cb)
blend = make_blended_tile(blend, tile.image, current_region)
#logging.info('obtained blend')
slice_image[region[0], region[1], region[2]] = blend
stitched_indicator[region[0], region[1], region[2]] = 1
#logging.info('updated image region with tile data')
def initialize_image(dimensions, nchannels, dtype, order='C'):
return np.zeros((dimensions['row'], dimensions['column'], nchannels), dtype=dtype, order=order)
def initialize_images(dimensions, nchannels):
return initialize_image(dimensions, nchannels, np.uint16), initialize_image(dimensions, nchannels, np.int8)
def make_blended_tile(blend, tile, current_region):
return np.multiply((1 - blend), tile) + np.multiply(blend, current_region)
def get_indicator_bound_point(indicator, lg, axis):
'''Finds the index of first change in a binary mask
along a specified axis in a specified direction
'''
delta = np.diff(indicator, axis=axis)
points = np.where(lg(delta, 0))
del delta
points = np.unique(points[axis])
size = indicator.shape[axis]
points = points[lg(points, size / 2.0)]
if len(points) > 0:
return points[-1]
return None
def blend_component_from_point(point, mesh, lg):
'''Obtains a normalized component of the blend, which describes depth of
overlap along a specified axis in a specified direction
'''
# this has the effect that the shallowest part of the blend
# is always 0 - symmetric with the deepest after normalization.
blend = point - mesh + 1
blend[lg(blend, 0)] = 0
blend = np.fabs(blend)
mx = np.amax(blend)
mx = mx if mx > 0.0 else 1.0
return blend / mx
def get_blend_component(indicator, lg, axis, meshes):
'''
'''
point = get_indicator_bound_point(indicator, lg, axis)
if point is None:
return []
return [blend_component_from_point(point, meshes[axis], lg)]
def get_overall_blend(indicator, meshes):
'''
'''
blends = []
for lg in (op.lt, op.gt):
for axis in (0, 1):
blends.extend(get_blend_component(indicator, lg, axis, meshes))
if len(blends) == 0:
return np.zeros_like(indicator)
return reduce(np.maximum, blends)
def get_blend(indicator_region, stup, cb=np.array):
'''
'''
meshes = np.meshgrid(*map(np.arange, stup), indexing='ij')
blend = get_overall_blend(indicator_region, meshes)
return cb(np.multiply(blend, indicator_region))