import cv2
import numpy as np
import os
import glob
import time
import random
from skimage.feature import hog
from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
from scipy.ndimage import label, maximum_filter
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
class VehicleDetector:
def __init__(self):
self.clf = None # SVM分类器
self.scaler = None # 特征标准化器
self.hog_params = {
'orientations': 9,
'pixels_per_cell': (8, 8),
'cells_per_block': (2, 2),
'transform_sqrt': True,
'block_norm': 'L2-Hys',
'visualize': False
}
self.window_size = (64, 64) # 检测窗口大小
def extract_features(self, image):
"""从图像中提取HOG特征"""
# 转换为灰度图
if len(image.shape) > 2:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 调整图像大小
image = cv2.resize(image, self.window_size)
# 计算HOG特征
features = hog(image, **self.hog_params)
return features
def load_dataset(self, vehicle_dir, non_vehicle_dir, sample_size=1500):
"""加载车辆和非车辆数据集"""
features = []
labels = []
# 获取所有图像路径
vehicle_paths = glob.glob(os.path.join(vehicle_dir, '*.png')) + \
glob.glob(os.path.join(vehicle_dir, '*.jpg'))
non_vehicle_paths = glob.glob(os.path.join(non_vehicle_dir, '*.png')) + \
glob.glob(os.path.join(non_vehicle_dir, '*.jpg'))
# 检查是否找到图像
if not vehicle_paths:
raise FileNotFoundError(f"在 {vehicle_dir} 中未找到车辆图像")
if not non_vehicle_paths:
raise FileNotFoundError(f"在 {non_vehicle_dir} 中未找到非车辆图像")
# 使用更多样本
vehicle_paths = random.sample(vehicle_paths, min(sample_size, len(vehicle_paths)))
non_vehicle_paths = random.sample(non_vehicle_paths, min(sample_size, len(non_vehicle_paths)))
print(f"找到 {len(vehicle_paths)} 张车辆图像, {len(non_vehicle_paths)} 张非车辆图像")
# 处理车辆图像
print("提取车辆图像特征...")
for i, path in enumerate(vehicle_paths):
if i % 100 == 0:
print(f"已处理 {i}/{len(vehicle_paths)} 张车辆图像")
image = cv2.imread(path)
if image is None:
print(f"无法加载车辆图像: {path}")
continue
feat = self.extract_features(image)
features.append(feat)
labels.append(1)
# 处理非车辆图像
print("提取非车辆图像特征...")
for i, path in enumerate(non_vehicle_paths):
if i % 100 == 0:
print(f"已处理 {i}/{len(non_vehicle_paths)} 张非车辆图像")
image = cv2.imread(path)
if image is None:
print(f"无法加载非车辆图像: {path}")
continue
feat = self.extract_features(image)
features.append(feat)
labels.append(0)
print(f"数据集加载完成: {len(features)}个样本")
return np.array(features), np.array(labels)
def train(self, X, y):
"""训练SVM分类器"""
print("正在训练分类器...")
start_time = time.time()
# 数据验证
if len(X) == 0 or len(y) == 0:
raise ValueError("训练数据为空!请检查数据集路径和格式")
# 标准化特征
self.scaler = StandardScaler()
X_scaled = self.scaler.fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42
)
# 创建并训练线性SVM
self.clf = LinearSVC(C=0.1, max_iter=10000, random_state=42, verbose=1)
self.clf.fit(X_train, y_train)
# 评估模型
train_acc = self.clf.score(X_train, y_train)
test_acc = self.clf.score(X_test, y_test)
print(f"训练完成! 耗时: {time.time() - start_time:.2f}秒")
print(f"训练准确率: {train_acc:.4f}, 测试准确率: {test_acc:.4f}")
# 生成分类报告
y_pred = self.clf.predict(X_test)
print("\n分类报告:")
print(classification_report(y_test, y_pred, target_names=['非车辆', '车辆']))
return train_acc, test_acc
def detect_vehicles(self, image, threshold=2.0, scale=1.3, window_step=16):
"""在图像中检测车辆 - 改进的检测方法"""
if self.clf is None:
raise ValueError("请先训练分类器!")
# 复制原始图像
orig = image.copy()
# 转换为HSV色彩空间 - 更好地处理光照变化
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 创建热力图
heatmap = np.zeros_like(image[:, :, 0]).astype(np.float32) # 使用np.float32
# 仅处理图像的下半部分(道路区域)
height, width = image.shape[:2]
roi_y_start = height // 2
roi = image[roi_y_start:, :]
# 创建图像金字塔
pyramid = []
current_scale = 1.0
min_size = self.window_size
# 添加原始图像
pyramid.append((roi, current_scale))
# 生成金字塔 (限制层数)
for _ in range(6): # 减少金字塔层数
# 计算下一层尺度
current_scale /= scale
w = int(roi.shape[1] * current_scale)
h = int(roi.shape[0] * current_scale)
# 如果图像太小则停止
if w < min_size[0] or h < min_size[1]:
break
# 缩放图像并添加到金字塔
resized = cv2.resize(roi, (w, h))
pyramid.append((resized, current_scale))
# 在每个金字塔层级上滑动窗口
for i, (layer, scale_factor) in enumerate(pyramid):
# 滑动窗口
for y in range(0, layer.shape[0] - self.window_size[1], window_step):
for x in range(0, layer.shape[1] - self.window_size[0], window_step):
# 提取窗口
window_img = layer[y:y+self.window_size[1], x:x+self.window_size[0]]
# 提取特征
features = self.extract_features(window_img)
# 标准化特征
if self.scaler:
features = self.scaler.transform([features])
# 预测
prediction = self.clf.decision_function(features)
# 如果置信度高于阈值,添加到热力图
if prediction > threshold:
# 计算原始图像中的坐标
x1 = int(x / scale_factor)
y1 = int(y / scale_factor) + roi_y_start
x2 = int((x + self.window_size[0]) / scale_factor)
y2 = int((y + self.window_size[1]) / scale_factor) + roi_y_start
# 在热力图上增加权重
heatmap[y1:y2, x1:x2] += prediction
# 应用高斯模糊和阈值处理
heatmap = cv2.GaussianBlur(heatmap, (35, 35), 0) # 增大高斯核尺寸
heatmap_thresh = np.zeros_like(heatmap)
heatmap_thresh[heatmap > threshold] = 1
# 应用形态学操作 - 开运算
kernel = np.ones((25, 25), np.uint8)
heatmap_thresh = cv2.morphologyEx(heatmap_thresh, cv2.MORPH_OPEN, kernel)
# 连接组件分析
labels, num_labels = label(heatmap_thresh)
# 为每个连接的组件创建边界框
detections = []
for i in range(1, num_labels + 1):
# 找到组件的像素坐标
nonzero = (labels == i).nonzero()
nonzeroy = np.array(nonzero[0])
nonzerox = np.array(nonzero[1])
# 定义边界框
x1 = np.min(nonzerox)
y1 = np.min(nonzeroy)
x2 = np.max(nonzerox)
y2 = np.max(nonzeroy)
# 计算置信度(热力图的平均值)
confidence = np.mean(heatmap[y1:y2, x1:x2])
# 添加边界框
detections.append((x1, y1, x2, y2, confidence))
# 绘制边界框
cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
label_text = f"Vehicle: {confidence:.2f}"
cv2.putText(image, label_text, (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
print(f"检测到 {len(detections)} 个有效车辆")
return image, detections
def test_single_image(self, image_path, threshold=2.0, scale=1.3, window_step=16):
"""测试单张图像并显示结果"""
# 读取测试图像
image = cv2.imread(image_path)
if image is None:
print(f"无法读取图像: {image_path}")
return None, []
# 检测车辆
start_time = time.time()
result, detections = self.detect_vehicles(
image.copy(),
threshold=threshold,
scale=scale,
window_step=window_step
)
print(f"检测耗时: {time.time() - start_time:.2f}秒")
# 转换为RGB格式以在matplotlib中显示
result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
orig_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 显示结果
plt.figure(figsize=(15, 8))
plt.subplot(1, 2, 1)
plt.imshow(orig_rgb)
plt.title('原始图像')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(result_rgb)
plt.title(f'检测结果 (检测到 {len(detections)} 辆车)')
plt.axis('off')
plt.tight_layout()
plt.savefig('vehicle_detection_result.png')
plt.show()
return result, detections
def main():
# 创建车辆检测器
detector = VehicleDetector()
# 数据集路径
vehicle_dir = r"C:\Users\29810\Desktop\shijue\vehicles"
non_vehicle_dir = r"C:\Users\29810\Desktop\shijue\non-vehicles"
# 检查数据集是否存在
if not os.path.exists(vehicle_dir):
print(f"车辆目录不存在: {vehicle_dir}")
return
if not os.path.exists(non_vehicle_dir):
print(f"非车辆目录不存在: {non_vehicle_dir}")
return
# 加载数据集
try:
X, y = detector.load_dataset(vehicle_dir, non_vehicle_dir, sample_size=1500)
except Exception as e:
print(f"加载数据集失败: {str(e)}")
return
# 训练分类器
try:
train_acc, test_acc = detector.train(X, y)
except Exception as e:
print(f"训练分类器失败: {str(e)}")
return
# 测试图像路径
test_images = [
"C:/Users/29810/Desktop/shijue/test_images/test 1.jpg",
"C:/Users/29810/Desktop/shijue/test_images/test 2.jpg",
"C:/Users/29810/Desktop/shijue/test_images/test 3.jpg",
]
# 测试参数 - 使用更严格的设置
params = {
'threshold': 2.0, # 提高阈值减少误报
'scale': 1.3, # 优化缩放比例
'window_step': 16 # 减小步长提高检测精度
}
# 测试所有图像
for i, img_path in enumerate(test_images):
print(f"\n测试图像 {i+1}/{len(test_images)}: {img_path}")
if not os.path.exists(img_path):
print(f"测试图像不存在: {img_path}")
continue
detector.test_single_image(img_path, **params)
print("\n车辆检测完成!")
if __name__ == "__main__":
main() 输出的三张照片里对车尾的检测不正确,修改代码