Applying mask for coloured image in Open cv - python

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

Related

How to mask infected area from the healthy area?

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.

Having a color, convert entire image to shades of that color

I want to convert the below sample code to a function which will get as input any pixel color, and re-color whole image using shades of the input color, so it will leave the impression of the image with one same color. I don't know how this technique is named, maybe somebody will suggest and will show how this can be done if it is even possible. How to do that in Python ?
import cv2
import numpy as np
src = cv2.imread('image.jpg', cv2.IMREAD_UNCHANGED)
print(src.shape)
# extract blue channel
blue_channel = src[:,:,0]
# create empty image with same shape as that of src image
blue_img = np.zeros(src.shape)
#assign the red channel of src to empty image
blue_img[:,:,0] = blue_channel
Here is another way to colorize the image in Python/OpenCV. Convert to gray, then create a 1D LUT using black, blue (or any other color) and white. Then apply the LUT to the gray image.
Input:
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.merge([gray,gray,gray])
# create 1D LUT
# create 1 pixel blue image
black = np.zeros((1, 1, 3), np.uint8)
white = np.full((1, 1, 3), (255,255,255), np.uint8)
blue = np.full((1, 1, 3), (255,0,0), np.uint8)
# append the 3 images
lut = np.concatenate((black, blue, white), axis=0)
# resize lut to 256 values
lut = cv2.resize(lut, (1,256), interpolation=cv2.INTER_CUBIC)
# apply lut to gray
result = cv2.LUT(gray, lut)
# save result
cv2.imwrite('lena_blue2.jpg', result)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is one way to colorize the image in Python OpenCV. Convert to gray, then multiply by a blue (or any other color) image.
Input:
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
gray = gray.astype(np.float32)
# create blue image
blue = np.full_like(img, (255,0,0), np.float32) / 255
# multiply gray by blue image
result = cv2.multiply(gray, blue)
result = result.astype(np.uint8)
# save result
cv2.imwrite('lena_blue1.jpg', result)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to use opencv to individually crop regions of a mask?

I have this image of a bunch of circles, all different colors (red, green, yellow, purple, etc.). I would like to individually crop all the red circles and save them as separate files (ex. circle(1).png, circle(2).png, etc.).
What I have so far is a solution to only show the red circles. I created a mask with cv2.inRange and used a cv2.bitwise_and to only show the red circles. Here is my code:
import cv2
import numpy as np
image = cv2.imread('dots.jpg')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_red = np.array([150,100,0])
upper_red = np.array([255,255,255])
# Threshold the HSV image to get only red cirlces
mask = cv2.inRange(hsv, lower_red, upper_red)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(image,image, mask=mask)
I guess what I'm looking for is something like cv2.selectROI() but runs automatically (no manual click&drag) and can crop multiple regions. Any ideas or tips appreciated. Thanks
For red, you can choose the HSV range (0,50,20) ~ (5,255,255) and (175,50,20)~(180,255,255) using the colormap given here. Your mask in above code won't detect both red circles in below image, for example. Check this yourself.
You can try below code:
import cv2
import numpy as np
image = cv2.imread('circles.jpg')
img_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# Gen lower mask (0-5) and upper mask (175-180) of RED
mask1 = cv2.inRange(img_hsv, (0,50,20), (5,255,255))
mask2 = cv2.inRange(img_hsv, (175,50,20), (180,255,255))
# Merge the mask and crop the red regions
mask = cv2.bitwise_or(mask1, mask2)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(image,image, mask=mask)
# coverting image with red colored region of interest from HSV to RGB
hsv2bgr = cv2.cvtColor(res, cv2.COLOR_HSV2BGR)
# RGB to GRAYSCALE
rgb2gray = cv2.cvtColor(hsv2bgr, cv2.COLOR_BGR2GRAY)
# Applying thresholding to the grayscale image for black & white color
thresh_gray = cv2.threshold(rgb2gray, 20,255, cv2.THRESH_BINARY)[1]
# Find the different contours
contours = cv2.findContours(rgb2gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
#print(len(contours))
i = 0
for c in contours:
_, radius = cv2.minEnclosingCircle(c)
if radius>10:
# create a mask and fill it with white color
mask = np.zeros(image.shape, dtype=np.uint8)
cv2.fillPoly(mask, pts=[c], color=(255, 255, 255))
# Bitwise-AND mask and original image
# output is red circle with black background
masked_image = cv2.bitwise_and(image, mask)
# to get individual red circle with white background
mask_ = cv2.bitwise_not(mask)
circle_ = cv2.bitwise_or(masked_image, mask_)
cv2.imwrite('circle({}).jpg'.format(i), circle_)
i+=1
Input Image: circles.jpg
There are two red circle object in the above input image, hence it will create two files- circle(0).jpg and circle(1).jpg each with individual red circles.

How can I select the right color to threshold an image with OpenCV?

I'm trying to select the green color in an image using OpenCV (the method to do it comes from this website. The image I'm treating is :
Here is the code I tried to write.
import cv2
import matplotlib.pyplot as plt
import numpy as np
greenhsv = (60, 255, 255)
green2hsv=(70,100,170)
g_square = np.full((10, 10, 3), greenhsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g_square))
plt.show()
g1_square = np.full((10, 10, 3), green2hsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g1_square))
plt.show()
nucl = cv2.imread('./Pictures/image_nucleation_essai0.png')
nucl = cv2.cvtColor(nucl, cv2.COLOR_BGR2RGB)
plt.imshow(nucl)
plt.show()
hsv_nucl = cv2.cvtColor(nucl, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv_nucl, greenhsv,green2hsv)
result = cv2.bitwise_and(nucl, nucl, mask=mask)
plt.imshow(mask, cmap="gray")
plt.show()
plt.imshow(result)
plt.show()
The result is :
So the mask did not work.
Your color ranges are not quite right yet. Also the variables in the inRange() function are in the wrong order. It's from-to, so the darker color must be first. Change your code to cv2.inRange(hsv_nucl, green2hsv,greenhsv) You can use/tweak the values in the code below, that works.
Result:
With white background:
import numpy as np
import cv2
# load image
img = cv2.imread("Eding.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
lower_val = np.array([50,100,170])
upper_val = np.array([70,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# apply mask to original image - this shows the green with black blackground
only_green = cv2.bitwise_and(img,img, mask= mask)
# create a black image with the dimensions of the input image
background = np.zeros(img.shape, img.dtype)
# invert to create a white image
background = cv2.bitwise_not(background)
# invert the mask that blocks everything except green -
# so now it only blocks the green area's
mask_inv = cv2.bitwise_not(mask)
# apply the inverted mask to the white image,
# so it now has black where the original image had green
masked_bg = cv2.bitwise_and(background,background, mask= mask_inv)
# add the 2 images together. It adds all the pixel values,
# so the result is white background and the the green from the first image
final = cv2.add(only_green, masked_bg)
#show image
cv2.imshow("img", final)
cv2.waitKey(0)
cv2.destroyAllWindows()

How does filtering out the colors of a hsv image in opencv python work?

I am working on a project that needs to identify the shape of an object in a field. My goal right now is to filter out any colors the background could be such as green, brown, or gray.
import cv2
import numpy as np
img = cv2.imread('pic1.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_green = np.array([0,0, 150])
upper_green = np.array([255, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
res = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('res1.png', res)
cv2.waitKey(0)
The image
is showing a sample of the shapes in the field.
This code results in this image:
Why do the values I use for the lower and upper greens remove all the green when I have only included red values?
Are there any ways to get filters that remove only one color so I am able to pick and choose which colors I would want to keep and which to remove?

Categories

Resources