Quick Start Guide#
This section shows the basic decode and encode flows. Error checking and resource cleanup are left out to keep the code short.
Every nvTIFF function returns an nvtiffStatus_t, which should be checked in real code. Handles and buffers should be released with the corresponding Destroy, Close and free functions.
Note
More examples are available in the CUDALibrarySamples GitHub repository. They are complete, buildable programs covering decoding, multi-image and pyramid TIFFs, region-of-interest decoding, encoding options, and GeoTIFF metadata.
nvTIFF Decode#
We start by creating a decoder and opening the TIFF file. The decoder holds the work buffers used during decoding, and the stream holds parsed TIFF metadata and I/O resource.
cudaStream_t stream;
cudaStreamCreate(&stream);
nvtiffDecoder_t decoder;
nvtiffDecoderCreateSimple(&decoder, stream);
nvtiffStream_t tiff_stream;
nvtiffStreamOpenFromFile("input.tif", &tiff_stream);
Next, we look up the image to decode. Images are identified by the offset of their IFD, but for brevity, an offset of 0 selects the first image.
nvtiffImageInfo_t info = {};
nvtiffStreamGetImageInfo(tiff_stream, 0, &info);
We allocate a device buffer for the decoded pixels and describe it with an nvtiffImage_t.
size_t image_size = (size_t)info.image_width * info.image_height * (info.bits_per_pixel / 8);
void* image_d = nullptr;
cudaMalloc(&image_d, image_size);
size_t pitch = 0; // 0 means tightly packed rows
nvtiffImage_t image = {};
image.plane_data = &image_d;
image.plane_pitch_bytes = &pitch;
image.num_planes = 1;
Now we can decode. Without any other parameters specified, nvtiffDecodeParams_t defaults to decoding the whole image.
nvtiffDecodeParams_t params;
nvtiffDecodeParamsCreate(¶ms);
nvtiffDecode(tiff_stream, decoder, params, &image, stream);
cudaStreamSynchronize(stream);
To decode a different image or a region of an image, call nvtiffDecodeParamsSetRegions before
nvtiffDecode. Several regions can be decoded in one call, each into its own output buffer.
A TIFF can also be opened from host memory with nvtiffStreamOpen.
nvTIFF Batched ROI Decode#
This example continues from the decode above and decodes two regions of the first image in a single call. We describe each region with an nvtiffDecodeRegion_t. A width or height of 0 extends the region to the edge of the image.
nvtiffDecodeRegion_t regions[2] = {};
regions[0].offset_x = 0;
regions[0].offset_y = 0;
regions[0].width = 256;
regions[0].height = 256;
regions[1].offset_x = 256;
regions[1].offset_y = 0;
regions[1].width = 256;
regions[1].height = 256;
Each region is decoded into its own output buffer, so we need one nvtiffImage_t per region.
size_t roi_size = (size_t)256 * 256 * (info.bits_per_pixel / 8);
void* roi_d[2] = {};
size_t roi_pitch[2] = {0, 0};
nvtiffImage_t roi_images[2] = {};
for (int i = 0; i < 2; i++) {
cudaMalloc(&roi_d[i], roi_size);
roi_images[i].plane_data = &roi_d[i];
roi_images[i].plane_pitch_bytes = &roi_pitch[i];
roi_images[i].num_planes = 1;
}
Now we set the regions on the decode parameters and decode.
nvtiffDecodeParamsSetRegions(params, regions, 2);
nvtiffDecode(tiff_stream, decoder, params, roi_images, stream);
cudaStreamSynchronize(stream);
The regions are decoded together as a batch when they are compatible. Otherwise nvtiffDecode returns NVTIFF_STATUS_BATCH_INCOMPATIBLE. By default a region belongs to the first image. To decode regions of another image, set ifd_offset in the region.
nvTIFF Encode#
Start by creating an encoder and an encode parameters object. The encoder holds the work buffers used during encoding. It can be reused for more than one encode.
nvtiffEncoder_t encoder;
nvtiffEncoderCreate(&encoder, nullptr, nullptr, stream);
nvtiffEncodeParams_t params;
nvtiffEncodeParamsCreate(¶ms);
Describe the image to write. This example is an 8-bit RGB image compressed with LZW.
nvtiffImageInfo_t info = {};
info.image_width = width;
info.image_height = height;
info.samples_per_pixel = 3;
info.bits_per_pixel = 24;
for (int i = 0; i < 3; i++) {
info.bits_per_sample[i] = 8;
info.sample_format[i] = NVTIFF_SAMPLEFORMAT_UINT;
}
info.photometric_int = NVTIFF_PHOTOMETRIC_RGB;
info.planar_config = NVTIFF_PLANARCONFIG_CONTIG;
info.compression = NVTIFF_COMPRESSION_LZW;
nvtiffEncodeParamsSetImageInfo(params, &info);
Give the encoder a device buffer that holds the pixels, which must match the image description above. Then we can encode.
size_t image_size = (size_t)width * height * 3;
uint8_t* image_d = nullptr;
cudaMalloc(&image_d, image_size);
// Fill image_d with interleaved 8-bit RGB pixels.
uint8_t* images_d[] = {image_d};
nvtiffEncodeParamsSetInputs(params, images_d, 1);
nvtiffEncode(encoder, ¶ms, 1, stream);
nvtiffEncodeFinalize(encoder, ¶ms, 1, stream);
Then write the TIFF to a file.
nvtiffWriteTiffFile(encoder, ¶ms, 1, "output.tif", stream);
To get the TIFF in memory instead, call nvtiffGetBitstreamSize to find the buffer size needed.
Then call nvtiffWriteTiffBuffer.
Other encoding options are set with the nvtiffEncodeParamsSet... functions described in the API
reference. These cover the compression type, strip or tile layout, JPEG quality, the predictor, BigTIFF,
GeoTIFF keys, and custom TIFF tags.