Home > Software design >  Change color of a sample pixels in contact with a determinated color - python
Change color of a sample pixels in contact with a determinated color - python

Time:03-23

I have this image:

enter image description here

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:

enter image description here

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:

enter image description here

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

enter image description here

# Find black pixels
magick start.png -fill white  opaque black -negate black.png

enter image description here

# Find pixels touching black pixels
magick black.png -morphology dilate square:1 black-d.png

enter image description here

# Find black pixels that are both white and touching black
magick white.png black-d.png -compose darken -composite result.png

enter image description here


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.

  • Related