简体   繁体   中英

Numpy filtering using array

I know this has been asked before but there doesn't seem to be anything for my specific use-case.

I have a numpy array obs which represents a color image and has shape (252, 288, 3) .

I want to convert every pixel that is not pure black to pure white.

What I have tried is obs[obs,= [0, 0, 0]] = [255, 255, 255] but it gives the following exception:

ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 807 output values where the mask is true

The result is the same with obs[obs[:, :],= [0, 0, 0]] = [255, 255, 255] . Also, (obs[:, :],= [0, 0. 0]).shape is (252, 288, 3) and I do not understand why it isnt simply (252, 288) (a matrix of bools).

I thought about using obs[obs != 0] = 255 but that would not have the effect I want since a pixel that is pure green ( [0, 255, 0] ) would be processed component wise and would still be [0, 255, 0] after the filtering, instead of being actually white ( [255, 255, 255] ).

Why isn't what I have tried up until now working and how should I go about this?

Boolean indexing like obs[obs,= [0, 0, 0]] return a 1D array with all the elements from obs that satisfy the given condition. Look at the follwoing example:

obs = np.array([
 [[88, 0,99],
  [ 0, 0, 0]],
 [[ 0, 0, 0],
  [88,77,66]]
])

obs,= [0, 0, 0] returns a boolean array:

array([[[ True, False,  True],
        [False, False, False]],
       [[False, False, False],
        [ True,  True,  True]]])

and obs[obs,= [0, 0, 0]] then returns a 1D array with all the elements where the mask is True : array([88, 99, 88, 77, 66]) .

So what you need is where to test if there'sany color component not equal 0:

np.where(obs.any(axis=-1, keepdims=True), 255, obs)

Result:

array([[[255, 255, 255],
        [  0,   0,   0]],
       [[  0,   0,   0],
        [255, 255, 255]]])

Note that you need keepdims=True to enable broadcasting to the original shape of obs . Otherwise you'd have to add the lost dimension by np.where(obs.any(-1)[...,np.newaxis], 255, obs) or np.where(np.atleast_3d(obs.any(-1)), 255, obs) which is less elegant.

There are a number of possibilities depending what you actually want to do. Let's do the set-up code (which is common to all possibilities) first so you can see what I mean.

#!/usr/bin/env python3

import numpy as np

# Make a repeatable random image
np.random.seed(764)
obs = np.random.randint(0,32,(252,288,3), dtype=np.uint8)

This image happens to have pure black pixels at the following locations for test purposes:

obs[ 21, 267]
obs[ 28, 252]
obs[ 69, 127]
obs[ 98,   0]
obs[124, 210]
obs[133,  98]
obs[160,  81]
obs[167,  48]
obs[217, 237]

Now, suppose you want a new, pure True/False boolean mask of black pixels, you can use:

mask = obs.any(axis=-1)

That solution has the following characteristics:

time: 876 µs
mask.shape: (252,288)
mask.nbytes: 72576
mask.dtype: 'bool'

You can subsequently use and re-use that mask like this:

# Make masked pixels red
obs[mask,:] = [255,0,0]

# Make unmasked pixels cyan
obs[~mask,:] = [0,255,255]

在此处输入图像描述


Now let's suppose you want a new, greyscale image, with black and white pixels, you can use:

grey = obs.any(axis=-1) * np.uint8(255)

That solution has the following characteristics:

time: 887 µs
grey.shape: (252,288)
grey.nbytes: 72576
grey.dtype: np.uint8

Now suppose you want in-place alteration of your already existing "obs" to pure black and white (but still RGB):

obs[obs.any(axis=-1),:] = [255,255,255]

That solution has the following characteristics:

time: 1.98 ms
obs.shape: (252,288,3)
obs.nbytes: 217728
obs.dtype: np.uint8

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM