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.

258 lines
10 KiB

6 months ago
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 = {
6 months ago
'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)},
6 months ago
'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
6 months ago
kf = None
cap = None
root = None
6 months ago
stop_thread = False
6 months ago
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
6 months ago
# 用于初始化卡尔曼滤波器的变量
initial_measurement = None
6 months ago
#初始化摄像头
6 months ago
def initialize_camera():
global cap
cap = cv2.VideoCapture(0)
def choose_color():
global selected_color
# 显示颜色选择对话框
color_options = list(color_hsv_ranges.keys())
6 months ago
selected_color = simpledialog.askstring("选择颜色", "输入颜色 (blue, green, red, custom):",
6 months ago
initialvalue=selected_color, parent=root)
# 检查颜色选择是否有效,更新阈值
if selected_color in color_options:
update_color_threshold(selected_color)
else:
6 months ago
messagebox.showerror("错误", "颜色选择无效")
6 months ago
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']
6 months ago
def rgb_to_hsv(rgba):
"""将RGB颜色转换为HSV颜色"""
rgb = rgba[:3] # 忽略alpha通道
hsv = cv2.cvtColor(np.uint8([[rgb]]), cv2.COLOR_RGB2HSV)[0][0]
return hsv
6 months ago
def choose_custom_color():
6 months ago
color_code = colorchooser.askcolor(title="选择自定义颜色")
6 months ago
if color_code[0] is not None:
6 months ago
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阈值
6 months ago
update_custom_hsv(lower, upper)
6 months ago
# 更新自定义颜色的HSV阈值
6 months ago
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')
6 months ago
6 months ago
def exit_app():
6 months ago
global running, cap, stop_thread
6 months ago
running = False
6 months ago
stop_thread = True
root.quit()
6 months ago
cap.release()
6 months ago
cv2.destroyAllWindows()
6 months ago
6 months ago
def initialize_gui():
"""初始化GUI界面"""
global root
root = tk.Tk()
6 months ago
root.geometry("200x600")
6 months ago
root.title("颜色追踪")
6 months ago
# 开始/停止摄像头按钮
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滑块
6 months ago
lower_h_scale = Scale(root, from_=0, to=180, orient=HORIZONTAL, label="色调 Min",
6 months ago
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()
6 months ago
lower_s_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="饱和度 Min",
6 months ago
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()
6 months ago
lower_v_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="亮度 Min",
6 months ago
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()
6 months ago
upper_h_scale = Scale(root, from_=0, to=180, orient=HORIZONTAL, label="色调 Max",
6 months ago
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()
6 months ago
upper_s_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="饱和度 Max",
6 months ago
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()
6 months ago
upper_v_scale = Scale(root, from_=0, to=255, orient=HORIZONTAL, label="亮度 Max",
6 months ago
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])
6 months ago
def start_camera():
6 months ago
"""开始或停止摄像头处理"""
6 months ago
global running
if not running:
running = True
thread = Thread(target=camera_thread)
thread.start()
else:
running = False
def camera_thread():
6 months ago
"""摄像头处理线程函数"""
6 months ago
global running, initial_measurement, lower_hsv, upper_hsv, stop_thread
while running and not stop_thread:
6 months ago
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)
6 months ago
# 应用开运算(先腐蚀后膨胀)以去除噪声
kernel_opening = np.ones((5, 5), np.uint8)
mask_opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_opening)
6 months ago
6 months ago
# 应用闭运算(先膨胀后腐蚀)以填充孔洞
kernel_closing = np.ones((5, 5), np.uint8)
mask_closed = cv2.morphologyEx(mask_opened, cv2.MORPH_CLOSE, kernel_closing)
6 months ago
6 months ago
# 使用闭合后的掩模找到轮廓
contours, _ = cv2.findContours(mask_closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
6 months ago
# 遍历轮廓,画出边界框
for contour in contours:
6 months ago
if cv2.contourArea(contour) > 300:
6 months ago
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)
cv2.imshow('Frame', frame)
cv2.imshow('Mask', mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
running = False
cv2.destroyAllWindows()
6 months ago
def main():
"""主函数,程序入口"""
initialize_kalman_filter()
initialize_camera()
initialize_gui()
root.mainloop()
6 months ago
6 months ago
if __name__ == "__main__":
main()