classdef wufafuzhi < matlab.apps.AppBase
% 属性定义
properties (Access = public)
UIFigure matlab.ui.Figure
ProcessButton matlab.ui.control.Button
ImageAxes matlab.ui.control.UIAxes
LoadImageButton matlab.ui.control.Button
StatusLabel matlab.ui.control.Label
ResultLabel matlab.ui.control.Label
LicensePlateAxes matlab.ui.control.UIAxes
ThresholdSlider matlab.ui.control.Slider
ThresholdLabel matlab.ui.control.Label
CharacterAxes matlab.ui.control.UIAxes
end
properties (Access = private)
originalImage uint8 % 原始图像
licensePlateImage uint8 % 车牌图像
processedImage uint8 % 处理后的图像
plateNumber string % 识别的车牌号码
thresholdValue double % 阈值
end
methods (Access = private)
% 加载图像回调函数
function LoadImageButtonPushed(app, event)
[fileName, filePath] = uigetfile({'*.jpg;*.jpeg;*.png;*.bmp', '图像文件 (*.jpg, *.jpeg, *.png, *.bmp)'});
if isequal(fileName, 0) || isequal(filePath, 0)
return;
end
% 读取图像
app.originalImage = imread(fullfile(filePath, fileName));
% 显示原始图像
cla(app.ImageAxes);
imshow(app.originalImage, 'Parent', app.ImageAxes);
title(app.ImageAxes, '原始图像');
% 更新状态
app.StatusLabel.Text = '图像已加载';
app.ProcessButton.Enable = 'on';
end
% 处理图像回调函数
function ProcessButtonPushed(app, event)
if isempty(app.originalImage)
app.StatusLabel.Text = '请先加载图像';
return;
end
try
% 显示处理中状态
app.StatusLabel.Text = '处理中...';
drawnow;
% 车牌定位
app.licensePlateImage = locateLicensePlate(app, app.originalImage);
% 车牌字符分割与识别
app.plateNumber = recognizeLicensePlate(app, app.licensePlateImage);
% 显示结果
cla(app.LicensePlateAxes);
imshow(app.licensePlateImage, 'Parent', app.LicensePlateAxes);
title(app.LicensePlateAxes, '车牌区域');
title(app.LicensePlateAxes, '车牌区域');
% 更新状态和结果
app.StatusLabel.Text = '处理完成';
app.ResultLabel.Text = "识别结果: " + app.plateNumber;
catch e
app.StatusLabel.Text = "处理出错: " + e.message;
end
end
% 车牌定位函数
function plateImage = locateLicensePlate(app, img)
% 转换为灰度图
grayImage = rgb2gray(img);
% 高斯模糊
blurredImage = imgaussfilt(grayImage, 1);
% 边缘检测
edgeImage = edge(blurredImage, 'Canny', [0.05 0.15]);
% 形态学操作 - 闭运算
se = strel('rectangle', [5, 5]);
closedImage = imclose(edgeImage, se);
% 查找轮廓
stats = regionprops(closedImage, 'BoundingBox', 'Area');
% 筛选可能的车牌区域
plateRegions = [];
for i = 1:length(stats)
bb = stats(i).BoundingBox;
area = stats(i).Area;
% 筛选条件: 宽高比和面积
aspectRatio = bb(3) / bb(4);
if aspectRatio > 2 && aspectRatio < 6 && area > 1000 && area < 50000
plateRegions = [plateRegions; i];
end
end
% 如果找到多个候选区域,选择面积最大的
if ~isempty(plateRegions)
[~, maxIdx] = max([stats(plateRegions).Area]);
plateBB = stats(plateRegions(maxIdx)).BoundingBox;
% 提取车牌区域
plateImage = imcrop(img, plateBB);
else
% 如果没有找到符合条件的区域,返回原图的一个裁剪区域
plateImage = imcrop(img, [size(img, 2)/3, size(img, 1)/2, size(img, 2)/3, size(img, 1)/4]);
warning('未找到符合条件的车牌区域,返回默认裁剪区域');
end
end
% 车牌字符识别函数
function plateNumber = recognizeLicensePlate(app, plateImage)
% 转换为灰度图
grayPlate = rgb2gray(plateImage);
% 二值化
threshold = app.ThresholdSlider.Value;
binaryPlate = imbinarize(grayPlate, threshold);
% 形态学操作
se = strel('rectangle', [2, 2]);
binaryPlate = imopen(binaryPlate, se);
binaryPlate = imclose(binaryPlate, se);
% 显示处理后的车牌
app.processedImage = binaryPlate;
cla(app.LicensePlateAxes);
imshow(binaryPlate, 'Parent', app.LicensePlateAxes);
title(app.LicensePlateAxes, '处理后的车牌');
% 字符分割
charImages = segmentCharacters(app, binaryPlate);
% 显示分割的字符
cla(app.CharacterAxes);
if ~isempty(charImages)
% 创建字符拼接图像
charHeight = size(charImages{1}, 1);
charWidth = size(charImages{1}, 2);
combinedChars = zeros(charHeight, charWidth * length(charImages));
for i = 1:length(charImages)
combinedChars(:, (i-1)*charWidth+1:i*charWidth) = charImages{i};
end
imshow(combinedChars, 'Parent', app.CharacterAxes);
title(app.CharacterAxes, '分割的字符');
end
% 字符识别 (这里使用简单的模板匹配示例)
% 在实际应用中,建议使用训练好的OCR模型
plateNumber = recognizeCharacters(app, charImages);
end
% 字符分割函数
function charImages = segmentCharacters(app, binaryPlate)
% 计算垂直投影
verticalProjection = sum(binaryPlate, 1);
% 寻找字符边界
threshold = max(verticalProjection) * 0.1;
isCharacter = verticalProjection > threshold;
% 寻找连续的字符区域
charRegions = [];
inCharacter = false;
startCol = 0;
for col = 1:length(isCharacter)
if isCharacter(col) && ~inCharacter
% 开始一个新字符
inCharacter = true;
startCol = col;
elseif ~isCharacter(col) && inCharacter
% 结束一个字符
inCharacter = false;
endCol = col - 1;
% 忽略过小的区域
if endCol - startCol > 5
charRegions = [charRegions; [startCol, endCol]];
end
end
end
% 提取字符图像
charImages = cell(1, size(charRegions, 1));
for i = 1:size(charRegions, 1)
startCol = charRegions(i, 1);
endCol = charRegions(i, 2);
% 扩展边界以获取完整字符
startRow = max(1, floor(size(binaryPlate, 1) * 0.1));
endRow = min(size(binaryPlate, 1), ceil(size(binaryPlate, 1) * 0.9));
charImages{i} = binaryPlate(startRow:endRow, startCol:endCol);
end
% 只保留6-8个字符(中国车牌标准)
if length(charImages) >= 6 && length(charImages) <= 8
% 正常情况,直接返回
elseif length(charImages) > 8
% 太多字符,尝试合并或筛选
charImages = charImages(1:8);
elseif length(charImages) < 6 && ~isempty(charImages)
% 太少字符,尝试扩展或补充
% 这里简单处理,用空图像补充
while length(charImages) < 6
charImages{end+1} = zeros(size(charImages{1}));
end
else
% 无法分割字符,返回空
charImages = {};
end
end
% 字符识别函数
function plateNumber = recognizeCharacters(~, charImages)
% 这里使用简单的模板匹配方法
% 在实际应用中,建议使用深度学习OCR模型
% 预设字符模板 (简化示例,实际应用需要更多字符)
characterTemplates = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ...
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', ...
'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', ...
'W', 'X', 'Y', 'Z', '京', '津', '沪', '渝', '冀', ...
'晋', '辽', '吉', '黑', '苏', '浙', '皖', '闽', '赣', ...
'鲁', '豫', '鄂', '湘', '粤', '琼', '川', '贵', '云', ...
'陕', '甘', '青', '宁', '新'};
% 初始化车牌号码
plateNumber = "";
% 对每个字符进行识别
for i = 1:length(charImages)
if isempty(charImages{i})
plateNumber = plateNumber + "?";
continue;
end
% 第一个字符通常是省份简称
if i == 1
% 只匹配省份简称
provinceTemplates = characterTemplates(35:end);
bestMatch = matchCharacter(charImages{i}, provinceTemplates);
plateNumber = plateNumber + bestMatch;
else
% 其他字符匹配数字和字母
alnumTemplates = characterTemplates(1:34);
bestMatch = matchCharacter(charImages{i}, alnumTemplates);
plateNumber = plateNumber + bestMatch;
end
end
% 辅助函数:字符匹配
function bestMatch = matchCharacter(~, templates)
% 简单的模板匹配实现
bestMatch = "?";
bestScore = 0;
% 这里应该有实际的模板匹配代码
% 简化示例,随机返回一个字符
if ~isempty(templates)
bestMatch = templates{randi(length(templates))};
end
end
end
% 阈值滑块值改变回调
function ThresholdSliderValueChanged(app, event)
app.thresholdValue = app.ThresholdSlider.Value;
app.ThresholdLabel.Text = "二值化阈值: " + num2str(round(app.thresholdValue, 2));
% 如果已经处理过图像,使用新阈值重新处理
if ~isempty(app.licensePlateImage)
app.StatusLabel.Text = '重新处理中...';
drawnow;
% 重新识别车牌
app.plateNumber = recognizeLicensePlate(app, app.licensePlateImage);
% 更新结果
app.StatusLabel.Text = '处理完成';
app.ResultLabel.Text = "识别结果: " + app.plateNumber;
end
end
end
% 构建和配置GUI
methods (Access = private)
function createComponents(app)
% 创建主窗口
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100, 100, 1000, 600];
app.UIFigure.Name = '车牌识别系统';
app.UIFigure.Resize = 'on';
% 创建加载图像按钮
app.LoadImageButton = uibutton(app.UIFigure, 'push');
app.LoadImageButton.ButtonPushedFcn = @(btn, event) LoadImageButtonPushed(app, event);
app.LoadImageButton.Position = [20, 520, 150, 30];
app.LoadImageButton.Text = '加载图像';
% 创建处理按钮
app.ProcessButton = uibutton(app.UIFigure, 'push');
app.ProcessButton.ButtonPushedFcn = @(btn, event) ProcessButtonPushed(app, event);
app.ProcessButton.Position = [180, 520, 150, 30];
app.ProcessButton.Text = '处理图像';
app.ProcessButton.Enable = 'off';
% 创建状态标签
app.StatusLabel = uilabel(app.UIFigure);
app.StatusLabel.Position = [350, 520, 200, 30];
app.StatusLabel.Text = '请加载图像';
% 创建结果标签
app.ResultLabel = uilabel(app.UIFigure);
app.ResultLabel.Position = [570, 520, 400, 30];
app.ResultLabel.FontSize = 14;
app.ResultLabel.Text = '识别结果: ';
% 创建原始图像显示区域
app.ImageAxes = uiaxes(app.UIFigure);
app.ImageAxes.Position = [20, 20, 300, 480];
app.ImageAxes.Title.String = '原始图像';
% 创建车牌区域显示区域
app.LicensePlateAxes = uiaxes(app.UIFigure);
app.LicensePlateAxes.Position = [340, 20, 300, 230];
app.LicensePlateAxes.Title.String = '车牌区域';
% 创建字符显示区域
app.CharacterAxes = uiaxes(app.UIFigure);
app.CharacterAxes.Position = [340, 260, 300, 240];
app.CharacterAxes.Title.String = '分割的字符';
% 创建阈值滑块
app.ThresholdSlider = uislider(app.UIFigure);
app.ThresholdSlider.ValueChangedFcn = @(src, event) ThresholdSliderValueChanged(app, event);
app.ThresholdSlider.Position = [660, 420, 300, 22];
app.ThresholdSlider.Limits = [0.1, 0.9];
app.ThresholdSlider.Value = 0.4;
% 创建阈值标签
app.ThresholdLabel = uilabel(app.UIFigure);
app.ThresholdLabel.Position = [660, 450, 300, 22];
app.ThresholdLabel.Text = '二值化阈值: 0.40';
% 初始化属性
app.thresholdValue = 0.4;
end
end
% 应用程序构造函数
methods (Access = public)
function app = wufafuzhi
% 创建并配置组件
createComponents(app);
% 显示主窗口
app.UIFigure.Visible = 'on';
end
end
end
最新发布