import heapq import os import random import tkinter as tk from collections import deque from tkinter import messagebox import cv2 import numpy as np # 停车场布局数据 left_side = list(range(119, 132)) right_side = list(range(99, 87, -1)) # 将右侧车位编号转换为三位数格式(例如:099, 098, ...) right_side = [str(number).zfill(3) for number in right_side] entry_left = [117, 115, 113] entry_right = [108, 106, 104, 102, 101, 100] p_row1 = [118, 116, 114, 112, 111, 110, None, 109, 107, 105, 103] middle_bottom_row1 = [292, 290, 288, 286, 284, 282, 280, 278, 276, 274, 272] middle_top_row2 = [293, 291, 289, 287, 285, 283, 281, 279, 277, 275, 273, 271] middle_bottom_row2 = list(range(259, 271)) middle_top_row3 = [None] * 10 + [00] + [None] middle_bottom_row3 = [258, 257, 256, None, None, None, None, None, None, 255, 254, None] # 样式参数 car_space_width = 40 car_space_height = 80 road_width = 40 margin = 0 canvas_width = 12 * car_space_width + road_width * \ 2 + 2 * car_space_height + 11 * margin canvas_height = 4 * road_width + 7 * car_space_height + 3 * margin middle_area_offset_y = 100 # 中间区域整体下移 # 车位状态对应颜色 status_color = { "free": "lightgreen", "occupied": "tomato", } default_color = "white" selected_color = "cyan" # 搜索高亮颜色 # 设置 YOLO 检测结果路径 detection_folder = r"D:\car2\parking_folders" # A*算法的启发式函数 def heuristic(a, b): return abs(a[0] - b[0]) + abs(a[1] - b[1]) # 曼哈顿距离 class ParkingLot(tk.Tk): def __init__(self): super().__init__() self.title("停车场平面图 🚗") self.geometry(f"{canvas_width}x{canvas_height}") self.canvas = tk.Canvas(self, width=canvas_width, height=canvas_height, bg="#CCCCCC") self.canvas.pack() # 搜索部分 self.search_entry = tk.Entry(self) self.search_entry.pack(pady=5) self.search_button = tk.Button( self, text="搜索车位", command=self.search_spot) self.search_button.pack(pady=5) self.spots = {} # 存储车位信息 self.path = [] # 存储路径 self.parkpot = {} self.grid = self.get_grid() self.obstacles = [] # 存储障碍物坐标 self.get_parkpot() self.current_path = [] self.draw_parking_lot() def get_grid(self): row = int(canvas_height / 40) column = int(canvas_width / 40) grid = np.zeros((row, column), np.int64) for i in range(4): for j in range(14): grid[2 + 5 * i][2 + j] = 1 for i in range(2): grid[3:, 2 + i * 13] = 1 grid[:2, 8] = 1 return grid def get_parkpot(self): """获取车位入口坐标""" for idx, name in enumerate(left_side): self.parkpot[name] = (3 + idx, 2, "left") for idx, name in enumerate(right_side): self.parkpot[name] = (3 + idx, 15, "right") for idx, name in enumerate(entry_left): self.parkpot[name] = (2, 4 + idx, "up") for idx, name in enumerate(entry_right): self.parkpot[name] = (2, 10 + idx, "up") for idx, name in enumerate(p_row1): self.parkpot[name] = (2, 4 + idx, "down") for idx, name in enumerate(middle_bottom_row1): self.parkpot[name] = (7, 4 + idx, "up") for idx, name in enumerate(middle_top_row2): self.parkpot[name] = (7, 3 + idx, "down") for idx, name in enumerate(middle_bottom_row2): self.parkpot[name] = (12, 3 + idx, "up") for idx, name in enumerate(middle_top_row3): self.parkpot[name] = (12, 3 + idx, "down") for idx, name in enumerate(middle_bottom_row3): self.parkpot[name] = (17, 3 + idx, "up") def draw_parking_lot(self): # 入口灰色路 inx_start = 5 * car_space_width + road_width + car_space_height + 5 * margin self.canvas.create_rectangle( inx_start, 0, inx_start + road_width, car_space_height, fill="gray", outline="gray" ) self.canvas.create_text( inx_start + road_width / 2, 20, text="入口", fill="red", font=("Arial", 16)) self.canvas.create_rectangle( car_space_height, car_space_height, canvas_width - car_space_height, car_space_height + road_width, fill="gray", outline="gray", ) self.canvas.create_rectangle( car_space_height, car_space_height + road_width, car_space_height + road_width, canvas_height, fill="gray", outline="gray", ) self.canvas.create_rectangle( canvas_width - car_space_height - road_width, car_space_height + road_width, canvas_width - car_space_height, canvas_height, fill="gray", outline="gray", ) self.canvas.create_rectangle( car_space_height, 3 * car_space_height + margin + road_width, canvas_width - car_space_height, 3 * car_space_height + margin + road_width + road_width, fill="gray", outline="gray", ) self.canvas.create_rectangle( car_space_height, 5 * car_space_height + 2 * margin + 2 * road_width, canvas_width - car_space_height, 5 * car_space_height + 2 * margin + 2 * road_width + road_width, fill="gray", outline="gray", ) self.canvas.create_rectangle( car_space_height, canvas_height - road_width, canvas_width - car_space_height, canvas_height, fill="gray", outline="gray", ) self.canvas.create_line(inx_start + road_width / 2, 30, inx_start + road_width / 2, 100, fill="blue", width=5) # 入口两边车位 for idx, number in enumerate(entry_left): x = car_space_height + road_width + car_space_width + \ idx * (car_space_width + margin) + margin y = 0 self.draw_parking_spot(x, y, number, direction="down") for idx, number in enumerate(entry_right): x = inx_start + road_width + car_space_width + \ idx * (car_space_width + margin) + margin * 2 y = 0 self.draw_parking_spot(x, y, number, direction="down") # 左右两列 for idx, number in enumerate(left_side): x = 0 y = car_space_height + road_width + \ idx * (car_space_width + margin) self.draw_parking_spot(x, y, number, direction="right") for idx, number in enumerate(right_side): x = canvas_width - car_space_height y = road_width + car_space_height + \ idx * (car_space_width + margin) self.draw_parking_spot(x, y, number, direction="left") # 中间区域 start_y = car_space_height + road_width start_x = car_space_height + road_width + margin + car_space_width self.draw_middle_row(start_x, start_y, p_row1, up=True) self.draw_middle_row( start_x, start_y + car_space_height + margin, middle_bottom_row1, up=False, ) start_y += road_width + margin + 2 * car_space_height start_x -= car_space_width + margin self.draw_middle_row(start_x, start_y, middle_top_row2, up=True) self.draw_middle_row( start_x, start_y + car_space_height + margin, middle_bottom_row2, up=False) start_y += road_width + margin + 2 * car_space_height self.draw_middle_row(start_x, start_y, middle_top_row3, up=True) self.draw_middle_row( start_x, start_y + car_space_height + margin, middle_bottom_row3, up=False) def draw_middle_row(self, start_x, start_y, row_data, up=True): """ 绘制中间行的停车位。 :param start_y: 行起始的y坐标 :param row_data: 行数据,包含停车位编号的列表 :param up: 布尔值,指示停车位的头部方向,默认为True(向上) """ current_x = start_x for number in row_data: # 绘制停车位矩形,去掉边框 rect = self.canvas.create_rectangle( current_x, start_y, current_x + car_space_width, start_y + car_space_height, fill=default_color, # outline="#CCCCCC", # 修改:将边框颜色设置为与背景相同的颜色 ) if number is not None: # 初始随机给状态 status = random.choice(["free", "occupied"]) # 存储停车位信息 self.spots[rect] = {"number": number, "status": status} # 绘制指向停车位的箭头 self.draw_arrow(current_x, start_y, "up" if up else "down") # 绘制停车位编号 self.canvas.create_text( current_x + car_space_width / 2, start_y + car_space_height / 2, text=str(number), font=("Arial", 8), ) # 绑定点击事件以切换停车位状态 self.canvas.tag_bind(rect, "", self.toggle_spot) # 按状态上色 fill_color = status_color.get(status, default_color) self.canvas.itemconfig(rect, fill=fill_color) # 添加障碍物坐标 self.obstacles.append((current_x, start_y)) # 更新当前x坐标以绘制下一个停车位 current_x += car_space_width + margin def draw_arrow(self, x, y, direction): if direction == "up": points = [ x + car_space_width / 2, y + 10, x + 10, y + 30, x + car_space_width - 10, y + 30, ] elif direction == "down": points = [ x + car_space_width / 2, y + car_space_height - 10, x + 10, y + car_space_height - 30, x + car_space_width - 10, y + car_space_height - 30, ] elif direction == "right": points = [ x + car_space_height - 5, y + car_space_width / 2, x + car_space_height - 20, y + 5, x + car_space_height - 20, y + car_space_width - 5, ] else: points = [ x + 5, y + car_space_width / 2, x + 20, y + 5, x + 20, y + car_space_width - 5, ] self.canvas.create_polygon(points, fill="black") def draw_parking_spot(self, x, y, number, direction="up"): if direction not in ["left", "right"]: rect = self.canvas.create_rectangle( x, y, x + car_space_width, y + car_space_height, fill=default_color, outline="black" ) elif direction == "left": rect = self.canvas.create_rectangle( x, y, x + car_space_height, y + car_space_width, fill=default_color, outline="black" ) else: rect = self.canvas.create_rectangle( x, y, x + car_space_height, y + car_space_width, fill=default_color, outline="black" ) # 获取车位编号对应的YOLO检测结果图像路径 detection_image_path = os.path.join( detection_folder, f"{number}.jpg") # 假设图像以车位编号命名 # 检查文件是否存在 if os.path.exists(detection_image_path): # 读取 YOLO 检测结果图像(假设是红绿图像,红色表示有车,绿色表示空位) detection_image = cv2.imread(detection_image_path) # 检查图片是否为空 if detection_image is not None: # 假设检测图像中左上角是车位的检测状态,我们可以检查该区域的颜色 # 获取车位区域的颜色 region_color = detection_image[10, 10] # 假设检测图像左上角有车位状态信息 # 判断颜色,红色表示有车,绿色表示空位 if np.array_equal(region_color, [0, 0, 255]): # 红色 status = "occupied" elif np.array_equal(region_color, [0, 255, 0]): # 绿色 status = "free" else: status = "free" # 默认状态为免费 else: status = "free" # 默认状态为免费,如果读取失败 else: status = "free" # 默认状态为免费,如果没有检测结果 self.spots[rect] = {"number": number, "status": status} # 绘制箭头 self.draw_arrow(x, y, direction) # 绘制车位编号 if direction not in ["left", "right"]: self.canvas.create_text( x + car_space_width / 2, y + car_space_height / 2, text=str(number), font=("Arial", 8), ) else: self.canvas.create_text( x + car_space_height / 2, y + car_space_width / 2, text=str(number), font=("Arial", 8), ) # 绑定点击事件 self.canvas.tag_bind(rect, "", self.toggle_spot) # 按状态设置颜色 fill_color = status_color.get(status, default_color) self.canvas.itemconfig(rect, fill=fill_color) def find_path(self, matrix): # 定义起点和终点 start = (0, 8) end = None for i in range(len(matrix)): for j in range(len(matrix[0])): if matrix[i][j] == 2: end = (i, j) break if end: break # 定义方向数组,表示上下左右四个方向 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 初始化队列,存储当前坐标和路径 queue = deque() queue.append((start, [start])) # 初始化访问标记数组 visited = [[False for _ in range(len(matrix[0]))] for _ in range(len(matrix))] visited[start[0]][start[1]] = True # 开始 BFS while queue: (x, y), path = queue.popleft() # 如果当前点是终点,返回路径 if (x, y) == end: return path # 返回找到的路径 # 检查四个方向 for dx, dy in directions: nx, ny = x + dx, y + dy # 判断新坐标是否在矩阵范围内,并且是路径或目的地且未被访问过 if 0 <= nx < len(matrix) and 0 <= ny < len(matrix[0]): if matrix[nx][ny] in [1, 2] and not visited[nx][ny]: visited[nx][ny] = True queue.append(((nx, ny), path + [(nx, ny)])) return [] # 如果没有找到路径,返回空列表 def toggle_spot(self, event): clicked = event.widget.find_withtag("current")[0] spot = self.spots.get(clicked) if spot["status"] != "free": messagebox.showinfo("提示", "该车位已满,请选择其他车位!") else: messagebox.showinfo( "车位信息", f"车位编号: {spot['number']}\n状态: {spot['status']}", ) coords = self.canvas.coords(clicked) X1, Y1, X2, Y2 = coords # X1 = X1*40+20 # Y1 = Y1*40+20 # X2=X2*40+20 # Y2=Y2*40+20 number = spot["number"] parkspot = self.parkpot[number] # 清除之前的路径 self.clear_previous_path() matrix = self.grid.copy() matrix[parkspot[0]][parkspot[1]] = 2 path = self.find_path(matrix) direction = parkspot[2] if path: # 转换为像素坐标 path_coords = [(x * 40 + 20, y * 40 + 20) for (x, y) in path] if direction == 'left': path_coords.append((X2, (Y1+Y2)/2)) elif direction == 'right': path_coords.append((X1, (Y1+Y2)/2)) elif direction == 'up': path_coords.append(((X1+X2)/2, Y2)) else: path_coords.append(((X1+X2)/2, Y1)) # 保存路径图形项ID self.current_path = [] print(path_coords) # 绘制路径并记录图形项ID for i in range(len(path_coords) - 1): y1, x1 = path_coords[i] y2, x2 = path_coords[i + 1] if i == len(path_coords) - 2: line_id = self.canvas.create_line( x1, y1, y2, x2, fill="blue", width=5, arrow=tk.LAST) else: line_id = self.canvas.create_line( x1, y1, x2, y2, fill="blue", width=5) self.current_path.append(line_id) else: messagebox.showinfo("提示", "未找到到达该车位的路径!") def clear_previous_path(self): """清除之前绘制的路径""" for item_id in self.current_path: self.canvas.delete(item_id) self.current_path.clear() def search_spot(self): query = self.search_entry.get() found = False for rect, spot in self.spots.items(): if str(spot["number"]) == query: self.canvas.itemconfig(rect, fill=selected_color) self.find_path(rect) found = True else: # 恢复成状态颜色 self.canvas.itemconfig(rect, fill=status_color.get( spot["status"], default_color)) if not found: messagebox.showwarning("提示", "未找到该车位编号!") def is_obstacle(self, coords): # 判断当前位置是否为车位(障碍物) for x, y in self.obstacles: if x <= coords[0] <= x + car_space_width and y <= coords[1] <= y + car_space_height: return True return False if __name__ == "__main__": app = ParkingLot() app.mainloop()