Most layers in Caffe are written in C + +. But for the input of their own data to write the corresponding input layer, such as you want to go to the part of the image, you can not use Lmdb, or your label needs a special tag. This is the time to write an input layer in Python.
As in FCN's voc_layers.py there are two classes:
Vocsegdatalayer
Sbddsegdatalayer
Contains: Setup,reshape,forward, Backward, Load_image, Load_label, respectively. No backward is required for no parameter updates.
Import Caffeimport NumPy as Npfrom PIL import imageimport randomclass vocsegdatalayer (Caffe. Layer): "" "Load (input image, label image) pairs from PASCAL VOC one-at-a-time while reshaping the net to Preser ve dimensions. Use the to feeds data to a fully convolutional network. "" "Def setup (self, bottom, top):" "" setup data layer according to parameters:-Voc_dir:path to PASCAL VOC year dir-split:train/val/test-mean:tuple of mean values to Subtract-randomize : Load in random order (default:true)-Seed:seed for randomization (default:none/current time) for PAS CAL VOC Semantic segmentation. Example params = Dict (voc_dir= "/path/to/pascal/voc2011", mean= (104.00698793, 116.66876762, 122.67891434) , split= "Val") "" "" # config params = eval (self.param_str) Self.voc_dir = params[' vo C_dir '] self.split = params[' Split ' Self.mean= Np.array (params[' mean ')) Self.random = Params.get (' randomize ', True) self.seed = params.get (' seed ', None) # tops:data and label If Len (top)! = 2:raise Exception ("need to define", Tops:data and L Abel. ") # data layers has no bottoms if Len (bottom)! = 0:raise Exception ("Do not define a bottom.") # Load indices for images and labels Split_f = ' {}/imagesets/segmentation/{}.txt '. Format (Self.voc_dir, Self.split) self.indices = open (Split_f, ' R '). Read (). Splitlines () self.idx = 0 # make eval determ inistic if ' train ' not in self.split:self.random = False # randomization:seed and pick I F self.random:random.seed (self.seed) self.idx = Random.randint (0, Len (self.indices)-1) def Resha PE (self, Bottom, top): # Load image + label image pair Self.data = Self.load_image (Self.indices[self.idx]) Self.lAbel = Self.load_label (Self.indices[self.idx]) # Reshape tops to fit (leading 1 are for batch dimension) top[ 0].reshape (1, *self.data.shape) top[1].reshape (1, *self.label.shape) def forward (self, bottom, top): # AS Sign Output top[0].data[...] = Self.data top[1].data[...] = self.label # pick next input if SE Lf.random:self.idx = Random.randint (0, Len (self.indices)-1) Else:self.idx + = 1 I F Self.idx = = Len (self.indices): self.idx = 0 def backward (self, top, propagate_down, bottom): PA SS Def load_image (self, idx): "" "Load input image and preprocess for Caffe:-Cast to float -Switch channels RGB, Bgr-subtract mean-transpose to channel x Height x Width order "" " im = Image.open (' {}/jpegimages/{}.jpg '. Format (Self.voc_dir, idx)) In_ = Np.array (IM, dtype=np.float32) In_ = In_[:,:,::-1] In_-= Self.mean In_ = In_.transpose ((2,0,1)) return In_ def load_label (self, idx): "" " Load label image as 1 x Height x width integer array of label indices. The leading singleton dimension is required by the loss. "" "Im = Image.open (' {}/segmentationclass/{}.png '. Format (Self.voc_dir, idx)) label = Np.array (IM, dtype=np.u int8) label = Label[np.newaxis, ...] Return Labelclass Sbddsegdatalayer (Caffe. Layer): "" "Load (input image, label image) pairs from the SBDD extended labeling of PASCAL VOC for semantic segm Entation One-at-a-time While reshaping the net to preserve dimensions. Use the to feeds data to a fully convolutional network. "" "Def setup (self, bottom, top):" "" setup data layer according to parameters:-Sbdd_dir:path t o sbdd ' DataSet ' dir-split:train/seg11valid-mean:tuple of mean values to Subtract-randomize : Load in Random orDer (Default:true)-Seed:seed for randomization (default:none/current time) for SBDD semantic Segmenta tion. N.b.segv11alid is the set of SEGVAL11, does not intersect with SBDD. Find it here:https://gist.github.com/shelhamer/edb330760338892d511e. Example params = Dict (sbdd_dir= "/path/to/sbdd/dataset", mean= (104.00698793, 116.66876762, 122.67891434), split= "valid") "" "# config params = eval (self.param_str) Self.sbdd_dir = params[' Sbdd_dir '] self.split = params[' split ' Self.mean = Np.array (params[' mean ']) Self.random = Params.get (' Randomize ', True) self.seed = params.get (' seed ', None) # both Tops:data and label if Len (top)! = 2: Raise Exception ("need to define, Tops:data and label.") # data layers has no bottoms if Len (bottom)! = 0:raise Exception ("Do not define a bottom.") # Load indices for IMAGES and Labels Split_f = ' {}/{}.txt '. Format (Self.sbdd_dir, self.split) self.indices = open (s Plit_f, ' R '). Read (). Splitlines () self.idx = 0 # Make eval deterministic if ' train ' isn't in Self.split: Self.random = False # randomization:seed and pick if Self.random:random.seed (SELF.S eed) Self.idx = Random.randint (0, Len (self.indices)-1) def reshape (self, bottom, top): # Load Image + Label image Pair Self.data = Self.load_image (Self.indices[self.idx]) Self.label = Self.load_label (Self.indi CES[SELF.IDX]) # Reshape tops to fit (leading 1 are for batch dimension) Top[0].reshape (1, *self.data.shape) Top[1].reshape (1, *self.label.shape) def forward (self, bottom, top): # Assign Output top[0].data[. ..] = Self.data top[1].data[...] = self.label # pick Next input if Self.random:self.idx = Ran Dom.randint (0, Len (self.indices)-1) Else:self.idx + = 1 if self.idx = Len (self.indices): Self.idx = 0 def backward (self, top, propagate_down, bottom): Pass def load_image (self, idx): "" "Load Input image and preprocess for Caffe:-Cast to Float-switch channels RGB--bgr-subtract mean -Transpose to channel x Height x Width order "" "Im = Image.open (' {}/img/{}.jpg '. Format (self.sbdd_d IR, idx)) In_ = Np.array (IM, dtype=np.float32) in_ = in_[:,:,::-1] in_-= Self.mean In_ = in_. Transpose ((2,0,1)) return In_ def load_label (self, idx): "" "Load label image as 1 x Height x Widt H Integer array of label indices. The leading singleton dimension is required by the loss. "" "Import scipy.io mat = Scipy.io.loadmat (' {}/cls/{}.mat '. Format (Self.sbdd_dir, idx)) label = Mat[' G Tcls '][0][' segmentation '][0].astype (np.uint8) label = Label[np.newaxis, ...] Return label
For the final loss layer:
Layer defined in Prototxt:
Layer { type: ' Python ' #python name: ' Loss ' # loss layer top: ' Loss ' bottom: ' IPX ' bottom: ' Ipy ' python_param { module: ' Pyloss ' # Written in the Pyloss file layer: ' Euclideanlosslayer ' # corresponds to the name of this class } # Set loss weight so Caffe knows the is a loss layer loss_weight:1}
Implementation of the loss layer:
Import Caffeimport NumPy as Npclass Euclideanlosslayer (Caffe. Layer): "" "Compute the Euclidean Loss in the same manner as the C + + Euclideanlosslayer to demonstrate the class Interface for developing layers in Python. "" "Def setup (self, bottom, top): # Top is the last loss, bottom has two values, a network output, and a label. # Check input pair if len (bottom)! = 2:raise Exception ("need, inputs to compute distance.") def reshape (self, bottom, top): # Check input dimensions match if bottom[0].count! = Bottom[1].count: Raise Exception ("Inputs must has the same dimension.") # difference are shape of inputs Self.diff = Np.zeros_like (Bottom[0].data, dtype=np.float32) # loss output is Scalar Top[0].reshape (1) def forward (self, bottom, top): self.diff[...] = Bottom[0].data-bottom[1].dat A top[0].data[...] = np.sum (self.diff**2)/BOTTOM[0].NUM/2. def backward (self, top, propagate_down, bottom): For I in range (2): If not Propagate_down[i]: continue if i = = 0:sign = 1 else:sign = 1 bottom[i].diff[...] = sign * self.diff/bottom[i].num
Python data layer in Caffe