OpenCV Python中HSV图像颜色过滤原理及单颜色过滤实现咨询
Hey Danny, let's tackle your OpenCV questions step by step—they're super common for computer vision projects like your field object recognition task!
HSV (Hue, Saturation, Value) is way more intuitive for color-based filtering than BGR because it separates color information from brightness. Here's a straightforward workflow:
Step 1: Convert your image to HSV
First, read your image and switch from BGR (OpenCV's default format) to HSV usingcv2.cvtColor():import cv2 import numpy as np img = cv2.imread('your_image.png') hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)Step 2: Define your target color's HSV range
This is the tricky part—you need to know the HSV values for the color you want to filter. You can:- Tweak values manually (remember OpenCV scales Hue from 0-179, not 0-360, and Saturation/Value from 0-255)
- Build a quick interactive tool with trackbars to adjust ranges in real-time (I'll share a script for this below)
Step 3: Create a mask
Usecv2.inRange()to generate a binary mask where pixels within your HSV range are white (255) and others are black (0):lower_bound = np.array([h_min, s_min, v_min]) upper_bound = np.array([h_max, s_max, v_max]) mask = cv2.inRange(hsv_img, lower_bound, upper_bound)Step 4: Apply the mask to your image
Usecv2.bitwise_and()to keep only the pixels that match the mask:filtered_img = cv2.bitwise_and(img, img, mask=mask) cv2.imshow('Filtered Result', filtered_img) cv2.waitKey(0) cv2.destroyAllWindows()
Let's start with why your current code removes green—you didn't actually set a green range! Your bounds [0,0,150] to [255,255,255] are selecting all bright pixels (since Value ranges from 150-255, covering full Hue and Saturation). Most field greenery is darker (lower Value), so those pixels get excluded from the mask—hence why green gets removed. That was a happy accident, not intentional green filtering!
How to Filter (or Keep) a Single Color
First, let's make a tool to find the right HSV ranges for your specific field images. Run this script, load your image, and adjust the sliders until the mask shows exactly the color you want to target:
import cv2 import numpy as np def nothing(x): pass # Load image img = cv2.imread('pic1.png') hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Create window with trackbars cv2.namedWindow('HSV Tuner') cv2.createTrackbar('H Min', 'HSV Tuner', 0, 179, nothing) cv2.createTrackbar('H Max', 'HSV Tuner', 179, 179, nothing) cv2.createTrackbar('S Min', 'HSV Tuner', 0, 255, nothing) cv2.createTrackbar('S Max', 'HSV Tuner', 255, 255, nothing) cv2.createTrackbar('V Min', 'HSV Tuner', 0, 255, nothing) cv2.createTrackbar('V Max', 'HSV Tuner', 255, 255, nothing) while True: # Get trackbar values h_min = cv2.getTrackbarPos('H Min', 'HSV Tuner') h_max = cv2.getTrackbarPos('H Max', 'HSV Tuner') s_min = cv2.getTrackbarPos('S Min', 'HSV Tuner') s_max = cv2.getTrackbarPos('S Max', 'HSV Tuner') v_min = cv2.getTrackbarPos('V Min', 'HSV Tuner') v_max = cv2.getTrackbarPos('V Max', 'HSV Tuner') # Create mask and filtered image lower = np.array([h_min, s_min, v_min]) upper = np.array([h_max, s_max, v_max]) mask = cv2.inRange(hsv, lower, upper) res = cv2.bitwise_and(img, img, mask=mask) # Show results cv2.imshow('Original', img) cv2.imshow('Mask', mask) cv2.imshow('Filtered', res) # Exit on 'q' press if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows()
Option 1: Remove a Single Color
To remove a specific color (e.g., green), create a mask for that color, invert it, then apply to the image:
# Example: Remove green (adjust ranges based on your tuner results) lower_green = np.array([40, 40, 40]) upper_green = np.array([70, 255, 255]) green_mask = cv2.inRange(hsv, lower_green, upper_green) inverted_mask = cv2.bitwise_not(green_mask) # Keep everything except green no_green_img = cv2.bitwise_and(img, img, mask=inverted_mask)
Option 2: Keep Only a Single Color
To retain only one color (e.g., your target object color), use the mask directly. Note: For colors like red that wrap around the Hue scale, you'll need two ranges:
# Example: Keep only red lower_red1 = np.array([0, 100, 100]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([160, 100, 100]) upper_red2 = np.array([179, 255, 255]) red_mask1 = cv2.inRange(hsv, lower_red1, upper_red1) red_mask2 = cv2.inRange(hsv, lower_red2, upper_red2) red_mask = cv2.bitwise_or(red_mask1, red_mask2) only_red_img = cv2.bitwise_and(img, img, mask=red_mask)
For Your Field Project (Filter Green/Brown/Gray Background)
Combine masks for all three background colors, invert the combined mask, and apply to your image:
# Adjust these ranges using the HSV tuner tool! # Green range lower_green = np.array([40, 40, 40]) upper_green = np.array([70, 255, 255]) green_mask = cv2.inRange(hsv, lower_green, upper_green) # Brown range lower_brown = np.array([10, 100, 20]) upper_brown = np.array([30, 255, 200]) brown_mask = cv2.inRange(hsv, lower_brown, upper_brown) # Gray range (low saturation) lower_gray = np.array([0, 0, 50]) upper_gray = np.array([179, 30, 200]) gray_mask = cv2.inRange(hsv, lower_gray, upper_gray) # Combine all background masks background_mask = cv2.bitwise_or(green_mask, brown_mask) background_mask = cv2.bitwise_or(background_mask, gray_mask) # Invert to keep only non-background objects object_mask = cv2.bitwise_not(background_mask) final_img = cv2.bitwise_and(img, img, mask=object_mask) cv2.imshow('Field Objects', final_img) cv2.waitKey(0)
内容的提问来源于stack exchange,提问作者Danny Banany

