Reading lower and upper threshold arrays when used with inRange - python

I was reading on how to filter colors using OpenCV and came across the following snippet.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,160,50])
upper_red = np.array([255,255,180])
mask = cv2.inRange(hsv, lower_red, upper_red)
res = cv2.bitwise_and(img,img, mask= mask)
What does each value in lower_red mean? Does it denote lower and upper limits of H,S,V sequentially? Should it be read as minimum value of H as 0 and maximum value of H as 255?
I want to filter red color.

You are well on your way. I've added some code that shows a solution to your problem - combining two HSV color ranges in one mask.
Result:
Code:
import numpy as np
import cv2
# load image
img = cv2.imread("HSV.JPG")
# convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Create first mask
lower_red = np.array([0,150,50])
upper_red = np.array([5,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_red, upper_red)
# apply mask to original image
res = cv2.bitwise_and(img,img, mask= mask)
#show image
cv2.imshow("Mask1", res)
# Create second mask
lower_red2 = np.array([175,150,50])
upper_red2 = np.array([179,255,255])
# Threshold the HSV image to get only green colors
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
# apply mask to original image
res2 = cv2.bitwise_and(img,img, mask= mask2)
#show image
cv2.imshow("Mask2", res2)
#combine masks
final_mask = cv2.bitwise_or(mask, mask2)
# apply mask to original image
result = cv2.bitwise_and(img,img, mask= final_mask)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Note: in the result image I show the results if the separate masks were applied to the original image. Of course you really only need the masks, which are black and white.

Related

How to blur red color in image with python opencv so that its not clearly visible?

I want to blur red color in image ("1.png" is attached) so that its not clearly visible. I tried below code where I can change red color to black color but how can I blur it? Please help.
import cv2
import numpy as np
frame = cv2.imread("1.png")
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower = np.array([0,50,50])
upper = np.array([10,255,255])
# define range of blue color in HSV
# lower = np.array([38, 86, 0])
# upper = np.array([121, 255, 255])
# define range of pink color in HSV
# http://www.workwithcolor.com/pink-color-hue-range-01.htm
# lower = np.array([158, 127, 0])
# upper = np.array([179, 255, 255])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower, upper)
color_only = cv2.bitwise_and(frame, frame, mask = mask)
# convert mask to 3-channel image to perform subtract
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
res = cv2.subtract(frame, mask) #negative values become 0 -> black
cv2.imshow("frame", frame)
# cv2.imshow("mask", mask)
# cv2.imshow("color_only", color_only)
cv2.imshow("res", res)
cv2.waitKey()
cv2.destroyAllWindows()
1.png
You need to blur the segmented image and then use alpha blending to composite the blurred ROI with the background image. This code takes you through all the steps:
Read image and segment the color of interest:
import cv2
import numpy as np
frame = cv2.imread("/home/stephen/Desktop/1.png")
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower = np.array([0,50,50])
upper = np.array([10,255,255])
mask = cv2.inRange(hsv, lower, upper)
color_only = cv2.bitwise_and(frame, frame, mask = mask)
### THE BACKGROUND MUST BE MADE WHITE, NOT BLACK ###
color_only[np.where((color_only==[0,0,0]).all(axis=2))] = [255,255,255]
cv2.imshow("color_only", color_only)
Next, blur the segmented image. Note, I am using a 13,13 kernel to blur the image:
blur = cv2.blur(color_only, (13,13))
cv2.imshow('blur', blur)
Next, blur the mask of the segmented image. We are going to use this to combine the images later. It's easy to combine images using a bitwise function, but that approach will not work here because the blurred image no longer occupies the same space as the segmented image:
maskForAlphaBlending = blur
## BLACK OUT THE WHITE BACKGROUND OF THE ALPHA MASK
maskForAlphaBlending[np.where((maskForAlphaBlending==[255,255,255]).all(axis=2))] = [0,0,0]
maskForAlphaBlending = cv2.cvtColor(maskForAlphaBlending, cv2.COLOR_BGR2GRAY)
cv2.imshow('maskForAlphaBlending', maskForAlphaBlending)
Finally alpha blending can be used to composite the blurred segmented image and the green and white background image:
foreground = blur
background = frame
alpha = cv2.cvtColor(maskForAlphaBlending, cv2.COLOR_GRAY2BGR)
foreground = foreground.astype(float)
background = background.astype(float)
alpha = alpha.astype(float)/100
foreground = cv2.multiply(alpha, foreground)
background = cv2.multiply(1.0 - alpha, background)
outImage = cv2.add(foreground, background)
cv2.imshow("outImg", outImage/255)
Note how the red lines are blurred, but the green and white border is not:

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.

Remove Mask from Image OpenCV Python

I may be overshooting a bit but I'm trying to use a mask generated from my image and subtract it from the main image. I'm quite open to instead extracting the characters but am not sure how to collect the entire blue sample, I haven't that correct balance yet.
The page here demonstrates the inverse of what I'm trying to achieve.
Base image
The mask utilizing hsv bounds then inverting it to show it better
Darkening it
I wish to now take that mask and remove it from the main image.
import cv2
import numpy as np
import random as rng
from PIL import Image
from PIL import ImageOps
from utils import helper
image_name = 'capt13.jpg'
img = cv2.imread(image_name)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,120,70])
upper_red = np.array([10,255,255])
lower_mask = cv2.inRange(hsv, lower_red, upper_red)
lower_red = np.array([160,120,70])
upper_red = np.array([180,255,255])
upper_mask = cv2.inRange(hsv, lower_red, upper_red)
'''
lower_blue = np.array([80,40,30])
upper_blue = np.array([140,255,255])
lower_mask = cv2.inRange(hsv, lower_blue, upper_blue)
lower_blue = np.array([240,220,200])
upper_blue = np.array([360,255,255])
upper_mask = cv2.inRange(hsv, lower_blue, upper_blue)
'''
mask = lower_mask + upper_mask
res_lines = cv2.bitwise_and(img,img, mask= mask)
# Keep the inverted
image = Image.fromarray(res_lines)
image.save('res.png')
inverted = ImageOps.invert(image)
inverted = inverted.convert('L')
inverted.save('inverted.png')
binary = np.array(inverted)
for row in range(len(binary)):
for col in range(len(binary[row])):
if binary[row][col] != 255:
binary[row][col] = 0
binary_image = Image.fromarray(binary)
binary_image.save('binary.png')
Extracting the Blue (As stated above that I'm open to a better solution for this)
The mask utilizing hsv bounds then inverted it
Darkening it
Straight subtraction works, provided both images are the same size:
im = cv2.imread("image.png")
mask = cv2.imread("mask.png")
diff_im = im - im2
Alternatively, you can use OpenCV's built in subtract, which does an element-wise subtraction:
diff_im = cv2.subtract(im, im2)
As a final thought, you should also try absdiff, as it will convert negative results to zeroes, which may be what you want.
diff_im = cv2.absdiff(im, im2)

Detect whether a pixel is red or not

We can define the range of red color in HSV as below. I want to detect that whether a certain pixel is red or not? How can I do that in Python? I spend whole day, but unable to find solution. Please resolve my problem. I'm very new to Python. Code that I'm using is:
img=cv2.imread("img.png")
img_hsv=cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# lower mask (0-10)
lower_red = np.array([0,50,50])
upper_red = np.array([10,255,255])
mask0 = cv2.inRange(img_hsv, lower_red, upper_red)
# upper mask (170-180)
lower_red = np.array([170,50,50])
upper_red = np.array([180,255,255])
mask1 = cv2.inRange(img_hsv, lower_red, upper_red)
image_height,image_width,_=img.shape
for i in range(image_height):
for j in range(image_width):
if img_hsv[i][j][1]>=lower_red and img_hsv[i][j][1]<=upper_red:
print("Found red")
You are almost right. You can merge the masks of lower RED and higher RED together to a single mask.
For this ColorChecker.png:
My Steps to find the RED:
Read the image and convert to hsv.
I choose the red ranges (lower 0~5, upper 175~180) using this colormap:
Then merge the masks, you can judge whether the pixel is red or not by the mask. Or "crop" the region(s) for visualization:
#!/usr/bin/python3
# 2018.07.08 10:39:15 CST
# 2018.07.08 11:09:44 CST
import cv2
import numpy as np
## Read and merge
img = cv2.imread("ColorChecker.png")
img_hsv = cv2.cvtColor(img, 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 )
croped = cv2.bitwise_and(img, img, mask=mask)
## Display
cv2.imshow("mask", mask)
cv2.imshow("croped", croped)
cv2.waitKey()
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
How to detect two different colors using `cv2.inRange` in Python-OpenCV?

Analyze an image in specific area and determine if it's red using Opencv Python

I'm using Opencv python in raspberry pi, to analize a heatmap, i'm looking for color red, which represents the highest temperature, i need to detect if in an specific area exist red color, in case it does i can use this information to activate a condition, i'm using a heatmap like this:
for the red color detection i'm using this code:
import cv2
import numpy as np
while(1):
# Take each frame
frame = cv2.imread('heatmap.png')
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_red = np.array([-20, 100, 100])
upper_red = np.array([13, 255, 255])
# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_red, upper_red)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame,frame, mask= mask)
cv2.imshow('heatmap',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
the code above give me al the pixels in red, but i need to determine if the heatmap contour is red, i mean the image contour or border would be a red color not permited area, does anyone how i can do that?
Your HSV range is not right. For red, (0,20,20)~(8,255,255), (170,20,20) ~ (180,255,255).
Here is my result:
The code:
#!/usr/bin/python3
# 2018/05/16 13:54:09
import cv2
import numpy as np
img = cv2.imread('heatmap.png')
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask1 = cv2.inRange(hsv, (0,20,20), (8,255,255))
mask2 = cv2.inRange(hsv, (170,20,20), (180,255,255))
mask = cv2.bitwise_or(mask1, mask2)
dst = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow("dst", dst)
cv2.imwrite("__.png", dst)
cv2.waitKey()
Some useful links:
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
How to define a threshold value to detect only green colour objects in an image :Opencv

Categories

Resources