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     doffset = pd.get(13, 0);
30     coffset = pd.get(2, 0);
31     outw = pd.get(3, 0);
32     outh = pd.get(4, 0);
33     outd = pd.get(14, 0);
34     outc = pd.get(5, 0);
35     woffset2 = pd.get(6, 0);
36     hoffset2 = pd.get(7, 0);
37     doffset2 = pd.get(15, 0);
38     coffset2 = pd.get(8, 0);
39 
40     starts = pd.get(9, Mat());
41     ends = pd.get(10, Mat());
42     axes = pd.get(11, Mat());
43 
44     bool numpy_style_slice = !starts.empty() && !ends.empty();
45 
46     if (outw == 0 && outh == 0 && outd == 0 && outc == 0 && woffset2 == 0 && hoffset2 == 0 && doffset2 == 0 && coffset2 == 0 && !numpy_style_slice)
47     {
48         one_blob_only = false;
49     }
50 
51     return 0;
52 }
53 
54 template<typename T>
copy_cut_border_image(const Mat & src,Mat & dst,int top,int left)55 static void copy_cut_border_image(const Mat& src, Mat& dst, int top, int left)
56 {
57     int w = dst.w;
58     int h = dst.h;
59 
60     const T* ptr = src.row<T>(top) + left;
61     T* outptr = dst; //.data;
62 
63     for (int y = 0; y < h; y++)
64     {
65         if (w < 12)
66         {
67             for (int x = 0; x < w; x++)
68             {
69                 outptr[x] = ptr[x];
70             }
71         }
72         else
73         {
74             memcpy(outptr, ptr, w * sizeof(T));
75         }
76         outptr += w;
77         ptr += src.w;
78     }
79 }
80 
forward(const Mat & bottom_blob,Mat & top_blob,const Option & opt) const81 int Crop::forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
82 {
83     int w = bottom_blob.w;
84     int h = bottom_blob.h;
85     int d = bottom_blob.d;
86     int channels = bottom_blob.c;
87     int dims = bottom_blob.dims;
88     size_t elemsize = bottom_blob.elemsize;
89 
90     int _woffset, _hoffset, _doffset, _coffset;
91     int _outw = -1, _outh = -1, _outd = -1, _outc;
92     resolve_crop_roi(bottom_blob.shape(), _woffset, _hoffset, _doffset, _coffset, _outw, _outh, _outd, _outc);
93 
94     if (dims == 1)
95     {
96         if (_outw == w)
97         {
98             top_blob = bottom_blob;
99             return 0;
100         }
101 
102         top_blob.create(_outw, elemsize, opt.blob_allocator);
103         if (top_blob.empty())
104             return -100;
105 
106         if (elemsize == 1)
107             copy_cut_border_image<signed char>(bottom_blob, top_blob, 0, _woffset);
108         if (elemsize == 2)
109             copy_cut_border_image<unsigned short>(bottom_blob, top_blob, 0, _woffset);
110         if (elemsize == 4)
111             copy_cut_border_image<float>(bottom_blob, top_blob, 0, _woffset);
112 
113         return 0;
114     }
115 
116     if (dims == 2)
117     {
118         if (_outw == w && _outh == h)
119         {
120             top_blob = bottom_blob;
121             return 0;
122         }
123 
124         top_blob.create(_outw, _outh, elemsize, opt.blob_allocator);
125         if (top_blob.empty())
126             return -100;
127 
128         if (elemsize == 1)
129             copy_cut_border_image<signed char>(bottom_blob, top_blob, _hoffset, _woffset);
130         if (elemsize == 2)
131             copy_cut_border_image<unsigned short>(bottom_blob, top_blob, _hoffset, _woffset);
132         if (elemsize == 4)
133             copy_cut_border_image<float>(bottom_blob, top_blob, _hoffset, _woffset);
134 
135         return 0;
136     }
137 
138     if (dims == 3)
139     {
140         if (_outw == w && _outh == h && _outc == channels)
141         {
142             top_blob = bottom_blob;
143             return 0;
144         }
145 
146         const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
147 
148         if (_outw == w && _outh == h)
149         {
150             top_blob = bottom_blob_sliced.clone();
151             if (top_blob.empty())
152                 return -100;
153 
154             return 0;
155         }
156 
157         top_blob.create(_outw, _outh, _outc, elemsize, opt.blob_allocator);
158         if (top_blob.empty())
159             return -100;
160 
161         #pragma omp parallel for num_threads(opt.num_threads)
162         for (int q = 0; q < _outc; q++)
163         {
164             const Mat m = bottom_blob_sliced.channel(q);
165             Mat borderm = top_blob.channel(q);
166 
167             if (elemsize == 1)
168                 copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
169             if (elemsize == 2)
170                 copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
171             if (elemsize == 4)
172                 copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
173         }
174 
175         return 0;
176     }
177 
178     if (dims == 4)
179     {
180         if (_outw == w && _outh == h && _outd == d && _outc == channels)
181         {
182             top_blob = bottom_blob;
183             return 0;
184         }
185 
186         const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
187 
188         if (_outw == w && _outh == h && _outd == d)
189         {
190             top_blob = bottom_blob_sliced.clone();
191             if (top_blob.empty())
192                 return -100;
193 
194             return 0;
195         }
196 
197         top_blob.create(_outw, _outh, _outd, _outc, elemsize, opt.blob_allocator);
198         if (top_blob.empty())
199             return -100;
200 
201         #pragma omp parallel for num_threads(opt.num_threads)
202         for (int q = 0; q < _outc; q++)
203         {
204             for (int z = 0; z < _outd; z++)
205             {
206                 const Mat m = bottom_blob_sliced.channel(q).depth(z + _doffset);
207                 Mat borderm = top_blob.channel(q).depth(z);
208 
209                 if (elemsize == 1)
210                     copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
211                 if (elemsize == 2)
212                     copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
213                 if (elemsize == 4)
214                     copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
215             }
216         }
217 
218         return 0;
219     }
220 
221     return 0;
222 }
223 
forward(const std::vector<Mat> & bottom_blobs,std::vector<Mat> & top_blobs,const Option & opt) const224 int Crop::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const
225 {
226     const Mat& bottom_blob = bottom_blobs[0];
227     const Mat& reference_blob = bottom_blobs[1];
228 
229     int w = bottom_blob.w;
230     int h = bottom_blob.h;
231     int d = bottom_blob.d;
232     int channels = bottom_blob.c;
233     int dims = bottom_blob.dims;
234     size_t elemsize = bottom_blob.elemsize;
235 
236     Mat& top_blob = top_blobs[0];
237 
238     int _woffset, _hoffset, _doffset, _coffset = -1;
239     int _outw = -1, _outh = -1, _outd = -1, _outc;
240     if (woffset == -233)
241     {
242         resolve_crop_roi(bottom_blob.shape(), (const int*)reference_blob, _woffset, _hoffset, _doffset, _coffset, _outw, _outh, _outd, _outc);
243     }
244     else
245     {
246         resolve_crop_roi(bottom_blob.shape(), reference_blob.shape(), _woffset, _hoffset, _doffset, _coffset, _outw, _outh, _outd, _outc);
247     }
248 
249     if (dims == 1)
250     {
251         if (_outw == w)
252         {
253             top_blob = bottom_blob;
254             return 0;
255         }
256 
257         top_blob.create(_outw, elemsize, opt.blob_allocator);
258         if (top_blob.empty())
259             return -100;
260 
261         if (elemsize == 1)
262             copy_cut_border_image<signed char>(bottom_blob, top_blob, 0, _woffset);
263         if (elemsize == 2)
264             copy_cut_border_image<unsigned short>(bottom_blob, top_blob, 0, _woffset);
265         if (elemsize == 4)
266             copy_cut_border_image<float>(bottom_blob, top_blob, 0, _woffset);
267 
268         return 0;
269     }
270 
271     if (dims == 2)
272     {
273         if (_outw == w && _outh == h)
274         {
275             top_blob = bottom_blob;
276             return 0;
277         }
278 
279         top_blob.create(_outw, _outh, elemsize, opt.blob_allocator);
280         if (top_blob.empty())
281             return -100;
282 
283         if (elemsize == 1)
284             copy_cut_border_image<signed char>(bottom_blob, top_blob, _hoffset, _woffset);
285         if (elemsize == 2)
286             copy_cut_border_image<unsigned short>(bottom_blob, top_blob, _hoffset, _woffset);
287         if (elemsize == 4)
288             copy_cut_border_image<float>(bottom_blob, top_blob, _hoffset, _woffset);
289 
290         return 0;
291     }
292 
293     if (dims == 3)
294     {
295         if (_outw == w && _outh == h && _outc == channels)
296         {
297             top_blob = bottom_blob;
298             return 0;
299         }
300 
301         const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
302 
303         if (_outw == w && _outh == h)
304         {
305             top_blob = bottom_blob_sliced.clone();
306             if (top_blob.empty())
307                 return -100;
308 
309             return 0;
310         }
311 
312         top_blob.create(_outw, _outh, _outc, elemsize, opt.blob_allocator);
313         if (top_blob.empty())
314             return -100;
315 
316         #pragma omp parallel for num_threads(opt.num_threads)
317         for (int q = 0; q < _outc; q++)
318         {
319             const Mat m = bottom_blob_sliced.channel(q);
320             Mat borderm = top_blob.channel(q);
321 
322             if (elemsize == 1)
323                 copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
324             if (elemsize == 2)
325                 copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
326             if (elemsize == 4)
327                 copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
328         }
329 
330         return 0;
331     }
332 
333     if (dims == 4)
334     {
335         if (_outw == w && _outh == h && _outd == d && _outc == channels)
336         {
337             top_blob = bottom_blob;
338             return 0;
339         }
340 
341         const Mat bottom_blob_sliced = bottom_blob.channel_range(_coffset, _outc);
342 
343         if (_outw == w && _outh == h && _outd == d)
344         {
345             top_blob = bottom_blob_sliced.clone();
346             if (top_blob.empty())
347                 return -100;
348 
349             return 0;
350         }
351 
352         top_blob.create(_outw, _outh, _outd, _outc, elemsize, opt.blob_allocator);
353         if (top_blob.empty())
354             return -100;
355 
356         #pragma omp parallel for num_threads(opt.num_threads)
357         for (int q = 0; q < _outc; q++)
358         {
359             for (int z = 0; z < _outd; z++)
360             {
361                 const Mat m = bottom_blob_sliced.channel(q).depth(z + _doffset);
362                 Mat borderm = top_blob.channel(q).depth(z);
363 
364                 if (elemsize == 1)
365                     copy_cut_border_image<signed char>(m, borderm, _hoffset, _woffset);
366                 if (elemsize == 2)
367                     copy_cut_border_image<unsigned short>(m, borderm, _hoffset, _woffset);
368                 if (elemsize == 4)
369                     copy_cut_border_image<float>(m, borderm, _hoffset, _woffset);
370             }
371         }
372 
373         return 0;
374     }
375 
376     return 0;
377 }
378 
resolve_crop_roi(const Mat & bottom_blob,int & _woffset,int & _hoffset,int & _doffset,int & _coffset,int & _outw,int & _outh,int & _outd,int & _outc) const379 void Crop::resolve_crop_roi(const Mat& bottom_blob, int& _woffset, int& _hoffset, int& _doffset, int& _coffset, int& _outw, int& _outh, int& _outd, int& _outc) const
380 {
381     int w = bottom_blob.w;
382     int h = bottom_blob.h;
383     int d = bottom_blob.d;
384     int channels = bottom_blob.c;
385     int dims = bottom_blob.dims;
386 
387     bool numpy_style_slice = !starts.empty() && !ends.empty();
388     if (numpy_style_slice)
389     {
390         _woffset = 0;
391         _hoffset = 0;
392         _doffset = 0;
393         _coffset = 0;
394         _outw = w;
395         _outh = h;
396         _outd = d;
397         _outc = channels;
398 
399         const int* starts_ptr = starts;
400         const int* ends_ptr = ends;
401         const int* axes_ptr = axes;
402 
403         int _axes[4] = {0, 1, 2, 3};
404         int num_axis = axes.w;
405         if (num_axis == 0)
406         {
407             num_axis = dims;
408         }
409         else
410         {
411             for (int i = 0; i < num_axis; i++)
412             {
413                 int axis = axes_ptr[i];
414                 if (axis < 0)
415                     axis = dims + axis;
416                 _axes[i] = axis;
417             }
418         }
419 
420         for (int i = 0; i < num_axis; i++)
421         {
422             int axis = _axes[i];
423             int start = starts_ptr[i];
424             int end = ends_ptr[i];
425 
426             if (dims == 1) // axis == 0
427             {
428                 if (start == -233) start = 0;
429                 if (end == -233) end = w;
430                 _woffset = start >= 0 ? start : w + start;
431                 _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
432             }
433             if (dims == 2)
434             {
435                 if (axis == 0)
436                 {
437                     if (start == -233) start = 0;
438                     if (end == -233) end = h;
439                     _hoffset = start >= 0 ? start : h + start;
440                     _outh = std::min(h, end > 0 ? end : h + end) - _hoffset;
441                 }
442                 if (axis == 1)
443                 {
444                     if (start == -233) start = 0;
445                     if (end == -233) end = w;
446                     _woffset = start >= 0 ? start : w + start;
447                     _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
448                 }
449             }
450             if (dims == 3)
451             {
452                 if (axis == 0)
453                 {
454                     if (start == -233) start = 0;
455                     if (end == -233) end = channels;
456                     _coffset = start >= 0 ? start : channels + start;
457                     _outc = std::min(channels, end > 0 ? end : channels + end) - _coffset;
458                 }
459                 if (axis == 1)
460                 {
461                     if (start == -233) start = 0;
462                     if (end == -233) end = h;
463                     _hoffset = start >= 0 ? start : h + start;
464                     _outh = std::min(h, end > 0 ? end : h + end) - _hoffset;
465                 }
466                 if (axis == 2)
467                 {
468                     if (start == -233) start = 0;
469                     if (end == -233) end = w;
470                     _woffset = start >= 0 ? start : w + start;
471                     _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
472                 }
473             }
474             if (dims == 4)
475             {
476                 if (axis == 0)
477                 {
478                     if (start == -233) start = 0;
479                     if (end == -233) end = channels;
480                     _coffset = start >= 0 ? start : channels + start;
481                     _outc = std::min(channels, end > 0 ? end : channels + end) - _coffset;
482                 }
483                 if (axis == 1)
484                 {
485                     if (start == -233) start = 0;
486                     if (end == -233) end = d;
487                     _doffset = start >= 0 ? start : d + start;
488                     _outd = std::min(d, end > 0 ? end : d + end) - _doffset;
489                 }
490                 if (axis == 2)
491                 {
492                     if (start == -233) start = 0;
493                     if (end == -233) end = h;
494                     _hoffset = start >= 0 ? start : h + start;
495                     _outh = std::min(h, end > 0 ? end : h + end) - _hoffset;
496                 }
497                 if (axis == 3)
498                 {
499                     if (start == -233) start = 0;
500                     if (end == -233) end = w;
501                     _woffset = start >= 0 ? start : w + start;
502                     _outw = std::min(w, end > 0 ? end : w + end) - _woffset;
503                 }
504             }
505         }
506     }
507     else
508     {
509         _woffset = woffset;
510         _hoffset = hoffset;
511         _doffset = doffset;
512         _coffset = coffset;
513         _outw = w;
514         _outh = h;
515         _outd = d;
516         _outc = channels;
517 
518         if (dims == 1)
519         {
520             _outw = w - woffset - woffset2;
521             if (outw != -233)
522                 _outw = std::min(outw, _outw);
523         }
524         if (dims == 2)
525         {
526             _outw = w - woffset - woffset2;
527             if (outw != -233)
528                 _outw = std::min(outw, _outw);
529 
530             _outh = h - hoffset - hoffset2;
531             if (outh != -233)
532                 _outh = std::min(outh, _outh);
533         }
534         if (dims == 3)
535         {
536             _outw = w - woffset - woffset2;
537             if (outw != -233)
538                 _outw = std::min(outw, _outw);
539 
540             _outh = h - hoffset - hoffset2;
541             if (outh != -233)
542                 _outh = std::min(outh, _outh);
543 
544             _outc = channels - coffset - coffset2;
545             if (outc != -233)
546                 _outc = std::min(outc, _outc);
547         }
548         if (dims == 4)
549         {
550             _outw = w - woffset - woffset2;
551             if (outw != -233)
552                 _outw = std::min(outw, _outw);
553 
554             _outh = h - hoffset - hoffset2;
555             if (outh != -233)
556                 _outh = std::min(outh, _outh);
557 
558             _outd = d - doffset - doffset2;
559             if (outd != -233)
560                 _outd = std::min(outd, _outd);
561 
562             _outc = channels - coffset - coffset2;
563             if (outc != -233)
564                 _outc = std::min(outc, _outc);
565         }
566     }
567 }
568 
resolve_crop_roi(const Mat & bottom_blob,const Mat & reference_blob,int & _woffset,int & _hoffset,int & _doffset,int & _coffset,int & _outw,int & _outh,int & _outd,int & _outc) const569 void Crop::resolve_crop_roi(const Mat& bottom_blob, const Mat& reference_blob, int& _woffset, int& _hoffset, int& _doffset, int& _coffset, int& _outw, int& _outh, int& _outd, int& _outc) const
570 {
571     int channels = bottom_blob.c;
572     int dims = bottom_blob.dims;
573 
574     int ref_w = reference_blob.w;
575     int ref_h = reference_blob.h;
576     int ref_d = reference_blob.d;
577     int ref_channels = reference_blob.c;
578     int ref_dims = reference_blob.dims;
579 
580     if (dims == 1)
581     {
582         _woffset = woffset;
583         _outw = ref_w;
584     }
585     if (dims == 2)
586     {
587         _woffset = woffset;
588         _hoffset = hoffset;
589         _outw = ref_w;
590         _outh = ref_h;
591     }
592     if (dims == 3)
593     {
594         _woffset = woffset;
595         _hoffset = hoffset;
596         _coffset = coffset;
597         _outw = ref_w;
598         _outh = ref_h;
599         _outc = ref_dims == 3 ? ref_channels : channels;
600     }
601     if (dims == 4)
602     {
603         _woffset = woffset;
604         _hoffset = hoffset;
605         _doffset = doffset;
606         _coffset = coffset;
607         _outw = ref_w;
608         _outh = ref_h;
609         _outd = ref_d;
610         _outc = ref_dims == 4 ? ref_channels : channels;
611     }
612 }
613 
resolve_crop_roi(const Mat & bottom_blob,const int * param_data,int & _woffset,int & _hoffset,int & _doffset,int & _coffset,int & _outw,int & _outh,int & _outd,int & _outc) const614 void Crop::resolve_crop_roi(const Mat& bottom_blob, const int* param_data, int& _woffset, int& _hoffset, int& _doffset, int& _coffset, int& _outw, int& _outh, int& _outd, int& _outc) const
615 {
616     int dims = bottom_blob.dims;
617 
618     if (dims == 1)
619     {
620         _woffset = param_data[0];
621         _outw = param_data[3];
622     }
623     if (dims == 2)
624     {
625         _woffset = param_data[0];
626         _hoffset = param_data[1];
627         _outw = param_data[3];
628         _outh = param_data[4];
629     }
630     if (dims == 3)
631     {
632         _woffset = param_data[0];
633         _hoffset = param_data[1];
634         _coffset = param_data[2];
635         _outw = param_data[3];
636         _outh = param_data[4];
637         _outc = param_data[5];
638     }
639     if (dims == 4)
640     {
641         _woffset = param_data[0];
642         _hoffset = param_data[1];
643         _doffset = param_data[2];
644         _coffset = param_data[3];
645         _outw = param_data[4];
646         _outh = param_data[5];
647         _outd = param_data[6];
648         _outc = param_data[7];
649     }
650 }
651 
652 } // namespace ncnn
653