Skip to content

Commit 4e38c93

Browse files
committed
add CropLayer for cropping one blob to another using induced coordinates
1 parent 0e781dc commit 4e38c93

File tree

4 files changed

+207
-1
lines changed

4 files changed

+207
-1
lines changed

include/caffe/vision_layers.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,41 @@ class CuDNNPoolingLayer : public PoolingLayer<Dtype> {
453453
};
454454
#endif
455455

456+
template <typename Dtype>
457+
class CropLayer : public Layer<Dtype> {
458+
public:
459+
explicit CropLayer(const LayerParameter& param)
460+
: Layer<Dtype>(param) {}
461+
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
462+
const vector<Blob<Dtype>*>& top);
463+
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
464+
const vector<Blob<Dtype>*>& top);
465+
466+
virtual inline LayerParameter_LayerType type() const {
467+
return LayerParameter_LayerType_CROP;
468+
}
469+
virtual inline int ExactNumBottomBlobs() const { return 2; }
470+
virtual inline int ExactNumTopBlobs() const { return 1; }
471+
virtual inline DiagonalAffineMap<Dtype> coord_map() {
472+
vector<pair<Dtype, Dtype> > coefs;
473+
coefs.push_back(make_pair(1, - crop_h_));
474+
coefs.push_back(make_pair(1, - crop_w_));
475+
return DiagonalAffineMap<Dtype>(coefs);
476+
}
477+
478+
protected:
479+
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
480+
const vector<Blob<Dtype>*>& top);
481+
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
482+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
483+
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
484+
const vector<Blob<Dtype>*>& top);
485+
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
486+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
487+
488+
int crop_h_, crop_w_;
489+
};
490+
456491
} // namespace caffe
457492

458493
#endif // CAFFE_VISION_LAYERS_HPP_

src/caffe/layers/crop_layer.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#include <algorithm>
2+
#include <map>
3+
#include <set>
4+
#include <vector>
5+
6+
#include "caffe/layer.hpp"
7+
#include "caffe/net.hpp"
8+
#include "caffe/vision_layers.hpp"
9+
10+
namespace caffe {
11+
12+
template <typename Dtype>
13+
void CropLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
14+
const vector<Blob<Dtype>*>& top) {
15+
// Construct a map from top blobs to layer inds, skipping over in-place
16+
// connections.
17+
map<Blob<Dtype>*, int> down_map;
18+
for (int layer_ind = 0; layer_ind < this->net_->top_vecs().size();
19+
++layer_ind) {
20+
vector<Blob<Dtype>*> tops = this->net_->top_vecs()[layer_ind];
21+
for (int top_ind = 0; top_ind < tops.size(); ++top_ind) {
22+
if (down_map.find(tops[top_ind]) == down_map.end()) {
23+
down_map[tops[top_ind]] = layer_ind;
24+
}
25+
}
26+
}
27+
// Walk back from the first bottom, keeping track of all the blobs we pass.
28+
set<Blob<Dtype>*> path_blobs;
29+
Blob<Dtype>* blob = bottom[0];
30+
int layer_ind;
31+
// TODO this logic can be simplified if all blobs are tops
32+
path_blobs.insert(blob);
33+
while (down_map.find(blob) != down_map.end()) {
34+
layer_ind = down_map[blob];
35+
if (this->net_->bottom_vecs()[layer_ind].size() == 0) {
36+
break;
37+
}
38+
blob = this->net_->bottom_vecs()[layer_ind][0];
39+
path_blobs.insert(blob);
40+
}
41+
// Now walk back from the second bottom, until we find a blob of intersection.
42+
Blob<Dtype>* inter_blob = bottom[1];
43+
while (path_blobs.find(inter_blob) == path_blobs.end()) {
44+
CHECK(down_map.find(inter_blob) != down_map.end())
45+
<< "Cannot align apparently disconnected blobs.";
46+
layer_ind = down_map[inter_blob];
47+
CHECK_GT(this->net_->bottom_vecs()[layer_ind].size(), 0)
48+
<< "Cannot align apparently disconnected blobs.";
49+
inter_blob = this->net_->bottom_vecs()[layer_ind][0];
50+
}
51+
// Compute the coord map from the blob of intersection to each bottom.
52+
vector<DiagonalAffineMap<Dtype> > coord_maps(2,
53+
DiagonalAffineMap<Dtype>::identity(2));
54+
for (int i = 0; i < 2; ++i) {
55+
for (Blob<Dtype>* blob = bottom[i]; blob != inter_blob;
56+
blob = this->net_->bottom_vecs()[down_map[blob]][0]) {
57+
shared_ptr<Layer<Dtype> > layer = this->net_->layers()[down_map[blob]];
58+
coord_maps[i] = coord_maps[i].compose(layer->coord_map());
59+
}
60+
}
61+
// Compute the mapping from first bottom coordinates to second.
62+
DiagonalAffineMap<Dtype> crop_map =
63+
coord_maps[1].compose(coord_maps[0].inv());
64+
for (int i = 0; i < 2; ++i) {
65+
// Check for scale mismatch (unfortunately, CHECK_DOUBLE_EQ does not
66+
// support a message like the other CHECKs).
67+
CHECK_DOUBLE_EQ(crop_map.coefs()[i].first, 1);
68+
CHECK_LE(crop_map.coefs()[i].second, 0) << "Negative crop width.";
69+
// Check that the crop width is an integer.
70+
CHECK_DOUBLE_EQ(crop_map.coefs()[i].second,
71+
round(crop_map.coefs()[i].second));
72+
}
73+
crop_h_ = - round(crop_map.coefs()[0].second);
74+
crop_w_ = - round(crop_map.coefs()[1].second);
75+
}
76+
77+
template <typename Dtype>
78+
void CropLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
79+
const vector<Blob<Dtype>*>& top) {
80+
top[0]->Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[1]->height(),
81+
bottom[1]->width());
82+
}
83+
84+
template <typename Dtype>
85+
void CropLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
86+
const vector<Blob<Dtype>*>& top) {
87+
const Dtype* bottom_data = bottom[0]->cpu_data();
88+
Dtype* top_data = top[0]->mutable_cpu_data();
89+
for (int n = 0; n < top[0]->num(); ++n) {
90+
for (int c = 0; c < top[0]->channels(); ++c) {
91+
for (int h = 0; h < top[0]->height(); ++h) {
92+
caffe_copy(top[0]->width(),
93+
bottom_data + bottom[0]->offset(n, c, crop_h_ + h, crop_w_),
94+
top_data + top[0]->offset(n, c, h));
95+
}
96+
}
97+
}
98+
}
99+
100+
template <typename Dtype>
101+
void CropLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
102+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
103+
const Dtype* top_diff = top[0]->cpu_diff();
104+
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
105+
if (propagate_down[0]) {
106+
caffe_set(bottom[0]->count(), static_cast<Dtype>(0), bottom_diff);
107+
for (int n = 0; n < top[0]->num(); ++n) {
108+
for (int c = 0; c < top[0]->channels(); ++c) {
109+
for (int h = 0; h < top[0]->height(); ++h) {
110+
caffe_copy(top[0]->width(),
111+
top_diff + top[0]->offset(n, c, h),
112+
bottom_diff + bottom[0]->offset(n, c, crop_h_ + h, crop_w_));
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
#ifdef CPU_ONLY
120+
STUB_GPU(CropLayer);
121+
#endif
122+
123+
INSTANTIATE_CLASS(CropLayer);
124+
REGISTER_LAYER_CLASS(CROP, CropLayer);
125+
126+
} // namespace caffe

src/caffe/layers/crop_layer.cu

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <vector>
2+
3+
#include "caffe/vision_layers.hpp"
4+
5+
namespace caffe {
6+
7+
template <typename Dtype>
8+
void CropLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
9+
const vector<Blob<Dtype>*>& top) {
10+
const Dtype* bottom_data = bottom[0]->gpu_data();
11+
Dtype* top_data = top[0]->mutable_gpu_data();
12+
for (int n = 0; n < top[0]->num(); ++n) {
13+
for (int c = 0; c < top[0]->channels(); ++c) {
14+
for (int h = 0; h < top[0]->height(); ++h) {
15+
caffe_copy(top[0]->width(),
16+
bottom_data + bottom[0]->offset(n, c, crop_h_ + h, crop_w_),
17+
top_data + top[0]->offset(n, c, h));
18+
}
19+
}
20+
}
21+
}
22+
23+
template <typename Dtype>
24+
void CropLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
25+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
26+
const Dtype* top_diff = top[0]->gpu_diff();
27+
Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();
28+
if (propagate_down[0]) {
29+
caffe_gpu_set(bottom[0]->count(), static_cast<Dtype>(0), bottom_diff);
30+
for (int n = 0; n < top[0]->num(); ++n) {
31+
for (int c = 0; c < top[0]->channels(); ++c) {
32+
for (int h = 0; h < top[0]->height(); ++h) {
33+
caffe_copy(top[0]->width(),
34+
top_diff + top[0]->offset(n, c, h),
35+
bottom_diff + bottom[0]->offset(n, c, crop_h_ + h, crop_w_));
36+
}
37+
}
38+
}
39+
}
40+
}
41+
42+
INSTANTIATE_LAYER_GPU_FUNCS(CropLayer);
43+
44+
} // namespace caffe

src/caffe/proto/caffe.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ message LayerParameter {
227227
// line above the enum. Update the next available ID when you add a new
228228
// LayerType.
229229
//
230-
// LayerType next available ID: 40 (last added: DECONVOLUTION)
230+
// LayerType next available ID: 41 (last added: CROP)
231231
enum LayerType {
232232
// "NONE" layer type is 0th enum element so that we don't cause confusion
233233
// by defaulting to an existent LayerType (instead, should usually error if
@@ -240,6 +240,7 @@ message LayerParameter {
240240
CONCAT = 3;
241241
CONTRASTIVE_LOSS = 37;
242242
CONVOLUTION = 4;
243+
CROP = 40;
243244
DATA = 5;
244245
DECONVOLUTION = 39;
245246
DROPOUT = 6;

0 commit comments

Comments
 (0)