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