Image Handling

viren2d was designed to have as little external dependencies as possible. Thus, it uses its own ImageBuffer class to encapsulate image data. In real applications, however, you usually work with standard libraries, such as OpenCV or NumPy. The following examples show how to pass image data between these libraries.

viren2d NumPy

The ImageBuffer exposes a buffer view following the Python buffer protocol, and thus, provides fast direct access to its raw internal data representation. This means that conversion to a numpy.ndarray is as simple as:

 1import numpy as np
 2import viren2d
 3
 4# Get an ImageBuffer, e.g. as the result of previous drawing steps:
 5painter = viren2d.Painter(height=600, width=800, color='azure')
 6img_buf = painter.get_canvas()
 7
 8# Create a NumPy array which shares the allocated memory with `img_buf`:
 9img_np = np.array(img_buf, copy=False)
10
11# Create a NumPy array as a copy of the ImageBuffer's memory:
12img_np = np.array(img_buf, copy=True)

NumPy viren2d

Conversion from numpy.ndarray to ImageBuffer is easy thanks to the buffer protocol. You only need to decide whether to copy the data or not:

 1import numpy as np
 2import viren2d
 3
 4img_np = np.ones((600, 800, 3), dtype=np.uint8)
 5
 6# Create a shared buffer (no memory allocation):
 7img_buf = viren2d.ImageBuffer(img_np, copy=False)
 8
 9# Create a copy:
10img_buf = viren2d.ImageBuffer(img_np, copy=True)

The only caveat is that converting NumPy array views (e.g. slices, non-contiguous buffers, etc.) to ImageBuffer will always result in a deeply copied buffer (as viren2d requires contiguous memory layouts):

 1import numpy as np
 2import viren2d
 3
 4# Create a non-contiguous view, e.g. by flipping the color channels:
 5img_np_bgr = np.ones((600, 800, 3), dtype=np.uint8)
 6img_np_rgb = img_np_bgr[:, :, ::-1]
 7
 8# Convert this view to an ImageBuffer:
 9img_buf = viren2d.ImageBuffer(img_np_rgb, copy=True)
10
11# Non-contiguous views will *always* result in a copy!
12# If the `copy` flag contradicts this, viren2d will log a warning
13# message and ignore the `copy` request:
14img_buf = viren2d.ImageBuffer(img_np_rgb, copy=False)
15
16# Note that this warning can be suppressed:
17img_buf = viren2d.ImageBuffer(img_np_rgb, copy=False, disable_warnings=True)

viren2d OpenCV

Since both ImageBuffer and OpenCV’s cv::Mat use standard C-style memory layout, i.e. row-major storage order, converting between these classes is straightforward.

The only caveat is that OpenCV works with images in BGR(A) format by design, whereas viren2d uses the RGB(A) format. Thus, usually you’ll need to swap the red and blue channels:

 1// Request a copy of the canvas, because an ImageBuffer swaps
 2// its channels **in-place**.
 3viren2d::ImageBuffer img_buffer = painter->GetCanvas(true);
 4
 5// Convert color format to BGR(A)
 6img_buffer.SwapChannels(0, 2);
 7
 8// Create an OpenCV matrix header which reuses the memory (to
 9// avoid additional memory allocation).
10// Note that the painter will always return its visualization
11// as ImageBuffer of type `ImageBufferType::UInt8`, which
12// corresponds to OpenCV's `CV_8U`, as the underlying type
13// for both is `uint8_t`.
14cv::Mat cv_buffer(
15    img_buffer.Height(), img_buffer.Width(),
16    CV_MAKETYPE(CV_8U, img_buffer.Channels()),
17    img_buffer.MutableData(), img_buffer.RowStride());

OpenCV viren2d

Converting from a cv::Mat to a viren2d::ImageBuffer is similarly straightforward:

 1// Suppose we already have an OpenCV image of type `CV_32F`
 2cv::Mat img_cv = ...
 3
 4// Either create a shared ImageBuffer:
 5viren2d::ImageBuffer img_buf;
 6
 7img_buf.CreateSharedBuffer(
 8    img_cv.data, img_cv.rows, img_cv.cols,
 9    img_cv.channels(), img_cv.step,
10    viren2d::ImageBufferType::Float);
11
12// Or let the ImageBuffer copy the data:
13img_buf.CreateCopy(
14    img_cv.data,img_cv.rows, img_cv.cols,
15    img_cv.channels(), img_cv.step,
16    viren2d::ImageBufferType::Float);
17
18// If the OpenCV image is in BGR(A) format, you
19// may want to convert it to RGB(A). Note, however,
20// that this operation changes the buffer in-place!
21img_buf.SwapChannels(0, 2);