11

I have a picture were I want to change all white-ish pixels to grey, but only for a certain area of the image. Example picture, I just want to change the picture outside of the red rectangle, without changing the image within the red rectangle:

Image

I already have the general code, which was part of someone elses Stackoverflow question, that changes the colour of every white pixel instead of only just the one outside of an area.

image = cv.imread("meme 2.jpg")
hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)

# Define lower and uppper limits of what we call "white-ish"
sensitivity = 19
lower_white = np.array([0, 0, 255 - sensitivity])
upper_white = np.array([255, sensitivity, 255])

# Mask image to only select white
mask = cv.inRange(hsv, lower_white, upper_white)

# Change image to grey where we found brown
image[mask > 0] = (170, 170, 170)
cv.imwrite(file, image)
halfer
  • 19,824
  • 17
  • 99
  • 186
paul_on_pc
  • 139
  • 1
  • 1
  • 7
  • Can you make a link to the other person's question? – Riley May 23 '20 at 23:59
  • Here the link to the other persons code https://stackoverflow.com/questions/50210304/i-want-to-change-the-colors-in-image-with-python-from-specific-color-range-to-an – paul_on_pc May 24 '20 at 11:36

2 Answers2

4

Here is one way to do that in Python/OpenCV.

  • Read the input
  • Convert to HSV color space
  • Threshold on desired color to make a mask
  • Use the mask to change the color of all corresponding pixels in the image
  • Draw a new rectangular mask for the region where you do not want to change
  • Invert the new mask for the region where you do want to change
  • Apply the new mask to the original image
  • Apply the inverted new mask to the color changed image
  • Add the two results together to form the final image
  • Save the results


Input:

enter image description here

import cv2
import numpy as np

# Read image
image = cv2.imread('4animals.jpg')

# Convert to HSV
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# Define lower and uppper limits of what we call "white-ish"
sensitivity = 19
lower_white = np.array([0, 0, 255 - sensitivity])
upper_white = np.array([255, sensitivity, 255])

# Create mask to only select white
mask = cv2.inRange(hsv, lower_white, upper_white)

# Change image to grey where we found white
image2 = image.copy()
image2[mask > 0] = (170, 170, 170)

# Create new rectangular mask that is white on black background
x,y,w,h = 33,100,430,550
mask2 = np.zeros_like(image)
cv2.rectangle(mask2, (x,y), (x+w,y+h), (255, 255, 255), -1)

# invert mask
mask2_inv = 255 - mask2

# apply mask to image
image_masked = cv2.bitwise_and(image, mask2)

# apply inverted mask to image2
image2_masked = cv2.bitwise_and(image2, mask2_inv)

# add together
result = cv2.add(image_masked, image2_masked)

# save results
cv2.imwrite('4animals_mask.jpg', mask)
cv2.imwrite('4animals_modified.png', image2)
cv2.imwrite('4animals_mask2.jpg', mask2)
cv2.imwrite('4animals_mask2_inv.jpg', mask2_inv)
cv2.imwrite('4animals_masked.jpg', image_masked)
cv2.imwrite('4animals_modified_masked.jpg', image2_masked)
cv2.imwrite('4animals_result.jpg', result)

cv2.imshow('mask', mask)
cv2.imshow('image2', image2)
cv2.imshow('mask2', mask2 )
cv2.imshow('mask2_inv', mask2_inv)
cv2.imshow('image_masked', image_masked)
cv2.imshow('image2_masked', image2_masked)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Color mask:

enter image description here

Rectangle mask:

enter image description here

Inverted rectangle mask:

enter image description here

Color changed image:

enter image description here

Masked input:

enter image description here

Masked color changed image:

enter image description here

Result:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
3

Here is another simpler method in Python/OpenCV. My previous answer was overly complicated.

  • Read the input
  • Convert to HSV color space
  • Create a mask image by color thresholding
  • Draw a black rectangle on the previous mask for where you do not want to change the color
  • Apply the new combined mask to the image to change the color in the desired region
  • Save the result


Input:

enter image description here

import cv2
import numpy as np

# Read image
image = cv2.imread('4animals.jpg')

# Convert to HSV
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# Define lower and uppper limits of what we call "white-ish"
sensitivity = 19
lower_white = np.array([0, 0, 255 - sensitivity])
upper_white = np.array([255, sensitivity, 255])

# Create mask to only select white
mask = cv2.inRange(hsv, lower_white, upper_white)

# Draw new rectangular mask on old mask that is black inside the rectangle and white outside the rectangle
x,y,w,h = 33,100,430,550
mask2 = mask.copy()
cv2.rectangle(mask2, (x,y), (x+w,y+h), 0, -1)

# Change image to grey where we found white for combined mask
result = image.copy()
result[mask2 > 0] = (170, 170, 170)

# save results
cv2.imwrite('4animals_mask.jpg', mask)
cv2.imwrite('4animals_mask2.jpg', mask2)
cv2.imwrite('4animals_result.jpg', result)

cv2.imshow('mask', mask)
cv2.imshow('mask2', mask2 )
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Mask from color thresholding:

enter image description here

Modified mask with rectangle drawn over it:

enter image description here

Result:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • 1
    If they helped you, please consider up-voting one or the other – fmw42 May 24 '20 at 17:42
  • I dont have enough reputation to upvote the comments unfortunatelly. This is what it says "Thanks for the feedback! Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score.". But it should still count for u! – paul_on_pc May 24 '20 at 20:27
  • 1
    OK. Thanks for trying. – fmw42 May 24 '20 at 21:14