The fastest way around this, if the image is stored in "chunky" format, i.e. the color planes dimension is the last, and this last dimension is contiguous, is to take a np.void view of every 24bits pixel, then run the result through np.unique and np.bincount:
>>> arr = np.random.randint(256, size=(10, 10, 3)).astype(np.uint8) >>> dt = np.dtype((np.void, arr.shape[-1]*arr.dtype.itemsize)) >>> if arr.strides[-1] != arr.dtype.itemsize: ... arr = np.ascontiguousarray(arr) ... >>> arr_view = arr.view(dt)
The contents of arr_view look like garbage:
>>> arr_view [0, 0] array([Â], dtype='|V3')
But it's not us that have to understand the content:
>>> unq, _ = np.unique(arr_view, return_inverse=True) >>> unq_cnts = np.bincount(_) >>> unq = unq.view(arr.dtype).reshape(-1, arr.shape[-1])
And now you have the unique pixels and their counts in those two arrays:
>>> unq[:5] array([[ 0, 82, 78], [ 6, 221, 188], [ 9, 209, 85], [ 14, 210, 24], [ 14, 254, 88]], dtype=uint8) >>> unq_cnts[:5] array([1, 1, 1, 1, 1], dtype=int64)
np.uniquegive me how often each color appears in the array?np.unique/np.bincountcombo on it. You'll need to view the return ofnp.uniqueas 3uint8s and reshape it to(-1, 3)to make sense of the data, but it will be much faster, as nothing is done aside from viewing the exact same memory in a different way.