import cv2
import numpy as np
img = cv2.imread('AFTER_5746.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# find the green color
mask_green = cv2.inRange(hsv, (36,0,0), (86,255,255))
# find the brown color
mask_brown = cv2.inRange(hsv, (90, 60, 20), (30, 255, 200))
# find the yellow color in the leaf
mask_yellow = cv2.inRange(hsv, (14, 39, 64), (40, 255, 255))
# find any of the three colors(green or brown or yellow) in the image
mask = cv2.bitwise_or(mask_green, mask_brown)
mask = cv2.bitwise_or(mask, mask_yellow)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)
cv2.imshow("original", img)
cv2.imshow("final image", res)
cv2.waitKey(0)
cv2.destroyAllWindows()
i use this image segmentation using HSV colormap but the brown area always go missing in the extracted leaf image as shown here:
how to make the mask_brown visible?
Your brown mask is empty (no white). How did you get your values for brown? They are not inclusive of brown in OpenCV HSV. Your brown is at the hue=0/180 wrap-around transition. OpenCV inRange() does not seem to like specifying 160 to 20 as lower and upper, respectfully (without separating into two browns, one from 160 to 180 and the other from 0 to 20). So I just gave it 0 to 180 to cover the range. Don't go too high in Value (V) or you will start including the sky.
Here is a selection of colors for the brown that seems to work for me in Python/OpenCV.
Input:
import cv2
import numpy as np
img = cv2.imread('AFTER_5746.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# find the green color
mask_green = cv2.inRange(hsv, (36,0,0), (86,255,255))
# find the brown color
mask_brown = cv2.inRange(hsv, (0, 0, 0), (180, 255, 160))
# find the yellow color in the leaf
mask_yellow = cv2.inRange(hsv, (14, 39, 64), (40, 255, 255))
# find any of the three colors(green or brown or yellow) in the image
mask = cv2.bitwise_or(mask_green, mask_brown)
mask = cv2.bitwise_or(mask, mask_yellow)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)
cv2.imshow("original", img)
cv2.imshow("mask_green", mask_green)
cv2.imshow("mask_brown", mask_brown)
cv2.imshow("mask_yellow", mask_yellow)
cv2.imshow("mask", mask)
cv2.imshow("final image", res)
cv2.waitKey(0)
cv2.destroyAllWindows()
I assume this is what you want.
Related
I'm trying to make a circle under the player on soccer field, similar to this:
If I just do a circle around the player's feet, it looks bad:
I'm trying to draw the circle only on the green part of the field (to make it more 3D).
First I masked only the green part on the field, using the following code:
def mask(img):
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## mask of green (36,25,25) ~ (86, 255,255)
lower = (36, 25, 25)
upper = (86, 255, 255)
mask = cv2.inRange(hsv, lower, upper)
## slice the green
imask = mask == 0
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]
img = green
return img
This gives the following result:
How can I draw the ellipse only on the green (after the masking - black) part?
P.S. This is the full code:
import cv2
import numpy as np
def mask(img):
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## mask of green (36,25,25) ~ (86, 255,255)
lower = (36, 25, 25)
upper = (86, 255, 255)
mask = cv2.inRange(hsv, lower, upper)
## slice the green
imask = mask == 0
green = np.zeros_like(img, np.uint8)
# green.fill(255)
green[imask] = img[imask]
img = green
return img
def player_ellipse(img, player_point):
axesLength = (45, 20)
angle = 0
startAngle = 0
endAngle = 360
# Red color in BGR
color = (0, 0, 255)
# Line thickness of 5 px
thickness = 5
img = cv2.ellipse(img, player_point, axesLength, angle, startAngle, endAngle, color, thickness)
return img
def main():
img = cv2.imread('img.png')
point = (160, 665)
img = player_ellipse(img, point)
img = mask(img)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
main()
And this is player without any editing:
You are almost there. All that lefts to do is to copy the pixels from the original image (without ellipse) using the founded mask as answered by Micka and Christoph Rackwitz in comments.
Optionally you may apply some morphological operations to make the mask more appealing.
So the steps are:
Draw an ellipse:
Extract a mask using green color:
[Optional] Apply mask erode:
Copy pixels from original image using the mask:
img_with_ellipse[green_mask] = img[green_mask]
Complete example:
import cv2
import numpy as np
def mask(img):
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## mask of green (36,25,25) ~ (86, 255,255)
lower = (36, 25, 25)
upper = (86, 255, 255)
mask = cv2.inRange(hsv, lower, upper)
mask = cv2.erode(mask, np.ones((3, 3), np.uint8), iterations=5) == 0
return mask
def player_ellipse(img, player_point):
axesLength = (45, 20)
angle = 0
startAngle = 0
endAngle = 360
# Red color in BGR
color = (0, 0, 255)
# Line thickness of 5 px
thickness = 5
img = cv2.ellipse(
img.copy(),
player_point,
axesLength,
angle,
startAngle,
endAngle,
color,
thickness,
)
return img
def main():
img = cv2.imread("img.png")
point = (160, 665)
green_mask = mask(img)
img_with_ellipse = player_ellipse(img, point)
img_with_ellipse[green_mask] = img[green_mask]
cv2.imshow("img", img_with_ellipse)
cv2.waitKey(0)
main()
I have done masking the infected area. I want to calculate the percentage of infected area on leaves. This is my code. How to calculate the percentage of infected area?
import cv2
import numpy as np
img = cv2.imread('AFTER_5736.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# find the green color
mask_green = cv2.inRange(hsv, (36, 0, 0), (86,255,255))
# find the brown color
mask_brown = cv2.inRange(hsv, (8, 60, 20), (30, 255, 255))
# find the yellow color in the leaf
mask_yellow = cv2.inRange(hsv, (14, 39, 64), (40, 255, 255))
# find any of the three colors(green or brown or yellow) in the image
#mask = cv2.bitwise_or(mask_green, mask_brown)
#mask = cv2.bitwise_or(mask, mask_yellow)
mask = cv2.bitwise_not(mask_green)
# Bitwise-AND mask and original image
res = cv2.bitwise_not(img, img, mask= mask)
cv2.imshow("final image", res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Original Image
Mask Image indicate the infected area of the leaves
Try taking the sum of the image to see how many pixels are masked out for that color.
>>> sum(sum(mask_brown))
16203
>>> sum(sum(mask_green))
22906
>>> sum(sum(mask_yellow))
9292
>>> brown = sum(sum(mask_brown))
>>> green = sum(sum(mask_green))
>>> yellow = sum(sum(mask_yellow))
>>> total = brown + green + yellow
>>> percentHealthy = green / total
>>> percentHealthy
0.47325468482056154
>>> percentDiseased = (brown + yellow) / total
>>> percentDiseased
0.5267453151794385
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:
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)
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:
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:
Rectangle mask:
Inverted rectangle mask:
Color changed image:
Masked input:
Masked color changed image:
Result:
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:
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:
Modified mask with rectangle drawn over it:
Result:
What changes should I make in below code so that if I know HSV value of green colour, I should get image segments which don't contain the green colour as my output?
Image on which I was working is: source image
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread("sun.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (36, 25, 25), (70, 255,255)) ## mask of green (36,25,25) ~ (86, 255,255)
imask = mask>0 ## slice the green
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]
cv2.imshow('image ',green)
cv2.waitKey(0)
cv2.imwrite("green1.png", green) ##saving
actual output: is the only green coloured portion
expected output: except the green colour portion
You should change the line
green[imask] = img[imask]
with
green = img[!imask] # not of mask to get non-green regions in image
I have road photos from the racing game.
I want to detect yellow and white lanes.
If I use RGB space,
def select_rgb_white_yellow(image):
# white color mask
lower = np.uint8([123, 116, 116])
upper = np.uint8([186, 172, 160])
white_mask = cv2.inRange(image, lower, upper)
# yellow color mask
lower = np.uint8([134, 121, 100])
upper = np.uint8([206, 155, 87])
yellow_mask = cv2.inRange(image, lower, upper)
# combine the mask
mask = cv2.bitwise_or(white_mask, yellow_mask)
masked = cv2.bitwise_and(image, image, mask = mask)
return masked
I only get white lanes.
So tweaked little bit, using HLS space, by using this code.
def convert_hls(image):
return cv2.cvtColor(image, cv2.COLOR_RGB2HLS)
Then, extracting the yellow and white lane again,
def select_white_yellow(image):
converted = convert_hls(image)
# white color mask
lower = np.uint8([0, 0, 0])
upper = np.uint8([0, 0, 255])
white_mask = cv2.inRange(converted, lower, upper)
# yellow color mask
lower = np.uint8([ 10, 0, 100])
upper = np.uint8([ 40, 255, 255])
yellow_mask = cv2.inRange(converted, lower, upper)
# combine the mask
mask = cv2.bitwise_or(white_mask, yellow_mask)
return cv2.bitwise_and(image, image, mask = mask)
Then it cannot detect white lane anymore. Is there good way to detect both white and yellow lane?
Here are all RGB colour code that I found
Yellow
c2974A (194, 149, 74)
a07444 (160, 116, 68)
b38e55 (179, 142, 85)
867964 (134, 121, 100)
ce9b57 (206, 155, 87)
ce9853 (206, 152, 83)
white
b4a59d (180, 165, 157)
b9a99a (185, 169, 154)
baaca0 (186, 172, 160)
867e79 (134, 126, 121)
7b7474 (123, 116, 116)
827d7c (130, 125, 124)
To extend the comment: this is an implementation of a 2 stage approach. Take some time to look at the intermediate images/masks to fully understand everything that happens.
Cropping to region of interest
You can automate this, but I cheated a little and did it manually. The cropped sky area will rarely ever have road surface, this is an easy solution that suffices (for now) I think. Similarly, I also cut of the HUD boxes on the right hand side, as they have similar gray colors as the road and were interfering. It's more neat to draw black boxes over them, so they are excluded from processing.
Isolating road
Convert the cropped image to HSV an select only gray-ish values. After some noise removal I improved the mask using findContours to draw the convexhull. If performance is an issue, you could maybe skip it by tweaking the close mask step.
Selecting lines
Using the mask you can create an image of only the road surface. You can use this for color separation without having to worry about also selecting surroundings. My result is not perfect, but I assume you have larger versions of the images which will give better results.
Result:
Code:
import cv2
import numpy as np
# load image
image = cv2.imread('pw12b.jpg')
# crop image
h,w = image.shape[:2]
image = image[200:h-20,20:550]
# create hsv
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
low_val = (0,0,0)
high_val = (179,45,96)
# Threshold the HSV image
mask = cv2.inRange(hsv, low_val,high_val)
# remove noise
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel=np.ones((8,8),dtype=np.uint8))
# close mask
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel=np.ones((20,20),dtype=np.uint8))
# improve mask by drawing the convexhull
ret, contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
hull = cv2.convexHull(cnt)
cv2.drawContours(mask,[hull],0,(255), -1)
# erode mask a bit to migitate mask bleed of convexhull
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel=np.ones((5,5),dtype=np.uint8))
# remove this line, used to show intermediate result of masked road
road = cv2.bitwise_and(image, image,mask=mask)
# apply mask to hsv image
road_hsv = cv2.bitwise_and(hsv, hsv,mask=mask)
# set lower and upper color limits
low_val = (0,0,102)
high_val = (179,255,255)
# Threshold the HSV image
mask2 = cv2.inRange(road_hsv, low_val,high_val)
# apply mask to original image
result = cv2.bitwise_and(image, image,mask=mask2)
#show image
cv2.imshow("Result", result)
cv2.imshow("Road", road)
cv2.imshow("Mask", mask)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()