Unfortunately there is no fast way to do this. Whether you use a loop or an optimized numpy solution, under the hood you have to check each pixel against each element of rgbL. Given all that, you can use isin combined with a structured array view of the data to perform the correct grouping.
The trick is to make a custom type that represents pixels. That way, you can compare entire elements rather than color channels, which is what isin does by default.
pixel = np.dtype([('r', img.dtype), ('g', img.dtype), ('b', img.dtype)])
You can not define the pixel datatype as a sub-array type like this pixel = np.dtype((img.dtype, img.shape[-1])). That will cause an error when you try to view the data.
You can now view img as a 2D array of pixel elements, rather than a 3D array of uint8 or float, as the case may be:
data = img.view(pixel).squeeze(axis=-1)
You can do the same with your list:
rgbA = np.array(rgbL).view(pixel).squeeze(axis=-1)
You can also do
rgbA = np.array([tuple(p) for p in rgbL], dtype=pixel)
You can't use np.array(rgbL, dtype=pixel) because the elements will not be initialized correctly in this case.
Now isin will give you the mask you want:
mask = np.isin(data, rgbA)
You can apply the mask directly:
data[mask] = 255
If you want to set a value that differs between channels, remember to use a tuple to specify pixel entries:
>>> data[mask] = (255, 255, 255) # OK
>>> data[mask] = [255, 255, 255]
ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 1 output values where the mask is true
Since data is a view into img, you are done.
Note: This will only work if your image is contiguous in the third dimension. Otherwise, you will not be able to use the same datatype for both arrays. If that is not the case, you will have to copy the image data so that it is contiguous at least in the third dimension. For most sane arrays that aren't weird views across a hyperspectral image or something, you will be OK.
TL;DR
pixel = np.dtype([('r', img.dtype), ('g', img.dtype), ('b', img.dtype)])
def replace(img, pixels, value):
data = img.view(pixel).squeeze(-1)
pixels = np.array(pixels, dtype=img.dtype).view(pixel).squeeze(-1)
data[isin(data, pixels)] = tuple(value)