rainbow diffraction patternenter image description hereI have this image that i try to process and want to keep only the visible rainbows and have the background black instead of this grayish color. Is it possible?
What I tried was clustering but only detected the colors like a countor:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from skimage.io import imread
from skimage.util import img_as_float

# Read image and convert to float [0,1]
img = img_as_float(imread('upscale.jpg'))

# Reshape image into N x 3 color pixels
pixels = img.reshape(-1, 3)

# Number of color clusters (you can adjust this)
k = 5

# Run k-means clustering on the colors
kmeans = KMeans(n_clusters=k, random_state=0, n_init=3)
idx = kmeans.fit_predict(pixels)
C = kmeans.cluster_centers_

# Reconstruct clustered image
clustered_img = C[idx].reshape(img.shape)

# Show original and clustered version
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].imshow(img)
axes[0].set_title('Original Image')
axes[0].axis('off')

axes[1].imshow(clustered_img)
axes[1].set_title(f'Color Clusters (k = {k})')
axes[1].axis('off')

plt.tight_layout()
plt.show()

What I want to see is just the rainbow spots on the black background. I also took the CCD image of the rainbow from a diffraction grating.

3 Replies 3

You might want to look into HSV (Hue, Saturation, Value) color space or similar. In those spaces you might be able to filter out the gray pixels (e.g., low saturation and low value).

Agree with bdarbell regarding the use of HSV to filter out the gray values. Here's what I would do concretely:

  • Convert the image to HSV.
  • Create a mask by multiplying the S (saturation) and V (value/brightness) channels. In doing so, you get a mask that is low in regions of low brightness (small V) or low saturation (small S) and high only in bright, saturated regions (large V and S).
  • Set S and V to the maximum value in your image (effectively ignoring them), convert back to RGB, then add your new mask as an alpha channel (transparency).

Now you have multiple options, for example:

  • Save the resulting image as RGBA: The RGB channels represent your (now fully bright, fully saturated) colors, the alpha channel dims them, as described for the mask above.
  • Combine with a constant (e.g. black) background, then save as RGB.
  • For visualization, combine with a checkerboard pattern.

Here is exemplary code that implements all three options:

from os.path import expanduser, join
import numpy as np
from PIL import Image

folder = expanduser("~/Desktop")  # TODO: provide your actual path here
img = Image.open(join(folder, "rainbow.jpg"))

# Helper functions: array (range 0–1) to image (range 0–255) and back, checkerboard pattern
to_img = lambda i, *a, **kw: Image.fromarray((np.asarray(i) * 255).astype(np.uint8), *a, **kw)
to_arr = lambda i: np.asarray(i).astype(float) / 255

def checkerboard(size, square_size, color1=(191,191,191,255), color2=(255,255,255,255)):
    c, r, s = np.arange(size[0]), np.arange(size[1]), square_size
    p = (np.add.outer((r // s), (c // s)) % 2).astype(bool)[..., None]  # pattern
    return p * np.asarray(color1, dtype=np.uint8) + ~p * np.asarray(color2, dtype=np.uint8)


# Convert from RGB to HSV, then split the channels
hue, sat, val = np.split(to_arr(img.convert("HSV")), 3, axis=2)

# Create mask from saturation and value (brightness)
alpha = np.squeeze(sat * val, axis=-1)  # H×W×1 → H×W

# Saturate the S and V channels, then create corresponding RGBA image
sat, val = np.ones_like(sat), np.ones_like(val)
rgba_img = to_img(np.concatenate([hue, sat, val], axis=-1), mode="HSV").convert("RGB")
rgba_img.putalpha(to_img(alpha, mode="L"))

# Save as RGBA image
rgba_img.save(join(folder, "rainbow_rgba.png"))
# Blend with black background, then save as RGB image
black_img = Image.new("RGBA", rgba_img.size, (0, 0, 0, 255))
Image.alpha_composite(black_img, rgba_img).convert("RGB").save(join(folder, "rainbow_black.png"))
# Blend with checkerboard background, then save as RGB image
checker_img = Image.fromarray(checkerboard(rgba_img.size, square_size=8), mode="RGBA")
Image.alpha_composite(checker_img, rgba_img).convert("RGB").save(join(folder, "rainbow_checker.png"))

Here's what the result looks like with black background: result with black background Here's the result with checkerboard background: result with checkerboard background

It might also help to "play" with the mask (array alpha) and/or the corresponding channels (arrays sat, val), e.g. normalize their actual minimum and maximum value to the full 0–1 range and adjust their gamma value, before combining the channels to the mask or the mask with the image.

shut the rainbow off. then you have just background. that you can subtract.

make sure you have full control of your camera. people usually don't have the level of control that's needed for this.

messing around with HSV requires the camera to be white-balanced to the background. if it's not, the results will be off for sure. if you had the camera white-balanced to the background, then you should also have had a chance to take a picture of only the background, which means you can just subtract as I said above.

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.