1 module des.il.image; 2 3 import std.exception; 4 5 import std.algorithm; 6 import std..string; 7 import std.exception; 8 import std.range; 9 import std.traits; 10 import std.conv; 11 12 import des.math.linear.vector; 13 import des.math.util.accessstring; 14 import des.il.region; 15 import des.il.util; 16 import des.util.testsuite; 17 import des.util.stdext.algorithm; 18 public import des.util.data.type; 19 20 import std.stdio; 21 22 /// 23 struct Image 24 { 25 /// 26 CrdVector!0 size; 27 /// 28 ElemInfo info; 29 /// 30 void[] data; 31 32 invariant() 33 { 34 enforce( isAllCompPositive( size ) ); 35 if( data.length > 0 ) 36 enforce( data.length == expectedDataLength ); 37 } 38 39 pure: 40 41 /// copy ctor 42 this(this) 43 { 44 size = CrdVector!0( size ); 45 data = data.dup; 46 } 47 48 /// from other image 49 this( in Image img ) 50 { 51 size = CrdVector!0( img.size ); 52 info = img.info; 53 data = img.data.dup; 54 } 55 56 /// from other image 57 immutable this( in Image img ) 58 { 59 size = immutable CrdVector!0( img.size ); 60 info = img.info; 61 data = img.data.idup; 62 } 63 64 /// from size, element info and data 65 this(size_t N,T)( in Vector!(N,T) size, ElemInfo info, in void[] data=[] ) 66 if( isIntegral!T ) 67 in { assert( isAllCompPositive(size) ); } body 68 { 69 this.size = CrdVector!0( size ); 70 this.info = info; 71 72 if( data.length ) this.data = data.dup; 73 else this.data = new void[]( dataSize ); 74 } 75 76 /// from size, channel count, component type and data 77 this(size_t N,T)( in Vector!(N,T) size, size_t ch, DataType type, in void[] data=[] ) 78 if( isIntegral!T ) 79 in { assert( isAllCompPositive(size) ); } body 80 { 81 this.size = CrdVector!0( size ); 82 this.info = ElemInfo( ch, type ); 83 84 if( data.length ) this.data = data.dup; 85 else this.data = new void[]( dataSize ); 86 } 87 88 static 89 { 90 /// use external memory (not copy) 91 auto external(size_t N,T)( in Vector!(N,T) size, ElemInfo info, void[] data ) 92 if( isIntegral!T ) 93 in { assert( isAllCompPositive(size) ); } body 94 { 95 Image ret; 96 ret.size = size; 97 ret.info = info; 98 ret.data = data; 99 return ret; 100 } 101 102 /// ditto 103 auto external(size_t N,T)( in Vector!(N,T) size, size_t ch, DataType type, void[] data ) 104 if( isIntegral!T ) 105 in { assert( isAllCompPositive(size) ); } body 106 { return external( size, ElemInfo(ch,type), data ); } 107 } 108 109 pure const @safe nothrow @property @nogc 110 { 111 /// image data size 112 size_t dataSize() { return pixelCount * info.bpe; } 113 114 /// 115 size_t pixelCount() 116 { 117 size_t sz = 1; 118 foreach( v; size.data ) sz *= v; 119 return sz; 120 } 121 122 size_t dims() { return size.data.length; } 123 } 124 125 /// fill data zeros 126 void clear() 127 { 128 if( data ) fill( cast(ubyte[])data, ubyte(0) ); 129 else data = new void[]( expectedDataLength ); 130 } 131 132 const @property 133 { 134 /// get copy of image 135 auto dup() { return Image( this ); } 136 137 /// get immutable copy of image 138 auto idup() { return immutable(Image)( this ); } 139 } 140 141 CrdVector!0 robSize( size_t K ) const 142 { return CrdVector!(0).fillOne( K, size, 1 ); } 143 144 /// 145 immutable(void[]) dump() const 146 { 147 return (cast(void[])([size.length]) ~ 148 cast(void[])(size.data) ~ 149 cast(void[])([info]) ~ data).idup; 150 } 151 152 /// 153 static auto load( immutable(void[]) rawdata ) 154 { 155 immutable(void)[] readVals(T)( T* ptr, size_t cnt, immutable(void[]) arr ) 156 { 157 auto data = cast(immutable(T[]))arr[0..T.sizeof*cnt]; 158 foreach( i, val; data ) *ptr++ = val; 159 return arr[T.sizeof*cnt..$]; 160 } 161 162 size_t dims; 163 auto buf = readVals!size_t( &dims, 1, rawdata ); 164 165 auto szdata = new coord_t[](dims); 166 buf = readVals!coord_t( szdata.ptr, dims, buf ); 167 168 auto size = CrdVector!0( szdata ); 169 170 ElemInfo info; 171 172 buf = readVals!ElemInfo( &info, 1, buf ); 173 174 return Image( size, info, buf ); 175 } 176 177 /// access to pixel 178 ref T pixel(T,C)( in C[] crd... ) 179 if( isIntegral!C ) 180 in 181 { 182 assert( isAllCompPositive(crd), "negative coordinate" ); 183 assert( all!"a[0]>a[1]"( zip( size.dup, crd.dup ) ), "range violation" ); 184 assert( crd.length == size.length ); 185 } 186 body 187 { 188 checkDataType!T; 189 return (cast(T[])data)[index(crd)]; 190 } 191 192 /// ditto 193 ref const(T) pixel(T,C)( in C[] crd... ) const 194 if( isIntegral!C ) 195 in 196 { 197 assert( isAllCompPositive(crd), "negative coordinate" ); 198 assert( crd.length == size.length ); 199 assert( all!"a[0]>a[1]"( zip( size.data, crd ) ), "range violation" ); 200 } 201 body 202 { 203 checkDataType!T; 204 return (cast(const(T)[])data)[index(crd)]; 205 } 206 207 /// ditto 208 ref T pixel(T,C,size_t N)( in Vector!(N,C) crd ) 209 if( isIntegral!C ) 210 in 211 { 212 assert( isAllCompPositive(crd), "negative coordinate" ); 213 assert( crd.length == size.length ); 214 assert( all!"a[0]>a[1]"( zip( size.data, crd.data.dup ) ), "range violation" ); 215 } 216 body 217 { 218 checkDataType!T; 219 return (cast(T[])data)[index(crd)]; 220 } 221 222 /// ditto 223 ref const(T) pixel(T,C,size_t N)( in Vector!(N,C) crd ) const 224 if( isIntegral!C ) 225 in 226 { 227 assert( isAllCompPositive(crd), "negative coordinate" ); 228 assert( crd.length == size.length ); 229 assert( all!"a[0]>a[1]"( zip( size.data, crd.data.dup ) ), "range violation" ); 230 } 231 body 232 { 233 checkDataType!T; 234 return (cast(const(T)[])data)[index(crd)]; 235 } 236 237 /// cast data to `T[]` 238 @property T[] mapAs(T)() 239 { 240 checkDataType!T; 241 return cast(T[])data; 242 } 243 244 /// ditto 245 @property const(T)[] mapAs(T)() const 246 { 247 checkDataType!T; 248 return cast(const(T)[])data; 249 } 250 251 /// return line index by coordinate 252 size_t index(T)( in T[] crd ) const 253 if( isIntegral!T ) 254 in { assert( isAllCompPositive(crd) ); } body 255 { return getIndex( size, crd ); } 256 257 private: 258 259 void checkDataType(T)() const 260 { 261 enforce( T.sizeof == info.bpe, 262 new ImageException( "access with wrong type" ) ); 263 } 264 265 size_t expectedDataLength() const @property 266 { 267 size_t sz = 1; 268 foreach( v; size.data ) sz *= v; 269 return sz * info.bpe; 270 } 271 } 272 273 /// 274 unittest 275 { 276 import std.stdio; 277 auto a = Image( ivec2(3,3), 3, DataType.UBYTE ); 278 assert( a.data.length != 0 ); 279 assert( eq( a.size, [3,3] ) ); 280 a.pixel!bvec3(0,0) = bvec3(1,2,3); 281 auto b = a; 282 assert( b == a ); 283 assert( eq( b.pixel!bvec3(0,0), bvec3(1,2,3) ) ); 284 a.pixel!bvec3(1,1) = bvec3(3,4,5); 285 assert( b != a ); 286 assert( b == Image.load( b.dump() ) ); 287 assert( b == b.dup ); 288 assert( b.data == b.idup.data.dup ); 289 a = b; 290 assert( b == a ); 291 assert( a == Image.load( b.dump() ) ); 292 assert( a == b.dup ); 293 assert( a.data == b.idup.data.dup ); 294 auto crd = ivec2(1,2); 295 b.pixel!bvec3(crd) = bvec3(5,6,8); 296 assert( eq( b.pixel!bvec3(1,2), bvec3(5,6,8) ) ); 297 298 b.clear(); 299 assert( eq( b.pixel!bvec3(1,2), bvec3(0,0,0) ) ); 300 } 301 302 /// 303 unittest 304 { 305 auto a = Image( ivec2(3,3), ElemInfo( 1, DataType.UBYTE ), to!(ubyte[])([ 1,2,3,4,5,6,7,8,9 ]) ); 306 auto b = Image(a); 307 308 assert( a.pixel!ubyte(0,0) == 1 ); 309 assert( b.pixel!ubyte(0,0) == 1 ); 310 a.pixel!ubyte(0,0) = 2; 311 assert( a.pixel!ubyte(0,0) == 2 ); 312 assert( b.pixel!ubyte(0,0) == 1 ); 313 314 auto c = immutable Image(a); 315 assert( c.pixel!ubyte(0,0) == 2 ); 316 } 317 318 /// 319 unittest 320 { 321 auto a = Image( ivec!1(3), 1, DataType.UBYTE, to!(ubyte[])([ 1,2,3 ]) ); 322 assert( mustExcept!Throwable({ a.pixel!(ubyte)(7) = 0; }) ); 323 assert( !mustExcept({ a.pixel!(ubyte)(0) = 0; }) ); 324 325 assert( a.pixel!ubyte(0) == 0 ); 326 327 auto b = Image(a); 328 b.size.length = 2; 329 b.size[1] = 1; 330 331 assert( b.size[0] == 3 ); 332 assert( b.size[1] == 1 ); 333 334 assert( b.pixel!ubyte(0,0) == 0 ); 335 assert( b.pixel!ubyte(1,0) == 2 ); 336 assert( mustExcept!Throwable({ b.pixel!ubyte(1,1) = 2; }) ); 337 338 auto c = Image(a); 339 c.size.length = 2; 340 c.size = ivec2(1,3); 341 342 assert( c.size[0] == 1 ); 343 assert( c.size[1] == 3 ); 344 345 assert( c.pixel!ubyte(0,0) == 0 ); 346 assert( c.pixel!ubyte(0,1) == 2 ); 347 assert( mustExcept!Throwable({ c.pixel!ubyte(1,1) = 2; }) ); 348 349 c.size = ivec2(2,2); 350 351 assert( c.size[0] == 2 ); 352 assert( c.size[1] == 2 ); 353 } 354 355 /// 356 unittest 357 { 358 auto a = Image( ivec2(3,3), 2, DataType.FLOAT ); 359 360 assert( a.index([1,2]) == 7 ); 361 assert( a.index(ivec2(1,2)) == 7 ); 362 363 a.mapAs!(vec2)[a.index([1,2])] = vec2(1,1); 364 assert( a.pixel!vec2(1,2) == vec2(1,1) ); 365 366 a.pixel!vec2(1,2) = vec2(2,2); 367 assert( a.pixel!vec2(1,2) == vec2(2,2) ); 368 } 369 370 unittest 371 { 372 vec2 sum( in Image img ) pure 373 { 374 auto buf = img.mapAs!vec2; 375 return reduce!((a,b)=>(a+=b))(vec2(0,0),buf); 376 } 377 378 auto a = Image( ivec2(3,3), ElemInfo( 2, DataType.FLOAT ) ); 379 380 a.pixel!vec2(0,0) = vec2(1,2); 381 a.pixel!vec2(1,2) = vec2(2,2); 382 383 assert( sum(a) == vec2(3,4) ); 384 } 385 386 /// use external memory 387 unittest 388 { 389 float[] data = [ 1.0, 2, 3, 4 ]; 390 auto img = Image.external( ivec2(2,2), 1, DataType.FLOAT, data ); 391 392 img.pixel!float(0,0) = 8.0f; 393 394 assert( data[0] == 8.0f ); 395 } 396 397 /// 398 unittest 399 { 400 Image img; 401 assert( img.size.length == 0 ); 402 assert( img.data.length == 0 ); 403 404 img.size = ivec2(3,3); 405 img.info = ElemInfo( 3, DataType.NORM_FIXED ); 406 img.clear(); 407 408 assert( img.data.length == 27 * float.sizeof ); 409 assert( img.info.bpe == 3 * float.sizeof ); 410 411 img.pixel!vec3(0,1) = vec3( .2,.1,.3 ); 412 assert( img.pixel!vec3(0,1) == vec3(.2,.1,.3) ); 413 414 auto di = Image.load( img.dump() ); 415 assert( di.size == img.size ); 416 assert( di.info == img.info ); 417 assert( di.data == img.data ); 418 419 auto ii = immutable(Image)( img ); 420 assert( ii.size == img.size ); 421 assert( ii.info == img.info ); 422 assert( ii.data == img.data ); 423 424 assert( ii.pixel!vec3(0,1) == vec3(.2,.1,.3) ); 425 426 auto dii = immutable(Image).load( ii.dump() ); 427 static assert( is( typeof(dii) == Image ) ); 428 assert( dii.size == img.size ); 429 assert( dii.info == img.info ); 430 assert( dii.data == img.data ); 431 432 auto dd = ii.dup; 433 static assert( is( typeof(dd) == Image ) ); 434 assert( dd.size == img.size ); 435 assert( dd.info == img.info ); 436 assert( dd.data == img.data ); 437 438 auto ddi = ii.idup; 439 static assert( is( typeof(ddi) == immutable(Image) ) ); 440 assert( ddi.size == img.size ); 441 assert( ddi.info == img.info ); 442 assert( ddi.data == img.data ); 443 } 444 445 /// 446 unittest 447 { 448 auto data = 449 [ 450 vec2( 1, 2 ), vec2( 3, 4 ), vec2( 5, 6 ), 451 vec2( 7, 8 ), vec2( 9, 1 ), vec2( 1, 2 ), 452 vec2( 2, 3 ), vec2( 4, 5 ), vec2( 6, 7 ) 453 ]; 454 455 auto img = Image( ivec2(3,3), 2, DataType.FLOAT, data ); 456 457 assert( img.size == ivec2(3,3) ); 458 assert( img.info.bpe == 2 * float.sizeof ); 459 460 assert( img.pixel!vec2(1,1) == vec2(9,1) ); 461 assert( img.info.type == DataType.FLOAT ); 462 463 auto imdata = img.mapAs!vec2; 464 assert( data == imdata ); 465 466 img.clear(); 467 assert( img.pixel!vec2(1,1) == vec2(0,0) ); 468 469 img.mapAs!(vec2)[] = data[]; 470 imdata = img.mapAs!vec2; 471 assert( data == imdata ); 472 473 auto constdata = img.idup.mapAs!vec2; 474 assertEq( constdata, imdata ); 475 assert( is( typeof(constdata) == const(vec2)[] ) ); 476 } 477 478 /// 479 unittest 480 { 481 assert( mustExcept({ Image( ivec2(3,3), ElemInfo(3,DataType.UBYTE), [ 1, 2, 3 ] ); }) ); 482 483 auto dt = [ vec2(1,0), vec2(0,1) ]; 484 assert( mustExcept({ Image( ivec2(3,3), 2, DataType.FLOAT, dt ); }) ); 485 486 auto img = Image( ivec2(3,3), ElemInfo( 3, DataType.NORM_FIXED ) ); 487 assert( mustExcept({ auto d = img.mapAs!vec2; }) ); 488 489 assert( !mustExcept({ img.pixel!vec3(1,0) = vec3(1,1,1); }) ); 490 assert( mustExcept({ img.pixel!vec2(1,0) = vec2(1,1); }) ); 491 static assert( !__traits(compiles, { img.pixel!vec3(4,4) = vec3(1,1); }) ); 492 }