example: Lazily evaluated channels#

Relevant documentation links:

#include <string>
#include <filesystem>
#include <algorithm>
#include <execution>

#include <compressed/channel.h>


auto main() -> int
{
	// The compressed_image API provides multiple ways of generating lazy chunks to represent sparse data. This generates
	// chunks represented by a single value that take up just a couple of bytes. This method is especially useful when you
	// are planning to fill the channel with sparse data to then pass along to an image or somewhere else.
	auto channel_zeros = compressed::channel<uint16_t>::zeros(1920, 1080);
	auto channel_full = compressed::channel<uint16_t>::full(1920, 1080, 65535 /* fill value */);

	// We can also directly mirror another channel, this doesn't have to be a lazy channel!
	auto channel_zeros_like = compressed::channel<uint16_t>::zeros_like(channel_zeros);
	auto channel_full_like = compressed::channel<uint16_t>::full_like(channel_full, 24 /* fill value */);

	// When working with these lazy channels one has to slightly rethink how they approach modifying chunks within a 
	// channel. This is because the usual `set_chunk` method will actually trigger a non-lazy chunk to be generated using
	// up more memory and being slower
	//
	// So instead of:
	for ([[maybe_unused]] auto chunk : channel_zeros)
	{
		// modify the chunk
	}

	// One should instead do the following:

	// Generate a vector with uninitialized data since we'll set it directly after.
	compressed::util::default_init_vector<uint16_t> chunk_buffer(channel_zeros.chunk_size());

	for (size_t chunk_idx = 0; chunk_idx < channel_zeros.num_chunks(); ++chunk_idx)
	{
		// Only conditionally modify the chunk, do this to avoid breaking the laziness of chunks unless necessary. 
		if (true /*some arbitrary condition*/)
		{
			// Note: we need to ensure this is set to chunk_elems(chunk_idx) as the last chunk of an channel may be smaller
			// than the rest of the chunks in the channel, this way we don't have to worry about the chunk size.
			std::span<uint16_t> chunk_span(chunk_buffer.data(), channel_zeros.chunk_elems(chunk_idx));

			channel_zeros.get_chunk(chunk_span, chunk_idx);

			// modify the data to your hearts content

			channel_zeros.set_chunk(chunk_span, chunk_idx);
		}
	}

	// While lazy chunks are mentioned as a good way of generating sparse data they are also generally the fastest way to 
	// initialize a channel you are planning to populate fully as it is very cheap to instantiate and you only pay the 
	// memory price as you go!
}

Relevant documentation links:

import compressed_image as compressed
import numpy as np


# The compressed_image API provides multiple ways of generating lazy chunks to represent sparse data. This generates
# chunks represented by a single value that take up just a couple of bytes. This method is especially useful when you
# are planning to fill the channel with sparse data to then pass along to an image or somewhere else.
channel_zeros = compressed.Channel.zeros(np.uint16, 1920, 1080)
channel_full = compressed.Channel.full(np.uint16, fill_value=65535, width=1920, height=1080)

# We can also directly mirror another channel, this doesn't have to be a lazy channel!
channel_zeros_like = compressed.Channel.zeros_like(channel_zeros);
channel_full_like = compressed.Channel.full_like(channel_full, fill_value=24);

# When working with these lazy channels one has to slightly rethink how they approach modifying chunks within a 
# channel. This is because the usual `set_chunk` method will actually trigger a non-lazy chunk to be generated using
# up more memory and being slower
#
# So instead of:
for chunk_index in range(channel_zeros.num_chunks()):
    chunk = channel_zeros.get_chunk(chunk_index)
    # Modify channel
    channel_zeros.set_chunk(chunk_index, chunk)

# One should instead do the following:

for chunk_index in range(channel_zeros.num_chunks()):
	# Only conditionally modify the chunk, do this to avoid breaking the laziness of chunks unless necessary. 
	if True: # some condition
		chunk = channel_zeros.get_chunk(chunk_index)
		# Modify channel
		channel_zeros.set_chunk(chunk_index, chunk)

# While lazy chunks are mentioned as a good way of generating sparse data they are also generally the fastest way to 
# initialize a channel you are planning to populate fully as it is very cheap to instantiate and you only pay the 
# memory price as you go!