停车场车位检测代码详解

一、代码整体功能说明

该代码基于OpenCV、NumPy和skimage库,实现了对单张停车场图片的车位检测功能。通过加载预训练模型,对预设的车位坐标区域进行分析,判断每个车位是否有车,并在图片上标记结果(红色框表示有车,绿色框表示空闲),最终保存并显示处理结果。

二、代码模块详解

1. 库导入

import cv2  # 用于图像处理和显示
import numpy as np  # 用于数值计算
import pickle  # 用于加载预训练模型
from skimage.transform import resize  # 用于图像缩放
from skimage.color import rgb2gray  # 用于将图像转为灰度图
  • cv2:核心图像处理库,负责读取图片、绘制矩形、保存结果等。
  • numpy:提供数组操作能力,支持图像数据的数值计算。
  • pickle:用于加载通过pickle序列化保存的机器学习模型。
  • skimage:提供专业的图像预处理工具(缩放、灰度转换)。

2. 模型加载

try:
    model = pickle.load(open('./model.p', 'rb'))  # 加载模型文件
except Exception as e:
    print(f"模型加载失败:{e}")
    exit()  # 加载失败则退出程序
  • 作用:加载预训练的车位状态检测模型(需确保model.p文件在当前目录)。
  • 异常处理:若模型文件不存在或损坏,捕获错误并提示,避免程序崩溃。

3. 图片读取与预处理

image_path = './first_frame.jpg'  # 图片路径
img = cv2.imread(image_path)  # 读取图片(OpenCV默认读取为BGR格式)

if img is None:
    print("无法读取图片,请检查路径")
    exit()  # 图片读取失败则退出

# 打印图像信息(用于调试)
img_height, img_width = img.shape[:2]
print(f"图像尺寸:宽={img_width}, 高={img_height}")

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为RGB格式(模型可能需要RGB输入)
  • 图片读取:通过cv2.imread读取图片,返回None表示读取失败(路径错误或文件损坏)。
  • 格式转换:OpenCV默认读取的图片为BGR格式,需转为RGB格式以匹配模型训练时的输入格式。

4. 车位坐标定义

parking_spots = [
    (78, 394, 149, 425),
    (78, 426, 149, 457),
    # ... 省略中间坐标(共455个车位坐标)
    (1776, 920, 1833, 949)     # 24
]
  • 坐标格式:每个车位用(x1, y1, x2, y2)表示,其中(x1,y1)是矩形左上角坐标,(x2,y2)是右下角坐标。
  • 坐标来源:根据停车场实际布局人工标注或自动生成(代码中坐标按固定间隔递增,符合实际车位排列规律)。

5. 车位坐标处理与过滤

对每个车位坐标进行预处理,确保后续能正确提取车位区域:

valid_count = 0  # 有效坐标计数器
invalid_coords = []  # 无效坐标记录

for i, (x1, y1, x2, y2) in enumerate(parking_spots, 1):
    # 修复1:统一坐标顺序(确保x1<x2, y1<y2)
    x1, x2 = min(x1, x2), max(x1, x2)
    y1, y2 = min(y1, y2), max(y1, y2)

    # 修复2:坐标截断到图像范围内(避免超出图片边界)
    x1 = max(0, x1)
    y1 = max(0, y1)
    x2 = min(img_width - 1, x2)
    y2 = min(img_height - 1, y2)

    # 修复3:过滤无效坐标(尺寸过小的区域无意义)
    if x2 - x1 < 5 or y2 - y1 < 5:
        invalid_coords.append(f"车位{i}: 尺寸过小 ({x1},{y1},{x2},{y2})")
        continue

    # 提取车位图像(基于处理后的坐标)
    spot_img = img_rgb[y1:y2, x1:x2]
    if spot_img.size == 0:  # 检查区域是否有效
        invalid_coords.append(f"车位{i}: 超出有效范围 ({x1},{y1},{x2},{y2})")
        continue
  • 坐标修复:确保x1 < x2y1 < y2(避免因标注错误导致的负数区域)。
  • 边界截断:将坐标限制在图片范围内(避免提取超出图片的无效区域)。
  • 无效过滤:排除尺寸过小(宽或高<5像素)或超出图片范围的区域,保证后续处理有效。

6. 模型预测与结果标记

# 预处理和预测
try:
    # 缩放为训练时的尺寸(15x15,与模型输入一致)
    spot_img_resized = resize(spot_img, (15, 15), anti_aliasing=True)
    spot_img_flat = spot_img_resized.flatten().reshape(1, -1)  # 展平为一维数组

    # 模型预测(1表示有车,0表示空闲)
    prediction = model.predict(spot_img_flat)[0]

    # 亮度过滤(优化预测结果)
    gray_spot = rgb2gray(spot_img)  # 转为灰度图
    brightness = np.mean(gray_spot)  # 计算平均亮度
    if brightness > 0.6:  # 亮度过高时判定为空闲(可能是阳光直射或无车)
        prediction = 0

    # 绘制矩形和编号
    color = (0, 0, 255) if prediction == 1 else (0, 255, 0)  # 红=有车,绿=空闲
    cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)  # 绘制矩形框(线宽2)

    # 显示车位编号(避免重叠)
    text_pos = (x1 + 5, y1 + 15)  # 编号位置(左上角偏移)
    cv2.putText(img, f"{i}", text_pos,
                cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)  # 白色文字,字重1

    valid_count += 1  # 有效车位计数
except Exception as e:
    invalid_coords.append(f"车位{i}: 处理失败 - {str(e)}")  # 记录处理失败的车位
  • 预处理:将车位区域缩放为15x15(模型训练时的输入尺寸),并展平为一维数组。
  • 模型预测:通过加载的模型判断车位状态(1=有车,0=空闲)。
  • 亮度优化:当车位区域平均亮度>0.6时,强制判定为空闲(解决强光下模型误判问题)。
  • 结果标记:用不同颜色的矩形框标记车位状态,并在框内添加编号(便于对应)。

7. 结果统计与保存

# 结果统计
print(f"原始处理车位数量:{len(parking_spots)}")
print(f"有效标记车位:{valid_count}/{len(parking_spots)}")
if invalid_coords:
    print("无效坐标列表:")
    for msg in invalid_coords[:10]:  # 只显示前10个
        print(f"- {msg}")
    if len(invalid_coords) > 10:
        print(f"- 还有{len(invalid_coords)-10}个无效坐标未显示")

# 保存并显示结果
cv2.imwrite('parking_result.jpg', img)  # 保存结果图片

# 缩放显示窗口(避免过大)
display_scale = 0.8
display_img = cv2.resize(img, (int(img_width*display_scale), int(img_height*display_scale)))
cv2.imshow('Parking Detection Result', display_img)
cv2.waitKey(0)  # 等待按键后关闭窗口
cv2.destroyAllWindows()
  • 统计信息:输出总车位数量、有效标记数量及无效坐标原因(便于调试)。
  • 结果保存:将标记后的图片保存为parking_result.jpg
  • 缩放显示:将图片按0.8倍缩放后显示,避免因原图过大导致窗口超出屏幕。

三、完整代码

import cv2
import numpy as np
import pickle
from skimage.transform import resize
from skimage.color import rgb2gray  # 直接导入需要的函数

# 1. 加载模型
try:
    model = pickle.load(open('./model.p', 'rb'))
except Exception as e:
    print(f"模型加载失败:{e}")
    exit()

# 2. 读取图片并转换为RGB
image_path = './first_frame.jpg'
img = cv2.imread(image_path)
if img is None:
    print("无法读取图片,请检查路径")
    exit()

# 打印图像信息(用于排查)
img_height, img_width = img.shape[:2]
print(f"图像尺寸:宽={img_width}, 高={img_height}")

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 3. 车位坐标(完整坐标列表)
parking_spots = [
    (78, 394, 149, 425),
    (78, 426, 149, 457),
    (78, 458, 149, 489),
    (78, 490, 149, 521),
    (78, 522, 149, 553),
    (78, 554, 149, 585),
    (78, 586, 149, 617),
    (78, 618, 149, 649),
    (78, 650, 149, 681),
    (78, 682, 149, 713),
    (78, 714, 149, 745),
    (78, 746, 149, 777),
    (78, 778, 149, 809),
    (78, 810, 149, 841),
    (78, 842, 149, 873),
    (78, 874, 149, 905),
    (78, 906, 149, 937),
    (78, 938, 149, 969),
    (78, 970, 149, 1001),
    (78, 1002, 149, 1033),
    (78, 1034, 149, 1065),
    #-------------------------------------
    (146, 136, 215, 164),   # 1
    (146, 168, 215, 196),   # 2: 136+32=168, 164+32=196
    (146, 200, 215, 228),   # 3: 168+32=200, 196+32=228
    (146, 232, 215, 260),   # 4
    (146, 264, 215, 292),   # 5
    (146, 296, 215, 324),   # 6
    (146, 328, 215, 356),   # 7
    (146, 360, 215, 388),   # 8
    (146, 392, 215, 420),   # 9
    (146, 424, 215, 452),   # 10
    (146, 456, 215, 484),   # 11
    (146, 488, 215, 516),   # 12
    (146, 520, 215, 548),   # 13
    (146, 552, 215, 580),   # 14
    (146, 584, 215, 612),   # 15
    (146, 616, 215, 644),   # 16
    (146, 648, 215, 676),   # 17
    (146, 680, 215, 708),   # 18
    (146, 712, 215, 740),   # 19
    (146, 744, 215, 772),   # 20
    (146, 776, 215, 804),   # 21
    (146, 808, 215, 836),   # 22
    (146, 840, 215, 868),   # 23
    (146, 872, 215, 900),   # 24
    (146, 904, 215, 932),   # 25
    (146, 936, 215, 964),   # 26
    (146, 968, 215, 996),   # 27
    (146, 1000, 215, 1028), # 28
    (146, 1032, 215, 1060),  # 29
    #----------------------------------------
    (308, 134, 371, 164),   # 1
    (308, 166, 371, 196),   # 2: 134+32=166, 164+32=196
    (308, 198, 371, 228),   # 3: 166+32=198, 196+32=228
    (308, 230, 371, 260),   # 4
    (308, 262, 371, 292),   # 5
    (308, 294, 371, 324),   # 6
    (308, 326, 371, 356),   # 7
    (308, 358, 371, 388),   # 8
    (308, 390, 371, 420),   # 9
    (308, 422, 371, 452),   # 10
    (308, 454, 371, 484),   # 11
    (308, 486, 371, 516),   # 12
    (308, 518, 371, 548),   # 13
    (308, 550, 371, 580),   # 14
    (308, 582, 371, 612),   # 15
    (308, 614, 371, 642),   # 16
    (308, 646, 371, 676),   # 17
    (308, 678, 371, 708),   # 18
    (308, 710, 371, 740),   # 19
    (308, 742, 371, 772),   # 20
    (308, 774, 371, 804),   # 21
    (308, 806, 371, 836),   # 22
    (308, 838, 371, 868),   # 23
    (308, 870, 371, 900),   # 24
    (308, 902, 371, 932),   # 25
    (308, 934, 371, 964),   # 26
    (308, 966, 371, 996),   # 27
    (308, 998, 371, 1028),  # 28
    (308, 1030, 371, 1060), # 29
    (308, 1062, 371, 1092), # 30
    #----------------------------------------
    (386, 134, 448, 162),   # 1
    (386, 166, 448, 194),   # 2: 134+32=166, 162+32=194
    (386, 198, 448, 226),   # 3: 166+32=198, 194+32=226
    (386, 230, 448, 258),   # 4
    (386, 262, 448, 290),   # 5
    (386, 294, 448, 322),   # 6
    (386, 326, 448, 354),   # 7
    (386, 358, 448, 386),   # 8
    (386, 390, 448, 418),   # 9
    (386, 422, 448, 450),   # 10
    (386, 454, 448, 482),   # 11
    (386, 486, 448, 514),   # 12
    (386, 518, 448, 546),   # 13
    (386, 550, 448, 578),   # 14
    (386, 582, 448, 610),   # 15
    (386, 614, 448, 642),   # 16
    (386, 646, 448, 674),   # 17
    (386, 678, 448, 706),   # 18
    (386, 710, 448, 738),   # 19
    (386, 742, 448, 770),   # 20
    (386, 774, 448, 802),   # 21
    (386, 806, 448, 834),   # 22
    (386, 838, 448, 866),   # 23
    (386, 870, 448, 898),   # 24
    (386, 902, 448, 930),   # 25
    (386, 934, 448, 962),   # 26
    (386, 966, 448, 994),   # 27
    (386, 998, 448, 1026),  # 28
    (386, 1030, 448, 1058), # 29
    (386, 1062, 448, 1090), # 30
    #--------------------------------------------------
    (538, 265, 607, 294),   # 1
    (538, 297, 607, 326),   # 2: 265+32=297, 294+32=326
    (538, 329, 607, 358),   # 3: 297+32=329, 326+32=358
    (538, 361, 607, 390),   # 4
    (538, 393, 607, 422),   # 5
    (538, 425, 607, 454),   # 6
    (538, 457, 607, 486),   # 7
    (538, 489, 607, 518),   # 8
    (538, 521, 607, 550),   # 9
    (538, 553, 607, 582),   # 10
    (538, 585, 607, 614),   # 11
    (538, 617, 607, 646),   # 12
    (538, 649, 607, 678),   # 13
    (538, 681, 607, 710),   # 14
    (538, 713, 607, 742),   # 15
    (538, 745, 607, 774),   # 16
    (538, 777, 607, 806),   # 17
    (538, 809, 607, 838),   # 18
    (538, 841, 607, 870),   # 19
    (538, 873, 607, 902),   # 20
    (538, 905, 607, 934),   # 21
    (538, 937, 607, 966),   # 22
    (538, 969, 607, 998),   # 23
    (538, 1001, 607, 1030), # 24
    (538, 1033, 607, 1062), # 25
    (538, 1065, 607, 1094),  # 26
    #-------------------------------------
    (607, 264, 675, 292),   # 1
    (607, 296, 675, 324),   # 2: 264+32=296, 292+32=324
    (607, 328, 675, 356),   # 3: 296+32=328, 324+32=356
    (607, 360, 675, 388),   # 4
    (607, 392, 675, 420),   # 5
    (607, 424, 675, 452),   # 6
    (607, 456, 675, 484),   # 7
    (607, 488, 675, 516),   # 8
    (607, 520, 675, 548),   # 9
    (607, 552, 675, 580),   # 10
    (607, 584, 675, 612),   # 11
    (607, 616, 675, 644),   # 12
    (607, 648, 675, 676),   # 13
    (607, 680, 675, 708),   # 14
    (607, 712, 675, 740),   # 15
    (607, 744, 675, 772),   # 16
    (607, 776, 675, 804),   # 17
    (607, 808, 675, 836),   # 18
    (607, 840, 675, 868),   # 19
    (607, 872, 675, 900),   # 20
    (607, 904, 675, 932),   # 21
    (607, 936, 675, 964),   # 22
    (607, 968, 675, 996),   # 23
    (607, 1000, 675, 1028), # 24
    (607, 1032, 675, 1060), # 25
    (607, 1064, 675, 1092)  # 26
    #----------------------------------------
    ,(770, 162, 836, 192),
    (770, 194, 836, 224),
    (770, 226, 836, 256),
    (770, 258, 836, 288),
    (770, 290, 836, 320),
    (770, 322, 836, 352),
    (770, 354, 836, 384),
    (770, 386, 836, 416),
    (770, 418, 836, 448),
    (770, 450, 836, 480),
    (770, 482, 836, 512),
    (770, 514, 836, 544),
    (770, 546, 836, 576),
    (770, 578, 836, 608),
    (770, 610, 836, 640),
    (770, 642, 836, 672),
    (770, 674, 836, 704),
    (770, 706, 836, 736),
    (770, 738, 836, 768),
    (770, 770, 836, 800),
    (770, 802, 836, 832),
    (770, 834, 836, 864),
    (770, 866, 836, 896),
    (770, 898, 836, 928),
    (770, 930, 836, 960),
    (770, 962, 836, 992),
    (770, 994, 836, 1024),
    (770, 1026, 836, 1056),
    (770, 1058, 836, 1088)
    #------------------------------------
    ,(836, 163, 904, 191),   # 1
    (836, 195, 904, 223),   # 2: 163+32=195, 191+32=223
    (836, 227, 904, 255),   # 3: 195+32=227, 223+32=255
    (836, 259, 904, 287),   # 4
    (836, 291, 904, 319),   # 5
    (836, 323, 904, 351),   # 6
    (836, 355, 904, 383),   # 7
    (836, 387, 904, 415),   # 8
    (836, 419, 904, 447),   # 9
    (836, 451, 904, 479),   # 10
    (836, 483, 904, 511),   # 11
    (836, 515, 904, 543),   # 12
    (836, 547, 904, 575),   # 13
    (836, 579, 904, 607),   # 14
    (836, 611, 904, 639),   # 15
    (836, 643, 904, 671),   # 16
    (836, 675, 904, 703),   # 17
    (836, 707, 904, 735),   # 18
    (836, 739, 904, 767),   # 19
    (836, 771, 904, 799),   # 20
    (836, 803, 904, 831),   # 21
    (836, 835, 904, 863),   # 22
    (836, 867, 904, 895),   # 23
    (836, 899, 904, 927),   # 24
    (836, 931, 904, 959),   # 25
    (836, 963, 904, 991)    # 26
    #--------------------------------------
    ,(994, 221, 1060, 248),   # 1
    (994, 253, 1060, 280),   # 2: 221+32=253, 248+32=280
    (994, 285, 1060, 312),   # 3: 253+32=285, 280+32=312
    (994, 317, 1060, 344),   # 4
    (994, 349, 1060, 376),   # 5
    (994, 381, 1060, 408),   # 6
    (994, 413, 1060, 440),   # 7
    (994, 445, 1060, 472),   # 8
    (994, 477, 1060, 504),   # 9
    (994, 509, 1060, 536),   # 10
    (994, 541, 1060, 568),   # 11
    (994, 573, 1060, 600),   # 12
    (994, 605, 1060, 632),   # 13
    (994, 637, 1060, 664),   # 14
    (994, 669, 1060, 696),   # 15
    (994, 701, 1060, 728),   # 16
    (994, 733, 1060, 760),   # 17
    (994, 765, 1060, 792),   # 18
    (994, 797, 1060, 824),   # 19
    (994, 829, 1060, 856),   # 20
    (994, 861, 1060, 888),   # 21
    (994, 893, 1060, 920),   # 22
    (994, 925, 1060, 952)    # 23
    #---------------------------------------
    ,(1065, 220, 1130, 248),   # 1
    (1065, 252, 1130, 280),   # 2: 220+32=252, 248+32=280
    (1065, 284, 1130, 312),   # 3: 252+32=284, 280+32=312
    (1065, 316, 1130, 344),   # 4
    (1065, 348, 1130, 376),   # 5
    (1065, 380, 1130, 408),   # 6
    (1065, 412, 1130, 440),   # 7
    (1065, 444, 1130, 472),   # 8
    (1065, 476, 1130, 504),   # 9
    (1065, 508, 1130, 536),   # 10
    (1065, 540, 1130, 568),   # 11
    (1065, 572, 1130, 600),   # 12
    (1065, 604, 1130, 632),   # 13
    (1065, 636, 1130, 664),   # 14
    (1065, 668, 1130, 696),   # 15
    (1065, 700, 1130, 728),   # 16
    (1065, 732, 1130, 760),   # 17
    (1065, 764, 1130, 792),   # 18
    (1065, 796, 1130, 824),   # 19
    (1065, 828, 1130, 856),   # 20
    (1065, 860, 1130, 888),   # 21
    (1065, 892, 1130, 920),   # 22
    (1065, 924, 1130, 952)    # 23
    #-------------------------------------
    ,(1226, 153, 1287, 184),   # 1
    (1226, 185, 1287, 216),   # 2: 153+32=185, 184+32=216
    (1226, 217, 1287, 248),   # 3: 185+32=217, 216+32=248
    (1226, 249, 1287, 280),   # 4
    (1226, 281, 1287, 312),   # 5
    (1226, 313, 1287, 344),   # 6
    (1226, 345, 1287, 376),   # 7
    (1226, 377, 1287, 408),   # 8
    (1226, 409, 1287, 440),   # 9
    (1226, 441, 1287, 472),   # 10
    (1226, 473, 1287, 504),   # 11
    (1226, 505, 1287, 536),   # 12
    (1226, 537, 1287, 568),   # 13
    (1226, 569, 1287, 600),   # 14
    (1226, 601, 1287, 632),   # 15
    (1226, 633, 1287, 664),   # 16
    (1226, 665, 1287, 696),   # 17
    (1226, 697, 1287, 728),   # 18
    (1226, 729, 1287, 760),   # 19
    (1226, 761, 1287, 792),   # 20
    (1226, 793, 1287, 824),   # 21
    (1226, 825, 1287, 856),   # 22
    (1226, 857, 1287, 888),   # 23
    (1226, 889, 1287, 920),   # 24
    (1226, 921, 1287, 952)    # 25
    #-------------------------------------------
    ,(1293, 152, 1344, 183),   # 1
    (1293, 184, 1344, 215),   # 2: 152+32=184, 183+32=215
    (1293, 216, 1344, 247),   # 3: 184+32=216, 215+32=247
    (1293, 248, 1344, 279),   # 4
    (1293, 280, 1344, 311),   # 5
    (1293, 312, 1344, 343),   # 6
    (1293, 344, 1344, 375),   # 7
    (1293, 376, 1344, 407),   # 8
    (1293, 408, 1344, 439),   # 9
    (1293, 440, 1344, 471),   # 10
    (1293, 472, 1344, 503),   # 11
    (1293, 504, 1344, 535),   # 12
    (1293, 536, 1344, 567),   # 13
    (1293, 568, 1344, 599),   # 14
    (1293, 600, 1344, 631),   # 15
    (1293, 632, 1344, 663),   # 16
    (1293, 664, 1344, 695),   # 17
    (1293, 696, 1344, 727),   # 18
    (1293, 728, 1344, 759),   # 19
    (1293, 760, 1344, 791),   # 20
    (1293, 792, 1344, 823),   # 21
    (1293, 824, 1344, 855),   # 22
    (1293, 856, 1344, 887),   # 23
    (1293, 888, 1344, 919),   # 24
    (1293, 920, 1344, 951)    # 25
    #-------------------------------------------
    ,(1677, 149, 1738, 179),   # 1
    (1677, 181, 1738, 211),   # 2: 149+32=181, 179+32=211
    (1677, 213, 1738, 243),   # 3: 181+32=213, 211+32=243
    (1677, 245, 1738, 275),   # 4
    (1677, 277, 1738, 307),   # 5
    (1677, 309, 1738, 339),   # 6
    (1677, 341, 1738, 371),   # 7
    (1677, 373, 1738, 403),   # 8
    (1677, 405, 1738, 435),   # 9
    (1677, 437, 1738, 467),   # 10
    (1677, 469, 1738, 499),   # 11
    (1677, 501, 1738, 531),   # 12
    (1677, 533, 1738, 563),   # 13
    (1677, 565, 1738, 595),   # 14
    (1677, 597, 1738, 627),   # 15
    (1677, 629, 1738, 659),   # 16
    (1677, 661, 1738, 691),   # 17
    (1677, 693, 1738, 723),   # 18
    (1677, 725, 1738, 755),   # 19
    (1677, 757, 1738, 787),   # 20
    (1677, 789, 1738, 819),   # 21
    (1677, 821, 1738, 851),   # 22
    (1677, 853, 1738, 883),   # 23
    (1677, 885, 1738, 915),   # 24
    (1677, 917, 1738, 947)    # 25
    #-------------------------------------------
    ,(1449, 149, 1514, 182),   # 1
    (1449, 183, 1514, 216),   # 2(y1=149+34=183,y2=182+34=216)
    (1449, 217, 1514, 250),   # 3(y1=183+34=217,y2=216+34=250)
    (1449, 251, 1514, 284),   # 4
    (1449, 285, 1514, 318),   # 5
    (1449, 319, 1514, 352),   # 6
    (1449, 353, 1514, 386),   # 7
    (1449, 387, 1514, 420),   # 8
    (1449, 421, 1514, 454),   # 9
    (1449, 455, 1514, 488),   # 10
    (1449, 489, 1514, 522),   # 11
    (1449, 523, 1514, 556),   # 12
    (1449, 557, 1514, 590),   # 13
    (1449, 591, 1514, 624),   # 14
    (1449, 625, 1514, 658),   # 15
    (1449, 659, 1514, 692),   # 16
    (1449, 693, 1514, 726),   # 17
    (1449, 727, 1514, 760),   # 18
    (1449, 761, 1514, 794),   # 19
    (1449, 795, 1514, 828),   # 20
    (1449, 829, 1514, 862),   # 21
    (1449, 863, 1514, 896),   # 22
    (1449, 897, 1514, 930),   # 23
    (1449, 931, 1514, 964),   # 24
    (1449, 965, 1514, 998), # 25
    #---------------------------------------
    (1520, 153, 1584, 182),    # 1
    (1520, 187, 1584, 216),    # 2(y1=153+34=187;y2=182+34=216)
    (1520, 221, 1584, 250),    # 3(y1=187+34=221;y2=216+34=250)
    (1520, 255, 1584, 284),    # 4
    (1520, 289, 1584, 318),    # 5
    (1520, 323, 1584, 352),    # 6
    (1520, 357, 1584, 386),    # 7
    (1520, 391, 1584, 420),    # 8
    (1520, 425, 1584, 454),    # 9
    (1520, 459, 1584, 488),    # 10
    (1520, 493, 1584, 522),    # 11
    (1520, 527, 1584, 556),    # 12
    (1520, 561, 1584, 590),    # 13
    (1520, 595, 1584, 624),    # 14
    (1520, 629, 1584, 658),    # 15
    (1520, 663, 1584, 692),    # 16
    (1520, 697, 1584, 726),    # 17
    (1520, 731, 1584, 760),    # 18
    (1520, 765, 1584, 794),    # 19
    (1520, 799, 1584, 828),    # 20
    (1520, 833, 1584, 862),    # 21
    (1520, 867, 1584, 896),    # 22
    (1520, 901, 1584, 930),    # 23
    (1520, 935, 1584, 964),    # 24
    (1520, 969, 1584, 998)     # 25
    #-----------------------------------------
    ,(1776, 184, 1833, 213),    # 1
    (1776, 216, 1833, 245),    # 2(y1=184+32=216;y2=213+32=245)
    (1776, 248, 1833, 277),    # 3(y1=216+32=248;y2=245+32=277)
    (1776, 280, 1833, 309),    # 4
    (1776, 312, 1833, 341),    # 5
    (1776, 344, 1833, 373),    # 6
    (1776, 376, 1833, 405),    # 7
    (1776, 408, 1833, 437),    # 8
    (1776, 440, 1833, 469),    # 9
    (1776, 472, 1833, 501),    # 10
    (1776, 504, 1833, 533),    # 11
    (1776, 536, 1833, 565),    # 12
    (1776, 568, 1833, 597),    # 13
    (1776, 600, 1833, 629),    # 14
    (1776, 632, 1833, 661),    # 15
    (1776, 664, 1833, 693),    # 16
    (1776, 696, 1833, 725),    # 17
    (1776, 728, 1833, 757),    # 18
    (1776, 760, 1833, 789),    # 19
    (1776, 792, 1833, 821),    # 20
    (1776, 824, 1833, 853),    # 21
    (1776, 856, 1833, 885),    # 22
    (1776, 888, 1833, 917),    # 23
    (1776, 920, 1833, 949)     # 24
]

print(f"原始处理车位数量:{len(parking_spots)}")

# 4. 处理每个车位
valid_count = 0  # 有效坐标计数器
invalid_coords = []  # 无效坐标记录

for i, (x1, y1, x2, y2) in enumerate(parking_spots, 1):
    # 修复1:统一坐标顺序(确保x1<x2, y1<y2)
    x1, x2 = min(x1, x2), max(x1, x2)
    y1, y2 = min(y1, y2), max(y1, y2)

    # 修复2:坐标截断到图像范围内
    x1 = max(0, x1)
    y1 = max(0, y1)
    x2 = min(img_width - 1, x2)
    y2 = min(img_height - 1, y2)

    # 修复3:过滤无效坐标(至少保持最小矩形尺寸)
    if x2 - x1 < 5 or y2 - y1 < 5:
        invalid_coords.append(f"车位{i}: 尺寸过小 ({x1},{y1},{x2},{y2})")
        continue

    # 提取车位图像
    spot_img = img_rgb[y1:y2, x1:x2]
    if spot_img.size == 0:
        invalid_coords.append(f"车位{i}: 超出有效范围 ({x1},{y1},{x2},{y2})")
        continue

    # 预处理和预测
    try:
        # 缩放为训练时的尺寸
        spot_img_resized = resize(spot_img, (15, 15), anti_aliasing=True)
        spot_img_flat = spot_img_resized.flatten().reshape(1, -1)

        # 模型预测
        prediction = model.predict(spot_img_flat)[0]

        # 亮度过滤
        gray_spot = rgb2gray(spot_img)
        brightness = np.mean(gray_spot)
        if brightness > 0.6:
            prediction = 0  # 亮度高判定为空闲

        # 绘制矩形和编号
        color = (0, 0, 255) if prediction == 1 else (0, 255, 0)  # 红=有车,绿=空闲
        cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)

        # 优化编号显示(避免重叠)
        text_pos = (x1 + 5, y1 + 15)
        cv2.putText(img, f"{i}", text_pos,
                    cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)

        valid_count += 1
    except Exception as e:
        invalid_coords.append(f"车位{i}: 处理失败 - {str(e)}")

# 5. 结果统计
print(f"有效标记车位:{valid_count}/{len(parking_spots)}")
if invalid_coords:
    print("无效坐标列表:")
    for msg in invalid_coords[:10]:  # 只显示前10个
        print(f"- {msg}")
    if len(invalid_coords) > 10:
        print(f"- 还有{len(invalid_coords)-10}个无效坐标未显示")

# 6. 保存并显示(优化显示尺寸)
cv2.imwrite('parking_result.jpg', img)

# 缩放显示窗口(避免过大)
display_scale = 0.8
display_img = cv2.resize(img, (int(img_width*display_scale), int(img_height*display_scale)))
cv2.imshow('Parking Detection', display_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果图示:

停车场车位识别系统

在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐