RLE，Run-Length Encoding，变动长度编码算法，是一种对于二值图像的编码方法，以不同码字来表示连续的黑、白像素数. RLE 是计算连续出现的资料长度再进行压缩，是一种简单的非破坏性资料压缩法，且压缩和解压缩都非常快.

## 1. RLE 说明

COCO 关于 RLE 的描述如下：

RLE is a simple yet efficient format for storing binary masks.
RLE first divides a vector (or vectorized image) into a series of piecewise constant regions and then for each piece simply stores the length of that piece.
For example, given M=[0 0 1 1 1 0 1] the RLE counts would be [2 3 1 1], or for M=[1 1 1 1 1 1 0] the counts would be [0 6 1] (note that the odd counts are always the numbers of zeros).
Instead of storing the counts directly, additional compression is achieved with a variable bitrate representation based on a common scheme called LEB128.

Compression is greatest given large piecewise constant regions. Specifically, the size of the RLE is proportional to the number of boundaries in M (or for an image the number of boundaries in the y direction).
Assuming fairly simple shapes, the RLE representation is O(sqrt(n)) where n is number of pixels in the object. Hence space usage is substantially lower, especially for large simple objects (large n).

Many common operations on masks can be computed directly using the RLE (without need for decoding). This includes computations such as area, union, intersection, etc. All of these operations are linear in the size of the RLE, in other words they are O(sqrt(n)) where n is the area of the object. Computing these operations on the original mask is O(n).

Thus, using the RLE can result in substantial computational savings.

RLE 首先将向量(或向量化的图片)分片，得到一系列分片的常量区域；然后，每一个分片只简单的保存其长度.

## 2. RLE 与 PNG 转换

### 2.1. PNG2RLE

#!--*-- coding: utf- --*--
import numpy as np

'''
Returns run length as string formated
'''
pixels = np.concatenate([[0], pixels, [0]])
runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
runs[1::2] -= runs[::2]
return ' '.join(str(x) for x in runs)

### 2.2. RLE2PNG

#!--*-- coding: utf- --*--
import numpy as np

'''
mask_rle: run-length as string formated (start length)
shape: (height,width) of array to return
Returns numpy array, 1 - mask, 0 - background
'''
starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]
starts -= 1
ends = starts + lengths
binary_mask = np.zeros(shape[0] * shape[1], dtype=np.uint8)
for lo, hi in zip(starts, ends):
return binary_mask.reshape(shape)

### 2.3. 示例

'''
RLE: Run-Length Encode
'''
from PIL import Image
import numpy as np

def __main__():

# encode

# decode
binary_mask_decode = self.rle_decode(rle_mask, binary_mask.shape[:2])

'''
RLE: Run-Length Encode
'''
#!--*-- coding: utf- --*--
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

# M1:
class general_rle(object):
'''
ref.: https://www.kaggle.com/stainsby/fast-tested-rle
'''
def __init__(self):
pass

# We avoid issues with '1' at the start or end (at the corners of
# the original image) by setting those pixels to '0' explicitly.
# We do not expect these to be non-zero for an accurate mask,
# so this should not harm the score.
pixels[0] = 0
pixels[-1] = 0
runs = np.where(pixels[1:] != pixels[:-1])[0] + 2
runs[1::2] = runs[1::2] - runs[:-1:2]
return runs

def rle_to_string(self, runs):
return ' '.join(str(x) for x in runs)

def check(self):
test_mask = np.asarray([[0, 0, 0, 0],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 0, 0]])
assert rle_to_string(rle_encode(test_mask)) == '7 2 11 2'

# M2:
'''
ref.: https://www.kaggle.com/paulorzp/run-length-encode-and-decode
'''
def __init__(self):
pass

'''
Returns run length as string formated
'''
pixels = np.concatenate([[0], pixels, [0]])
runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
runs[1::2] -= runs[::2]
return ' '.join(str(x) for x in runs)

'''
mask_rle: run-length as string formated (start length)
shape: (height,width) of array to return
Returns numpy array, 1 - mask, 0 - background
'''
starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]
starts -= 1
ends = starts + lengths
binary_mask = np.zeros(shape[0] * shape[1], dtype=np.uint8)
for lo, hi in zip(starts, ends):

def check(self):

# encode
assert binary_mask2.shape == binary_mask.shape