% 清空命令窗口、工作区和关闭所有图形窗口
clc; clear all; close all;
% 加载数字模板(0 - 9的BMP图像)
templateDir = 'F:\Learing\zifumuban'; % 替换为实际模板路径
digitalTemplates = loadDigitalTemplates(templateDir);
% 读取图像
a = imread('红灯2.jpg');
figure('Name', '原始图像', 'NumberTitle', 'off');
imshow(a);
title('检测图像');
% 图像预处理
% 灰度化
gray_image = rgb2gray(a);
% 灰度图像显示
figure('Name', '灰度图像', 'NumberTitle', 'off');
imshow(gray_image);
title('灰度化后的图像');
% 降噪:使用中值滤波
denoised_image = medfilt2(gray_image, [3 3]);
% 平滑:使用高斯滤波
smoothed_image = imgaussfilt(denoised_image, 2);
% 转换为HSV空间
hsv = rgb2hsv(a);
h = hsv(:, :, 1);
s = hsv(:, :, 2);
v = hsv(:, :, 3);
% 定义颜色阈值范围(HSV空间)
% 优化红色阈值范围,减少误检
red_low = [0, 0.6, 0.4];
red_high = [0.08, 1.0, 1.0];
red_low_2 = [0.9, 0.6, 0.4];
red_high_2 = [1.0, 1.0, 1.0];
% 优化黄色阈值范围
yellow_low = [0.12, 0.6, 0.4];
yellow_high = [0.16, 0.8, 0.9];
% 优化绿色阈值范围
green_low = [0.25, 0.6, 0.4];
green_high = [0.35, 0.8, 0.9];
% 生成颜色掩膜
red_mask = ((h > red_low(1) & h < red_high(1)) | (h > red_low_2(1) & h < red_high_2(1))) & ...
(s > red_low(2) & s < red_high(2)) & ...
(v > red_low(3) & v < red_high(3));
yellow_mask = (h > yellow_low(1) & h < yellow_high(1)) & ...
(s > yellow_low(2) & s < yellow_high(2)) & ...
(v > yellow_low(3) & v < yellow_high(3));
green_mask = (h > green_low(1) & h < green_high(1)) & ...
(s > green_low(2) & s < green_high(2)) & ...
(v > green_low(3) & v < green_high(3));
% 合并掩膜并分析
color_mask = red_mask | yellow_mask | green_mask;
se = strel('disk', 3);
clean_mask = imopen(color_mask, se); % 开运算去除小的噪声点
clean_mask = imfill(clean_mask, 'holes'); % 填充内部孔洞
% 连通区域分析
[L, num] = bwlabel(clean_mask, 8);
STATS = regionprops(L, 'Area', 'Centroid', 'BoundingBox', 'PixelIdxList');
% 显示结果
figure('Name', '检测结果', 'NumberTitle', 'off');
imshow(a);
hold on;
% 创建倒计时数字检测图
timeDetectionFig = figure('Name', '倒计时数字检测', 'NumberTitle', 'off');
for i = 1:num
if STATS(i).Area < 300 || STATS(i).Area > 8000
continue;
end
bbox = STATS(i).BoundingBox;
pixel_idx = STATS(i).PixelIdxList;
red_pixel_count = sum(red_mask(pixel_idx));
yellow_pixel_count = sum(yellow_mask(pixel_idx));
green_pixel_count = sum(green_mask(pixel_idx));
total_pixels = length(pixel_idx);
red_ratio = red_pixel_count / total_pixels;
yellow_ratio = yellow_pixel_count / total_pixels;
green_ratio = green_pixel_count / total_pixels;
ratio_threshold = 0.2;
if red_ratio > ratio_threshold && yellow_ratio > ratio_threshold
color = '红黄异常';
edge_color = [1 0.5 0];
elseif red_ratio > ratio_threshold
color = '红灯 禁止通行';
edge_color = [1 0 0];
elseif yellow_ratio > ratio_threshold
color = '黄色 尽快通行';
edge_color = [1 1 0];
elseif green_ratio > ratio_threshold
color = '绿色 可以通行';
edge_color = [0 1 0];
else
color = '未知';
edge_color = [0.5 0.5 0.5];
end
rectangle('Position', bbox, 'EdgeColor', edge_color, 'LineWidth', 3);
text(bbox(1)+bbox(3)/2-20, bbox(2)+bbox(4)/2, color, ...
'Color', 'white', 'FontSize', 12, 'HorizontalAlignment', 'center');
% --- 倒计时数字检测优化版(开始) ---
% 初始化识别结果
timeStr = '';
digitBoxes = [];
timeRegion = [];
% 策略1: 尝试在信号灯右侧检测数字
[timeStr, digitBoxes, timeRegion] = detectTimeOnRight(a, bbox, digitalTemplates);
% 策略2: 如果在右侧未检测到,尝试在信号灯下方检测
if isempty(timeStr)
[timeStr, digitBoxes, timeRegion] = detectTimeBelow(a, bbox, digitalTemplates);
end
% 策略3: 如果在下方未检测到,尝试在信号灯内部检测
if isempty(timeStr)
[timeStr, digitBoxes, timeRegion] = detectTimeInside(a, bbox, digitalTemplates, red_ratio);
end
% 如果检测到倒计时数字
if ~isempty(timeStr)
% 在原图上显示识别结果(在信号灯上方)
text(bbox(1) + bbox(3)/2, bbox(2) - 30, ['剩余: ', timeStr, '秒'], ...
'Color', 'cyan', 'FontSize', 14, 'FontWeight', 'bold', ...
'HorizontalAlignment', 'center');
% 绘制识别到的数字区域
for db = 1:size(digitBoxes, 1)
rectX = digitBoxes(db,1);
rectY = digitBoxes(db,2);
rectangle('Position', [rectX, rectY, digitBoxes(db,3), digitBoxes(db,4)], ...
'EdgeColor', 'cyan', 'LineWidth', 1.5);
end
% 在倒计时数字检测图中显示
figure(timeDetectionFig);
if ~isempty(timeRegion)
subplot(1,3,1), imshow(timeRegion), title('原始数字区域');
% 显示处理后的数字区域
grayRegion = rgb2gray(timeRegion);
enhancedRegion = imadjust(grayRegion);
subplot(1,3,2), imshow(enhancedRegion), title('增强对比度');
% 显示边缘检测
edgeRegion = edge(enhancedRegion, 'Canny');
subplot(1,3,3), imshow(edgeRegion), title('边缘检测');
sgtitle(['识别结果: ', timeStr, '秒']);
end
end
% --- 倒计时数字检测优化版(结束) ---
end
hold off;
%% 数字模板加载函数
function templates = loadDigitalTemplates(dirPath)
templates = struct();
digits = '0123456789';
for i = 1:length(digits)
digitStr = digits(i);
filePath = fullfile(dirPath, [digitStr, '.bmp']);
if exist(filePath, 'file')
% 读取模板图像并转为二值图
templateImg = imread(filePath);
if size(templateImg, 3) > 1
templateImg = rgb2gray(templateImg);
end
templateImg = imbinarize(templateImg);
% 存储模板,使用字符串形式的字段名
templates.(sprintf('%s', digitStr)) = templateImg;
else
warning('模板文件不存在: %s', filePath);
end
end
end
%% 优化后的时间数字识别函数(使用边缘检测和垂直投影法)
function [timeStr, digitBoxes] = recognizeTimeDigitsEnhanced(region, templates)
% 初始化输出
timeStr = '';
digitBoxes = [];
% 转换为灰度图像
if size(region, 3) == 3
grayRegion = rgb2gray(region);
else
grayRegion = region; % 已经是灰度图
end
% 增强对比度
grayRegion = imadjust(grayRegion);
% 使用自适应阈值二值化
bwRegion = imbinarize(grayRegion, 'adaptive', 'Sensitivity', 0.6);
% 形态学操作: 去除噪声
se = strel('rectangle', [2 2]);
cleanedRegion = imopen(bwRegion, se);
% 连接分量分析提取可能数字区域
cc = bwconncomp(cleanedRegion);
stats = regionprops(cc, 'BoundingBox', 'Area', 'Image', 'PixelList');
% 过滤过大或过小的区域
minDigitArea = 50; % 最小数字区域面积
maxDigitArea = 2000; % 最大数字区域面积
aspectRatioRange = [0.3, 1.5]; % 宽高比范围
validRegions = [];
for k = 1:length(stats)
area = stats(k).Area;
bbox = stats(k).BoundingBox;
aspectRatio = bbox(3)/bbox(4);
if area > minDigitArea && area < maxDigitArea && ...
aspectRatio > aspectRatioRange(1) && aspectRatio < aspectRatioRange(2)
validRegions = [validRegions; stats(k)];
end
end
% 没有有效数字区域
if isempty(validRegions)
return;
end
% 根据X坐标排序(从左到右)
[~, idx] = sort([validRegions.BoundingBox]);
validRegions = validRegions(idx);
% 合并重叠或邻近的区域
mergedRegions = [];
if ~isempty(validRegions)
mergedRegions = validRegions(1);
for i = 2:length(validRegions)
current = validRegions(i);
last = mergedRegions(end);
% 检查是否重叠或邻近
if current.BoundingBox(1) <= (last.BoundingBox(1) + last.BoundingBox(3) + 5)
% 合并区域
x1 = min(last.BoundingBox(1), current.BoundingBox(1));
y1 = min(last.BoundingBox(2), current.BoundingBox(2));
x2 = max(last.BoundingBox(1)+last.BoundingBox(3), ...
current.BoundingBox(1)+current.BoundingBox(3));
y2 = max(last.BoundingBox(2)+last.BoundingBox(4), ...
current.BoundingBox(2)+current.BoundingBox(4));
newBbox = [x1, y1, x2-x1, y2-y1];
mergedRegions(end).BoundingBox = newBbox;
else
mergedRegions = [mergedRegions; current];
end
end
end
% 最多处理前2个最大的区域(倒计时通常是1-2位数字)
if ~isempty(mergedRegions)
[~, areaIdx] = sort([mergedRegions.Area], 'descend');
mergedRegions = mergedRegions(areaIdx(1:min(2, length(areaIdx))));
% 根据X坐标排序(从左到右)
[~, idx] = sort([mergedRegions.BoundingBox]);
mergedRegions = mergedRegions(idx);
end
timeDigits = '';
digitBoxes = [];
for k = 1:length(mergedRegions)
region = mergedRegions(k);
charImg = region.Image;
% 垂直投影法分割可能的多数字区域
verticalProjection = sum(charImg, 1);
if isempty(verticalProjection)
continue;
end
% 平滑投影曲线
smoothedProjection = smoothdata(verticalProjection, 'gaussian', 5);
% 寻找分割点(投影值低于最大值的20%)
threshold = max(smoothedProjection) * 0.2;
splitPoints = find(smoothedProjection < threshold);
% 如果没有明显的分割点,直接处理整个区域
if isempty(splitPoints)
% 调整尺寸以匹配模板
templateSize = size(templates.('0'));
if ~isempty(templateSize)
scaleFactor = min(floor(size(charImg,1)/templateSize(1)), ...
floor(size(charImg,2)/templateSize(2)));
if scaleFactor < 0.5, scaleFactor = 0.5; end
resizedChar = imresize(charImg, scaleFactor * templateSize);
[digit, score] = matchDigitTemplate(resizedChar, templates);
% 足够置信匹配
if score > 0.6
timeDigits = [timeDigits digit];
digitBoxes = [digitBoxes; region.BoundingBox];
end
end
else
% 有分割点,处理多个数字
% 找到连续的分割区域
diffSplit = diff(splitPoints);
gapIndices = find(diffSplit > 1);
if isempty(gapIndices)
% 只有一个连续区域
splitStarts = splitPoints(1);
splitEnds = splitPoints(end);
else
splitStarts = [splitPoints(1), splitPoints(gapIndices + 1)];
splitEnds = [splitPoints(gapIndices), splitPoints(end)];
end
% 分割数字
for s = 1:length(splitStarts)
left = splitStarts(s);
right = splitEnds(s);
if right - left < 3 % 太窄的区域忽略
continue;
end
% 提取单个数字
if left > size(charImg, 2) || right > size(charImg, 2)
continue;
end
singleDigit = charImg(:, left:right);
% 调整尺寸以匹配模板
templateSize = size(templates.('0'));
if ~isempty(templateSize)
scaleFactor = min(floor(size(singleDigit,1)/templateSize(1)), ...
floor(size(singleDigit,2)/templateSize(2)));
if scaleFactor < 0.5, scaleFactor = 0.5; end
resizedChar = imresize(singleDigit, scaleFactor * templateSize);
[digit, score] = matchDigitTemplate(resizedChar, templates);
% 足够置信匹配
if score > 0.6
timeDigits = [timeDigits digit];
% 计算分割后数字的边界框
digitBox = region.BoundingBox;
digitBox(1) = digitBox(1) + left - 1;
digitBox(3) = right - left + 1;
digitBoxes = [digitBoxes; digitBox];
end
end
end
end
end
% 整体置信度检验
if ~isempty(timeDigits)
% 确保数字顺序正确(从左到右)
if ~isempty(digitBoxes)
[~, idx] = sort(digitBoxes(:,1));
timeDigits = timeDigits(idx);
digitBoxes = digitBoxes(idx,:);
end
timeStr = timeDigits;
else
timeStr = '';
digitBoxes = [];
end
end
%% 数字模板匹配函数
function [digit, bestScore] = matchDigitTemplate(charImg, templates)
digit = '';
bestScore = -Inf;
% 尝试与每个数字模板匹配
digitKeys = fieldnames(templates);
for d = 1:length(digitKeys)
template = templates.(digitKeys{d});
% 确保模板和图像尺寸匹配
if size(charImg,1) ~= size(template,1) || size(charImg,2) ~= size(template,2)
resizedChar = imresize(charImg, size(template));
else
resizedChar = charImg;
end
% 使用归一化互相关进行匹配
corrScore = normxcorr2(template, resizedChar);
score = max(corrScore(:));
if score > bestScore
bestScore = score;
digit = digitKeys{d};
end
end
end
%% 右侧倒计时检测函数
function [timeStr, digitBoxes, timeRegion] = detectTimeOnRight(img, bbox, templates)
timeStr = '';
digitBoxes = [];
timeRegion = [];
% 定义数字区域: 在信号灯右侧相同高度位置
timeRegionX = bbox(1) + bbox(3); % 从信号灯右侧开始
timeRegionY = max(1, bbox(2) - bbox(4)*0.5); % 扩展搜索区域到上下各0.5倍高度
timeWidth = bbox(3) * 3; % 宽度扩展为信号灯宽度的3倍
timeHeight = bbox(4) * 2; % 高度扩展为信号灯高度的2倍
% 确保区域在图像范围内
if timeRegionX + timeWidth > size(img, 2)
timeWidth = size(img, 2) - timeRegionX - 1;
end
if timeRegionY + timeHeight > size(img, 1)
timeHeight = size(img, 1) - timeRegionY - 1;
end
% 提取数字区域
if timeWidth > 10 && timeHeight > 10
timeRegion = img(floor(timeRegionY):floor(timeRegionY+timeHeight), ...
floor(timeRegionX):floor(timeRegionX+timeWidth), :);
% 调用优化后的数字识别函数
[timeStr, digitBoxes] = recognizeTimeDigitsEnhanced(timeRegion, templates);
% 调整坐标到原始图像
if ~isempty(digitBoxes)
digitBoxes(:,1) = digitBoxes(:,1) + timeRegionX;
digitBoxes(:,2) = digitBoxes(:,2) + timeRegionY;
end
end
end
%% 下方倒计时检测函数
function [timeStr, digitBoxes, timeRegion] = detectTimeBelow(img, bbox, templates)
timeStr = '';
digitBoxes = [];
timeRegion = [];
% 定义数字区域: 在信号灯下方
timeRegionX = max(1, bbox(1) - bbox(3)*0.5); % 扩展搜索区域到左右各0.5倍宽度
timeRegionY = bbox(2) + bbox(4); % 从信号灯底部开始
timeWidth = bbox(3) * 2; % 宽度扩展为信号灯宽度的2倍
timeHeight = bbox(4) * 1.5; % 高度扩展为信号灯高度的1.5倍
% 确保区域在图像范围内
if timeRegionX + timeWidth > size(img, 2)
timeWidth = size(img, 2) - timeRegionX - 1;
end
if timeRegionY + timeHeight > size(img, 1)
timeHeight = size(img, 1) - timeRegionY - 1;
end
% 提取数字区域
if timeWidth > 10 && timeHeight > 10
timeRegion = img(floor(timeRegionY):floor(timeRegionY+timeHeight), ...
floor(timeRegionX):floor(timeRegionX+timeWidth), :);
% 调用优化后的数字识别函数
[timeStr, digitBoxes] = recognizeTimeDigitsEnhanced(timeRegion, templates);
% 调整坐标到原始图像
if ~isempty(digitBoxes)
digitBoxes(:,1) = digitBoxes(:,1) + timeRegionX;
digitBoxes(:,2) = digitBoxes(:,2) + timeRegionY;
end
end
end
%% 内部倒计时检测函数(适用于红色数字倒计时)
function [timeStr, digitBoxes, timeRegion] = detectTimeInside(img, bbox, templates, red_ratio)
timeStr = '';
digitBoxes = [];
timeRegion = [];
% 仅当红灯比例较高时尝试内部检测
if red_ratio < 0.5
return;
end
% 定义数字区域: 整个信号灯内部
timeRegionX = bbox(1);
timeRegionY = bbox(2);
timeWidth = bbox(3);
timeHeight = bbox(4);
% 提取信号灯区域
timeRegion = img(floor(timeRegionY):floor(timeRegionY+timeHeight), ...
floor(timeRegionX):floor(timeRegionX+timeWidth), :);
% 转换为HSV并提取亮度通道
hsvRegion = rgb2hsv(timeRegion);
vChannel = hsvRegion(:,:,3);
% 增强对比度
vEnhanced = imadjust(vChannel);
% 二值化 - 寻找高亮区域(倒计时数字通常更亮)
binaryRegion = vEnhanced > graythresh(vEnhanced) * 1.5;
% 形态学操作:去除小噪点
cleanedRegion = bwareaopen(binaryRegion, 20);
% 转换为RGB图像用于识别
timeRegion = uint8(cat(3, cleanedRegion*255, cleanedRegion*255, cleanedRegion*255));
% 调用优化后的数字识别函数
[timeStr, digitBoxes] = recognizeTimeDigitsEnhanced(timeRegion, templates);
% 调整坐标到原始图像
if ~isempty(digitBoxes)
digitBoxes(:,1) = digitBoxes(:,1) + timeRegionX;
digitBoxes(:,2) = digitBoxes(:,2) + timeRegionY;
end
end 还是无法检测到倒计时数字 也没有在原图像上显示是否可以通行
最新发布