Grok
| Language | C++ (C API in grok.h), bindings for Python, C#, Java (SWIG) and Rust |
|---|---|
| License | AGPL-3.0 (copyleft, also for network use) |
| Repository | github.com/GrokImageCompression/grok |
| Documentation | wiki, comments in src/lib/core/grok.h, examples in examples/core |
| Depends on | nothing for the library; libtiff, libpng, libjpeg, lcms2 for the command-line tools |
| Read / write | yes / yes; JP2, J2K, HTJ2K (Part 15), Motion JPEG 2000, IMF and DCI cinema profiles |
Grok started as a fork of OpenJPEG and was largely rewritten in modern C++ for speed. It decodes and encodes HTJ2K, decodes regions of huge tiled images quickly using TLM and PLT markers, reads directly from S3 and other cloud storage, and handles ICC profiles, XMP, IPTC and Exif metadata. GDAL has a Grok driver (JP2Grok), which is the easiest way to use it for geospatial files.
The license is the main limitation: AGPL-3.0 requires you to release the source of your program if you distribute it or let users interact with it over a network. The C API changes between major versions, so pin the version you build against.
Installation#
# Debian / Ubuntu
sudo apt install grokj2k-tools libgrokj2k1-dev
# macOS
brew install grokj2k
# from source (CMake, C++20 compiler)
git clone --recursive https://github.com/GrokImageCompression/grok.git
cd grok
cmake -B build
cmake --build build --parallel
Basic usage: command-line tools#
grk_compress and grk_decompress take the same kind of options as OpenJPEG’s tools:
grk_decompress -i in.jp2 -o out.png # decode
grk_decompress -i in.jp2 -o thumb.png -r 3 # discard 3 resolution levels (1/8 size)
grk_decompress -i in.jp2 -o part.tif -d 0,0,2048,2048 # decode a region
grk_compress -i in.tif -o out.jp2 # lossless
grk_compress -i in.tif -o out.jp2 -I -r 40,20,10 # lossy 9/7 wavelet, 3 quality layers
grk_compress -i in.tif -o out.jp2 -I --qfactor 85 # lossy with a JPEG-style quality factor
grk_compress -i in.tif -o out.j2k -w 24 # DCI 2K cinema profile at 24 fps
grk_compress -i in.tif -o out.j2k -M 64 # HTJ2K, much faster to encode and decode
grk_transcode rewrites an existing file without decoding it, for example to add TLM and PLT markers for fast random access: grk_transcode -i in.jp2 -o out.jp2 -X -L.
How to decode a JP2 file with the C API#
The decoder reads from a file name, a memory buffer or callbacks, described by grk_stream_params:
#include <grok.h>
#include <cstdio>
#include <cstring>
int main() {
grk_initialize(nullptr, 0, nullptr); // no plugin, use all CPU cores
grk_stream_params stream = {};
strncpy(stream.file, "in.jp2", sizeof(stream.file) - 1);
grk_decompress_parameters params = {};
grk_object *codec = grk_decompress_init(&stream, ¶ms);
if (!codec) {
fprintf(stderr, "cannot open file\n");
return 1;
}
grk_header_info header = {};
if (!grk_decompress_read_header(codec, &header) || !grk_decompress(codec, nullptr)) {
fprintf(stderr, "decode failed\n");
grk_object_unref(codec);
return 1;
}
grk_image *image = grk_decompress_get_image(codec); // owned by the codec
printf("%u x %u, %u components\n", image->x1 - image->x0, image->y1 - image->y0,
image->numcomps);
for (uint16_t c = 0; c < image->numcomps; c++) {
const grk_image_comp &comp = image->comps[c];
// samples are int16_t when comp.data_type == GRK_INT_16, else int32_t;
// rows are comp.stride samples apart
printf("component %u: %u x %u, %u bits\n", c, comp.w, comp.h, comp.prec);
}
grk_object_unref(codec);
grk_deinitialize();
return 0;
}
How to read only the header#
After grk_decompress_read_header, grk_header_info describes the file without decoding pixels: header.header_image has the image bounds and component layout, t_width / t_height the tile size and t_grid_width / t_grid_height the number of tiles.
How to decode at reduced resolution or a region#
Set the fields of grk_decompress_parameters before grk_decompress_init:
grk_decompress_parameters params = {};
params.core.reduce = 2; // 1/4 width and height
params.core.layers_to_decompress = 1; // first quality layer only
params.dw_x0 = 1000; // decode window, full-resolution coordinates
params.dw_y0 = 1000;
params.dw_x1 = 3000;
params.dw_y1 = 2500;
grk_object *codec = grk_decompress_init(&stream, ¶ms);
Tips#
- For encoding from your own pixel buffers, start from
examples/core/core_simple.cppin the repository: it showsgrk_image_new,grk_compress_initandgrk_compress. grk_compress -M 64encodes HTJ2K (high-throughput block coding); mode 64 can’t be combined with other-Mflags, and-r/-qrate control isn’t supported for HTJ2K.