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