in_ = self.inputs[0]
self.transformer = caffe.io.Transformer(
{in_: self.blobs[in_].data.shape})
- self.transformer.set_transpose(in_, (2,0,1))
+ self.transformer.set_transpose(in_, (2, 0, 1))
if mean is not None:
self.transformer.set_mean(in_, mean)
if input_scale is not None:
image_dims = self.crop_dims
self.image_dims = image_dims
-
def predict(self, inputs, oversample=True):
"""
Predict classification probabilities of inputs.
input_ = input_[:, crop[0]:crop[2], crop[1]:crop[3], :]
# Classify
- caffe_in = np.zeros(np.array(input_.shape)[[0,3,1,2]],
+ caffe_in = np.zeros(np.array(input_.shape)[[0, 3, 1, 2]],
dtype=np.float32)
for ix, in_ in enumerate(input_):
caffe_in[ix] = self.transformer.preprocess(self.inputs[0], in_)
in_ = self.inputs[0]
self.transformer = caffe.io.Transformer(
{in_: self.blobs[in_].data.shape})
- self.transformer.set_transpose(in_, (2,0,1))
+ self.transformer.set_transpose(in_, (2, 0, 1))
if mean is not None:
self.transformer.set_mean(in_, mean)
if input_scale is not None:
self.configure_crop(context_pad)
-
def detect_windows(self, images_windows):
"""
Do windowed detection over given images and windows. Windows are
extracted then warped to the input dimensions of the net.
- Take
+ Parameters
+ ----------
images_windows: (image filename, window list) iterable.
context_crop: size of context border to crop in pixels.
- Give
+ Returns
+ -------
detections: list of {filename: image filename, window: crop coordinates,
predictions: prediction vector} dicts.
"""
for ix, window_in in enumerate(window_inputs):
caffe_in[ix] = self.transformer.preprocess(in_, window_in)
out = self.forward_all(**{in_: caffe_in})
- predictions = out[self.outputs[0]].squeeze(axis=(2,3))
+ predictions = out[self.outputs[0]].squeeze(axis=(2, 3))
# Package predictions with images and windows.
detections = []
ix += 1
return detections
-
def detect_selective_search(self, image_fnames):
"""
Do windowed detection over Selective Search proposals by extracting
the crop and warping to the input dimensions of the net.
- Take
+ Parameters
+ ----------
image_fnames: list
- Give
+ Returns
+ -------
detections: list of {filename: image filename, window: crop coordinates,
predictions: prediction vector} dicts.
"""
# Run windowed detection on the selective search list.
return self.detect_windows(zip(image_fnames, windows_list))
-
def crop(self, im, window):
"""
Crop a window from the image for detection. Include surrounding context
according to the `context_pad` configuration.
- Take
+ Parameters
+ ----------
im: H x W x K image ndarray to crop.
window: bounding box coordinates as ymin, xmin, ymax, xmax.
- Give
+ Returns
+ -------
crop: cropped window.
"""
# Crop window from the image.
return crop
-
def configure_crop(self, context_pad):
"""
Configure crop dimensions and amount of context for cropping.
If context is included, make the special input mean for context padding.
- Take
- context_pad: amount of context for cropping.
+ Parameters
+ ----------
+ context_pad : amount of context for cropping.
"""
# crop dimensions
in_ = self.inputs[0]
crop_mean = mean.copy().transpose(inv_transpose)
if channel_order is not None:
channel_order_inverse = [channel_order.index(i)
- for i in range(crop_mean.shape[2])]
- crop_mean = crop_mean[:,:, channel_order_inverse]
+ for i in range(crop_mean.shape[2])]
+ crop_mean = crop_mean[:, :, channel_order_inverse]
if raw_scale is not None:
crop_mean /= raw_scale
self.crop_mean = crop_mean
import pydot
# Internal layer and blob styles.
-LAYER_STYLE_DEFAULT = {'shape': 'record', 'fillcolor': '#6495ED',
- 'style': 'filled'}
-NEURON_LAYER_STYLE = {'shape': 'record', 'fillcolor': '#90EE90',
- 'style': 'filled'}
-BLOB_STYLE = {'shape': 'octagon', 'fillcolor': '#E0E0E0',
- 'style': 'filled'}
+LAYER_STYLE_DEFAULT = {'shape': 'record',
+ 'fillcolor': '#6495ED',
+ 'style': 'filled'}
+NEURON_LAYER_STYLE = {'shape': 'record',
+ 'fillcolor': '#90EE90',
+ 'style': 'filled'}
+BLOB_STYLE = {'shape': 'octagon',
+ 'fillcolor': '#E0E0E0',
+ 'style': 'filled'}
+
def get_pooling_types_dict():
"""Get dictionary mapping pooling type number to type name
"""
desc = caffe_pb2.PoolingParameter.PoolMethod.DESCRIPTOR
d = {}
- for k,v in desc.values_by_name.items():
+ for k, v in desc.values_by_name.items():
d[v.number] = k
return d
label=edge['label']))
return pydot_graph
+
def draw_net(caffe_net, rankdir, ext='png'):
- """Draws a caffe net and returns the image string encoded using the given
- extension.
+ """Draws a caffe net and returns the image string encoded using the given
+ extension.
- Input:
+ Parameters
+ ----------
caffe_net: a caffe.proto.caffe_pb2.NetParameter protocol buffer.
ext: the image extension. Default 'png'.
- """
- return get_pydot_graph(caffe_net, rankdir).create(format=ext)
+ """
+ return get_pydot_graph(caffe_net, rankdir).create(format=ext)
+
def draw_net_to_file(caffe_net, filename, rankdir='LR'):
- """Draws a caffe net, and saves it to file using the format given as the
- file extension. Use '.raw' to output raw text that you can manually feed
- to graphviz to draw graphs.
- """
- ext = filename[filename.rfind('.')+1:]
- with open(filename, 'wb') as fid:
- fid.write(draw_net(caffe_net, rankdir, ext))
+ """Draws a caffe net, and saves it to file using the format given as the
+ file extension. Use '.raw' to output raw text that you can manually feed
+ to graphviz to draw graphs.
+ """
+ ext = filename[filename.rfind('.')+1:]
+ with open(filename, 'wb') as fid:
+ fid.write(draw_net(caffe_net, rankdir, ext))
from caffe.proto import caffe_pb2
except:
import sys
- if sys.version_info >= (3,0):
+ if sys.version_info >= (3, 0):
print("Failed to include caffe_pb2, things might go wrong!")
else:
raise
-## proto / datum / ndarray conversion
+## proto / datum / ndarray conversion
def blobproto_to_array(blob, return_diff=False):
- """Convert a blob proto to an array. In default, we will just return the data,
- unless return_diff is True, in which case we will return the diff.
+ """Convert a blob proto to an array. In default, we will just return the
+ data, unless return_diff is True, in which case we will return the diff.
"""
if return_diff:
return np.array(blob.diff).reshape(
if arr.ndim != 4:
raise ValueError('Incorrect array shape.')
blob = caffe_pb2.BlobProto()
- blob.num, blob.channels, blob.height, blob.width = arr.shape;
+ blob.num, blob.channels, blob.height, blob.width = arr.shape
blob.data.extend(arr.astype(float).flat)
if diff is not None:
blob.diff.extend(diff.astype(float).flat)
as one can easily get it by calling datum.label.
"""
if len(datum.data):
- return np.fromstring(datum.data, dtype = np.uint8).reshape(
+ return np.fromstring(datum.data, dtype=np.uint8).reshape(
datum.channels, datum.height, datum.width)
else:
return np.array(datum.float_data).astype(float).reshape(
Note: this is mostly for illustrative purposes and it is likely better
to define your own input preprocessing routine for your needs.
- Take
- net: a Net for which the input should be prepared
+ Parameters
+ ----------
+ net : a Net for which the input should be prepared
"""
def __init__(self, inputs):
self.inputs = inputs
self.mean = {}
self.input_scale = {}
-
def __check_input(self, in_):
if in_ not in self.inputs:
raise Exception('{} is not one of the net inputs: {}'.format(
in_, self.inputs))
-
def preprocess(self, in_, data):
"""
Format input for Caffe:
caffe_in *= input_scale
return caffe_in
-
def deprocess(self, in_, data):
"""
Invert Caffe formatting; see preprocess().
decaf_in = decaf_in.transpose([transpose[t] for t in transpose])
return decaf_in
-
def set_transpose(self, in_, order):
"""
Set the input channel order for e.g. RGB to BGR conversion
'dimensions as the input.')
self.transpose[in_] = order
-
def set_channel_swap(self, in_, order):
"""
Set the input channel order for e.g. RGB to BGR conversion
'dimensions as the input channels.')
self.channel_swap[in_] = order
-
def set_raw_scale(self, in_, scale):
"""
Set the scale of raw features s.t. the input blob = input * scale.
self.__check_input(in_)
self.raw_scale[in_] = scale
-
def set_mean(self, in_, mean):
"""
Set the mean to subtract for centering the data.
raise ValueError('Mean shape incompatible with input shape.')
self.mean[in_] = mean
-
def set_input_scale(self, in_, scale):
"""
Set the scale of preprocessed inputs s.t. the blob = blob * scale.
# Extract crops
crops = np.empty((10 * len(images), crop_dims[0], crop_dims[1],
- im_shape[-1]), dtype=np.float32)
+ im_shape[-1]), dtype=np.float32)
ix = 0
for im in images:
for crop in crops_ix:
from collections import OrderedDict
try:
- from itertools import izip_longest
+ from itertools import izip_longest
except:
- from itertools import zip_longest as izip_longest
+ from itertools import zip_longest as izip_longest
import numpy as np
from ._caffe import Net, SGDSolver
"""
Forward pass: prepare inputs and run the net forward.
- Take
- blobs: list of blobs to return in addition to output blobs.
- kwargs: Keys are input blob names and values are blob ndarrays.
- For formatting inputs for Caffe, see Net.preprocess().
- If None, input is taken from data layers.
- start: optional name of layer at which to begin the forward pass
- end: optional name of layer at which to finish the forward pass (inclusive)
-
- Give
- outs: {blob name: blob ndarray} dict.
+ Parameters
+ ----------
+ blobs : list of blobs to return in addition to output blobs.
+ kwargs : Keys are input blob names and values are blob ndarrays.
+ For formatting inputs for Caffe, see Net.preprocess().
+ If None, input is taken from data layers.
+ start : optional name of layer at which to begin the forward pass
+ end : optional name of layer at which to finish the forward pass
+ (inclusive)
+
+ Returns
+ -------
+ outs : {blob name: blob ndarray} dict.
"""
if blobs is None:
blobs = []
"""
Backward pass: prepare diffs and run the net backward.
- Take
- diffs: list of diffs to return in addition to bottom diffs.
- kwargs: Keys are output blob names and values are diff ndarrays.
+ Parameters
+ ----------
+ diffs : list of diffs to return in addition to bottom diffs.
+ kwargs : Keys are output blob names and values are diff ndarrays.
If None, top diffs are taken from forward loss.
- start: optional name of layer at which to begin the backward pass
- end: optional name of layer at which to finish the backward pass (inclusive)
+ start : optional name of layer at which to begin the backward pass
+ end : optional name of layer at which to finish the backward pass
+ (inclusive)
- Give
+ Returns
+ -------
outs: {blob name: diff ndarray} dict.
"""
if diffs is None:
"""
Run net forward in batches.
- Take
- blobs: list of blobs to extract as in forward()
- kwargs: Keys are input blob names and values are blob ndarrays.
- Refer to forward().
+ Parameters
+ ----------
+ blobs : list of blobs to extract as in forward()
+ kwargs : Keys are input blob names and values are blob ndarrays.
+ Refer to forward().
- Give
- all_outs: {blob name: list of blobs} dict.
+ Returns
+ -------
+ all_outs : {blob name: list of blobs} dict.
"""
# Collect outputs from batches
all_outs = {out: [] for out in set(self.outputs + (blobs or []))}
"""
Run net forward + backward in batches.
- Take
+ Parameters
+ ----------
blobs: list of blobs to extract as in forward()
diffs: list of diffs to extract as in backward()
kwargs: Keys are input (for forward) and output (for backward) blob names
and values are ndarrays. Refer to forward() and backward().
Prefilled variants are called for lack of input or output blobs.
- Give
+ Returns
+ -------
all_blobs: {blob name: blob ndarray} dict.
all_diffs: {blob name: diff ndarray} dict.
"""
"""
Batch blob lists according to net's batch size.
- Take
+ Parameters
+ ----------
blobs: Keys blob names and values are lists of blobs (of any length).
Naturally, all the lists should have the same length.
- Give (yield)
+ Yields
+ ------
batch: {blob name: list of blobs} dict for a single batch.
"""
num = len(blobs.itervalues().next())
import caffe
+
def simple_net_file(num_output):
"""Make a simple net prototxt, based on test_net.cpp, returning the name
of the (temporary) file."""
f.close()
return f.name
+
class TestNet(unittest.TestCase):
def setUp(self):
self.num_output = 13
import caffe
+
class SimpleLayer(caffe.Layer):
"""A layer that just multiplies by ten"""
def backward(self, top, propagate_down, bottom):
bottom[0].diff[...] = 10 * top[0].diff
+
def python_net_file():
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write("""name: 'pythonnet' force_backward: true
python_param { module: 'test_python_layer' layer: 'SimpleLayer' } }""")
return f.name
+
class TestPythonLayer(unittest.TestCase):
def setUp(self):
net_file = python_net_file()
import caffe
from test_net import simple_net_file
+
class TestSolver(unittest.TestCase):
def setUp(self):
self.num_output = 13