1.. _chap-imagebuf: 2 3ImageBuf: Image Buffers 4####################### 5 6 7ImageBuf Introduction and Theory of Operation 8============================================= 9 10ImageBuf is a utility class that stores an entire image. It provides a 11nice API for reading, writing, and manipulating images as a single unit, 12without needing to worry about any of the details of storage or I/O. 13 14All I/O involving ImageBuf (that is, calls to `read` or `write`) are 15implemented underneath in terms of ImageCache, ImageInput, and ImageOutput, 16and so support all of the image file formats supported by OIIO. 17 18The ImageBuf class definition requires that you:: 19 20 #include <OpenImageIO/imagebuf.h> 21 22 23.. doxygenenum:: OIIO::ImageBuf::IBStorage 24 25 26Constructing, destructing, resetting an ImageBuf 27================================================ 28 29There are several ways to construct an ImageBuf. Each constructor has a 30corresponding `reset` method that takes the same arguments. Calling `reset` 31on an existing ImageBuf is equivalent to constructing a new ImageBuf from 32scratch (even if the ImageBuf, prior to reset, previously held an image). 33 34 35Making an empty or uninitialized ImageBuf 36----------------------------------------- 37 38.. doxygenfunction:: OIIO::ImageBuf::ImageBuf() 39.. doxygenfunction:: OIIO::ImageBuf::reset() 40 41 42Constructing a readable ImageBuf 43-------------------------------- 44 45.. doxygenfunction:: OIIO::ImageBuf::ImageBuf(string_view name, int subimage = 0, int miplevel = 0, ImageCache *imagecache = nullptr, const ImageSpec *config = nullptr, Filesystem::IOProxy *ioproxy = nullptr) 46.. doxygenfunction:: OIIO::ImageBuf::reset(string_view name, int subimage = 0, int miplevel = 0, ImageCache *imagecache = nullptr, const ImageSpec *config = nullptr, Filesystem::IOProxy *ioproxy = nullptr) 47 48 49Constructing a writable ImageBuf 50-------------------------------------------------- 51 52.. doxygenfunction:: OIIO::ImageBuf::ImageBuf(const ImageSpec &spec, InitializePixels zero = InitializePixels::Yes) 53.. doxygenfunction:: OIIO::ImageBuf::reset(const ImageSpec &spec, InitializePixels zero = InitializePixels::Yes) 54.. doxygenfunction:: OIIO::ImageBuf::make_writable 55 56 57Constructing an ImageBuf that "wraps" an application buffer 58------------------------------------------------------------- 59 60.. doxygenfunction:: OIIO::ImageBuf::ImageBuf(const ImageSpec &spec, void *buffer) 61.. doxygenfunction:: OIIO::ImageBuf::reset(const ImageSpec &spec, void *buffer) 62 63 64 65Reading and Writing disk images 66------------------------------- 67 68.. doxygenfunction:: OIIO::ImageBuf::read(int subimage = 0, int miplevel = 0, bool force = false, TypeDesc convert = TypeDesc::UNKNOWN, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) 69.. doxygenfunction:: OIIO::ImageBuf::read(int subimage, int miplevel, int chbegin, int chend, bool force, TypeDesc convert, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) 70.. doxygenfunction:: OIIO::ImageBuf::init_spec 71 72.. doxygenfunction:: OIIO::ImageBuf::write(string_view filename, TypeDesc dtype = TypeUnknown, string_view fileformat = string_view(), ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) const 73.. doxygenfunction:: OIIO::ImageBuf::write(ImageOutput *out, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) const 74.. doxygenfunction:: OIIO::ImageBuf::set_write_format(TypeDesc format) 75.. doxygenfunction:: OIIO::ImageBuf::set_write_format(cspan<TypeDesc> format) 76.. doxygenfunction:: OIIO::ImageBuf::set_write_tiles 77.. doxygenfunction:: OIIO::ImageBuf::set_write_ioproxy 78 79 80 81Getting and setting information about an ImageBuf 82================================================= 83 84.. doxygenfunction:: OIIO::ImageBuf::initialized 85.. doxygenfunction:: OIIO::ImageBuf::storage 86.. doxygenfunction:: OIIO::ImageBuf::spec 87.. doxygenfunction:: OIIO::ImageBuf::nativespec 88.. doxygenfunction:: OIIO::ImageBuf::specmod 89.. doxygenfunction:: OIIO::ImageBuf::name 90.. doxygenfunction:: OIIO::ImageBuf::file_format_name 91.. doxygenfunction:: OIIO::ImageBuf::subimage 92.. doxygenfunction:: OIIO::ImageBuf::nsubimages 93.. doxygenfunction:: OIIO::ImageBuf::miplevel 94.. doxygenfunction:: OIIO::ImageBuf::nmiplevels 95.. doxygenfunction:: OIIO::ImageBuf::nchannels 96 97 98.. cpp:function:: int xbegin() const 99 int xend() const 100 int ybegin() const 101 int yend() const 102 int zbegin() const 103 int zend() const 104 105 Returns the `[begin,end)` range of the pixel data window of the buffer. 106 These are equivalent to `spec().x`, `spec().x+spec().width`, `spec().y`, 107 `spec().y+spec().height`, `spec().z`, and `spec().z+spec().depth`, 108 respectively. 109 110.. doxygenfunction:: OIIO::ImageBuf::orientation 111.. doxygenfunction:: OIIO::ImageBuf::set_orientation 112 113.. cpp:function:: int oriented_width() const 114 int oriented_height() const 115 int oriented_x() const 116 int oriented_y() const 117 int oriented_full_width() const 118 int oriented_full_height() const 119 int oriented_full_x() const 120 int oriented_full_y() const 121 122 The oriented width, height, x, and y describe the pixel data window 123 after taking the display orientation into consideration. The *full* 124 versions the "full" (a.k.a. display) window after taking the display 125 orientation into consideration. 126 127 128.. doxygenfunction:: OIIO::ImageBuf::roi 129.. doxygenfunction:: OIIO::ImageBuf::roi_full 130.. doxygenfunction:: OIIO::ImageBuf::set_origin 131.. doxygenfunction:: OIIO::ImageBuf::set_full 132.. doxygenfunction:: OIIO::ImageBuf::set_roi_full 133.. doxygenfunction:: OIIO::ImageBuf::contains_roi 134.. doxygenfunction:: OIIO::ImageBuf::pixeltype 135.. doxygenfunction:: OIIO::ImageBuf::threads() const 136.. doxygenfunction:: OIIO::ImageBuf::threads(int n) const 137 138 139 140Copying ImageBuf's and blocks of pixels 141======================================== 142 143.. doxygenfunction:: OIIO::ImageBuf::operator=(const ImageBuf &src) 144.. doxygenfunction:: OIIO::ImageBuf::operator=(ImageBuf &&src) 145.. doxygenfunction:: OIIO::ImageBuf::copy(const ImageBuf &src, TypeDesc format = TypeUnknown) 146.. doxygenfunction:: OIIO::ImageBuf::copy(TypeDesc format) const 147.. doxygenfunction:: OIIO::ImageBuf::copy_metadata 148.. doxygenfunction:: OIIO::ImageBuf::copy_pixels 149.. doxygenfunction:: OIIO::ImageBuf::swap 150 151 152 153Getting and setting pixel values 154================================ 155 156**Getting and setting individual pixels -- slow** 157 158.. doxygenfunction:: OIIO::ImageBuf::getchannel 159.. doxygenfunction:: OIIO::ImageBuf::getpixel(int x, int y, int z, float *pixel, int maxchannels = 1000, WrapMode wrap = WrapBlack) const 160 161.. doxygenfunction:: OIIO::ImageBuf::interppixel 162.. doxygenfunction:: OIIO::ImageBuf::interppixel_bicubic 163.. doxygenfunction:: OIIO::ImageBuf::interppixel_NDC 164.. doxygenfunction:: OIIO::ImageBuf::interppixel_bicubic_NDC 165 166.. doxygenfunction:: OIIO::ImageBuf::setpixel(int x, int y, int z, cspan<float> pixel) 167.. doxygenfunction:: OIIO::ImageBuf::setpixel(int i, cspan<float> pixel) 168 169| 170 171**Getting and setting regions of pixels -- fast** 172 173.. doxygenfunction:: OIIO::ImageBuf::get_pixels 174.. doxygenfunction:: OIIO::ImageBuf::set_pixels 175 176 177 178Deep data in an ImageBuf 179======================== 180 181.. doxygenfunction:: OIIO::ImageBuf::deep 182.. doxygenfunction:: OIIO::ImageBuf::deep_samples 183.. doxygenfunction:: OIIO::ImageBuf::set_deep_samples 184.. doxygenfunction:: OIIO::ImageBuf::deep_insert_samples 185.. doxygenfunction:: OIIO::ImageBuf::deep_erase_samples 186.. doxygenfunction:: OIIO::ImageBuf::deep_value(int x, int y, int z, int c, int s) const 187.. doxygenfunction:: OIIO::ImageBuf::deep_value_uint(int x, int y, int z, int c, int s) const 188.. doxygenfunction:: OIIO::ImageBuf::set_deep_value(int x, int y, int z, int c, int s, float value) 189.. doxygenfunction:: OIIO::ImageBuf::set_deep_value(int x, int y, int z, int c, int s, uint32_t value) 190.. doxygenfunction:: OIIO::ImageBuf::deep_pixel_ptr 191 192.. cpp:function:: DeepData& OIIO::ImageBuf::deepdata() 193 const DeepData& OIIO::ImageBuf::deepdata() const 194 195 Returns a reference to the underlying `DeepData` for a deep image. 196 197 198 199Error Handling 200============== 201 202.. doxygenfunction:: OIIO::ImageBuf::errorf 203.. doxygenfunction:: OIIO::ImageBuf::has_error 204.. doxygenfunction:: OIIO::ImageBuf::geterror 205 206 207Miscellaneous 208============= 209 210.. cpp:function:: void* localpixels() 211 const void* localpixels() const 212 213 Return a raw pointer to the "local" pixel memory, if they are fully in 214 RAM and not backed by an ImageCache, or `nullptr` otherwise. You can 215 also test it like a `bool` to find out if pixels are local. 216 217.. cpp:function:: void* pixeladdr(int x, int y, int z = 0, int ch = 0) 218 const void* pixeladdr(int x, int y, int z = 0, int ch = 0) const 219 220 Return the address where pixel `(x,y,z)`, channel `ch`, is stored in the 221 image buffer. Use with extreme caution! Will return `nullptr` if the 222 pixel values aren't local in RAM. 223 224 225.. doxygenfunction:: OIIO::ImageBuf::pixelindex 226.. doxygenfunction:: OIIO::ImageBuf::WrapMode_from_string 227 228 229 230Iterators -- the fast way of accessing individual pixels 231======================================================== 232 233Sometimes you need to visit every pixel in an ImageBuf (or at least, every 234pixel in a large region). Using the `getpixel()` and `setpixel()` for this 235purpose is simple but very slow. But ImageBuf provides templated `Iterator` 236and `ConstIterator` types that are very inexpensive and hide all the details 237of local versus cached storage. 238 239 .. note:: `ImageBuf::ConstIterator` is identical to the Iterator, 240 except that `ConstIterator` may be used on a `const ImageBuf` and 241 may not be used to alter the contents of the ImageBuf. For 242 simplicity, the remainder of this section will only discuss the 243 `Iterator`. 244 245An Iterator is associated with a particular ImageBuf. The Iterator has a 246*current pixel* coordinate that it is visiting, and an *iteration range* 247that describes a rectangular region of pixels that it will visits as it 248advances. It always starts at the upper left corner of the iteration 249region. We say that the iterator is *done* after it has visited every pixel 250in its iteration range. We say that a pixel coordinate *exists* if it is 251within the pixel data window of the ImageBuf. We say that a pixel 252coordinate is *valid* if it is within the iteration range of the iterator. 253 254The `Iterator<BUFT,USERT>` is templated based on two types: `BUFT` the type 255of the data stored in the ImageBuf, and `USERT` type type of the data that 256you want to manipulate with your code. `USERT` defaults to `float`, since 257usually you will want to do all your pixel math with `float`. We will 258thus use `Iterator<T>` synonymously with `Iterator<T,float>`. 259 260For the remainder of this section, we will assume that you have a 261`float`-based ImageBuf, for example, if it were set up like this:: 262 263 ImageBuf buf ("myfile.exr"); 264 buf.read (0, 0, true, TypeDesc::FLOAT); 265 266 267.. cpp:function:: Iterator<BUFT> (ImageBuf &buf, WrapMode wrap=WrapDefault) 268 269 Initialize an iterator that will visit every pixel in the data window 270 of `buf`, and start it out pointing to the upper left corner of 271 the data window. The `wrap` describes what values will be retrieved 272 if the iterator is positioned outside the data window of the buffer. 273 274.. cpp:function:: Iterator<BUFT> (ImageBuf &buf, const ROI &roi, WrapMode wrap=WrapDefault) 275 276 Initialize an iterator that will visit every pixel of `buf` within the 277 region described by `roi`, and start it out pointing to pixel 278 (`roi.xbegin, roi.ybegin, roi.zbegin`). The `wrap` describes what values 279 will be retrieved if the iterator is positioned outside the data window 280 of the buffer. 281 282.. cpp:function:: Iterator<BUFT> (ImageBuf &buf, int x, int y, int z, WrapMode wrap=WrapDefault) 283 284 Initialize an iterator that will visit every pixel in the data window 285 of `buf`, and start it out pointing to pixel (x, y, z). 286 The `wrap` describes what values will be retrieved 287 if the iterator is positioned outside the data window of the buffer. 288 289.. cpp:function:: Iterator::operator++ () 290 291 The `++` operator advances the iterator to the next pixel in its 292 iteration range. (Both prefix and postfix increment operator are 293 supported.) 294 295.. cpp:function:: bool Iterator::done () const 296 297 Returns `true` if the iterator has completed its visit of all pixels in 298 its iteration range. 299 300.. cpp:function:: ROI Iterator::range () const 301 302 Returns the iteration range of the iterator, expressed as an ROI. 303 304.. cpp:function:: int Iterator::x () const 305 int Iterator::y () const 306 int Iterator::z () const 307 308 Returns the x, y, and z pixel coordinates, respectively, of the pixel 309 that the iterator is currently visiting. 310 311.. cpp:function:: bool Iterator::valid () const 312 313 Returns `true` if the iterator's current pixel coordinates are within 314 its iteration range. 315 316.. cpp:function:: bool Iterator::valid (int x, int y, int z=0) const 317 318 Returns `true` if pixel coordinate (x, y, z) are within the iterator's 319 iteration range (regardless of where the iterator itself is currently 320 pointing). 321 322.. cpp:function:: bool Iterator::exists () const 323 324 Returns `true` if the iterator's current pixel coordinates are within 325 the data window of the ImageBuf. 326 327.. cpp:function:: bool Iterator::exists (int x, int y, int z=0) const 328 329 Returns `true` if pixel coordinate (x, y, z) are within the pixel data 330 window of the ImageBuf (regardless of where the iterator itself is 331 currently pointing). 332 333.. cpp:function:: USERT& Iterator::operator[] (int i) 334 335 The value of channel `i` of the current pixel. (The wrap mode, set up 336 when the iterator was constructed, determines what value is returned if 337 the iterator points outside the pixel data window of its buffer.) 338 339.. cpp:function:: int Iterator::deep_samples () const 340 341 For deep images only, retrieves the number of deep samples for the 342 current pixel. 343 344.. cpp:function:: void Iterator::set_deep_samples () 345 346 For deep images only (and non-const ImageBuf), set the number of deep 347 samples for the current pixel. This only is useful if the ImageBuf has 348 not yet had the `deep_alloc()` method called. 349 350.. cpp:function:: USERT Iterator::deep_value (int c, int s) const 351 uint32_t Iterator::deep_value_int (int c, int s) const 352 353 For deep images only, returns the value of channel `c`, sample number 354 `s`, at the current pixel. 355 356.. cpp:function:: void Iterator::set_deep_value (int c, int s, float value) 357 void Iterator::set_deep_value (int c, int s, uint32_t value) 358 359 For deep images only (and non-cconst ImageBuf, sets the value of channel 360 `c`, sample number `s`, at the current pixel. This only is useful if the 361 ImageBuf has already had the `deep_alloc()` method called. 362 363 364Example: Visiting all pixels to compute an average color 365-------------------------------------------------------- 366 367.. code-block:: cpp 368 369 void print_channel_averages (const std::string &filename) 370 { 371 // Set up the ImageBuf and read the file 372 ImageBuf buf (filename); 373 bool ok = buf.read (0, 0, true, TypeDesc::FLOAT); // Force a float buffer 374 if (! ok) 375 return; 376 377 // Initialize a vector to contain the running total 378 int nc = buf.nchannels(); 379 std::vector<float> total (n, 0.0f); 380 381 // Iterate over all pixels of the image, summing channels separately 382 for (ImageBuf::ConstIterator<float> it (buf); ! it.done(); ++it) 383 for (int c = 0; c < nc; ++c) 384 total[c] += it[c]; 385 386 // Print the averages 387 imagesize_t npixels = buf.spec().image_pixels(); 388 for (int c = 0; c < nc; ++c) 389 std::cout << "Channel " << c << " avg = " (total[c] / npixels) << "\n"; 390 } 391 392 393.. _sec-make-black: 394 395Example: Set all pixels in a region to black 396-------------------------------------------- 397 398.. code-block:: cpp 399 400 bool make_black (ImageBuf &buf, ROI region) 401 { 402 if (buf.spec().format != TypeDesc::FLOAT) 403 return false; // Assume it's a float buffer 404 405 // Clamp the region's channel range to the channels in the image 406 roi.chend = std::min (roi.chend, buf.nchannels); 407 408 // Iterate over all pixels in the region... 409 for (ImageBuf::Iterator<float> it (buf, region); ! it.done(); ++it) { 410 if (! it.exists()) // Make sure the iterator is pointing 411 continue; // to a pixel in the data window 412 for (int c = roi.chbegin; c < roi.chend; ++c) 413 it[c] = 0.0f; // clear the value 414 } 415 return true; 416 } 417 418 419Dealing with buffer data types 420============================== 421 422The previous section on iterators presented examples and discussion based on 423the assumption that the ImageBuf was guaranteed to store `float` data and 424that you wanted all math to also be done as `float` computations. Here we 425will explain how to deal with buffers and files that contain different data 426types. 427 428Strategy 1: Only have `float` data in your ImageBuf 429----------------------------------------------------- 430 431When creating your own buffers, make sure they are `float`:: 432 433 ImageSpec spec (640, 480, 3, TypeDesc::FLOAT); // <-- float buffer 434 ImageBuf buf ("mybuf", spec); 435 436When using ImageCache-backed buffers, force the ImageCache 437to convert everything to `float`:: 438 439 // Just do this once, to set up the cache: 440 ImageCache *cache = ImageCache::create (true /* shared cache */); 441 cache->attribute ("forcefloat", 1); 442 ... 443 ImageBuf buf ("myfile.exr"); // Backed by the shared cache 444 445Or force the read to convert to `float` in the buffer if 446it's not a native type that would automatically stored as a `float` 447internally to the ImageCache:[#]_ 448 449.. [#] ImageCache only supports a limited set of types internally, currently 450 float, half, uint8, uint16, and all other data types are converted to 451 these automatically as they are read into the cache. 452 453.. code-block:: cpp 454 455 ImageBuf buf ("myfile.exr"); // Backed by the shared cache 456 buf.read (0, 0, false /* don't force read to local mem */, 457 TypeDesc::FLOAT /* but do force conversion to float*/); 458 459Or force a read into local memory unconditionally (rather 460than relying on the ImageCache), and convert to `float`:: 461 462 ImageBuf buf ("myfile.exr"); 463 buf.read (0, 0, true /*force read*/, 464 TypeDesc::FLOAT /* force conversion */); 465 466Strategy 2: Template your iterating functions based on buffer type 467------------------------------------------------------------------ 468 469Consider the following alternate version of the `make_black` function 470from Section `Example: Set all pixels in a region to black`_ :: 471 472 template<type BUFT> 473 static bool make_black_impl (ImageBuf &buf, ROI region) 474 { 475 // Clamp the region's channel range to the channels in the image 476 roi.chend = std::min (roi.chend, buf.nchannels); 477 478 // Iterate over all pixels in the region... 479 for (ImageBuf::Iterator<BUFT> it (buf, region); ! it.done(); ++it) { 480 if (! it.exists()) // Make sure the iterator is pointing 481 continue; // to a pixel in the data window 482 for (int c = roi.chbegin; c < roi.chend; ++c) 483 it[c] = 0.0f; // clear the value 484 } 485 return true; 486 } 487 488 bool make_black (ImageBuf &buf, ROI region) 489 { 490 if (buf.spec().format == TypeDesc::FLOAT) 491 return make_black_impl<float> (buf, region); 492 else if (buf.spec().format == TypeDesc::HALF) 493 return make_black_impl<half> (buf, region); 494 else if (buf.spec().format == TypeDesc::UINT8) 495 return make_black_impl<unsigned char> (buf, region); 496 else if (buf.spec().format == TypeDesc::UINT16) 497 return make_black_impl<unsigned short> (buf, region); 498 else { 499 buf.error ("Unsupported pixel data format %s", buf.spec().format); 500 retrn false; 501 } 502 } 503 504In this example, we make an implementation that is templated on the buffer 505type, and then a wrapper that calls the appropriate template specialization 506for each of 4 common types (and logs an error in the buffer for any other 507types it encounters). 508 509In fact, :file:`imagebufalgo_util.h` provides a macro to do this (and 510several variants, which will be discussed in more detail in the next 511chapter). You could rewrite the example even more simply:: 512 513 #include <OpenImageIO/imagebufalgo_util.h> 514 515 template<type BUFT> 516 static bool make_black_impl (ImageBuf &buf, ROI region) 517 { 518 ... same as before ... 519 } 520 521 bool make_black (ImageBuf &buf, ROI region) 522 { 523 bool ok; 524 OIIO_DISPATCH_COMMON_TYPES (ok, "make_black", make_black_impl, 525 buf.spec().format, buf, region); 526 return ok; 527 } 528 529This other type-dispatching helper macros will be discussed in more 530detail in Chapter :ref:`chap-imagebufalgo`. 531 532