1 // Tencent is pleased to support the open source community by making ncnn available.
2 //
3 // Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
4 //
5 // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // https://opensource.org/licenses/BSD-3-Clause
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14
15 #include "crop.h"
16
17 namespace ncnn {
18
Crop()19 Crop::Crop()
20 {
21 one_blob_only = true;
22 support_inplace = false;
23 }
24
load_param(const ParamDict & pd)25 int Crop::load_param(const ParamDict& pd)
26 {
27 woffset = pd.get(0, 0);
28 hoffset = pd.get(1, 0);
29 coffset = pd.get(2, 0);
30 outw = pd.get(3, 0);
31 outh = pd.get(4, 0);
32 outc = pd.get(5, 0);
33 woffset2 = pd.get(6, 0);
34 hoffset2 = pd.get(7, 0);
35 coffset2 = pd.get(8, 0);
36
37 starts = pd.get(9, Mat());
38 ends = pd.get(10, Mat());
39 axes = pd.get(11, Mat());
40
41 bool numpy_style_slice = !starts.empty() && !ends.empty();
42
43 if (outw == 0 && outh == 0 && outc == 0 && woffset2 == 0 && hoffset2 == 0 && coffset2 == 0 && !numpy_style_slice)
44 {
45 one_blob_only = false;
46 }
47
48 return 0;
49 }
50
51 template<typename T>
copy_cut_border_image(const Mat & src,Mat & dst,int top,int left)52 static void copy_cut_border_image(const Mat& src, Mat& dst, int top, int left)
53 {
54 int w = dst.w;
55 int h = dst.h;
56
57 const T* ptr = src.row<T>(top) + left;
58 T* outptr = dst; //.data;
59
60 for (int y = 0; y < h; y++)
61 {
62 if (w < 12)
63 {
64 for (int x = 0; x < w; x++)
65 {
66 outptr[x] = ptr[x];
67 }
68 }
69 else
70 {
71 memcpy(outptr, ptr, w * sizeof(T));
72 }
73 outptr += w;
74 ptr += src.w;
75 }
76 }
77
forward(const Mat & bottom_blob,Mat & top_blob,const Option & opt) const78 int Crop::forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
79 {
80 int w = bottom_blob.w;
81 int h = bottom_blob.h;
82 int channels = bottom_blob.c;
83 int dims = bottom_blob.dims;
84 size_t elemsize = bottom_blob.elemsize;
85
86 int _woffset, _hoffset, _coffset;
87 int _outw = -1, _outh = -1, _outc;
88 resolve_crop_roi(bottom_blob.shape(), _woffset, _hoffset, _coffset, _outw, _outh, _outc);
89
90 if (dims == 1)
91 {
92 if (_outw == w)
93 {
94 top_blob = bottom_blob;
95 return 0;
96 }
97
98 top_blob.create(_outw, elemsize, opt.blob_allocator);
99 if (top_blob.empty())
100 return -100;
101
102 if (elemsize == 1)
103 copy_cut_border_image<signed char>(bottom_blob, top_blob, 0, _woffset);
104 if (elemsize == 2)
105 copy_cut_border_image<unsigned short>(bottom_blob, top_blob, 0, _woffset);
106 if (elemsize == 4)
107 copy_cut_border_image<float>(bottom_blob, top_blob, 0, _woffset);
108
109 return 0;
110 }
111
112 if (dims == 2)
113 {
114 if (_outw == w && _outh == h)
115 {
116 top_blob = bottom_blob;
117 return 0;
118 }
119
120 top_blob.create(_outw, _outh, elemsize, opt.blob_allocator);
121 if (top_blob.empty())
122 return -100;
123
124 if (elemsize == 1)
125 copy_cut_border_image<signed char>(bottom_blob, top_blob, _hoffset, _woffset);
126 if (elemsize == 2)
127 copy_cut_border_image<unsigned short>(bottom_blob, top_blob, _hoffset, _woffset);
128 if (elemsize == 4)
129 copy_cut_border_image<float>(bottom_blob, top_blob, _hoffset, _woffset);
130
131 return 0;
132 }
133
134 if (dims == 3)
135 {
136 if (_outw == w && _outh == h && _outc == channels)
137 {
138 top_blob = bottom_blob;
139 return 0;
140 }
141
142 const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
143
144 if (_outw == w && _outh == h)
145 {
146 top_blob = bottom_blob_sliced.clone();
147 if (top_blob.empty())
148 return -100;
149
150 return 0;
151 }
152
153 top_blob.create(_outw, _outh, _outc, elemsize, opt.blob_allocator);
154 if (top_blob.empty())
155 return -100;
156
157 #pragma omp parallel for num_threads(opt.num_threads)
158 for (int q = 0; q < _outc; q++)
159 {
160 const Mat m = bottom_blob_sliced.channel(q);
161 Mat borderm = top_blob.channel(q);
162
163 if (elemsize == 1)
164 copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
165 if (elemsize == 2)
166 copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
167 if (elemsize == 4)
168 copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
169 }
170
171 return 0;
172 }
173
174 return 0;
175 }
176
forward(const std::vector<Mat> & bottom_blobs,std::vector<Mat> & top_blobs,const Option & opt) const177 int Crop::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const
178 {
179 const Mat& bottom_blob = bottom_blobs[0];
180 const Mat& reference_blob = bottom_blobs[1];
181
182 int w = bottom_blob.w;
183 int h = bottom_blob.h;
184 int channels = bottom_blob.c;
185 int dims = bottom_blob.dims;
186 size_t elemsize = bottom_blob.elemsize;
187
188 Mat& top_blob = top_blobs[0];
189
190 int _woffset, _hoffset, _coffset = -1;
191 int _outw = -1, _outh = -1, _outc;
192 if (woffset == -233)
193 {
194 resolve_crop_roi(bottom_blob.shape(), (const int*)reference_blob, _woffset, _hoffset, _coffset, _outw, _outh, _outc);
195 }
196 else
197 {
198 resolve_crop_roi(bottom_blob.shape(), reference_blob.shape(), _woffset, _hoffset, _coffset, _outw, _outh, _outc);
199 }
200
201 if (dims == 1)
202 {
203 if (_outw == w)
204 {
205 top_blob = bottom_blob;
206 return 0;
207 }
208
209 top_blob.create(_outw, elemsize, opt.blob_allocator);
210 if (top_blob.empty())
211 return -100;
212
213 if (elemsize == 1)
214 copy_cut_border_image<signed char>(bottom_blob, top_blob, 0, _woffset);
215 if (elemsize == 2)
216 copy_cut_border_image<unsigned short>(bottom_blob, top_blob, 0, _woffset);
217 if (elemsize == 4)
218 copy_cut_border_image<float>(bottom_blob, top_blob, 0, _woffset);
219
220 return 0;
221 }
222
223 if (dims == 2)
224 {
225 if (_outw == w && _outh == h)
226 {
227 top_blob = bottom_blob;
228 return 0;
229 }
230
231 top_blob.create(_outw, _outh, elemsize, opt.blob_allocator);
232 if (top_blob.empty())
233 return -100;
234
235 if (elemsize == 1)
236 copy_cut_border_image<signed char>(bottom_blob, top_blob, _hoffset, _woffset);
237 if (elemsize == 2)
238 copy_cut_border_image<unsigned short>(bottom_blob, top_blob, _hoffset, _woffset);
239 if (elemsize == 4)
240 copy_cut_border_image<float>(bottom_blob, top_blob, _hoffset, _woffset);
241
242 return 0;
243 }
244
245 if (dims == 3)
246 {
247 if (_outw == w && _outh == h && _outc == channels)
248 {
249 top_blob = bottom_blob;
250 return 0;
251 }
252
253 const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
254
255 if (_outw == w && _outh == h)
256 {
257 top_blob = bottom_blob_sliced.clone();
258 if (top_blob.empty())
259 return -100;
260
261 return 0;
262 }
263
264 top_blob.create(_outw, _outh, _outc, elemsize, opt.blob_allocator);
265 if (top_blob.empty())
266 return -100;
267
268 #pragma omp parallel for num_threads(opt.num_threads)
269 for (int q = 0; q < _outc; q++)
270 {
271 const Mat m = bottom_blob_sliced.channel(q);
272 Mat borderm = top_blob.channel(q);
273
274 if (elemsize == 1)
275 copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
276 if (elemsize == 2)
277 copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
278 if (elemsize == 4)
279 copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
280 }
281
282 return 0;
283 }
284
285 return 0;
286 }
287
resolve_crop_roi(const Mat & bottom_blob,int & _woffset,int & _hoffset,int & _coffset,int & _outw,int & _outh,int & _outc) const288 void Crop::resolve_crop_roi(const Mat& bottom_blob, int& _woffset, int& _hoffset, int& _coffset, int& _outw, int& _outh, int& _outc) const
289 {
290 int w = bottom_blob.w;
291 int h = bottom_blob.h;
292 int channels = bottom_blob.c;
293 int dims = bottom_blob.dims;
294
295 bool numpy_style_slice = !starts.empty() && !ends.empty();
296 if (numpy_style_slice)
297 {
298 _woffset = 0;
299 _hoffset = 0;
300 _coffset = 0;
301 _outw = w;
302 _outh = h;
303 _outc = channels;
304
305 const int* starts_ptr = starts;
306 const int* ends_ptr = ends;
307 const int* axes_ptr = axes;
308
309 int _axes[3] = {0, 1, 2};
310 int num_axis = axes.w;
311 if (num_axis == 0)
312 {
313 num_axis = dims;
314 }
315 else
316 {
317 for (int i = 0; i < num_axis; i++)
318 {
319 int axis = axes_ptr[i];
320 if (axis < 0)
321 axis = dims + axis;
322 _axes[i] = axis;
323 }
324 }
325
326 for (int i = 0; i < num_axis; i++)
327 {
328 int axis = _axes[i];
329 int start = starts_ptr[i];
330 int end = ends_ptr[i];
331
332 if (dims == 1) // axis == 0
333 {
334 if (start == -233) start = 0;
335 if (end == -233) end = w;
336 _woffset = start >= 0 ? start : w + start;
337 _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
338 }
339 if (dims == 2)
340 {
341 if (axis == 0)
342 {
343 if (start == -233) start = 0;
344 if (end == -233) end = h;
345 _hoffset = start >= 0 ? start : h + start;
346 _outh = std::min(h, end > 0 ? end : h + end) - _hoffset;
347 }
348 if (axis == 1)
349 {
350 if (start == -233) start = 0;
351 if (end == -233) end = w;
352 _woffset = start >= 0 ? start : w + start;
353 _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
354 }
355 }
356 if (dims == 3)
357 {
358 if (axis == 0)
359 {
360 if (start == -233) start = 0;
361 if (end == -233) end = channels;
362 _coffset = start >= 0 ? start : channels + start;
363 _outc = std::min(channels, end > 0 ? end : channels + end) - _coffset;
364 }
365 if (axis == 1)
366 {
367 if (start == -233) start = 0;
368 if (end == -233) end = h;
369 _hoffset = start >= 0 ? start : h + start;
370 _outh = std::min(h, end > 0 ? end : h + end) - _hoffset;
371 }
372 if (axis == 2)
373 {
374 if (start == -233) start = 0;
375 if (end == -233) end = w;
376 _woffset = start >= 0 ? start : w + start;
377 _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
378 }
379 }
380 }
381 }
382 else
383 {
384 _woffset = woffset;
385 _hoffset = hoffset;
386 _coffset = coffset;
387 _outw = w;
388 _outh = h;
389 _outc = channels;
390
391 if (dims == 1)
392 {
393 _outw = w - woffset - woffset2;
394 if (outw != -233)
395 _outw = std::min(outw, _outw);
396 }
397 if (dims == 2)
398 {
399 if (hoffset == -233)
400 {
401 _woffset = 0;
402 _hoffset = woffset;
403
404 _outw = w;
405 _outh = h - woffset - woffset2;
406 if (outw != -233)
407 _outh = std::min(outw, _outh);
408 }
409 else
410 {
411 _outw = w - woffset - woffset2;
412 if (outw != -233)
413 _outw = std::min(outw, _outw);
414
415 _outh = h - hoffset - hoffset2;
416 if (outh != -233)
417 _outh = std::min(outh, _outh);
418 }
419 }
420 if (dims == 3)
421 {
422 if (hoffset == -233 && coffset == -233)
423 {
424 _woffset = 0;
425 _hoffset = 0;
426
427 _outw = w;
428 _outh = h;
429 _outc = channels - woffset - woffset2;
430 if (outw != -233)
431 _outc = std::min(outw, _outc);
432 }
433 else if (coffset == -233)
434 {
435 _woffset = 0;
436
437 _outw = w;
438 _outh = h - woffset - woffset2;
439 if (outw != -233)
440 _outh = std::min(outw, _outh);
441
442 _outc = channels - hoffset - hoffset2;
443 if (outh != -233)
444 _outc = std::min(outh, _outc);
445 }
446 else
447 {
448 _outw = w - woffset - woffset2;
449 if (outw != -233)
450 _outw = std::min(outw, _outw);
451
452 _outh = h - hoffset - hoffset2;
453 if (outh != -233)
454 _outh = std::min(outh, _outh);
455
456 _outc = channels - coffset - coffset2;
457 if (outc != -233)
458 _outc = std::min(outc, _outc);
459 }
460 }
461 }
462 }
463
resolve_crop_roi(const Mat & bottom_blob,const Mat & reference_blob,int & _woffset,int & _hoffset,int & _coffset,int & _outw,int & _outh,int & _outc) const464 void Crop::resolve_crop_roi(const Mat& bottom_blob, const Mat& reference_blob, int& _woffset, int& _hoffset, int& _coffset, int& _outw, int& _outh, int& _outc) const
465 {
466 int channels = bottom_blob.c;
467 int dims = bottom_blob.dims;
468
469 int ref_w = reference_blob.w;
470 int ref_h = reference_blob.h;
471 int ref_channels = reference_blob.c;
472 int ref_dims = reference_blob.dims;
473
474 if (dims == 1)
475 {
476 _woffset = woffset;
477 _outw = ref_w;
478 }
479 if (dims == 2)
480 {
481 _woffset = woffset;
482 _hoffset = hoffset;
483 _outw = ref_w;
484 _outh = ref_h;
485 }
486 if (dims == 3)
487 {
488 _woffset = woffset;
489 _hoffset = hoffset;
490 _coffset = coffset;
491 _outw = ref_w;
492 _outh = ref_h;
493 _outc = ref_dims == 3 ? ref_channels : channels;
494 }
495 }
496
resolve_crop_roi(const Mat & bottom_blob,const int * param_data,int & _woffset,int & _hoffset,int & _coffset,int & _outw,int & _outh,int & _outc) const497 void Crop::resolve_crop_roi(const Mat& bottom_blob, const int* param_data, int& _woffset, int& _hoffset, int& _coffset, int& _outw, int& _outh, int& _outc) const
498 {
499 int dims = bottom_blob.dims;
500
501 if (dims == 1)
502 {
503 _woffset = param_data[0];
504 _outw = param_data[3];
505 }
506 if (dims == 2)
507 {
508 _woffset = param_data[0];
509 _hoffset = param_data[1];
510 _outw = param_data[3];
511 _outh = param_data[4];
512 }
513 if (dims == 3)
514 {
515 _woffset = param_data[0];
516 _hoffset = param_data[1];
517 _coffset = param_data[2];
518 _outw = param_data[3];
519 _outh = param_data[4];
520 _outc = param_data[5];
521 }
522 }
523
524 } // namespace ncnn
525