1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4 
5 
6 #include "precomp.hpp"
7 #include "opencl_kernels_core.hpp"
8 #include "convert.hpp"
9 
10 /****************************************************************************************\
11 *                       Generalized split/merge: mixing channels                         *
12 \****************************************************************************************/
13 
14 namespace cv
15 {
16 
17 template<typename T> static void
mixChannels_(const T ** src,const int * sdelta,T ** dst,const int * ddelta,int len,int npairs)18 mixChannels_( const T** src, const int* sdelta,
19               T** dst, const int* ddelta,
20               int len, int npairs )
21 {
22     int i, k;
23     for( k = 0; k < npairs; k++ )
24     {
25         const T* s = src[k];
26         T* d = dst[k];
27         int ds = sdelta[k], dd = ddelta[k];
28         if( s )
29         {
30             for( i = 0; i <= len - 2; i += 2, s += ds*2, d += dd*2 )
31             {
32                 T t0 = s[0], t1 = s[ds];
33                 d[0] = t0; d[dd] = t1;
34             }
35             if( i < len )
36                 d[0] = s[0];
37         }
38         else
39         {
40             for( i = 0; i <= len - 2; i += 2, d += dd*2 )
41                 d[0] = d[dd] = 0;
42             if( i < len )
43                 d[0] = 0;
44         }
45     }
46 }
47 
48 
mixChannels8u(const uchar ** src,const int * sdelta,uchar ** dst,const int * ddelta,int len,int npairs)49 static void mixChannels8u( const uchar** src, const int* sdelta,
50                            uchar** dst, const int* ddelta,
51                            int len, int npairs )
52 {
53     mixChannels_(src, sdelta, dst, ddelta, len, npairs);
54 }
55 
mixChannels16u(const ushort ** src,const int * sdelta,ushort ** dst,const int * ddelta,int len,int npairs)56 static void mixChannels16u( const ushort** src, const int* sdelta,
57                             ushort** dst, const int* ddelta,
58                             int len, int npairs )
59 {
60     mixChannels_(src, sdelta, dst, ddelta, len, npairs);
61 }
62 
mixChannels32s(const int ** src,const int * sdelta,int ** dst,const int * ddelta,int len,int npairs)63 static void mixChannels32s( const int** src, const int* sdelta,
64                             int** dst, const int* ddelta,
65                             int len, int npairs )
66 {
67     mixChannels_(src, sdelta, dst, ddelta, len, npairs);
68 }
69 
mixChannels64s(const int64 ** src,const int * sdelta,int64 ** dst,const int * ddelta,int len,int npairs)70 static void mixChannels64s( const int64** src, const int* sdelta,
71                             int64** dst, const int* ddelta,
72                             int len, int npairs )
73 {
74     mixChannels_(src, sdelta, dst, ddelta, len, npairs);
75 }
76 
77 typedef void (*MixChannelsFunc)( const uchar** src, const int* sdelta,
78         uchar** dst, const int* ddelta, int len, int npairs );
79 
getMixchFunc(int depth)80 static MixChannelsFunc getMixchFunc(int depth)
81 {
82     static MixChannelsFunc mixchTab[] =
83     {
84         (MixChannelsFunc)mixChannels8u, (MixChannelsFunc)mixChannels8u, (MixChannelsFunc)mixChannels16u,
85         (MixChannelsFunc)mixChannels16u, (MixChannelsFunc)mixChannels32s, (MixChannelsFunc)mixChannels32s,
86         (MixChannelsFunc)mixChannels64s, 0
87     };
88 
89     return mixchTab[depth];
90 }
91 
92 } // cv::
93 
94 
mixChannels(const Mat * src,size_t nsrcs,Mat * dst,size_t ndsts,const int * fromTo,size_t npairs)95 void cv::mixChannels( const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs )
96 {
97     CV_INSTRUMENT_REGION();
98 
99     if( npairs == 0 )
100         return;
101     CV_Assert( src && nsrcs > 0 && dst && ndsts > 0 && fromTo && npairs > 0 );
102 
103     size_t i, j, k, esz1 = dst[0].elemSize1();
104     int depth = dst[0].depth();
105 
106     AutoBuffer<uchar> buf((nsrcs + ndsts + 1)*(sizeof(Mat*) + sizeof(uchar*)) + npairs*(sizeof(uchar*)*2 + sizeof(int)*6));
107     const Mat** arrays = (const Mat**)(uchar*)buf.data();
108     uchar** ptrs = (uchar**)(arrays + nsrcs + ndsts);
109     const uchar** srcs = (const uchar**)(ptrs + nsrcs + ndsts + 1);
110     uchar** dsts = (uchar**)(srcs + npairs);
111     int* tab = (int*)(dsts + npairs);
112     int *sdelta = (int*)(tab + npairs*4), *ddelta = sdelta + npairs;
113 
114     for( i = 0; i < nsrcs; i++ )
115         arrays[i] = &src[i];
116     for( i = 0; i < ndsts; i++ )
117         arrays[i + nsrcs] = &dst[i];
118     ptrs[nsrcs + ndsts] = 0;
119 
120     for( i = 0; i < npairs; i++ )
121     {
122         int i0 = fromTo[i*2], i1 = fromTo[i*2+1];
123         if( i0 >= 0 )
124         {
125             for( j = 0; j < nsrcs; i0 -= src[j].channels(), j++ )
126                 if( i0 < src[j].channels() )
127                     break;
128             CV_Assert(j < nsrcs && src[j].depth() == depth);
129             tab[i*4] = (int)j; tab[i*4+1] = (int)(i0*esz1);
130             sdelta[i] = src[j].channels();
131         }
132         else
133         {
134             tab[i*4] = (int)(nsrcs + ndsts); tab[i*4+1] = 0;
135             sdelta[i] = 0;
136         }
137 
138         for( j = 0; j < ndsts; i1 -= dst[j].channels(), j++ )
139             if( i1 < dst[j].channels() )
140                 break;
141         CV_Assert(i1 >= 0 && j < ndsts && dst[j].depth() == depth);
142         tab[i*4+2] = (int)(j + nsrcs); tab[i*4+3] = (int)(i1*esz1);
143         ddelta[i] = dst[j].channels();
144     }
145 
146     NAryMatIterator it(arrays, ptrs, (int)(nsrcs + ndsts));
147     int total = (int)it.size, blocksize = std::min(total, (int)((BLOCK_SIZE + esz1-1)/esz1));
148     MixChannelsFunc func = getMixchFunc(depth);
149 
150     for( i = 0; i < it.nplanes; i++, ++it )
151     {
152         for( k = 0; k < npairs; k++ )
153         {
154             srcs[k] = ptrs[tab[k*4]] + tab[k*4+1];
155             dsts[k] = ptrs[tab[k*4+2]] + tab[k*4+3];
156         }
157 
158         for( int t = 0; t < total; t += blocksize )
159         {
160             int bsz = std::min(total - t, blocksize);
161             func( srcs, sdelta, dsts, ddelta, bsz, (int)npairs );
162 
163             if( t + blocksize < total )
164                 for( k = 0; k < npairs; k++ )
165                 {
166                     srcs[k] += blocksize*sdelta[k]*esz1;
167                     dsts[k] += blocksize*ddelta[k]*esz1;
168                 }
169         }
170     }
171 }
172 
173 #ifdef HAVE_OPENCL
174 
175 namespace cv {
176 
getUMatIndex(const std::vector<UMat> & um,int cn,int & idx,int & cnidx)177 static void getUMatIndex(const std::vector<UMat> & um, int cn, int & idx, int & cnidx)
178 {
179     int totalChannels = 0;
180     for (size_t i = 0, size = um.size(); i < size; ++i)
181     {
182         int ccn = um[i].channels();
183         totalChannels += ccn;
184 
185         if (totalChannels == cn)
186         {
187             idx = (int)(i + 1);
188             cnidx = 0;
189             return;
190         }
191         else if (totalChannels > cn)
192         {
193             idx = (int)i;
194             cnidx = i == 0 ? cn : (cn - totalChannels + ccn);
195             return;
196         }
197     }
198 
199     idx = cnidx = -1;
200 }
201 
ocl_mixChannels(InputArrayOfArrays _src,InputOutputArrayOfArrays _dst,const int * fromTo,size_t npairs)202 static bool ocl_mixChannels(InputArrayOfArrays _src, InputOutputArrayOfArrays _dst,
203                             const int* fromTo, size_t npairs)
204 {
205     std::vector<UMat> src, dst;
206     _src.getUMatVector(src);
207     _dst.getUMatVector(dst);
208 
209     size_t nsrc = src.size(), ndst = dst.size();
210     CV_Assert(nsrc > 0 && ndst > 0);
211 
212     Size size = src[0].size();
213     int depth = src[0].depth(), esz = CV_ELEM_SIZE(depth),
214             rowsPerWI = ocl::Device::getDefault().isIntel() ? 4 : 1;
215 
216     for (size_t i = 1, ssize = src.size(); i < ssize; ++i)
217         CV_Assert(src[i].size() == size && src[i].depth() == depth);
218     for (size_t i = 0, dsize = dst.size(); i < dsize; ++i)
219         CV_Assert(dst[i].size() == size && dst[i].depth() == depth);
220 
221     String declsrc, decldst, declproc, declcn, indexdecl;
222     std::vector<UMat> srcargs(npairs), dstargs(npairs);
223 
224     for (size_t i = 0; i < npairs; ++i)
225     {
226         int scn = fromTo[i<<1], dcn = fromTo[(i<<1) + 1];
227         int src_idx, src_cnidx, dst_idx, dst_cnidx;
228 
229         getUMatIndex(src, scn, src_idx, src_cnidx);
230         getUMatIndex(dst, dcn, dst_idx, dst_cnidx);
231 
232         CV_Assert(dst_idx >= 0 && src_idx >= 0);
233 
234         srcargs[i] = src[src_idx];
235         srcargs[i].offset += src_cnidx * esz;
236 
237         dstargs[i] = dst[dst_idx];
238         dstargs[i].offset += dst_cnidx * esz;
239 
240         declsrc += format("DECLARE_INPUT_MAT(%zu)", i);
241         decldst += format("DECLARE_OUTPUT_MAT(%zu)", i);
242         indexdecl += format("DECLARE_INDEX(%zu)", i);
243         declproc += format("PROCESS_ELEM(%zu)", i);
244         declcn += format(" -D scn%zu=%d -D dcn%zu=%d", i, src[src_idx].channels(), i, dst[dst_idx].channels());
245     }
246 
247     ocl::Kernel k("mixChannels", ocl::core::mixchannels_oclsrc,
248                   format("-D T=%s -D DECLARE_INPUT_MAT_N=%s -D DECLARE_OUTPUT_MAT_N=%s"
249                          " -D PROCESS_ELEM_N=%s -D DECLARE_INDEX_N=%s%s",
250                          ocl::memopTypeToStr(depth), declsrc.c_str(), decldst.c_str(),
251                          declproc.c_str(), indexdecl.c_str(), declcn.c_str()));
252     if (k.empty())
253         return false;
254 
255     int argindex = 0;
256     for (size_t i = 0; i < npairs; ++i)
257         argindex = k.set(argindex, ocl::KernelArg::ReadOnlyNoSize(srcargs[i]));
258     for (size_t i = 0; i < npairs; ++i)
259         argindex = k.set(argindex, ocl::KernelArg::WriteOnlyNoSize(dstargs[i]));
260     argindex = k.set(argindex, size.height);
261     argindex = k.set(argindex, size.width);
262     k.set(argindex, rowsPerWI);
263 
264     size_t globalsize[2] = { (size_t)size.width, ((size_t)size.height + rowsPerWI - 1) / rowsPerWI };
265     return k.run(2, globalsize, NULL, false);
266 }
267 
268 }
269 
270 #endif
271 
mixChannels(InputArrayOfArrays src,InputOutputArrayOfArrays dst,const int * fromTo,size_t npairs)272 void cv::mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst,
273                  const int* fromTo, size_t npairs)
274 {
275     CV_INSTRUMENT_REGION();
276 
277     if (npairs == 0 || fromTo == NULL)
278         return;
279 
280     CV_OCL_RUN(dst.isUMatVector(),
281                ocl_mixChannels(src, dst, fromTo, npairs))
282 
283     bool src_is_mat = src.kind() != _InputArray::STD_VECTOR_MAT &&
284             src.kind() != _InputArray::STD_ARRAY_MAT &&
285             src.kind() != _InputArray::STD_VECTOR_VECTOR &&
286             src.kind() != _InputArray::STD_VECTOR_UMAT;
287     bool dst_is_mat = dst.kind() != _InputArray::STD_VECTOR_MAT &&
288             dst.kind() != _InputArray::STD_ARRAY_MAT &&
289             dst.kind() != _InputArray::STD_VECTOR_VECTOR &&
290             dst.kind() != _InputArray::STD_VECTOR_UMAT;
291     int i;
292     int nsrc = src_is_mat ? 1 : (int)src.total();
293     int ndst = dst_is_mat ? 1 : (int)dst.total();
294 
295     CV_Assert(nsrc > 0 && ndst > 0);
296     cv::AutoBuffer<Mat> _buf(nsrc + ndst);
297     Mat* buf = _buf.data();
298     for( i = 0; i < nsrc; i++ )
299         buf[i] = src.getMat(src_is_mat ? -1 : i);
300     for( i = 0; i < ndst; i++ )
301         buf[nsrc + i] = dst.getMat(dst_is_mat ? -1 : i);
302     mixChannels(&buf[0], nsrc, &buf[nsrc], ndst, fromTo, npairs);
303 }
304 
mixChannels(InputArrayOfArrays src,InputOutputArrayOfArrays dst,const std::vector<int> & fromTo)305 void cv::mixChannels(InputArrayOfArrays src, InputOutputArrayOfArrays dst,
306                      const std::vector<int>& fromTo)
307 {
308     CV_INSTRUMENT_REGION();
309 
310     if (fromTo.empty())
311         return;
312 
313     CV_OCL_RUN(dst.isUMatVector(),
314                ocl_mixChannels(src, dst, &fromTo[0], fromTo.size()>>1))
315 
316     bool src_is_mat = src.kind() != _InputArray::STD_VECTOR_MAT &&
317             src.kind() != _InputArray::STD_ARRAY_MAT &&
318             src.kind() != _InputArray::STD_VECTOR_VECTOR &&
319             src.kind() != _InputArray::STD_VECTOR_UMAT;
320     bool dst_is_mat = dst.kind() != _InputArray::STD_VECTOR_MAT &&
321             dst.kind() != _InputArray::STD_ARRAY_MAT &&
322             dst.kind() != _InputArray::STD_VECTOR_VECTOR &&
323             dst.kind() != _InputArray::STD_VECTOR_UMAT;
324     int i;
325     int nsrc = src_is_mat ? 1 : (int)src.total();
326     int ndst = dst_is_mat ? 1 : (int)dst.total();
327 
328     CV_Assert(fromTo.size()%2 == 0 && nsrc > 0 && ndst > 0);
329     cv::AutoBuffer<Mat> _buf(nsrc + ndst);
330     Mat* buf = _buf.data();
331     for( i = 0; i < nsrc; i++ )
332         buf[i] = src.getMat(src_is_mat ? -1 : i);
333     for( i = 0; i < ndst; i++ )
334         buf[nsrc + i] = dst.getMat(dst_is_mat ? -1 : i);
335     mixChannels(&buf[0], nsrc, &buf[nsrc], ndst, &fromTo[0], fromTo.size()/2);
336 }
337 
338 #ifdef HAVE_IPP
339 
340 namespace cv
341 {
ipp_extractChannel(const Mat & src,Mat & dst,int channel)342 static bool ipp_extractChannel(const Mat &src, Mat &dst, int channel)
343 {
344 #ifdef HAVE_IPP_IW_LL
345     CV_INSTRUMENT_REGION_IPP();
346 
347     int srcChannels = src.channels();
348     int dstChannels = dst.channels();
349 
350     if(src.dims != dst.dims)
351         return false;
352 
353     if(src.dims <= 2)
354     {
355         IppiSize size = ippiSize(src.size());
356 
357         return CV_INSTRUMENT_FUN_IPP(llwiCopyChannel, src.ptr(), (int)src.step, srcChannels, channel, dst.ptr(), (int)dst.step, dstChannels, 0, size, (int)src.elemSize1()) >= 0;
358     }
359     else
360     {
361         const Mat      *arrays[] = {&dst, NULL};
362         uchar          *ptrs[2]  = {NULL};
363         NAryMatIterator it(arrays, ptrs);
364 
365         IppiSize size = {(int)it.size, 1};
366 
367         for( size_t i = 0; i < it.nplanes; i++, ++it )
368         {
369             if(CV_INSTRUMENT_FUN_IPP(llwiCopyChannel, ptrs[0], 0, srcChannels, channel, ptrs[1], 0, dstChannels, 0, size, (int)src.elemSize1()) < 0)
370                 return false;
371         }
372         return true;
373     }
374 #else
375     CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(channel);
376     return false;
377 #endif
378 }
379 
ipp_insertChannel(const Mat & src,Mat & dst,int channel)380 static bool ipp_insertChannel(const Mat &src, Mat &dst, int channel)
381 {
382 #ifdef HAVE_IPP_IW_LL
383     CV_INSTRUMENT_REGION_IPP();
384 
385     int srcChannels = src.channels();
386     int dstChannels = dst.channels();
387 
388     if(src.dims != dst.dims)
389         return false;
390 
391     if(src.dims <= 2)
392     {
393         IppiSize size = ippiSize(src.size());
394 
395         return CV_INSTRUMENT_FUN_IPP(llwiCopyChannel, src.ptr(), (int)src.step, srcChannels, 0, dst.ptr(), (int)dst.step, dstChannels, channel, size, (int)src.elemSize1()) >= 0;
396     }
397     else
398     {
399         const Mat      *arrays[] = {&dst, NULL};
400         uchar          *ptrs[2]  = {NULL};
401         NAryMatIterator it(arrays, ptrs);
402 
403         IppiSize size = {(int)it.size, 1};
404 
405         for( size_t i = 0; i < it.nplanes; i++, ++it )
406         {
407             if(CV_INSTRUMENT_FUN_IPP(llwiCopyChannel, ptrs[0], 0, srcChannels, 0, ptrs[1], 0, dstChannels, channel, size, (int)src.elemSize1()) < 0)
408                 return false;
409         }
410         return true;
411     }
412 #else
413     CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(channel);
414     return false;
415 #endif
416 }
417 }
418 #endif
419 
extractChannel(InputArray _src,OutputArray _dst,int coi)420 void cv::extractChannel(InputArray _src, OutputArray _dst, int coi)
421 {
422     CV_INSTRUMENT_REGION();
423 
424     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
425     CV_Assert( 0 <= coi && coi < cn );
426     int ch[] = { coi, 0 };
427 
428 #ifdef HAVE_OPENCL
429     if (ocl::isOpenCLActivated() && _src.dims() <= 2 && _dst.isUMat())
430     {
431         UMat src = _src.getUMat();
432         _dst.create(src.dims, &src.size[0], depth);
433         UMat dst = _dst.getUMat();
434         mixChannels(std::vector<UMat>(1, src), std::vector<UMat>(1, dst), ch, 1);
435         return;
436     }
437 #endif
438 
439     Mat src = _src.getMat();
440     _dst.create(src.dims, &src.size[0], depth);
441     Mat dst = _dst.getMat();
442 
443     CV_IPP_RUN_FAST(ipp_extractChannel(src, dst, coi))
444 
445     mixChannels(&src, 1, &dst, 1, ch, 1);
446 }
447 
insertChannel(InputArray _src,InputOutputArray _dst,int coi)448 void cv::insertChannel(InputArray _src, InputOutputArray _dst, int coi)
449 {
450     CV_INSTRUMENT_REGION();
451 
452     int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype);
453     int dtype = _dst.type(), ddepth = CV_MAT_DEPTH(dtype), dcn = CV_MAT_CN(dtype);
454     CV_Assert( _src.sameSize(_dst) && sdepth == ddepth );
455     CV_Assert( 0 <= coi && coi < dcn && scn == 1 );
456 
457     int ch[] = { 0, coi };
458 #ifdef HAVE_OPENCL
459     if (ocl::isOpenCLActivated() && _src.dims() <= 2 && _dst.isUMat())
460     {
461         UMat src = _src.getUMat(), dst = _dst.getUMat();
462         mixChannels(std::vector<UMat>(1, src), std::vector<UMat>(1, dst), ch, 1);
463         return;
464     }
465 #endif
466 
467     Mat src = _src.getMat(), dst = _dst.getMat();
468 
469     CV_IPP_RUN_FAST(ipp_insertChannel(src, dst, coi))
470 
471     mixChannels(&src, 1, &dst, 1, ch, 1);
472 }
473