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