I have this image:
And I would like to change the group with white pixels just in contact with the black pixels by red pixels. It's possible to replace especific colors by another using this code:
import numpy as np
from PIL import Image
im = Image.open('fig1.png')
data = np.array(im)
r1, g1, b1 = 255, 255, 255 # Original value
r2, g2, b2 = 0, 0, 0 # Value that we want to replace it with
red, green, blue = data[:,:,0], data[:,:,1], data[:,:,2]
mask = (red == r1) & (green == g1) & (blue == b1)
data[:,:,:3][mask] = [r2, g2, b2]
im = Image.fromarray(data)
Is it possible to replace a sample of white pixels for red pixels, but just the white pixels in contact with black pixels?
CodePudding user response:
Updated Answer
I am still not 100% certain what you want, but this should give you a good basis for doing what you want:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
from skimage.morphology import dilation
# Load image - and make into Numpy array
# It is a palette image. 0=black, 1=white, 2=red
im = Image.open('start.png')
na = np.array(im)
# Make mask of white pixels - True where white
whiteMask = na==1
Image.fromarray((whiteMask*255).astype(np.uint8)).save('DEBUG-whiteMask.png')
# Make mask of black pixels - True where black
blackMask = na==0
Image.fromarray((blackMask*255).astype(np.uint8)).save('DEBUG-blackMask.png')
# Footprint of structuring element for morphology
# Unused at the moment:
# footprint = np.zeros((3, 3), dtype=np.uint8)
# footprint[1, 1] = 1
# Do the morphology on blackMask
touchingBlack = dilation(blackMask, footprint=None)
# Now find pixels that are both white and touching black
resultMask = np.logical_and(whiteMask, touchingBlack)
Image.fromarray((resultMask*255).astype(np.uint8)).save('DEBUG-resultMask.png')
# Make those pixels red
na[resultMask] = 2
# Convert back into PIL Image and reinstate original palette
res = Image.fromarray(na)
res.putpalette(im.getpalette())
res.save('result.png')
It uses my "corrected" starting image from below, and produces this:
Original Answer
I'll update this answer if/when you provide a decent lossless PNG input image and clarify what you mean by "pixels touching black".
For the moment, I have "corrected" your image to just three colours:
and tried my algorithm with ImageMagick in the Terminal for expedience until we are sure what the aim is.
# Find white pixels
magick start.png -fill black opaque white white.png
# Find black pixels
magick start.png -fill white opaque black -negate black.png
# Find pixels touching black pixels
magick black.png -morphology dilate square:1 black-d.png
# Find black pixels that are both white and touching black
magick white.png black-d.png -compose darken -composite result.png
I "corrected" the original image, which was a lossy JPEG into a 3-colour PNG as follows...
First, I made a swatch of the 3 pure colours I wanted to remap the image to. I did that with ImageMagick as follows:
magick xc:red xc:black xc:white append swatch.png
That makes a tiny 3x1 image with 1 red, 1 black and 1 white pixel. Then I remapped the original image to those 3 colours with ImageMagick using:
magick original.jpg dither -remap swatch.png start.png
Then I hand-edited 6-8 stray pixels.