Function Templates

一、定义 Function Template

 

template <typename T>
inline const T& max(const T& a, const T& b)
{
        return a<b ? a : b;
}

 二、参数推导

当使用某一类型的参数调用max()时,模板参数(template parameter)将以调用参数的类型确定下来。如下述代码

int main()
{
      max(4, 7);        // ok, 两个T都被推导为int
      max(4, 4.2);     // error: 第一个T被推导为int,第二个T被推导为double
}

三种方法解决error问题

  1)把两个参数转型为相同数据类型

       max(static_cast<double>(4), 4.2);

  2)明确指定T的类型

       max<double>(4, 4.2);  

  3)修改function template的模板参数为两个不同的类型; 

 

三、模板参数

模板参数有两种:

I: template parameter (模板参数),在function template的一对尖括号里声明;

      template <typename T >

 

II:call parameter,在function template名称后的小圆括号中声明;

      max(const T& a, const T& b );

template parameter的数量不限(如果不觉得难看的话),但是不能在function template预设默认值。

按照3)的方法可以修改max的定义如下

template <typename T1, typename T2>
inline T1 max(const T1& a, const T2& b)
{
      return a<b ? b : a;
}
...
max(4, 4.2);  //比如此处调用返回值类型想为double, 实际返回类型为int。

 上述方法明显有不足之处,返回值必须是参数类型中的一个,而不能动态决定; 另一个不足之处是,由于发生了类型cast,导致临时对象产生,因此返回值不能为引用。

 

由于T的类型由调用参数决定,因此我们把T与call parameter之间的关联,称为function template argument deduction (函数模板参数推导)。这种机制允许调用function template如同调用non-function template一样。当然,developer也可以显示化地指定template parameter的类型,例如

max<double>(4, 4.2);

 不过推导过程,并不包含返回值类型的推导;假如,我们有如下版本的max function template

template <typename T1, typename T2, typename RT>
inline RT max (const T1& a, const T2& b);

max<int, double, double>(4, 4.2); //ok, but redundant.

 在本例中,返回类型RT必须在调用max时明确指定;不过调用起来显得如此拖沓冗长。这样的调用,function template的推导过程要么要求所有的模板参数全部显示指定,要么都不显示指定。但是如果我们修改下max function template的template parameter的顺序,像如下这样

template<typename RT, typename T1, typename T2>
inline RT max (const T1& a, const T2& b);

max<double>(4, 4.2); 

那么调用时,只需指定第一个template parameter就可以了。

 

四、重载Function Template

Function template与non-function template 函数一样,可以重载;

 

小结

1. Function templates 可以针对不同的template arguments定义一整族函数;
2. Function templates 讲依照传递而来的参数的类型而被具现化;
3. 可以明确指出template parameters;
4. Function templates 可以被重载;
5. 重载Function templates时,不同的重载形式之间最好只存在【绝对必要差异】
6. 确保所有形式的重载函数都被写在它们的被呼叫点之前。

 

% 清空命令窗口、工作区和关闭所有图形窗口 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 还是无法检测到倒计时数字 也没有在原图像上显示是否可以通行
最新发布
06-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值