31

I want to detect only green objects from an image captured in a natural environment. How to define it? Because in here I want to pass the threshold value let's say 'x', by using this x I want to get only green colour objects in to one colour (white) others are must appear in another colour (black). Please guide me to do this.

nathancy
  • 42,661
  • 14
  • 115
  • 137
Singh.
  • 319
  • 1
  • 3
  • 6

3 Answers3

77

Update:

I make a HSV colormap. It's more easy and accurate to find the color range using this map than before.

And maybe I should change use (40, 40,40) ~ (70, 255,255) in hsv to find the green.

enter image description here


Original answer:

  1. Convert to HSV color-space,
  2. Use cv2.inRange(hsv, hsv_lower, hsv_higher) to get the green mask.

We use the range (in hsv): (36,0,0) ~ (86,255,255) for this sunflower.


The source image:

enter image description here

The masked green regions:

enter image description here

More steps:

enter image description here


The core source code:

import cv2
import numpy as np

## Read
img = cv2.imread("sunflower.jpg")

## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

## mask of green (36,25,25) ~ (86, 255,255)
# mask = cv2.inRange(hsv, (36, 25, 25), (86, 255,255))
mask = cv2.inRange(hsv, (36, 25, 25), (70, 255,255))

## slice the green
imask = mask>0
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]

## save 
cv2.imwrite("green.png", green)

Similar:

  1. Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
Kinght 金
  • 17,681
  • 4
  • 60
  • 74
  • Nice! Do you care to share your code? – Mark Setchell Nov 25 '17 at 10:30
  • 1
    Since I m using python opencv i used hsv values for bgr as for lower green area as 50,100,100 and upper green area as 70,255,255, hsv value for green colour is given as 60,255,255. But it didn't work , by using that I am getting only plain black image as a result. – Singh. Nov 25 '17 at 10:50
  • 2
    In my case, I use `[36 0 0]` ~ `[ 86 255 255]` – Kinght 金 Nov 25 '17 at 11:04
  • @Silencer thank you it's works for me – Singh. Nov 25 '17 at 12:19
  • @Silencer Can u suggest me for yellow colour range for pictures taken from natural environemt also? – Singh. Nov 27 '17 at 04:14
  • @S.Am At least for my "sunflower", it works. As for other images, it maybe not the best, but it can be a choice. You can also choose other methods you found. – Kinght 金 Nov 27 '17 at 04:53
  • @Silencer oki .. Can I know the yellow colour range you have used? – Singh. Nov 27 '17 at 05:40
  • In this "sunflower", green in `(36,0,0)~(86,255,255)`, yellow in `(15,0,0)~(36,255,255)`, blue in `(90,0,0)~(110,255,255)` – Kinght 金 Nov 27 '17 at 06:04
  • @S.Am The `sunflower` is not that `colorful`. So the range is not that accurate for other `colorful` image. I make a hsv colormap to find the range easily and accurately. Maybe it helps. – Kinght 金 Nov 28 '17 at 02:43
  • Did I understand you correctly that this color map is calculated only for this image ? Which tool did you use to get HSV color map from image ? – Eugene Shmorgun Jan 20 '18 at 12:18
  • Yes, I just used this `sunflower` to illustrate how to use `inRange` in HSV. Because there is only three main colors, so even not precise range works ok. I wrote code to generate [this colormap](https://i.stack.imgur.com/TSKh8.png), maybe useful, maybe not. – Kinght 金 Jan 20 '18 at 12:23
  • Sorry, I'm new in OpenCV and trying to understand: can I use your color map for my image ? For example to detect threshold of blue pixels ? – Eugene Shmorgun Jan 20 '18 at 12:26
  • 1
    Why not? If you lookup the map, you will find `blue(HSV)` mainly locate in `(110,150,50) ~ (120,255,255)`. – Kinght 金 Jan 20 '18 at 12:31
  • it cuts non-green – Flash Thunder Feb 28 '19 at 19:36
  • @Kinght金 In this color map, what is in X and Y axis? – AAAA Jan 01 '20 at 16:23
9

Intro:

Applying a threshold to detect green color can be performed quite easily using LAB color space.

The LAB color space also has 3 channels but unlike its RGB counterpart (where all 3 are color channels), in LAB there are 2 color channels and 1 brightness channel:

  • L-channel: represents the brightness value in the image
  • A-channel: represents the red and green color in the image
  • B-channel: represents the blue and yellow color in the image

Observing the following diagram:

enter image description here

The green and red color are represented on the extremes of the A-channel. Applying a suitable threshold on either of these extremes on this channel can segment either green or red color.

Demo:

The following images are in the order:

1. Original image -->> 2. A-channel of LAB converted image

3. Threshold -->> 4. Mask on the original image

Sample 1:

enter image description here enter image description here

enter image description here enter image description here

Sample 2:

enter image description here enter image description here

enter image description here enter image description here

Sample 3:

enter image description here enter image description here

enter image description here enter image description here

Code:

The code just has few lines:

# read image in BGR
img = cv2.imread('image_path')
# convert to LAB space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# store the a-channel
a_channel = lab[:,:,1]
# Automate threshold using Otsu method
th = cv2.threshold(a_channel,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# Mask the result with the original image
masked = cv2.bitwise_and(img, img, mask = th)

Exception:

The method above will work perfectly if green color appears distinctly. But applying an automated threshold might not always work, especially when there various shades of green in the same image.

In such cases, one set the threshold manually on the A-channel.

img = cv2.imread('flower.jpg')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_channel = lab[:,:,1]
# manually set threshold value
th = cv2.threshold(a_channel, 105, 255, cv2.THRESH_BINARY_INV)
# perform masking
masked = cv2.bitwise_and(img, img, mask = th)

enter image description here enter image description here

    Threshold image                              Masked image
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
  • While searching for a color range in HSV involves 3 channels, in LAB space searching involves only 2 channels. This reduces manual search to a great extent – Jeru Luke May 27 '22 at 19:30
4

You can use a simple HSV color thresholder script to determine the lower/upper color ranges using trackbars for any image on the disk. Simply change the image path in cv2.imread(). Example to isolate green:

import cv2
import numpy as np

def nothing(x):
    pass

# Load image
image = cv2.imread('1.jpg')

# Create a window
cv2.namedWindow('image')

# Create trackbars for color change
# Hue is from 0-179 for Opencv
cv2.createTrackbar('HMin', 'image', 0, 179, nothing)
cv2.createTrackbar('SMin', 'image', 0, 255, nothing)
cv2.createTrackbar('VMin', 'image', 0, 255, nothing)
cv2.createTrackbar('HMax', 'image', 0, 179, nothing)
cv2.createTrackbar('SMax', 'image', 0, 255, nothing)
cv2.createTrackbar('VMax', 'image', 0, 255, nothing)

# Set default value for Max HSV trackbars
cv2.setTrackbarPos('HMax', 'image', 179)
cv2.setTrackbarPos('SMax', 'image', 255)
cv2.setTrackbarPos('VMax', 'image', 255)

# Initialize HSV min/max values
hMin = sMin = vMin = hMax = sMax = vMax = 0
phMin = psMin = pvMin = phMax = psMax = pvMax = 0

while(1):
    # Get current positions of all trackbars
    hMin = cv2.getTrackbarPos('HMin', 'image')
    sMin = cv2.getTrackbarPos('SMin', 'image')
    vMin = cv2.getTrackbarPos('VMin', 'image')
    hMax = cv2.getTrackbarPos('HMax', 'image')
    sMax = cv2.getTrackbarPos('SMax', 'image')
    vMax = cv2.getTrackbarPos('VMax', 'image')

    # Set minimum and maximum HSV values to display
    lower = np.array([hMin, sMin, vMin])
    upper = np.array([hMax, sMax, vMax])

    # Convert to HSV format and color threshold
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower, upper)
    result = cv2.bitwise_and(image, image, mask=mask)

    # Print if there is a change in HSV value
    if((phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
        print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
        phMin = hMin
        psMin = sMin
        pvMin = vMin
        phMax = hMax
        psMax = sMax
        pvMax = vMax

    # Display result image
    cv2.imshow('image', result)
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()

HSV lower/upper color threshold ranges

(hMin = 52 , sMin = 0, vMin = 55), (hMax = 104 , sMax = 255, vMax = 255)

Once you have determined your lower and upper HSV color ranges, you can segment your desired colors like this:

import numpy as np
import cv2

image = cv2.imread('1.png')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([52, 0, 55])
upper = np.array([104, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
result = cv2.bitwise_and(image, image, mask=mask)

cv2.imshow('result', result)
cv2.waitKey()
nathancy
  • 42,661
  • 14
  • 115
  • 137