简体   繁体   中英

How to change the set of pixel colors in contatc with black color

Considering this image:

在此处输入图像描述

I would like to change the set of white pixels in contact with black pixels by red, this way: 在此处输入图像描述

I tried to use this code in python :

import numpy as np
from PIL import Image

im = Image.open('image.png')
data = np.array(im)

r1, g1, b1 = 255, 255, 255 # Original value
r2, g2, b2 = 0, 0, 255 # 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)

But I changed all white pixels by red. But could be an UNIX approach suggestion too.

Please, post lossless versions of your input images. Lossy images modify the value of the pixels, creating artifacts that affect processing. I recreated your image and saved it as a lossless PNF file .

I'm using OpenCV to get the result you want. I created a mask with the non-zero elements of your original input. Then, I used Flood-fill to fill the outer shapes with the color you want. The final image can be obtained if you AND both images.

Let's see the code:

# import opencv:
import cv2

# image path
path = "D://opencvImages//"
fileName = "rectsLossless.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Grayscale image:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Get non-zero mask:
binaryThresh = 1
_, binaryMask = cv2.threshold(grayscaleImage, binaryThresh, 255, cv2.THRESH_BINARY)

This bit creates the non-zero pixels mask:

This will help to zero all the elements that are non-white. That image is the first part of the mask. Now, let's fill the outer shapes with red color. This is achieved in three steps:

# Get image dimensions:
(imageHeight, imageWidth) = inputImage.shape[:2]

# Get image center:
xCenter = int(0.5 * imageWidth)
yCenter = int(0.5 * imageHeight)

# Get flood-fill target color
floodColor = inputImage[yCenter, xCenter]
print("Flood Color: %s" % floodColor)
# numpy array to tuple
floodColor = (int(floodColor[0]), int(floodColor[1]), int(floodColor[2]))

The first step gets the actual filling color. I suppose that the red is located more or less at the center of the image. Then, the second step involves filling all the "foreground" pixels with white. Let's seed at the top left corner:

# Flood fill at top left corner:
leftCorner = (1, 1)
whiteColor = (255, 255, 255)
cv2.floodFill(inputImage, None, leftCorner, whiteColor)

This is the result:

Note how the shapes that are partially outside of the red rectangle are all now connected by the white color. Let's fill again, but this time using the red color I extracted previously:

# Second Flood-fill
cv2.floodFill(inputImage, None, leftCorner, floodColor)

This yields the following image:

Let's create the final image by AND ing this result with the original non-zero mask:

# Create final image:
outImage = cv2.bitwise_and(inputImage, inputImage, mask=binaryMask)

This is the final result:

The question is very close to this question .
My solution is close too...

Assuming the colors are black white and red (the general case may be trickier), we may use the following stages:

  • Fill the black background with white color (using cv2.floodFill ).
    The white object on the red boundary are merged with the background.
  • Fill the white background with black color (using cv2.floodFill ).
    The white object on the red boundary are going to be black.
  • Copy the red color channel from the original image to "filled" image.
    The red channel of a white pixel is 255, so black and white becomes red.

Code sample:

import cv2
import numpy as np

img = cv2.imread('red_white_black.jpg')

# Copy the original image to img2
img2 = img.copy()

# Fill the black background with white color
cv2.floodFill(img2, None, seedPoint=(0, 0), newVal=(255, 255, 255), loDiff=(50, 50, 50), upDiff=(50, 50, 50))

cv2.imshow('black background', img2)  # Show img2 for testing


# Fill the white background with black color
cv2.floodFill(img2, None, seedPoint=(0, 0), newVal=(0, 0, 0), loDiff=(50, 50, 50), upDiff=(50, 50, 50))

cv2.imshow('white background', img2)  # Show img2 for testing


# Copy the red color channel from the original image to img2
img2[:, :, 2] = img[:, :, 2]

cv2.imshow('img2', img2)  # Show img2 for testing
cv2.waitKey()
cv2.destroyAllWindows()

Results:
Black background:
在此处输入图像描述

White background:
在此处输入图像描述

img2 :
在此处输入图像描述

The black margins around the red, are because the original image is JPEG and not PNG (colors are not pure), and the red is not pure red.

We may fix it using the following code (the code in not very elegant)...

red = img[:, :, 2]
r = np.median(img[:, :, 2][red > 50])
g = np.median(img[:, :, 1][red > 50])
b = np.median(img[:, :, 0][red > 50])

mask = np.logical_and(img[:, :, 0] > 100, img2[:, :, 0] <= 100)
img3 = img2.copy()
img3[:, :, 2][mask] = r
img3[:, :, 1][mask] = g
img3[:, :, 0][mask] = b
img3[:, :, 2] = cv2.morphologyEx(img3[:, :, 2], cv2.MORPH_CLOSE, np.ones((3, 3), np.uint8))
img3[:, :, 1] = cv2.morphologyEx(img3[:, :, 1], cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))
img3[:, :, 0] = cv2.morphologyEx(img3[:, :, 0], cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))
cv2.imshow('img3', img3)
cv2.waitKey()
cv2.destroyAllWindows()

Result:
在此处输入图像描述

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