You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
10 KiB

import cv2
import numpy as np
import tkinter as tk
from tkinter import colorchooser, simpledialog, Scale, HORIZONTAL, messagebox
from threading import Thread
# 颜色的HSV阈值
color_hsv_ranges = {
'blue': {'lower': np.array([80, 170, 192], dtype=np.uint8),
'upper': np.array([120, 255, 255], dtype=np.uint8)},
'green': {'lower': np.array([25, 50, 147], dtype=np.uint8),
'upper': np.array([55, 255, 255], dtype=np.uint8)},
'red': {'lower': np.array([0, 112, 194] , dtype=np.uint8),
'upper': np.array([13, 255, 255], dtype=np.uint8)},
'custom': {'lower': np.array([0, 0, 0], dtype=np.uint8),
'upper': np.array([180, 255, 255], dtype=np.uint8)}
}
# 初始化选择的颜色
selected_color = 'green'
#获取初始颜色HSV阈值
lower_hsv, upper_hsv = (color_hsv_ranges[selected_color]['lower'],
color_hsv_ranges[selected_color]['upper'])
# 全局变量
running = False
kf = None
cap = None
root = None
stop_thread = False
def initialize_kalman_filter():
"""初始化卡尔曼滤波器"""
global kf
kf = cv2.KalmanFilter(4, 2)
kf.measurementMatrix = np.array([[1, 0, 0, 0],
[0, 1, 0, 0]], np.float32)
kf.transitionMatrix = np.array([[1, 0, 1, 0],
[0, 1, 0, 1],
[0, 0, 1, 0],
[0, 0, 0, 1]], np.float32)
kf.processNoiseCov = np.array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]], np.float32) * 0.03
# 用于初始化卡尔曼滤波器的变量
initial_measurement = None
#初始化摄像头
def initialize_camera():
global cap
cap = cv2.VideoCapture(0)
def choose_color():
global selected_color
# 显示颜色选择对话框
color_options = list(color_hsv_ranges.keys())
selected_color = simpledialog.askstring("选择颜色", "输入颜色 (blue, green, red, custom):",
initialvalue=selected_color, parent=root)
# 检查颜色选择是否有效,更新阈值
if selected_color in color_options:
update_color_threshold(selected_color)
else:
messagebox.showerror("错误", "颜色选择无效")
def rgb_to_hsv(rgba):
"""将RGB颜色转换为HSV颜色"""
rgb = rgba[:3] # 忽略alpha通道
hsv = cv2.cvtColor(np.uint8([[rgb]]), cv2.COLOR_RGB2HSV)[0][0]
return hsv
def choose_custom_color():
color_code = colorchooser.askcolor(title="选择自定义颜色")
if color_code[0] is not None:
hsv_color = rgb_to_hsv(color_code[0])
# 计算HSV的下限和上限
lower = np.array([hsv_color[0] - 35, hsv_color[1] * 0.5, hsv_color[2] * 0.65])
upper = np.array([hsv_color[0] + 10, hsv_color[1] * 1.2, hsv_color[2] * 1.2])
# 确保HSV值在有效范围内
lower = np.clip(lower, 0, 180)
upper = np.clip(upper, 0, 255)
# 更新自定义颜色的HSV阈值
update_custom_hsv(lower, upper)
def update_custom_hsv(lower, upper):
global color_hsv_ranges
color_hsv_ranges['custom']['lower'] = lower.astype(np.uint8)
color_hsv_ranges['custom']['upper'] = upper.astype(np.uint8)
update_color_threshold('custom')
def exit_app():
global running, cap, stop_thread
running = False
stop_thread = True
root.quit()
cap.release()
cv2.destroyAllWindows()
def initialize_gui():
"""初始化GUI界面"""
global root
root = tk.Tk()
root.title("颜色追踪")
# 开始/停止摄像头按钮
start_button = tk.Button(root, text="开始/停止", command=start_camera)
start_button.pack()
# 颜色选择按钮
choose_color_button = tk.Button(root, text="选择颜色", command=choose_color)
choose_color_button.pack()
# 自定义颜色按钮
custom_color_button = tk.Button(root, text="自定义颜色", command=choose_custom_color)
custom_color_button.pack()
# 退出按钮
exit_button = tk.Button(root, text="退出", command=exit_app)
exit_button.pack()
# 自定义颜色HSV滑块
def set_custom_hsv(lower_h, lower_s, lower_v, upper_h, upper_s, upper_v):
update_custom_hsv(
np.array([lower_h, lower_s, lower_v]),
np.array([upper_h, upper_s, upper_v])
)
# 自定义颜色HSV滑块
lower_h_scale = Scale(root, from_=0, to=180, orient=HORIZONTAL, label="色调 Min",
command=lambda v: set_custom_hsv(v, lower_s_scale.get(), lower_v_scale.get(),
upper_h_scale.get(),
upper_s_scale.get(), upper_v_scale.get()))
lower_h_scale.pack()
lower_s_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="饱和度 Min",
command=lambda v: set_custom_hsv(lower_h_scale.get(), v, lower_v_scale.get(),
upper_h_scale.get(),
upper_s_scale.get(), upper_v_scale.get()))
lower_s_scale.pack()
lower_v_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="亮度 Min",
command=lambda v: set_custom_hsv(lower_h_scale.get(), lower_s_scale.get(), v,
upper_h_scale.get(),
upper_s_scale.get(), upper_v_scale.get()))
lower_v_scale.pack()
upper_h_scale = Scale(root, from_=0, to=180, orient=HORIZONTAL, label="色调 Max",
command=lambda v: set_custom_hsv(lower_h_scale.get(), lower_s_scale.get(),
lower_v_scale.get(), v,
upper_s_scale.get(), upper_v_scale.get()))
upper_h_scale.pack()
upper_s_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="饱和度 Max",
command=lambda v: set_custom_hsv(lower_h_scale.get(), lower_s_scale.get(),
lower_v_scale.get(),
upper_h_scale.get(), v, upper_v_scale.get()))
upper_s_scale.pack()
upper_v_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="亮度 Max",
command=lambda v: set_custom_hsv(lower_h_scale.get(), lower_s_scale.get(),
lower_v_scale.get(),
upper_h_scale.get(), upper_s_scale.get(), v))
upper_v_scale.pack()
# 初始化滑块值
lower_h_scale.set(color_hsv_ranges['custom']['lower'][0])
lower_s_scale.set(color_hsv_ranges['custom']['lower'][1])
lower_v_scale.set(color_hsv_ranges['custom']['lower'][2])
upper_h_scale.set(color_hsv_ranges['custom']['upper'][0])
upper_s_scale.set(color_hsv_ranges['custom']['upper'][1])
upper_v_scale.set(color_hsv_ranges['custom']['upper'][2])
def start_camera():
"""开始或停止摄像头处理"""
global running
if not running:
running = True
thread = Thread(target=camera_thread)
thread.start()
else:
running = False
def camera_thread():
"""摄像头处理线程函数"""
global running, initial_measurement, lower_hsv, upper_hsv, stop_thread
while running and not stop_thread:
ret, frame = cap.read()
if not ret:
messagebox.showerror("Error", "Failed to read frame from camera.")
running = False
break
# 将BGR图像转换为HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
# 应用开运算(先腐蚀后膨胀)以去除噪声
kernel_opening = np.ones((5, 5), np.uint8)
mask_opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_opening)
# 应用闭运算(先膨胀后腐蚀)以填充孔洞
kernel_closing = np.ones((5, 5), np.uint8)
mask_closed = cv2.morphologyEx(mask_opened, cv2.MORPH_CLOSE, kernel_closing)
# 使用闭合后的掩模找到轮廓
contours, _ = cv2.findContours(mask_closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 遍历轮廓,画出边界框
for contour in contours:
if cv2.contourArea(contour) > 300:
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 初始化卡尔曼滤波器的测量值
if initial_measurement is None:
initial_measurement = np.array([[x], [y]], np.float32)
kf.statePost = initial_measurement
else:
# 更新卡尔曼滤波器
measurement = np.array([[x], [y]], np.float32)
kf.correct(measurement)
prediction = kf.predict()
cv2.rectangle(frame, (int(prediction[0]), int(prediction[1])),
(int(prediction[0] + w), int(prediction[1] + h)),
(255, 0, 0), 2)
print(lower_hsv, upper_hsv)
cv2.imshow('Frame', frame)
cv2.imshow('Mask', mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
running = False
cv2.destroyAllWindows()
def update_color_threshold(color_name):
"""更新颜色阈值"""
global lower_hsv, upper_hsv, selected_color
# 根据选择的颜色更新颜色阈值
selected_color = color_name # 更新selected_color
lower_hsv = color_hsv_ranges[color_name]['lower']
upper_hsv = color_hsv_ranges[color_name]['upper']
def main():
"""主函数,程序入口"""
initialize_kalman_filter()
initialize_camera()
initialize_gui()
root.mainloop()
if __name__ == "__main__":
main()