kwimage.im_runlen module

Logic pertaining to run-length encodings. Can encode an ndarray as a RLE or decode an RLE into an ndarray.

SeeAlso:
kwimage.structs.mask - stores binary segmentation masks, using RLEs as a

backend representation. Also contains cython logic for handling the coco-rle format.

kwimage.im_runlen.encode_run_length(img, binary=False, order='C')[source]

Construct the run length encoding (RLE) of an image.

Parameters:
  • img (ndarray) – 2D image

  • binary (bool) – If true, assume that the input image only contains 0’s and 1’s. Set to True for compatibility with COCO (which does not support multi-value RLE encodings).

  • order (str) – Order of the encoding. Either ‘C’ for row major or ‘F’ for column-major. Defaults to ‘C’.

Returns:

encoding: dictionary items are:

counts (ndarray): the run length encoding

shape (Tuple): the original image shape.

This should be in standard shape row-major (e.g. h/w) order

binary (bool):

if True, the counts are assumed to encode only 0’s and 1’s, otherwise the counts encoding specifies any numeric values.

order (str):

Encoding order, either ‘C’ for row major or ‘F’ for column-major. Defaults to ‘C’.

Return type:

Dict[str, object]

SeeAlso:
kwimage.Mask -

cython-backed data structure to handle coco-style RLEs

Example

>>> import ubelt as ub
>>> lines = ub.codeblock(
>>>     '''
>>>     ..........
>>>     ......111.
>>>     ..2...111.
>>>     .222..111.
>>>     22222.....
>>>     .222......
>>>     ..2.......
>>>     ''').replace('.', '0').splitlines()
>>> img = np.array([list(map(int, line)) for line in lines])
>>> encoding = encode_run_length(img)
>>> target = np.array([0,16,1,3,0,3,2,1,0,3,1,3,0,2,2,3,0,2,1,3,0,1,2,5,0,6,2,3,0,8,2,1,0,7])
>>> assert np.all(target == encoding['counts'])

Example

>>> binary = True
>>> img = np.array([[1, 0, 1, 1, 1, 0, 0, 1, 0]])
>>> encoding = encode_run_length(img, binary=True)
>>> assert encoding['counts'].tolist() == [0, 1, 1, 3, 2, 1, 1]

Example

>>> # Test empty case
>>> from kwimage.im_runlen import *  # NOQA
>>> binary = True
>>> img = np.zeros((0, 0), dtype=np.uint8)
>>> encoding = encode_run_length(img, binary=True)
>>> assert encoding['counts'].tolist() == []
>>> recon = decode_run_length(**encoding)
>>> assert np.all(recon == img)

Example

>>> # Test small full cases
>>> for d in [0, 1, 2, 3]:
>>>     img = np.zeros((d, d), dtype=np.uint8)
>>>     encoding = encode_run_length(img, binary=True)
>>>     recon = decode_run_length(**encoding)
>>>     assert np.all(recon == img)
>>>     img = np.ones((d, d), dtype=np.uint8)
>>>     encoding = encode_run_length(img, binary=True)
>>>     recon = decode_run_length(**encoding)
>>>     assert np.all(recon == img)
kwimage.im_runlen.decode_run_length(counts, shape, binary=False, dtype=<class 'numpy.uint8'>, order='C')[source]

Decode run length encoding back into an image.

Parameters:
  • counts (ndarray) – the run-length encoding

  • shape (Tuple[int, int]) – the height / width of the mask

  • binary (bool) – if the RLE is binary or non-binary. Set to True for compatibility with COCO.

  • dtype (type) – data type for decoded image. Defaults to np.uint8.

  • order (str) – Order of the encoding. Either ‘C’ for row major or ‘F’ for column-major. Defaults to ‘C’.

Returns:

the reconstructed image

Return type:

ndarray

Example

>>> from kwimage.im_runlen import *  # NOQA
>>> img = np.array([[1, 0, 1, 1, 1, 0, 0, 1, 0]])
>>> encoded = encode_run_length(img, binary=True)
>>> recon = decode_run_length(**encoded)
>>> assert np.all(recon == img)
>>> import ubelt as ub
>>> lines = ub.codeblock(
>>>     '''
>>>     ..........
>>>     ......111.
>>>     ..2...111.
>>>     .222..111.
>>>     22222.....
>>>     .222......
>>>     ..2.......
>>>     ''').replace('.', '0').splitlines()
>>> img = np.array([list(map(int, line)) for line in lines])
>>> encoded = encode_run_length(img)
>>> recon = decode_run_length(**encoded)
>>> assert np.all(recon == img)
kwimage.im_runlen.rle_translate(rle, offset, output_shape=None)[source]

Translates a run-length encoded image in RLE-space.

Parameters:
  • rle (dict) – an enconding dict returned by kwimage.encode_run_length()

  • offset (Tuple[int, int]) – x, y integer offsets.

  • output_shape (Tuple[int, int]) – h,w of transformed mask. If unspecified the input rle shape is used.

SeeAlso:

# ITK has some RLE code that looks like it can perform translations https://github.com/KitwareMedical/ITKRLEImage/blob/master/include/itkRLERegionOfInterestImageFilter.h

Doctest

>>> # test that translate works on all zero images
>>> img = np.zeros((7, 8), dtype=np.uint8)
>>> rle = encode_run_length(img, binary=True, order='F')
>>> new_rle = rle_translate(rle, (1, 2), (6, 9))
>>> assert np.all(new_rle['counts'] == [54])

Example

>>> from kwimage.im_runlen import *  # NOQA
>>> img = np.array([
>>>     [1, 1, 1, 1],
>>>     [0, 1, 0, 0],
>>>     [0, 1, 0, 1],
>>>     [1, 1, 1, 1],], dtype=np.uint8)
>>> rle = encode_run_length(img, binary=True, order='C')
>>> offset = (1, -1)
>>> output_shape = (3, 5)
>>> new_rle = rle_translate(rle, offset, output_shape)
>>> decoded = decode_run_length(**new_rle)
>>> print(decoded)
[[0 0 1 0 0]
 [0 0 1 0 1]
 [0 1 1 1 1]]

Example

>>> from kwimage.im_runlen import *  # NOQA
>>> img = np.array([
>>>     [0, 0, 0],
>>>     [0, 1, 0],
>>>     [0, 0, 0]], dtype=np.uint8)
>>> rle = encode_run_length(img, binary=True, order='C')
>>> new_rle = rle_translate(rle, (1, 0))
>>> decoded = decode_run_length(**new_rle)
>>> print(decoded)
[[0 0 0]
 [0 0 1]
 [0 0 0]]
>>> new_rle = rle_translate(rle, (0, 1))
>>> decoded = decode_run_length(**new_rle)
>>> print(decoded)
[[0 0 0]
 [0 0 0]
 [0 1 0]]
kwimage.im_runlen._rle_bytes_to_array(s, impl='auto')[source]

Uncompresses a coco-bytes RLE into an array representation.

Parameters:
  • s (bytes) – compressed coco bytes rle

  • impl (str) – which implementation to use (defaults to cython is possible)

CommandLine

xdoctest -m ~/code/kwimage/kwimage/im_runlen.py _rle_bytes_to_array

Benchmark

>>> import ubelt as ub
>>> from kwimage.im_runlen import _rle_bytes_to_array
>>> s = b';?1B10O30O4'
>>> ti = ub.Timerit(1000, bestof=50, verbose=2)
>>> # --- time python impl ---
>>> for timer in ti.reset('python'):
>>>     with timer:
>>>         _rle_bytes_to_array(s, impl='python')
>>> # --- time cython impl ---
>>> # xdoctest: +REQUIRES(--mask)
>>> for timer in ti.reset('cython'):
>>>     with timer:
>>>         _rle_bytes_to_array(s, impl='cython')
kwimage.im_runlen._rle_array_to_bytes(counts, impl='auto')[source]

Compresses an array RLE into a coco-bytes RLE.

Parameters:
  • counts (ndarray) – uncompressed array rle

  • impl (str) – which implementation to use (defaults to cython is possible)

Example

>>> # xdoctest: +REQUIRES(--mask)
>>> from kwimage.im_runlen import _rle_array_to_bytes
>>> from kwimage.im_runlen import _rle_bytes_to_array
>>> arr_counts = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
>>> str_counts = _rle_array_to_bytes(arr_counts)
>>> arr_counts2 = _rle_bytes_to_array(str_counts)
>>> assert np.all(arr_counts2 == arr_counts)

Benchmark

>>> # xdoctest: +REQUIRES(--mask)
>>> import ubelt as ub
>>> from kwimage.im_runlen import _rle_array_to_bytes
>>> from kwimage.im_runlen import _rle_bytes_to_array
>>> counts = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
>>> ti = ub.Timerit(1000, bestof=50, verbose=2)
>>> # --- time python impl ---
>>> #for timer in ti.reset('python'):
>>> #    with timer:
>>> #        _rle_array_to_bytes(s, impl='python')
>>> # --- time cython impl ---
>>> for timer in ti.reset('cython'):
>>>     with timer:
>>>         _rle_array_to_bytes(s, impl='cython')