function robustFormulaRecognizer()
% 创建主窗口
fig = figure(‘Name’, ‘鲁棒公式识别系统’, ‘NumberTitle’, ‘off’, …
‘Position’, [100, 100, 1200, 800], ‘MenuBar’, ‘none’, …
‘Color’, [0.95 0.95 0.95]);
% 创建UI控件
uicontrol('Style', 'pushbutton', 'String', '上传图像', ...
'Position', [30, 750, 100, 30], 'Callback', @uploadImage, ...
'FontSize', 11, 'BackgroundColor', [0.3 0.6 0.9], 'ForegroundColor', 'white');
uicontrol('Style', 'pushbutton', 'String', '识别公式', ...
'Position', [150, 750, 100, 30], 'Callback', @recognizeFormula, ...
'FontSize', 11, 'BackgroundColor', [0.1 0.7 0.3], 'ForegroundColor', 'white');
uicontrol('Style', 'pushbutton', 'String', '清除结果', ...
'Position', [270, 750, 100, 30], 'Callback', @clearResults, ...
'FontSize', 11, 'BackgroundColor', [0.9 0.5 0.1], 'ForegroundColor', 'white');
% 创建图像显示区域
ax_original = axes('Parent', fig, 'Position', [0.05, 0.5, 0.4, 0.35]);
title(ax_original, '原始图像');
axis(ax_original, 'off');
ax_processed = axes('Parent', fig, 'Position', [0.55, 0.5, 0.4, 0.35]);
title(ax_processed, '处理图像');
axis(ax_processed, 'off');
ax_equal = axes('Parent', fig, 'Position', [0.05, 0.1, 0.4, 0.35]);
title(ax_equal, '等号检测');
axis(ax_equal, 'off');
% 创建结果展示区域
result_panel = uipanel('Title', '识别结果', 'FontSize', 12, ...
'BackgroundColor', 'white', 'Position', [0.55, 0.1, 0.4, 0.35]);
uicontrol('Parent', result_panel, 'Style', 'text', 'String', '识别公式:', ...
'Position', [20, 200, 80, 20], 'FontSize', 11, 'HorizontalAlignment', 'left');
formula_text = uicontrol('Parent', result_panel, 'Style', 'text', 'String', '', ...
'Position', [110, 200, 300, 20], 'FontSize', 11, 'HorizontalAlignment', 'left', ...
'BackgroundColor', 'white');
uicontrol('Parent', result_panel, 'Style', 'text', 'String', '计算结果:', ...
'Position', [20, 170, 80, 20], 'FontSize', 11, 'HorizontalAlignment', 'left');
calc_text = uicontrol('Parent', result_panel, 'Style', 'text', 'String', '', ...
'Position', [110, 170, 300, 20], 'FontSize', 11, 'HorizontalAlignment', 'left', ...
'BackgroundColor', 'white');
uicontrol('Parent', result_panel, 'Style', 'text', 'String', '用户答案:', ...
'Position', [20, 140, 80, 20], 'FontSize', 11, 'HorizontalAlignment', 'left');
answer_text = uicontrol('Parent', result_panel, 'Style', 'text', 'String', '', ...
'Position', [110, 140, 300, 20], 'FontSize', 11, 'HorizontalAlignment', 'left', ...
'BackgroundColor', 'white');
uicontrol('Parent', result_panel, 'Style', 'text', 'String', '验证结果:', ...
'Position', [20, 110, 80, 20], 'FontSize', 11, 'HorizontalAlignment', 'left');
result_text = uicontrol('Parent', result_panel, 'Style', 'text', 'String', '', ...
'Position', [110, 110, 300, 20], 'FontSize', 11, 'HorizontalAlignment', 'left', ...
'BackgroundColor', 'white');
% 等号检测日志
equal_log = uicontrol('Parent', result_panel, 'Style', 'listbox', ...
'String', {}, 'Position', [20, 30, 360, 70], 'FontSize', 10, ...
'BackgroundColor', 'white');
% 存储GUI句柄
handles = struct();
handles.ax_original = ax_original;
handles.ax_processed = ax_processed;
handles.ax_equal = ax_equal;
handles.formula_text = formula_text;
handles.calc_text = calc_text;
handles.answer_text = answer_text;
handles.result_text = result_text;
handles.equal_log = equal_log;
handles.current_image = [];
handles.binary_image = [];
handles.region_stats = [];
handles.region_bboxes = [];
handles.equal_sign_index = [];
handles.ocr_results = [];
guidata(fig, handles);
% ======================== 回调函数 ========================
function uploadImage(~, ~)
[filename, pathname] = uigetfile({'*.jpg;*.png;*.bmp', '图像文件 (*.jpg, *.png, *.bmp)'}, ...
'选择公式图像');
if isequal(filename, 0)
return;
end
img_path = fullfile(pathname, filename);
img = imread(img_path);
handles = guidata(fig);
handles.current_image = img;
% 显示原始图像
axes(handles.ax_original);
imshow(img);
title('原始图像');
% 预处理图像
gray_img = im2gray(img);
% 图像锐化 - 增强边缘
sharpened_img = imsharpen(gray_img, 'Radius', 1.5, 'Amount', 1.2, 'Threshold', 0.1);
% 自适应二值化
binary_img = imbinarize(sharpened_img, 'adaptive', 'Sensitivity', 0.7);
inverted_img = ~binary_img;
handles.binary_image = inverted_img;
% 显示处理图像
axes(handles.ax_processed);
imshow(inverted_img);
title('二值化图像');
% 重置结果
set(handles.formula_text, 'String', '');
set(handles.calc_text, 'String', '');
set(handles.answer_text, 'String', '');
set(handles.result_text, 'String', '');
set(handles.equal_log, 'String', {});
axes(handles.ax_equal);
cla;
title('等号检测');
guidata(fig, handles);
end
function recognizeFormula(~, ~)
handles = guidata(fig);
if isempty(handles.current_image)
errordlg('请先上传图像', '错误');
return;
end
try
inverted_img = handles.binary_image;
[img_h, img_w] = size(inverted_img);
% ================= 第一步:区域分割 =================
axes(handles.ax_processed);
imshow(inverted_img);
title('区域分割');
drawnow;
% 区域分割
cc = bwconncomp(inverted_img);
stats = regionprops(cc, 'BoundingBox', 'Area', 'Eccentricity', 'Orientation', 'Solidity');
% 过滤噪点
min_area = max(20, img_h*img_w*0.0005); % 动态最小面积
valid_idx = [stats.Area] > min_area;
stats = stats(valid_idx);
% 按水平位置排序
bboxes = vertcat(stats.BoundingBox);
[~, order] = sort(bboxes(:,1));
bboxes = bboxes(order, :);
stats = stats(order);
% 存储区域信息
handles.region_stats = stats;
handles.region_bboxes = bboxes;
% 显示分割结果
imshow(inverted_img); hold on;
for i = 1:size(bboxes,1)
rectangle('Position', bboxes(i,:), 'EdgeColor', [0.8 0.2 0.2], 'LineWidth', 1);
end
hold off;
title(sprintf('分割出 %d 个区域', length(stats)));
drawnow;
axes(handles.ax_equal);
imshow(inverted_img);
title('等号检测过程');
hold on;
log_entries = {};
% 从右向左扫描区域(跳过数字区域)
eq_candidate_index = [];
for i = size(bboxes,1):-1:1
bbox = bboxes(i,:);
x = floor(bbox(1));
y = floor(bbox(2));
w = max(floor(bbox(3)), 1);
h = max(floor(bbox(4)), 1);
% 提取区域图像
region_img = inverted_img(max(1,y):min(img_h,y+h-1), max(1,x):min(img_w,x+w-1));
% 初步识别区域类型
[is_digit, digit_value] = isDigitRegion(region_img);
% 显示区域信息
rectangle('Position', bbox, 'EdgeColor', [0.8 0.2 0.2], 'LineWidth', 1);
text(bbox(1)+5, bbox(2)-10, sprintf('%d', i), ...
'Color', [0.2 0.2 0.8], 'FontSize', 10, 'FontWeight', 'bold');
if is_digit
% 标记数字区域
text(bbox(1)+10, bbox(2)+15, digit_value, ...
'Color', [0 0.5 0], 'FontSize', 12);
log_entries{end+1} = sprintf('区域 %d: 数字 %s', i, digit_value);
else
% 检查是否具有等号特征
[is_equal, confidence] = detectEqualSign(region_img, median(bboxes(:,4)));
if is_equal
% 标记候选等号区域
rectangle('Position', bbox, 'EdgeColor', [0.2 0.8 0.2], 'LineWidth', 2);
text(bbox(1)+10, bbox(2)+15, '等号候选', ...
'Color', [0 0.6 0], 'FontSize', 12, 'FontWeight', 'bold');
log_entries{end+1} = sprintf('区域 %d: 等号候选 (置信度 %.2f)', i, confidence);
eq_candidate_index = i;
break; % 找到第一个非数字候选区域即停止
else
% 标记运算符区域
[is_op, op_type] = detectOperator(region_img);
if is_op
text(bbox(1)+10, bbox(2)+15, op_type, ...
'Color', [0.8 0 0], 'FontSize', 12, 'FontWeight', 'bold');
log_entries{end+1} = sprintf('区域 %d: 运算符 %s', i, op_type);
else
text(bbox(1)+10, bbox(2)+15, '?', ...
'Color', [0.5 0.5 0.5], 'FontSize', 12);
log_entries{end+1} = sprintf('区域 %d: 未知类型', i);
end
end
end
end
% 处理等号候选
if isempty(eq_candidate_index)
% 如果没有找到候选等号,使用中间位置作为等号
[~, eq_idx] = min(abs(1:size(bboxes,1) - size(bboxes,1)/2));
log_entries{end+1} = sprintf('未找到等号候选,使用区域 %d 作为等号', eq_idx);
else
eq_idx = eq_candidate_index;
log_entries{end+1} = sprintf('确定等号位置: 区域 %d', eq_idx);
% 检查是否为双横线结构
bbox = bboxes(eq_idx,:);
region_img = inverted_img(max(1,floor(bbox(2))):min(img_h,floor(bbox(2)+bbox(4))-1), ...
max(1,floor(bbox(1))):min(img_w,floor(bbox(1)+bbox(3))-1));
[is_double, top_idx, bottom_idx] = checkDoubleLineStructure(region_img);
if is_double
% 如果是双横线结构,标记为确认等号
rectangle('Position', bbox, 'EdgeColor', [0 0.8 0], 'LineWidth', 3);
text(bbox(1)+bbox(3)/2, bbox(2)-15, '=', ...
'Color', [0 0.5 0], 'FontSize', 20, 'FontWeight', 'bold', ...
'HorizontalAlignment', 'center');
log_entries{end+1} = sprintf('区域 %d 确认双横线结构 (上横线:%d, 下横线:%d)', ...
eq_idx, top_idx, bottom_idx);
else
% 如果不是双横线,但位置特征符合,仍标记为等号
rectangle('Position', bbox, 'EdgeColor', [0.9 0.6 0], 'LineWidth', 3);
text(bbox(1)+bbox(3)/2, bbox(2)-15, '=', ...
'Color', [0.7 0.4 0], 'FontSize', 20, 'FontWeight', 'bold', ...
'HorizontalAlignment', 'center');
log_entries{end+1} = '单横线结构,但位置特征符合等号';
end
end
hold off;
% 更新日志
set(handles.equal_log, 'String', log_entries);
% 存储等号位置
handles.equal_sign_index = eq_idx;
% ================= 第三步:字符识别 =================
ocr_results = cell(1, size(bboxes,1));
for i = 1:size(bboxes,1)
% 如果是等号区域,直接标记
if ~isempty(equal_sign_index) && i == equal_sign_index
ocr_results{i} = '=';
continue;
end
bbox = bboxes(i,:);
x = floor(bbox(1));
y = floor(bbox(2));
w = max(floor(bbox(3)), 1);
h = max(floor(bbox(4)), 1);
% 提取区域图像
region_img = inverted_img(max(1,y):min(img_h,y+h-1), max(1,x):min(img_w,x+w-1));
% 运算符特征检测
[is_operator, operator_type] = detectOperator(region_img);
if is_operator
ocr_results{i} = operator_type;
else
% OCR识别数字和字母
char_set = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
ocr_result = ocr(region_img, 'CharacterSet', char_set, 'TextLayout', 'Block');
if ~isempty(ocr_result.Text)
char = strtrim(ocr_result.Text);
% 数字校正
if strcmp(char, '7') || strcmp(char, '1')
char = correctDigit(region_img, char);
end
ocr_results{i} = char;
else
ocr_results{i} = '?';
end
end
end
% ================= 第四步:构建表达式 =================
% 分割表达式和答案
expr_str = '';
answer_str = '';
% 如果没有找到等号,使用位置最中间的区域作为等号
if isempty(equal_sign_index)
[~, eq_idx] = min(abs(1:size(bboxes,1) - size(bboxes,1)/2));
log_entries{end+1} = sprintf('未找到等号,使用区域 %d 作为等号', eq_idx);
set(handles.equal_log, 'String', log_entries);
else
eq_idx = equal_sign_index;
end
for i = 1:length(ocr_results)
if i < eq_idx
expr_str = [expr_str ocr_results{i}];
elseif i > eq_idx
answer_str = [answer_str ocr_results{i}];
end
end
% 特殊字符转换
expr_str = strrep(expr_str, 'x', '*');
expr_str = strrep(expr_str, 'X', '*');
expr_str = strrep(expr_str, '÷', '/');
expr_str = strrep(expr_str, ' ', '');
% 尝试转换为数值
try
user_answer = str2double(answer_str);
catch
user_answer = NaN;
end
% 安全计算表达式
try
correct_result = eval(expr_str);
catch
% 修正常见OCR错误
expr_str = strrep(expr_str, 'O', '0');
expr_str = strrep(expr_str, 'o', '0');
expr_str = strrep(expr_str, 'l', '1');
expr_str = strrep(expr_str, 'I', '1');
expr_str = strrep(expr_str, 's', '5');
correct_result = eval(expr_str);
end
% 验证结果
is_correct = abs(user_answer - correct_result) < 1e-6;
% 更新结果展示
set(handles.formula_text, 'String', [expr_str '=' answer_str]);
set(handles.calc_text, 'String', num2str(correct_result));
set(handles.answer_text, 'String', num2str(user_answer));
if is_correct
set(handles.result_text, 'String', '✓ 答案正确', 'ForegroundColor', [0 0.6 0]);
else
set(handles.result_text, 'String', '✗ 答案错误', 'ForegroundColor', [0.8 0 0]);
end
% 显示最终识别结果
axes(handles.ax_processed);
imshow(inverted_img); hold on;
for i = 1:size(bboxes,1)
% 等号区域用绿色标注
if ~isempty(equal_sign_index) && i == equal_sign_index
rectangle('Position', bboxes(i,:), 'EdgeColor', [0.2 0.8 0.2], 'LineWidth', 2);
text(bboxes(i,1)+5, bboxes(i,2)-10, '=', ...
'Color', [0 0.5 0], 'FontSize', 12, 'FontWeight', 'bold');
else
% 运算符区域用不同颜色标注
if strcmp(ocr_results{i}, '+')
rect_color = [0.8 0.2 0.8]; % 紫色
text_color = [0.6 0 0.6]; % 深紫色
elseif strcmp(ocr_results{i}, '-')
rect_color = [0.8 0.5 0.2]; % 橙色
text_color = [0.6 0.3 0]; % 深橙色
else
rect_color = [1 0 0]; % 红色
text_color = [0 0 1]; % 蓝色
end
rectangle('Position', bboxes(i,:), 'EdgeColor', rect_color, 'LineWidth', 1.5);
if ~isempty(ocr_results{i})
text(bboxes(i,1)+5, bboxes(i,2)-10, ocr_results{i}, ...
'Color', text_color, 'FontSize', 10, 'FontWeight', 'bold');
end
end
end
hold off;
title('最终识别结果');
% 保存处理后的数据
handles.ocr_results = ocr_results;
guidata(fig, handles);
catch ME
errordlg(sprintf('识别失败: %s', ME.message), '错误');
end
end
function clearResults(~, ~)
handles = guidata(fig);
% 清除结果文本
set(handles.formula_text, 'String', '');
set(handles.calc_text, 'String', '');
set(handles.answer_text, 'String', '');
set(handles.result_text, 'String', '');
set(handles.equal_log, 'String', {});
% 清除图像
axes(handles.ax_processed);
cla;
title('处理图像');
axes(handles.ax_equal);
cla;
title('等号检测');
% 如果有原始图像,重新显示
if ~isempty(handles.current_image)
axes(handles.ax_original);
imshow(handles.current_image);
title('原始图像');
end
end
end
% ======================== 运算符检测函数 ========================
function [is_operator, operator_type] = detectOperator(region_img)
[h, w] = size(region_img);
% 计算形状特征
aspect_ratio = w / h;
eccentricity = regionprops(region_img, 'Eccentricity').Eccentricity;
is_operator = false;
operator_type = '';
% 减号检测
if aspect_ratio > 3 && eccentricity > 0.9
is_operator = true;
operator_type = '-';
return;
end
% 加号检测
if aspect_ratio > 1.2 && aspect_ratio < 2.5
% 中心区域分析
center_y = round(h/2);
center_x = round(w/2);
% 检查水平和垂直线段
horizontal_line = sum(region_img(center_y, :)) > w*0.7;
vertical_line = sum(region_img(:, center_x)) > h*0.7;
if horizontal_line && vertical_line
is_operator = true;
operator_type = '+';
return;
end
end
% 乘号检测(斜线)
if aspect_ratio > 0.8 && aspect_ratio < 1.2
% 使用Hough变换检测斜线
[H, theta, rho] = hough(region_img, 'Theta', -45:5:45);
peaks = houghpeaks(H, 2);
if size(peaks,1) >= 2
angles = theta(peaks(:,2));
angle_diff = abs(diff(angles));
% 检查是否接近垂直的斜线对
if abs(angle_diff) > 80 && abs(angle_diff) < 100
is_operator = true;
operator_type = '*';
end
end
end
end
% ======================== 数字校正函数 ========================
function char = correctDigit(region_img, original_char)
[h, w] = size(region_img);
% 1的特征:高宽比大,顶部无横线,底部较宽
aspect_ratio = h / w;
top_region = region_img(1:round(h*0.3), :);
bottom_region = region_img(round(h*0.7):end, :);
top_pixels = sum(top_region(:));
bottom_pixels = sum(bottom_region(:));
% 7的特征:顶部有横线,右上角有折角
top_line = sum(region_img(1, :)) > w*0.6;
right_top_corner = region_img(1, end) && region_img(2, end);
if strcmp(original_char, '7')
% 检查是否为1的特征
if aspect_ratio > 3 && top_pixels < numel(top_region)*0.2 && bottom_pixels > numel(bottom_region)*0.5
char = '1';
return;
end
elseif strcmp(original_char, '1')
% 检查是否为7的特征
if aspect_ratio < 2 && top_line && right_top_corner
char = '7';
return;
end
end
char = original_char; % 保持原识别结果
end
function [is_digit, digit_value] = isDigitRegion(region_img)
% 基本特征分析
[h, w] = size(region_img);
aspect_ratio = w / h;
% 数字通常具有特定的宽高比
is_digit = aspect_ratio >= 0.5 && aspect_ratio <= 1.5;
if ~is_digit
digit_value = '';
return;
end
% OCR识别数字
char_set = '0123456789';
ocr_result = ocr(region_img, 'CharacterSet', char_set, 'TextLayout', 'Block');
if ~isempty(ocr_result.Text)
digit_value = strtrim(ocr_result.Text);
% 常见错误校正
if strcmp(digit_value, '7')
% 检查是否为1
if isDigitOne(region_img)
digit_value = '1';
end
elseif strcmp(digit_value, '1')
% 检查是否为7
if isDigitSeven(region_img)
digit_value = '7';
end
end
else
% 尝试通过形状判断
if isDigitZero(region_img)
digit_value = '0';
elseif isDigitOne(region_img)
digit_value = '1';
elseif isDigitTwo(region_img)
digit_value = '2';
else
digit_value = '?';
is_digit = false;
end
end
end
function result = isDigitOne(img)
[h, w] = size(img);
aspect_ratio = h / w;
% 1的特征:高宽比大,顶部像素少
top_region = img(1:round(h*0.3), :);
top_pixels = sum(top_region(:));
result = aspect_ratio > 2.5 && top_pixels < numel(top_region)*0.3;
end
function result = isDigitSeven(img)
[h, w] = size(img);
% 7的特征:顶部有横线
top_line = sum(img(1, :)) > w*0.6;
right_top_corner = img(1, end) && img(2, end);
result = top_line && right_top_corner;
end
function result = isDigitZero(img)
[h, w] = size(img);
% 0的特征:封闭轮廓
filled_img = imfill(img, 'holes');
hole_area = sum(filled_img(:)) - sum(img(:));
result = hole_area > 0.1 * (w*h);
end
function result = isDigitTwo(img)
[h, w] = size(img);
% 2的特征:右上弯曲,左下弯曲
top_right = img(1:round(h*0.3), round(w*0.7):end);
bottom_left = img(round(h*0.7):end, 1:round(w*0.3));
result = sum(top_right(:)) > numel(top_right)*0.6 && ...
sum(bottom_left(:)) > numel(bottom_left)*0.6;
end
function [is_double, top_idx, bottom_idx] = checkDoubleLineStructure(region_img)
[h, w] = size(region_img);
% 水平投影
horizontal_proj = sum(region_img, 2);
% 如果没有足够的像素,直接返回
if max(horizontal_proj) == 0
is_double = false;
top_idx = 0;
bottom_idx = 0;
return;
end
% 归一化投影
horizontal_proj = horizontal_proj / max(horizontal_proj);
% 寻找波峰
[peaks, locs] = findpeaks(horizontal_proj, 'MinPeakHeight', 0.3, 'MinPeakDistance', max(1, round(h/4)));
% 需要至少两个波峰
if length(peaks) < 2
is_double = false;
top_idx = 0;
bottom_idx = 0;
return;
end
% 找到两个主要的波峰
[~, sorted_idx] = sort(peaks, 'descend');
top_locs = sort(locs(sorted_idx(1:min(2, length(sorted_idx)))));
top_idx = top_locs(1);
bottom_idx = top_locs(2);
% 检查波峰间距
distance = abs(top_idx - bottom_idx);
avg_height = h / 2; % 估计的平均高度
% 合理的间距应该在10%-40%的总高度之间
is_double = distance > 0.1*h && distance < 0.4*h;
end
function [is_equal, confidence] = detectEqualSign(region_img, median_height)
% 输入:
% region_img - 二值图像区域 (前景为白色1,背景为黑色0)
% median_height - 整个图像中区域高度的中位数
% 输出:
% is_equal - 逻辑值,是否为等号
% confidence - 置信度 (0~1)
[h, w] = size(region_img);
% 1. 初步检查:排除无效区域
if h == 0 || w == 0 || sum(region_img(:)) == 0
is_equal = false;
confidence = 0;
return;
end
% 2. 计算基本特征
aspect_ratio = w / h;
fill_ratio = sum(region_img(:)) / (h*w); % 填充比例
eccentricity = regionprops(region_img, 'Eccentricity').Eccentricity;
% 3. 水平投影分析
horizontal_proj = sum(region_img, 2); % 垂直方向求和
horizontal_proj = horizontal_proj / max(horizontal_proj); % 归一化
% 4. 寻找水平线(波峰)
min_peak_height = 0.3; % 最小波峰高度阈值
min_peak_distance = max(1, round(h/4)); % 最小波峰间距
% 查找主要波峰
[peaks, locs] = findpeaks(horizontal_proj, ...
'MinPeakHeight', min_peak_height, ...
'MinPeakDistance', min_peak_distance);
num_peaks = length(peaks);
% 5. 等号特征分析
is_equal = false;
confidence = 0;
% 特征1:双横线结构 (等号)
if num_peaks >= 2
% 获取两个最强的波峰
[~, sorted_idx] = sort(peaks, 'descend');
top_locs = sort(locs(sorted_idx(1:min(2, num_peaks))));
% 计算波峰间距离
peak_distance = abs(top_locs(1) - top_locs(2));
% 特征1.1:合理的垂直间距 (0.5-2倍中值高度)
is_reasonable_distance = peak_distance > 0.5*median_height && ...
peak_distance < 2*median_height;
% 特征1.2:两条线长度相似
line1_length = sum(region_img(top_locs(1), :) > 0);
line2_length = sum(region_img(top_locs(2), :) > 0);
length_ratio = min(line1_length, line2_length) / max(line1_length, line2_length);
% 特征1.3:水平对齐
line1_pos = find(region_img(top_locs(1), :));
line2_pos = find(region_img(top_locs(2), :));
% 计算水平重叠比例
overlap = numel(intersect(line1_pos, line2_pos)) / min(numel(line1_pos), numel(line2_pos));
% 置信度计算
dist_conf = min(1, max(0, 1 - abs(peak_distance - median_height)/(0.5*median_height)));
len_conf = length_ratio;
align_conf = overlap;
confidence = 0.4*dist_conf + 0.3*len_conf + 0.3*align_conf;
% 满足核心条件:至少两条线+合理距离
if num_peaks >= 2 && is_reasonable_distance && confidence > 0.5
is_equal = true;
end
end
% 特征2:单横线结构 (减号)
if num_peaks == 1 && ~is_equal
% 检查是否为长横线
line_length = sum(region_img(locs(1), :) > 0);
is_long_line = line_length > 0.7*w;
% 检查高度一致性 (单行)
[~, max_idx] = max(horizontal_proj);
line_height = sum(region_img(max(1, max_idx-1):min(h, max_idx+1), :) > 0);
is_single_line = line_height < 3; % 不超过3行像素
% 减号特征
is_dash = aspect_ratio > 3 && eccentricity > 0.9 && is_long_line && is_single_line;
% 置信度计算
if is_dash
confidence = 0.8; % 高置信度减号
else
confidence = 0.3; % 低置信度未知
end
end
% 6. 附加验证:高宽比和填充比例
if is_equal
% 等号应有较大的高宽比(横向延伸)
aspect_conf = min(1, aspect_ratio/5);
% 等号填充比例应较低(两条线之间有空隙)
fill_conf = max(0, 1 - 2*fill_ratio);
confidence = 0.7*confidence + 0.2*aspect_conf + 0.1*fill_conf;
end
% 7. 最终决策阈值
if confidence < 0.6
is_equal = false;
end
end
在这个代码下,把等号的识别逻辑更改为,如果相邻的两个识别区域的水平位置相差不大(即认为上下分布),或者几乎一样,则判断为等号,并且改为从左往右识别,等号继承靠前的那一个序号,后面的区域序号依次往前提就可以
给出完整代码