1 module des.il.func;
2 
3 import std.algorithm;
4 import std.traits;
5 import std.range;
6 
7 import des.il.util;
8 import des.il.image;
9 import des.il.region;
10 
11 import des.ts;
12 
13 import des.math.linear.vector;
14 
15 import std.c.string : memcpy, memset;
16 
17 ///
18 enum ImRepack
19 {
20     NONE,  ///
21     ROT90, ///
22     ROT180,///
23     ROT270,///
24     MIRHOR,///
25     MIRVER,///
26     MTRANS,///
27     STRANS,///
28 }
29 
30 private CrdVector!N permutateComp(size_t N,T)( in Vector!(N,T) v, ImRepack tr, size_t[2] crdNum ) pure
31     if( isIntegral!T )
32 in
33 {
34     assert( crdNum[0] < v.length );
35     assert( crdNum[1] < v.length );
36 }
37 body
38 {
39     switch( tr )
40     {
41         case ImRepack.ROT90:
42         case ImRepack.ROT270:
43         case ImRepack.MTRANS:
44         case ImRepack.STRANS:
45             auto ret = CrdVector!N(v);
46             ret[crdNum[0]] = v[crdNum[1]];
47             ret[crdNum[1]] = v[crdNum[0]];
48             return ret;
49         default: return CrdVector!N(v);
50     }
51 }
52 
53 private void function( coord_t, coord_t, coord_t, coord_t,
54                ref coord_t, ref coord_t ) getRepackCrdFunc( ImRepack repack )
55 out(fnc) { assert( fnc !is null ); } body
56 {
57     final switch( repack )
58     {
59         case ImRepack.NONE:   return &noRepackCrd;
60         case ImRepack.ROT90:  return &rotCrd90;
61         case ImRepack.ROT180: return &rotCrd180;
62         case ImRepack.ROT270: return &rotCrd270;
63         case ImRepack.MIRHOR: return &mirHorCrd;
64         case ImRepack.MIRVER: return &mirVerCrd;
65         case ImRepack.MTRANS: return &mTransCrd;
66         case ImRepack.STRANS: return &sTransCrd;
67     }
68 }
69 
70 private
71 {
72     void noRepackCrd( coord_t px, coord_t py, coord_t sx, coord_t sy,
73                         ref coord_t rx, ref coord_t ry )
74     { rx=px; ry=py; }
75 
76     void rotCrd90( coord_t px, coord_t py, coord_t sx, coord_t sy,
77                     ref coord_t rx, ref coord_t ry )
78     { rx=sy-1-py; ry=px; }
79 
80     void rotCrd180( coord_t px, coord_t py, coord_t sx, coord_t sy,
81                     ref coord_t rx, ref coord_t ry )
82     { rx=sx-1-px; ry=sy-1-py; }
83 
84     void rotCrd270( coord_t px, coord_t py, coord_t sx, coord_t sy,
85                     ref coord_t rx, ref coord_t ry )
86     { rx=py; ry=sx-1-px; }
87 
88     void mirHorCrd( coord_t px, coord_t py, coord_t sx, coord_t sy,
89                     ref coord_t rx, ref coord_t ry )
90     { rx=sx-1-px; ry=py; }
91 
92     void mirVerCrd( coord_t px, coord_t py, coord_t sx, coord_t sy,
93                     ref coord_t rx, ref coord_t ry )
94     { rx=px; ry=sy-1-py; }
95 
96     void mTransCrd( coord_t px, coord_t py, coord_t sx, coord_t sy,
97                     ref coord_t rx, ref coord_t ry )
98     { rx=py; ry=px; }
99 
100     void sTransCrd( coord_t px, coord_t py, coord_t sx, coord_t sy,
101                     ref coord_t rx, ref coord_t ry )
102     { rx=sy-1-py; ry=sx-1-px; }
103 }
104 
105 /// copy `src` image from `copy_reg` to `dst` image in `paste_pos` with repack
106 void imCopy( string file=__FILE__, size_t line=__LINE__, size_t A,
107             size_t B, T, E)( ref Image dst, in Vector!(A,T) paste_pos,
108                 in Image src, in Region!(B,E) copy_reg,
109                 ImRepack repack=ImRepack.NONE, size_t[2] repack_dim=[0,1] )
110 if( isIntegral!T && isIntegral!E )
111 {
112     auto dims = dst.dims;
113     imEnforce!(file,line)( dims > 0, "no dimensions in dst" );
114     imEnforce!(file,line)( src.dims > 0, "no dimensions in src" );
115     imEnforce!(file,line)( dims >= src.dims,
116             "too much source dimensions" );
117     imEnforce!(file,line)( dims == paste_pos.length,
118             "dst dims mismatch with dst_pos dims" );
119     imEnforce!(file,line)( dims == copy_reg.dims,
120             "dst dims mismatch with src_reg dims" );
121     imEnforce!(file,line)( repack_dim[0] < dims,
122             "repack_dim[0] not less what dst dims" );
123     imEnforce!(file,line)( dst.info == src.info,
124             "dst info mismatch with src info" );
125 
126     auto rd0 = repack_dim[0];
127     auto rd1 = repack_dim[1];
128 
129     if( dims == 1 ) rd1 = rd0;
130     else imEnforce!(file,line)( rd1 < dims,
131             "repack_dim[1] not less what dst dims" );
132 
133     auto src_size = src.robSize(dims);
134 
135     imEnforce!(file,line)( isAllCompPositive(copy_reg.pos),
136             "copy region must be in source image" );
137     imEnforce!(file,line)( isAllCompPositive(src_size-copy_reg.lim),
138             "copy region must be in source image" );
139 
140     auto paste_reg = CrdRegionD( paste_pos,
141             permutateComp( copy_reg.size, repack, repack_dim ) );
142 
143     auto crop = CrdRegionD.fromSize( dst.size ).overlapLocal( paste_reg );
144 
145     auto copy_count = reduce!((s,v)=>s*=v)( crop.size );
146 
147     auto repack_crd_func = getRepackCrdFunc( repack );
148 
149     auto bpe = dst.info.bpe;
150 
151     foreach( i; 0 .. copy_count )
152     {
153         auto local_crop_crd = CrdVectorD( getCoord( crop.size, i ) );
154 
155         auto dst_crd = crop.pos + local_crop_crd;
156 
157         auto dst_offset = getIndex( dst.size, dst_crd );
158 
159         auto paste_crd = dst_crd - paste_reg.pos;
160         auto src_crd = CrdVectorD( paste_crd );
161 
162         repack_crd_func( paste_crd[rd0], paste_crd[rd1],
163                          paste_reg.size[rd0], paste_reg.size[rd1],
164                          src_crd[rd0], src_crd[rd1] );
165 
166         auto src_offset = getIndex( src_size, src_crd + copy_reg.pos );
167 
168         memcpy( dst.data.ptr + dst_offset * bpe,
169                 src.data.ptr + src_offset * bpe, bpe );
170     }
171 }
172 
173 ///
174 unittest
175 {
176     auto etype = ElemInfo( 1, DataType.INT );
177     auto dst = Image( ivec2(3,3), etype );
178 
179     auto src = Image( ivec2(2,2), etype, [ 1,2, 3,4 ]);
180 
181     imCopy( dst, ivec2(1,1), src, CrdRegionD(0,0,2,2), ImRepack.ROT90 );
182 
183     assertEq( dst.data, [ 0,0,0,
184                           0,2,4,
185                           0,1,3 ] );
186 
187     imCopy( dst, ivec2(0,0), src, CrdRegionD(0,0,2,2), ImRepack.ROT270 );
188 
189     assertEq( dst.data, [ 3,1,0,
190                           4,2,4,
191                           0,1,3 ] );
192 }
193 
194 ///
195 unittest
196 {
197     auto etype = ElemInfo( 1, DataType.INT );
198     auto dst = Image( ivec3(3,3,3), etype );
199     auto src = Image( CrdVectorD(3), etype, [1,2,4] );
200 
201     imCopy( dst, ivec3(0,0,0), src, CrdRegionD(0,0,0,3,1,1), ImRepack.ROT270 );
202     assertEq( dst.mapAs!int, [ 1,0,0,
203                                2,0,0,
204                                4,0,0, // z=0
205 
206                                0,0,0,
207                                0,0,0,
208                                0,0,0, // z=1
209 
210                                0,0,0,
211                                0,0,0,
212                                0,0,0, // z=2
213                              ] );
214 
215     imCopy( dst, ivec3(1,0,0), src, CrdRegionD(0,0,0,3,1,1), ImRepack.ROT90, [0,2] );
216     assertEq( dst.mapAs!int, [ 1,4,0,
217                                2,0,0,
218                                4,0,0, // z=0
219 
220                                0,2,0,
221                                0,0,0,
222                                0,0,0, // z=1
223 
224                                0,1,0,
225                                0,0,0,
226                                0,0,0, // z=2
227                              ] );
228 }
229 
230 ///
231 unittest
232 {
233     auto etype = ElemInfo( 1, DataType.INT );
234     auto dst = Image( ivec3(3,3,3), etype );
235     auto src = Image( CrdVectorD(3), etype, [1,2,4] );
236 
237     imCopy( dst, ivec3(1,0,0), src, CrdRegionD(1,0,0,2,1,1), ImRepack.ROT270 );
238     assertEq( dst.mapAs!int, [ 0,2,0,
239                                0,4,0,
240                                0,0,0, // z=0
241 
242                                0,0,0,
243                                0,0,0,
244                                0,0,0, // z=1
245 
246                                0,0,0,
247                                0,0,0,
248                                0,0,0, // z=2
249                              ] );
250 }
251 
252 ///
253 void imCopy(string file=__FILE__, size_t line=__LINE__, size_t A,T)
254            ( ref Image dst, in Vector!(A,T) paste_pos,
255             in Image src, ImRepack repack=ImRepack.NONE,
256             size_t[2] repack_dim=[0,1] )
257 if( isIntegral!T )
258 {
259     imCopy!(file,line)( dst, paste_pos, src,
260             CrdRegionD.fromSize( src.robSize(dst.dims) ),
261             repack, repack_dim );
262 }
263 
264 ///
265 unittest
266 {
267     auto etype = ElemInfo( 1, DataType.INT );
268     auto dst = Image( ivec2(4,4), etype );
269     auto src = Image( ivec2(3,2), etype, [ 1,2,4, 5,6,8 ] );
270 
271     imCopy( dst, ivec2(-1,-1), src, ImRepack.ROT90 );
272     assertEq( dst.mapAs!int, [ 6,0,0,0,
273                                5,0,0,0,
274                                0,0,0,0,
275                                0,0,0,0 ] );
276     imCopy( dst, ivec2(2,1), src, ImRepack.ROT180 );
277     assertEq( dst.mapAs!int, [ 6,0,0,0,
278                                5,0,8,6,
279                                0,0,4,2,
280                                0,0,0,0 ] );
281     imCopy( dst, ivec2(-1,3), src, ImRepack.MIRVER );
282     assertEq( dst.mapAs!int, [ 6,0,0,0,
283                                5,0,8,6,
284                                0,0,4,2,
285                                6,8,0,0 ] );
286 }
287 
288 ///
289 unittest
290 {
291     auto etype = ElemInfo( 1, DataType.INT );
292     auto dst = Image( ivec2(2,2), etype );
293     auto src = Image( ivec2(3,3), etype, [ 1,2,4, 5,6,8, 9,7,3 ] );
294 
295     imCopy( dst, ivec2(-1,-1), src, CrdRegionD(0,0,3,3) );
296     assertEq( dst.mapAs!int, [ 6,8,
297                                7,3 ] );
298 
299 }
300 
301 ///
302 unittest
303 {
304     auto etype = ElemInfo( 1, DataType.INT );
305     auto dst = Image( ivec3(2,2,2), etype );
306     auto src = Image( ivec!1(2), etype, [ 1,2 ] );
307 
308     imCopy( dst, ivec3(0,0,0), src, ImRepack.NONE );
309     assertEq( dst.mapAs!int, [1,2, 0,0,  0,0, 0,0] );
310 
311     imCopy( dst, ivec3(0,0,0), src, ImRepack.ROT270 );
312     assertEq( dst.mapAs!int, [1,2, 2,0,  0,0, 0,0] );
313 
314     dst.clear();
315     imCopy( dst, ivec3(0,0,0), src, ImRepack.ROT90, [0,2] );
316     assertEq( dst.mapAs!int, [2,0, 0,0,  1,0, 0,0] );
317 }
318 
319 /// copy and repack image from region to new image
320 Image imGetCopy(string file=__FILE__,size_t line=__LINE__,size_t B,E)( in Image src, in Region!(B,E) copy_reg,
321                     ImRepack repack=ImRepack.NONE, size_t[2] repack_dim=[0,1] )
322 if( isIntegral!E )
323 {
324     if( src.dims == 1 ) repack_dim[1] = repack_dim[0];
325     auto sz = permutateComp( copy_reg.size, repack, repack_dim );
326     auto ret = Image( sz, src.info );
327     imCopy!(file,line)( ret, CrdVectorD.fill(src.dims,0), src, copy_reg, repack, repack_dim );
328     return ret;
329 }
330 
331 ///
332 unittest
333 {
334     auto a = Image( ivec!1(5), ElemInfo( 2, DataType.FLOAT ) );
335     a.pixel!vec2(3) = vec2(1,1);
336     a.pixel!vec2(4) = vec2(2,2);
337     auto b = imGetCopy( a, Region!(1,int)(3,2) );
338     assert( b.pixel!vec2(0) == a.pixel!vec2(3) );
339     assert( b.pixel!vec2(1) == a.pixel!vec2(4) );
340 }
341 
342 ///
343 unittest
344 {
345     ubyte[] imgdata = [
346         1, 2, 3, 4,
347         5, 6, 7, 8,
348         9,10,11,12,
349        13,14,15,16
350     ];
351 
352     auto img = Image( ivec2(4,4), 1, DataType.UBYTE, imgdata );
353 
354     {
355         ubyte[] r = [ 8,12,16, 7,11,15 ];
356         assertEq( imGetCopy( img, iRegion2(2,1,2,3), ImRepack.ROT90 ).mapAs!ubyte, r );
357     }
358 
359     {
360         ubyte[] r = [ 14,10,6, 15,11,7 ];
361         assertEq( imGetCopy( img, iRegion2(1,1,2,3), ImRepack.ROT270 ).mapAs!ubyte, r );
362     }
363 
364     {
365         ubyte[] r= [ 3,2,1, 7,6,5 ];
366         assertEq( imGetCopy( img, iRegion2(0,0,3,2), ImRepack.MIRHOR ).mapAs!ubyte, r );
367     }
368 
369     {
370         ubyte[] r = [ 5,6,7, 1,2,3 ];
371         assert( imGetCopy( img, iRegion2(0,0,3,2), ImRepack.MIRVER ).mapAs!ubyte == r );
372     }
373 }
374 
375 ///
376 unittest
377 {
378     ubyte[] img_data =
379     [
380         1,2,3,
381         4,5,6,
382 
383         7,8,9,
384         10,11,12,
385     ];
386 
387     ubyte[] d2l0 = [ 1,2,3,4,5,6 ];
388     ubyte[] d2l1 = [ 7,8,9,10,11,12 ];
389 
390     ubyte[] d1l0 = [ 1,2,3,7,8,9 ];
391     ubyte[] d1l1 = [ 4,5,6,10,11,12 ];
392 
393     ubyte[] d0l0 = [ 1, 4, 7, 10 ];
394     ubyte[] d0l1 = [ 2, 5, 8, 11 ];
395 
396     auto img = Image( ivec3(3,2,2), 1, DataType.UBYTE, img_data );
397 
398     assertEq( imGetCopy( img, CrdRegionD(0,0,0,3,2,1) ).mapAs!ubyte, d2l0 );
399     assertEq( imGetCopy( img, CrdRegionD(0,0,1,3,2,1) ).mapAs!ubyte, d2l1 );
400 
401     assertEq( imGetCopy( img, CrdRegionD(0,0,0,3,1,2) ).mapAs!ubyte, d1l0 );
402     assertEq( imGetCopy( img, CrdRegionD(0,1,0,3,1,2) ).mapAs!ubyte, d1l1 );
403 
404     assertEq( imGetCopy( img, CrdRegionD(0,0,0,1,2,2) ).mapAs!ubyte, d0l0 );
405     assertEq( imGetCopy( img, CrdRegionD(1,0,0,1,2,2) ).mapAs!ubyte, d0l1 );
406 }
407 
408 ///
409 unittest
410 {
411     ubyte[] data =
412     [
413         2, 1, 3, 5, 2,
414         9, 1, 2, 6, 3,
415         2, 5, 2, 9, 1,
416         8, 3, 6, 3, 0,
417         6, 2, 8, 1, 5
418     ];
419 
420     ubyte[] datav1 =
421     [
422         1, 2, 6, 3, 0, 0, 0,
423         5, 2, 9, 1, 0, 0, 0,
424         3, 6, 3, 0, 0, 0, 0,
425         2, 8, 1, 5, 0, 0, 0,
426         0, 0, 0, 0, 0, 0, 0,
427         0, 0, 0, 0, 0, 0, 0,
428         0, 0, 0, 0, 0, 0, 0
429     ];
430 
431     ubyte[] datav2 =
432     [
433         0, 0, 0, 0, 0, 0, 0,
434         0, 2, 1, 3, 5, 2, 0,
435         0, 9, 1, 2, 6, 3, 0,
436         0, 2, 5, 2, 9, 1, 0,
437         0, 8, 3, 6, 3, 0, 0,
438         0, 6, 2, 8, 1, 5, 0,
439         0, 0, 0, 0, 0, 0, 0
440     ];
441 
442 
443     auto orig = Image( ivec2( 7, 7 ), ElemInfo( 1, DataType.UBYTE ) );
444     auto im = Image( ivec2( 5, 5 ), 1, DataType.UBYTE, data );
445 
446     auto res = Image(orig);
447     imCopy( res, ivec2(-1,-1), im );
448     assert( res.data == datav1 );
449 
450     res = Image(orig);
451     imCopy( res, ivec2(1,1), im );
452     assert( res.data == datav2 );
453 }
454 
455 unittest
456 {
457     ubyte[] src_data = [ 1,2,3, 4,5,6, 7,8,9 ];
458 
459     ubyte[] dst1_data =
460         [
461         0,0,0, 0,0,0, 0,0,0,
462 
463         1,2,3, 4,5,6, 7,8,9,
464 
465         0,0,0, 0,0,0, 0,0,0
466         ];
467 
468     ubyte[] dst2_data =
469         [
470         0,1,0, 0,2,0, 0,3,0,
471 
472         0,4,0, 0,5,0, 0,6,0,
473 
474         0,7,0, 0,8,0, 0,9,0
475         ];
476 
477     auto src = Image( ivec2(3,3), ElemInfo( 1, DataType.UBYTE ), src_data );
478     auto dst = Image( ivec3(3,3,3), ElemInfo( 1, DataType.UBYTE ) );
479     imCopy( dst, ivec3(0,0,1), Image( ivec3(3,3,1), src.info, src.data ) );
480     assert( dst.data == dst1_data );
481     dst.clear();
482     imCopy( dst, ivec3(1,0,0), Image.external( ivec3(1,3,3), src.info, src.data ) );
483     assertEq( dst.data, dst2_data );
484 }
485 
486 unittest
487 {
488     ubyte[] dt =
489         [
490         0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
491 
492         0,0,0,0, 0,1,2,0, 0,3,4,0, 0,0,0,0,
493 
494         0,0,0,0, 0,5,6,0, 0,7,8,0, 0,0,0,0,
495 
496         0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
497         ];
498 
499     ubyte[] cp =
500         [
501         1,2,1,2, 3,4,3,4, 1,2,1,2, 3,4,3,4,
502 
503         5,6,5,6, 7,8,7,8, 5,6,5,6, 7,8,7,8,
504 
505         1,2,1,2, 3,4,3,4, 1,2,1,2, 3,4,3,4,
506 
507         5,6,5,6, 7,8,7,8, 5,6,5,6, 7,8,7,8,
508         ];
509 
510     ubyte[] rs = [ 8,7, 6,5, 4,3, 2,1 ];
511 
512     auto EType = ElemInfo( 1, DataType.UBYTE );
513 
514     auto a = Image( ivec3(4,4,4), EType, dt );
515     auto b = Image( ivec3(4,4,4), EType, cp );
516     auto c = Image( ivec3(4,4,4), EType );
517 
518     auto part = imGetCopy( a, iRegion3( ivec3(1,1,1), ivec3(2,2,2) ) );
519 
520     imCopy( c, ivec3(0,0,0), part );
521     imCopy( c, ivec3(0,2,0), part );
522     imCopy( c, ivec3(2,0,0), part );
523     imCopy( c, ivec3(2,2,0), part );
524 
525     imCopy( c, ivec3(0,0,2), part );
526     imCopy( c, ivec3(0,2,2), part );
527     imCopy( c, ivec3(2,0,2), part );
528     imCopy( c, ivec3(2,2,2), part );
529 
530     assert( b == c );
531 
532     auto part2 = imGetCopy( b, iRegion3(ivec3(1,1,1), ivec3(2,2,2)) );
533     auto rr = Image( ivec3(2,2,2), EType, rs );
534     assert( rr == part2 );
535 }
536 
537 unittest
538 {
539     auto type = ElemInfo(1,DataType.UBYTE);
540 
541     ubyte[] srcData =
542         [
543             1,2,3,
544             4,5,6,
545         ];
546 
547     auto src = Image( ivec2(3,2), type, srcData );
548     auto dst = Image( ivec3(3,3,3), type );
549     imCopy( dst, ivec3(1,0,0), src, ImRepack.ROT180 );
550     imCopy( dst, ivec3(-1,-1,1), src, ImRepack.ROT90 );
551     imCopy( dst, ivec3(0,0,2), src, ImRepack.NONE );
552 
553     auto expectedDstData =
554         [
555             0,6,5, 0,3,2, 0,0,0,
556 
557             5,0,0, 4,0,0, 0,0,0,
558 
559             1,2,3, 4,5,6, 0,0,0,
560         ];
561 
562     assertEq( expectedDstData, dst.mapAs!ubyte );
563 }
564 
565 // TODO: utDataAssign utDataOp
566 ///++ get histogram convolution
567 //    +/
568 //Image imHistoConv( in Image img, size_t dim ) pure
569 //in { assert( dim < img.dims ); } body
570 //{
571 //    auto ret = Image( ivecD( cut( img.size, dim ) ), img.info );
572 //
573 //    auto bpe = img.info.bpe;
574 //
575 //    foreach( i; 0 .. ret.pixelCount )
576 //    {
577 //        auto buf = ret.data.ptr + i * bpe;
578 //        utDataAssign( img.info, buf, 0 );
579 //        foreach( j; 0 .. img.size[dim] )
580 //            utDataOp!"+"( img.info, buf,
581 //            cast(void*)( img.data.ptr + getOrigIndexByLayerCoord( img.size, dim, i, j ) * bpe ) );
582 //    }
583 //
584 //    return ret;
585 //}
586 //
587 /////
588 //unittest
589 //{
590 //    ubyte[] img_data =
591 //    [
592 //        1,2,5,8,
593 //        4,3,1,1
594 //    ];
595 //
596 //    ubyte[] hi_x_data = [ 16, 9 ];
597 //    ubyte[] hi_y_data = [ 5, 5, 6, 9 ];
598 //
599 //    auto img = Image( ivec2(4,2), ElemInfo( 1, DataType.UBYTE ), img_data );
600 //    auto hi_x = Image( ivec!1(2), ElemInfo( 1, DataType.UBYTE ), hi_x_data );
601 //    auto hi_y = Image( ivec!1(4), ElemInfo( 1, DataType.UBYTE ), hi_y_data );
602 //
603 //    assert( imHistoConv(img,0) == hi_x );
604 //    assert( imHistoConv(img,1) == hi_y );
605 //}