Spaces:
Build error
Build error
| from scipy.ndimage.filters import gaussian_filter | |
| from scipy.interpolate import RectBivariateSpline | |
| from scipy.ndimage.interpolation import zoom | |
| import numpy | |
| def upsampleL(fieldmap, activation_data, reduction=1, shape=None, | |
| scaleshape=None, out=None): | |
| ''' | |
| Applies a bilinear upsampling. | |
| ''' | |
| offset, size, step = fieldmap | |
| input_count = activation_data.shape[0] | |
| if len(activation_data.shape) == 2: | |
| ay, ax = centered_arange(fieldmap, activation_data.shape, reduction) | |
| if shape is None: | |
| shape = upsampled_shape( | |
| fieldmap, activation_data.shape, reduction) | |
| else: | |
| ay, ax = centered_arange(fieldmap, activation_data.shape[1:], reduction) | |
| if shape is None: | |
| shape = upsampled_shape( | |
| fieldmap, activation_data.shape[1:], reduction) | |
| if scaleshape is not None: | |
| iy, ix = full_arange(scaleshape) | |
| # TODO: consider treaing each point as a center of a pixel | |
| iy *= shape[0] / scaleshape[0] | |
| ix *= shape[1] / scaleshape[1] | |
| else: | |
| iy, ix = full_arange(shape) | |
| if out is None: | |
| out = numpy.empty((input_count, len(iy), len(ix)), | |
| dtype=activation_data.dtype) | |
| if len(activation_data.shape) == 2: | |
| f = RectBivariateSpline(ay, ax, activation_data, kx=1, ky=1) | |
| return f(iy, ix, grid=True) | |
| else: | |
| for z in range(input_count): | |
| f = RectBivariateSpline(ay, ax, activation_data[z], kx=1, ky=1) | |
| out[z] = f(iy, ix, grid=True) | |
| return out | |
| def upsampleC(fieldmap, activation_data, shape=None, out=None): | |
| ''' | |
| Applies a bicubic upsampling. | |
| ''' | |
| offset, size, step = fieldmap | |
| input_count = activation_data.shape[0] | |
| ay, ax = centered_arange(fieldmap, activation_data.shape[1:]) | |
| if shape is None: | |
| shape = upsampled_shape(fieldmap, activation_data.shape[1:]) | |
| iy, ix = full_arange(shape) | |
| if out is None: | |
| out = numpy.empty((input_count,) + shape, | |
| dtype=activation_data.dtype) | |
| for z in range(input_count): | |
| f = RectBivariateSpline(ay, ax, activation_data[z], kx=3, ky=3) | |
| out[z] = f(iy, ix, grid=True) | |
| return out | |
| def upsampleG(fieldmap, activation_data, shape=None): | |
| ''' | |
| Upsampling utility functions | |
| ''' | |
| offset, size, step = fieldmap | |
| input_count = activation_data.shape[0] | |
| if shape is None: | |
| shape = upsampled_shape(fieldmap, activation_data.shape[1:]) | |
| activations = numpy.zeros((input_count,) + shape) | |
| activations[(slice(None),) + | |
| centered_slice(fieldmap, activation_data.shape[1:])] = ( | |
| activation_data * numpy.prod(step)) | |
| blurred = gaussian_filter( | |
| activations, | |
| sigma=(0, ) + tuple(t // 1.414 for o, s, t in zip(*fieldmap)), | |
| mode='constant') | |
| return blurred | |
| def topo_sort(layers): | |
| # First, build a links-from and also a links-to graph | |
| links_from = {} | |
| links_to = {} | |
| for layer in layers: | |
| for bot in layer.bottom: | |
| if bot not in links_from: | |
| links_from[bot] = [] | |
| links_from[bot].append(layer) | |
| for top in layer.top: | |
| if top not in links_to: | |
| links_to[top] = [] | |
| links_to[top].append(layer) | |
| # Now do a DFS to figure out the ordering (using links-from) | |
| visited = set() | |
| ordering = [] | |
| stack = [] | |
| for seed in links_from: | |
| if seed not in visited: | |
| stack.append((seed, True)) | |
| stack.append((seed, False)) | |
| visited.add(seed) | |
| while stack: | |
| (blob, completed) = stack.pop() | |
| if completed: | |
| ordering.append(blob) | |
| elif blob in links_from: | |
| for layer in links_from[blob]: | |
| for t in layer.top: | |
| if t not in visited: | |
| stack.append((t, True)) | |
| stack.append((t, False)) | |
| visited.add(t) | |
| # Return a result in front-to-back order, with incoming links for each | |
| return list((blob, links_to[blob] if blob in links_to else []) | |
| for blob in reversed(ordering)) | |
| def composed_fieldmap(layers, end): | |
| ts = topo_sort(layers) | |
| fm_record = {} | |
| for blob, layers in ts: | |
| # Compute fm's on all the edges that go to this blob. | |
| all_fms = [ | |
| (compose_fieldmap(fm_record[bot][0], layer_fieldmap(layer)), | |
| fm_record[bot][1] + [(bot, layer)]) | |
| for layer in layers for bot in layer.bottom if bot != blob] | |
| # And take the max fieldmap. | |
| fm_record[blob] = max_fieldmap(all_fms) | |
| if blob == end: | |
| return fm_record[blob] | |
| def max_fieldmap(maps): | |
| biggest, bp = None, None | |
| for fm, path in maps: | |
| if biggest is None: | |
| biggest, bp = fm, path | |
| elif fm[1][0] > biggest[1][0]: | |
| biggest, bp = fm, path | |
| # When there is no biggest, for example when maps is the empty array, | |
| # use the trivial identity fieldmap with no path. | |
| if biggest is None: | |
| return ((0, 0), (1, 1), (1, 1)), [] | |
| return biggest, bp | |
| def shortest_layer_path(start, end, layers): | |
| # First, build a blob-to-outgoing-layer graph | |
| links_from = {} | |
| for layer in layers: | |
| for bot in layer.bottom: | |
| if bot not in links_from: | |
| links_from[bot] = [] | |
| links_from[bot].append(layer) | |
| # Then do a BFS on the graph to find the shortest path to 'end' | |
| queue = [(s, []) for s in start] | |
| visited = set(start) | |
| while queue: | |
| (blob, path) = queue.pop(0) | |
| for layer in links_from[blob]: | |
| for t in layer.top: | |
| if t == end: | |
| return path + [layer] | |
| if t not in visited: | |
| queue.append((t, path + [layer])) | |
| visited.add(t) | |
| return None | |
| def upsampled_shape(fieldmap, shape, reduction=1): | |
| # Given the shape of a layer's activation and a fieldmap describing | |
| # the transformation to original image space, returns the shape of | |
| # the input size | |
| return tuple(((w - 1) * t + s + 2 * o) // reduction | |
| for (o, s, t), w in zip(zip(*fieldmap), shape)) | |
| def make_mask_set(image_shape, fieldmap, activation_data, | |
| output=None, sigma=0.1, threshold=0.5, percentile=None): | |
| """Creates a set of receptive field masks with uniform thresholds | |
| over a range of inputs. | |
| """ | |
| offset, shape, step = fieldmap | |
| input_count = activation_data.shape[0] | |
| activations = numpy.zeros((input_count,) + image_shape) | |
| activations[(slice(None),) + | |
| centered_slice(fieldmap, activation_data.shape[1:])] = ( | |
| activation_data) | |
| blurred = gaussian_filter( | |
| activations, | |
| sigma=(0, ) + tuple(s * sigma for s in shape), | |
| mode='constant') | |
| if percentile is not None: | |
| limit = blurred.ravel().percentile(percentile) | |
| return blurred > limit | |
| else: | |
| maximum = blurred.ravel().max() | |
| return (blurred > maximum * threshold) | |
| def safezoom(array, ratio, output=None, order=0): | |
| '''Like numpy.zoom, but does not crash when the first dimension | |
| of the array is of size 1, as happens often with segmentations''' | |
| dtype = array.dtype | |
| if array.dtype == numpy.float16: | |
| array = array.astype(numpy.float32) | |
| if array.shape[0] == 1: | |
| if output is not None: | |
| output = output[0,...] | |
| result = zoom(array[0,...], ratio[1:], | |
| output=output, order=order) | |
| if output is None: | |
| output = result[numpy.newaxis] | |
| else: | |
| result = zoom(array, ratio, output=output, order=order) | |
| if output is None: | |
| output = result | |
| return output.astype(dtype) | |
| def receptive_field(location, fieldmap): | |
| """Computes the receptive field of a specific location. | |
| Parameters | |
| ---------- | |
| location: tuple | |
| The x-y position of the unit being queried. | |
| fieldmap: | |
| The (offset, size, step) tuple fieldmap representing the | |
| receptive field map for the layer being queried. | |
| """ | |
| return compose_fieldmap(fieldmap, (location, (1, 1), (1, 1)))[:2] | |
| def proto_getattr(p, a, d): | |
| hf = True | |
| # Try using HasField to detect the presence of a field; | |
| # if there is no HasField, then just use getattr. | |
| try: | |
| hf = p.HasField(a) | |
| except: | |
| pass | |
| if hf: | |
| return getattr(p, a, d) | |
| return d | |
| def wh_attr(layer, attrname, default=0, minval=0): | |
| if not hasattr(default, '__len__'): | |
| default = (default, default) | |
| val = proto_getattr(layer, attrname, None) | |
| if val is None or val == []: | |
| h = max(minval, getattr(layer, attrname + '_h', default[0])) | |
| w = max(minval, getattr(layer, attrname + '_w', default[1])) | |
| elif hasattr(val, '__len__'): | |
| h = val[0] | |
| w = val[1] if len(val) >= 2 else h | |
| else: | |
| h = val | |
| w = val | |
| return (h, w) | |
| def layer_fieldmap(layer): | |
| # Only convolutional and pooling layers affect geometry. | |
| if layer.type == 'Convolution' or layer.type == 'Pooling': | |
| if layer.type == 'Pooling': | |
| config = layer.pooling_param | |
| if config.global_pooling: | |
| return ((0, 0), (None, None), (1, 1)) | |
| else: | |
| config = layer.convolution_param | |
| size = wh_attr(config, 'kernel_size', wh_attr(config, 'kernel', 1)) | |
| stride = wh_attr(config, 'stride', 1, minval=1) | |
| padding = wh_attr(config, 'pad', 0) | |
| neg_padding = tuple((-x) for x in padding) | |
| return (neg_padding, size, stride) | |
| # All other layers just pass through geometry unchanged. | |
| return ((0, 0), (1, 1), (1, 1)) | |
| def layerarray_fieldmap(layerarray): | |
| fieldmap = ((0, 0), (1, 1), (1, 1)) | |
| for layer in layerarray: | |
| fieldmap = compose_fieldmap(fieldmap, layer_fieldmap(layer)) | |
| return fieldmap | |
| # rf1 is the lower layer, rf2 is the higher layer | |
| def compose_fieldmap(rf1, rf2): | |
| """Composes two stacked fieldmap maps. | |
| Field maps are represented as triples of (offset, size, step), | |
| where each is an (x, y) pair. | |
| To find the pixel range corresponding to output pixel (x, y), just | |
| do the following: | |
| start_x = x * step[0] + offset[1] | |
| limit_x = start_x + size[0] | |
| start_y = y * step[1] + offset[1] | |
| limit_y = start_y + size[1] | |
| Parameters | |
| ---------- | |
| rf1: tuple | |
| The lower-layer receptive fieldmap, a tuple of (offset, size, step). | |
| rf2: tuple | |
| The higher-layer receptive fieldmap, a tuple of (offset, size, step). | |
| """ | |
| if rf1 == None: | |
| import pdb; pdb.set_trace() | |
| offset1, size1, step1 = rf1 | |
| offset2, size2, step2 = rf2 | |
| size = tuple((size2c - 1) * step1c + size1c | |
| for size1c, step1c, size2c in zip(size1, step1, size2)) | |
| offset = tuple(offset2c * step1c + offset1c | |
| for offset2c, step1c, offset1c in zip(offset2, step1, offset1)) | |
| step = tuple(step2c * step1c | |
| for step1c, step2c in zip(step1, step2)) | |
| return (offset, size, step) | |
| def _cropped_slices(offset, size, limit): | |
| corner = 0 | |
| if offset < 0: | |
| size += offset | |
| offset = 0 | |
| if limit - offset < size: | |
| corner = limit - offset | |
| size -= corner | |
| return (slice(corner, corner + size), slice(offset, offset + size)) | |
| def crop_field(image_data, fieldmap, location): | |
| """Crops image_data to the specified receptive field. | |
| Together fieldmap and location specify a receptive field on the image, | |
| which may overlap the edge. This returns a crop to that shape, including | |
| any zero padding necessary to fill out the shape beyond the image edge. | |
| """ | |
| offset, size = receptive_field(fieldmap, location) | |
| return crop_rectangle(image_data, offset, size) | |
| def crop_rectangle(image_data, offset, size): | |
| coloraxis = 0 if image_data.size <= 2 else 1 | |
| allcolors = () if not coloraxis else (slice(None),) * coloraxis | |
| colordepth = () if not coloraxis else (image_data.size[0], ) | |
| result = numpy.zeros(colordepth + size) | |
| (xto, xfrom), (yto, yfrom) = (_cropped_slices( | |
| o, s, l) for o, s, l in zip(offset, size, image_data.size[coloraxis:])) | |
| result[allcolors + (xto, yto)] = image_data[allcolors + (xfrom, yfrom)] | |
| return result | |
| def center_location(fieldmap, location): | |
| if isinstance(location, numpy.ndarray): | |
| offset, size, step = fieldmap | |
| broadcast = (numpy.newaxis, ) * (len(location.shape) - 1) + ( | |
| slice(None),) | |
| step = numpy.array(step)[broadcast] | |
| offset = numpy.array(offset)[broadcast] | |
| size = numpy.array(size)[broadcast] | |
| return location * step + offset + size // 2 | |
| else: | |
| offset, shape = receptive_field(location, fieldmap) | |
| return tuple(o + s // 2 for o, s in zip(offset, shape)) | |
| def centered_slice(fieldmap, activation_shape, reduction=1): | |
| offset, size, step = fieldmap | |
| r = reduction | |
| return tuple(slice((s // 2 + o) // r, (s // 2 + o + a * t) // r, t // r) | |
| for o, s, t, a in zip(offset, size, step, activation_shape)) | |
| def centered_arange(fieldmap, activation_shape, reduction=1): | |
| offset, size, step = fieldmap | |
| r = reduction | |
| return tuple(numpy.arange( | |
| (s // 2 + o) // r, (s // 2 + o + a * t) // r, t // r)[:a] # Hack to avoid a+1 points | |
| for o, s, t, a in zip(offset, size, step, activation_shape)) | |
| def full_arange(output_shape): | |
| return tuple(numpy.arange(o) for o in output_shape) | |