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 }