1007 The Best Path(无向图的欧拉路)

探讨了如何寻找一条经过所有边恰好一次的欧拉路径,并在此基础上计算最大点权亦或值。介绍了解决方案的算法思路及实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The Best Path

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 541    Accepted Submission(s): 220


Problem Description
Alice is planning her travel route in a beautiful valley. In this valley, there are N lakes, and M rivers linking these lakes. Alice wants to start her trip from one lake, and enjoys the landscape by boat. That means she need to set up a path which go through every river exactly once. In addition, Alice has a specific number ( a1,a2,...,an ) for each lake. If the path she finds is P0P1...Pt , the lucky number of this trip would be aP0XORaP1XOR...XORaPt . She want to make this number as large as possible. Can you help her?
 

Input
The first line of input contains an integer t , the number of test cases. t test cases follow.

For each test case, in the first line there are two positive integers N (N100000) and M (M500000) , as described above. The i -th line of the next N lines contains an integer ai(i,0ai10000) representing the number of the i -th lake.

The i -th line of the next M lines contains two integers ui and vi representing the i -th river between the ui -th lake and vi -th lake. It is possible that ui=vi .
 

Output
For each test cases, output the largest lucky number. If it dose not have any path, output "Impossible".
 

Sample Input
  
2 3 2 3 4 5 1 2 2 3 4 3 1 2 3 4 1 2 2 3 2 4
 

Sample Output
  
2 Impossible
 

Source

2016 ACM/ICPC Asia Regional Qingdao Online 


这题让我对欧拉路有了深一点的认识。   

欧拉路:分有向图的欧拉路和无向图的欧拉路。前几天做过有向图的欧拉路,这题就是无向图的欧拉路(后面也是针对无向图来讲)。

不管是无向图也好,有向图也罢,欧拉路分为点欧拉和边欧拉。 点欧拉就是每个点只能走一次,边欧拉就是每条边只能走一次。

这题是边欧拉,同时,每个点都要走一次,要你输出走这条欧拉路的最大点权的亦或值

边欧拉,两种情况:1.每个点的度数都是偶数,这时,不论从哪个点出发,都能走出欧拉路,且最后一定会回到出发点,也就是说起点会经历奇数次((度+1)/2)通过,其它点会经历偶数次(度/2)通过  (所以这题如果是这种情况,就只要考虑起点是哪一个就ok,因为同一个点偶数次亦或就是1) 2.有两个点的度是奇数,那么起点必定在这两个点中,无论哪个点都可以作为起点,他们会经历((度+1)/2)次(刚开始在那个点也算经历一次)。


#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
int T, val[100005], du[100005], pre[100005];
int find(int x){return pre[x] == x ? x : pre[x] = find(pre[x]);}
int main()
{
    cin >> T;
    while(T--){
        memset(du, 0, sizeof(du));
        int n, m, flag = 0, odd = 0;
        cin >> n >> m;
        for(int i = 1; i <= n; i++) pre[i] = i;
        for(int i = 1; i <= n; i++) scanf("%d", &val[i]);
        for(int i = 1; i <= m; i++){
            int u, v;
            scanf("%d %d", &u, &v);
            du[u]++; du[v]++;
            int x = find(u), y = find(v);
            if(x != y) pre[x] = y;
        }
        for(int i = 1; i <= n; i++){
            if(pre[i] == i) flag++;
            if(du[i] % 2 != 0) odd++;
        }
        if(flag > 1) { printf("Impossible\n"); continue;}
        if(odd == 2){
            int ans = 0;
            for(int i = 1; i <= n; i++){
                du[i] = (du[i] + 1) / 2;
                if(du[i] & 1)
                    ans ^= val[i];
            }
            printf("%d\n", ans);
        }
        else if(odd == 0){
            int ans = 0;
            for(int i = 1; i <= n; i++)
                ans = max(ans, ans ^ val[i]);
            printf("%d\n", ans);
        }
        else printf("Impossible\n");
    }
    return 0;
}


function recognize_math_expression_4() % 创建主窗口 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'); % 进度条 progress_ax = axes('Parent', fig, 'Position', [0.4, 0.78, 0.2, 0.02], ... 'XLim', [0 100], 'YLim', [0 1], 'Box', 'on', 'XTick', [], 'YTick', []); progress_bar = rectangle('Parent', progress_ax, 'Position', [0,0,0,1], ... 'FaceColor', [0.1 0.8 0.1], 'EdgeColor', 'none'); progress_text = uicontrol('Style', 'text', 'String', '就绪', ... 'Position', [580, 770, 200, 20], 'FontSize', 10, ... 'HorizontalAlignment', 'center', 'BackgroundColor', [0.95 0.95 0.95]); % 创建图像显示区域 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.progress_bar = progress_bar; handles.progress_text = progress_text; handles.progress_ax = progress_ax; handles.current_image = []; handles.binary_image = []; handles.region_stats = []; handles.region_bboxes = []; handles.equal_sign_index = []; handles.ocr_results = []; handles.fig = fig; % 存储figure句柄 guidata(fig, handles); %% ======================== 回调函数 ======================== function uploadImage(~, ~) % 获取全局 handles 结构体 handles = guidata(gcbf); % 使用 gcbf 获取当前回调的 figure 句柄 updateProgress(0, '准备上传图像...'); [filename, pathname] = uigetfile({'*.jpg;*.png;*.bmp', '图像文件 (*.jpg, *.png, *.bmp)'}, ... '选择公式图像'); if isequal(filename, 0) updateProgress(0, '取消上传'); return; end updateProgress(10, '读取图像...'); img_path = fullfile(pathname, filename); img = imread(img_path); % 更新 handles handles.current_image = img; % 显示原始图像 axes(handles.ax_original); imshow(img); title('原始图像'); % 预处理图像 updateProgress(20, '图像预处理...'); if size(img, 3) == 3 % 判断是否为彩色图像 gray_img = rgb2gray(img); % 将彩色图像转换为灰度图像 else gray_img = img; % 如果已经是灰度图像,则直接使用 end % 显示灰度图像 axes(handles.ax_processed); imshow(gray_img); title('灰度图像'); % 图像锐化 - 增强边缘 sharpened_img = imsharpen(gray_img, 'Radius', 1.5, 'Amount', 1.2, 'Threshold', 0.1); % 使用大律法计算全局阈值 global_level = graythresh(sharpened_img); % 计算图像的标准差(衡量对比度) image_contrast = std(double(sharpened_img(:))) / 255; % 动态调整敏感度 if image_contrast > 0.1 % 对比度较好 sensitivity = global_level * 0.9; % 较低的敏感度 if sensitivity<0.7 sensitivity=0.7; end else % 对比度较差 sensitivity = global_level * 1.05; % 较高的敏感度 end % 自适应二值化结合敏感度 binary_img = imbinarize(sharpened_img, 'adaptive', 'Sensitivity', sensitivity); inverted_img = ~binary_img; % 显示结果 axes(handles.ax_processed); imshow(inverted_img); title('结合大律法与自适应二值化的图像'); % 形态学去噪 - 定义结构元素 se = strel('disk', 2); % 创建一个半径为 2 的圆形结构元素 % 开运算:先腐蚀后膨胀,去除小的噪声点 cleaned_img = imopen(inverted_img, se); % 可选:闭运算(先膨胀后腐蚀),填补小的孔洞 %cleaned_img = imclose(cleaned_img, se); %inverted_img = ~binary_img; % 更新 handles handles.binary_image = cleaned_img; % 显示处理图像 axes(handles.ax_processed); imshow(cleaned_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('等号检测'); % 保存更新后的 handles guidata(gcbf, handles); % 关键修复:更新全局 handles updateProgress(100, '图像上传完成'); end %% 回调函数 function recognizeFormula(~, ~) handles = guidata(gcbf); % 使用 gcbf 获取当前句柄 if isempty(handles.binary_image) errordlg('请先上传图像', '错误'); return; end try updateProgress(0, '开始公式识别...'); inverted_img = handles.binary_image; [img_h, img_w] = size(inverted_img); % ================= 第一步:区域分割 ================= axes(handles.ax_processed); imshow(inverted_img); title('区域分割'); drawnow; updateProgress(10, '区域分割...'); % 动态计算膨胀参数 se_radius = calculateDilationRadius(inverted_img, img_h, img_w); se = strel('disk', se_radius); % 创建圆形结构元素 dilated_img = imdilate(inverted_img, se); % 区域分割 - 使用 8 连通性 cc = bwconncomp(dilated_img, 8); % 使用 8 连通性 stats = regionprops(cc, 'BoundingBox', 'Area', 'Eccentricity', 'Orientation', 'Solidity'); % 添加IsEqual字段(默认为false) [stats.IsEqual] = deal(0); [stats.Row] = deal(0); % 过滤噪点 min_area = max(5, img_h*img_w*0.0005); % 动态最小面积 valid_idx = [stats.Area] > min_area; stats = stats(valid_idx); % 按垂直位置排序 bboxes = vertcat(stats.BoundingBox); [~, order] = sort(bboxes(:,2)); 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; updateProgress(20, '按行分组...'); row_threshold = mean([bboxes(:,4)]) * 1; % 行间距阈值为平均字符高度的1倍 %rows = groupRegionsByRows(stats, row_threshold); % 更新 stats.Row 字段 current_row = 0; prev_center_y = -Inf; for i = 1:length(stats) bbox = stats(i).BoundingBox; center_y = bbox(2) + bbox(4)/2; % 当前字符的垂直中心坐标 % 如果垂直间距大于阈值,则开始新行 if center_y - prev_center_y > row_threshold current_row = current_row + 1; end % 更新 stats.Row 字段 stats(i).Row = current_row; prev_center_y = center_y; end %创建副本元胞数组 all_row_stats = {}; all_row_bboxes = {}; log_entries = {}; all_row_ocr_results={}; for row_num = 1:max([stats.Row]) % 根据 Row 字段的最大值确定行数 % 提取当前行的字符索引 row_indices = [stats.Row] == row_num; % 创建当前行的 stats 副本 row_stats = stats(row_indices); row_bboxes = vertcat(row_stats.BoundingBox); % 按垂直位置排序 [~, order] = sort(row_bboxes(:,1)); row_bboxes = row_bboxes(order, :); row_stats = row_stats(order); % ================= 新增:过滤除号小圆点 ================= updateProgress(30, '过滤除号小圆点...'); filtered_stats = []; % 初始化过滤后的 stats filtered_bboxes = []; % 初始化过滤后的边界框 % 计算所有区域的平均面积 all_areas = [row_stats.Area]; % 提取所有区域的面积 mean_area = mean(all_areas); % 计算平均面积 % 遍历每个区域,标记需要移除的区域索引 remove_indices = []; for i = 1:length(row_stats) try bbox = row_stats(i).BoundingBox; % 提取边界框 area = row_stats(i).Area; % 提取面积 width = bbox(3); % 宽度 height = bbox(4); % 高度 % 检查宽度和高度是否有效 if width <= 0 || height <= 0 log_entries{end+1} = sprintf('跳过区域 %d: 无效尺寸 (宽 %.2f, 高 %.2f)', ... i, width, height); remove_indices = [remove_indices, i]; continue; end % 判断条件: % 1. 面积小于平均面积的某个比例(例如 10%) % 2. 高宽比接近 1(例如在 0.8 到 1.2 范围内) aspect_ratio = abs(width - height) / max(width, height); % 计算高宽比偏差 if area < 0.2 * mean_area && aspect_ratio < 0.2 log_entries{end+1} = sprintf('过滤掉区域 %d: 小圆点 (面积 %.2f, 高宽比 %.2f)', ... i, area, aspect_ratio); remove_indices = [remove_indices, i]; % 标记为移除 end catch ME % 捕获异常并记录日志 log_entries{end+1} = sprintf('处理区域 %d 时发生错误: %s', i, ME.message); remove_indices = [remove_indices, i]; % 标记为移除 end end % 移除标记的区域 if ~isempty(remove_indices) remaining_indices = setdiff(1:length(row_stats), remove_indices); % 剩余区域索引 % 如果有剩余区域,则更新 stats 和 bboxes if ~isempty(remaining_indices) row_stats = row_stats(remaining_indices); % 更新 stats row_bboxes = row_bboxes(remaining_indices, :); else % 如果没有剩余区域,初始化为空 row_stats = []; row_bboxes = []; end else % 如果没有移除任何区域,直接提取边界框 % bboxes = vertcat([stats.BoundingBox]); end % 按水平位置重新排序 if ~isempty(row_bboxes) [~, row_order] = sort(row_bboxes(:,1)); % 按 x 坐标排序 row_bboxes = row_bboxes(row_order, :); row_stats = row_stats(row_order); end hold off; title(sprintf('算式 %d 过滤后分割出 %d 个区域', row_num,size(row_bboxes, 1))); drawnow; % % 存储更新后的区域信息 % handles.region_stats = stats; % handles.region_bboxes = bboxes; % guidata(gcbf, handles); % ================= 第二步:等号检测与合并 ================= updateProgress(30+row_num*10, '检测等号...'); axes(handles.ax_equal); if row_num==1 imshow(inverted_img); end title('等号检测过程'); hold on; % log_entries = {}; candidate_pairs = []; % 找出所有类似减号的区域 dash_like_indices = []; for i = 1:length(row_stats) bbox = row_bboxes(i,:); w = bbox(3); h = bbox(4); % 减号特征:高宽比大,离心率高 aspect_ratio = w / h; eccentricity = row_stats(i).Eccentricity; orientation = row_stats(i).Orientation; % 获取方向属性 if aspect_ratio > 1.5 && eccentricity > 0.85 && abs(orientation) < 10 dash_like_indices = [dash_like_indices; i]; rectangle('Position', bbox, 'EdgeColor', [0 0.8 0.8], 'LineWidth', 1.5); text(bbox(1)+5, bbox(2)-10, sprintf('%d', i), ... 'Color', [0 0.5 0.5], 'FontSize', 10, 'FontWeight', 'bold'); end end log_entries{end+1} = sprintf('找到 %d 个减号样区域', length(dash_like_indices)); % 垂直探索寻找等号对 for i = 1:length(dash_like_indices) idx1 = dash_like_indices(i); bbox1 = row_bboxes(idx1,:); center_x1 = bbox1(1) + bbox1(3)/2; center_y1 = bbox1(2) + bbox1(4)/2; % 在垂直方向探索 for j = 1:length(dash_like_indices) if i == j, continue; end % 跳过自身 idx2 = dash_like_indices(j); bbox2 = row_bboxes(idx2,:); center_x2 = bbox2(1) + bbox2(3)/2; center_y2 = bbox2(2) + bbox2(4)/2; % 检查垂直位置关系 vertical_gap = abs(center_y1 - center_y2); avg_height = (bbox1(4) + bbox2(4))/2; % 检查水平对齐 horizontal_diff = abs(center_x1 - center_x2); avg_width = (bbox1(3) + bbox2(3))/2; % 检查尺寸相似性 width_ratio = max(bbox1(3), bbox2(3)) / min(bbox1(3), bbox2(3)); height_ratio = max(bbox1(4), bbox2(4)) / min(bbox1(4), bbox2(4)); % 等号对条件: % 1. 垂直距离在合理范围(1-3倍平均高度) % 2. 水平位置对齐(中心偏差小于宽度的一半) % 3. 尺寸相似(宽高比小于1.5) if vertical_gap > 0.5*avg_height && vertical_gap < 5*avg_height && ... horizontal_diff < avg_width/2 && ... width_ratio < 2 && height_ratio < 2 % 检查是否已经配对 if ~isempty(candidate_pairs) && any(ismember([idx1, idx2], candidate_pairs(:))) continue; end % 标记候选对 candidate_pairs = [candidate_pairs; idx1, idx2]; % 绘制连接线 plot([center_x1, center_x2], [center_y1, center_y2], 'g-', 'LineWidth', 1.5); log_entry = sprintf('发现等号候选对: %d 和 %d (垂直距离: %.1f, 水平偏差: %.1f)', ... idx1, idx2, vertical_gap, horizontal_diff); log_entries{end+1} = log_entry; end end end % 处理等号候选对 equal_sign_index = []; if ~isempty(candidate_pairs) % 选择最可能的一对(水平对齐最好) [~, best_idx] = min(abs(candidate_pairs(:,1) - candidate_pairs(:,2))); best_pair = candidate_pairs(best_idx, :); % 计算合并后的边界框 bbox1 = row_bboxes(best_pair(1), :); bbox2 = row_bboxes(best_pair(2), :); min_x = min(bbox1(1), bbox2(1)); max_x = max(bbox1(1)+bbox1(3), bbox2(1)+bbox2(3)); min_y = min(bbox1(2), bbox2(2)); max_y = max(bbox1(2)+bbox1(4), bbox2(2)+bbox2(4)); equal_bbox = [min_x, min_y, max_x-min_x, max_y-min_y]; % 创建完整的等号区域属性结构体 equal_region = struct(... 'BoundingBox', equal_bbox, ... 'Area', equal_bbox(3)*equal_bbox(4), ... 'Eccentricity', 0.9, ... % 等号通常是细长的 'Orientation', 0, ... % 水平方向 'Solidity', 0.7, ... % 中等实心度 'IsEqual', 1 ,... 'Row',row_num); % 标记为等号区域 % 确保字段顺序一致 if ~isempty(row_stats) equal_region = orderfields(equal_region, fieldnames(row_stats)); end % 标记等号区域 rectangle('Position', equal_bbox, 'EdgeColor', [0.2 0.8 0.2], 'LineWidth', 3); text(equal_bbox(1)+5, equal_bbox(2)-15, '等号', ... 'Color', [0 0.6 0], 'FontSize', 12, 'FontWeight', 'bold'); % 更新区域列表 - 移除原始的两个区域 remaining_indices = setdiff(1:length(row_stats), best_pair); row_bboxes = row_bboxes(remaining_indices, :); row_stats = row_stats(remaining_indices); % 安全连接结构体 if isempty(row_stats) row_stats = equal_region; % 当没有剩余区域时 else % 确保列向量结构 if size(row_stats, 2) > 1 row_stats = row_stats'; end % 连接结构体 row_stats = [row_stats; equal_region]; end % 添加合并后的等号区域 row_bboxes = [row_bboxes; equal_bbox]; % 重新排序并记录等号位置 [~, row_order] = sort(row_bboxes(:,1)); original_positions = 1:size(row_bboxes,1); new_order_indices = original_positions(row_order); % 关键修复:正确计算等号位置 equal_position_in_original = size(row_bboxes,1); % 等号添加在末尾 equal_sign_index = find(new_order_indices == equal_position_in_original); % 应用排序 row_bboxes = row_bboxes(row_order, :); row_stats = row_stats(row_order); log_entries{end+1} = sprintf('确定等号: 区域 %d 和 %d 合并为区域 %d', ... best_pair(1), best_pair(2), equal_sign_index); else log_entries{end+1} = '未找到有效的等号对'; equal_sign_index = []; end hold off; % 更新日志 set(handles.equal_log, 'String', log_entries); % % 存储更新后的区域信息 % handles.region_stats = stats; % handles.region_bboxes = bboxes; % handles.equal_sign_index = equal_sign_index; % % 关键修复:保存更新后的handles % guidata(gcbf, handles); updateProgress(30+row_num*20, '减号二次识别...'); % 找出所有类似减号的区域(排除已标记为等号的区域) dash_like_indices = []; for i = 1:length(row_stats) if row_stats(i).IsEqual continue; % 跳过等号区域 end bbox = row_bboxes(i,:); w = bbox(3); h = bbox(4); % 减号特征:高宽比大,离心率高 aspect_ratio = w / h; eccentricity = row_stats(i).Eccentricity; orientation = row_stats(i).Orientation; % 获取方向属性 if aspect_ratio > 1.5 && eccentricity > 0.85 && abs(orientation) < 10 dash_like_indices = [dash_like_indices; i]; row_stats(i).IsEqual = 3; handles.region_row_stats = row_stats; guidata(gcbf, handles); % 保存更新后的 handles rectangle('Position', bbox, 'EdgeColor', [0 0.8 0.8], 'LineWidth', 1.5); text(bbox(1)+5, bbox(2)-10, sprintf('%d', i), ... 'Color', [0 0.5 0.5], 'FontSize', 10, 'FontWeight', 'bold'); end end log_entries{end+1} = sprintf('找到 %d 个减号样区域', length(dash_like_indices)); %% 对每个疑似减号区域进行二次识别 division_sign_indices = []; for i = 1:length(dash_like_indices) idx = dash_like_indices(i); bbox = row_bboxes(idx,:); center_x = bbox(1) + bbox(3)/2; center_y = bbox(2) + bbox(4)/2; bbox = row_stats(idx).BoundingBox; %center_x = bbox(1) + bbox(3)/2; % 区域中心的 x 坐标 top_y = bbox(2); % 区域顶部 y 坐标 bottom_y = bbox(2) + bbox(4); % 区域底部 y 坐标 % 定义搜索范围 (从上下边界向外扩展) search_radius = round(max(bbox(3), bbox(4)) * 1); % 搜索半径增大到 1.5 倍 % 搜索上方区域 top_start = max(1, top_y - search_radius); % 上方搜索起始位置 top_end = top_y - 1; % 上方搜索结束位置(不包含当前区域) if top_start <= top_end % 确保搜索范围有效 top_region = inverted_img(top_start:top_end, ... max(1, center_x - search_radius):min(img_w, center_x + search_radius)); else top_region = []; % 如果搜索范围无效,则置为空 end % 搜索下方区域 bottom_start = bottom_y + 1; % 下方搜索起始位置(不包含当前区域) bottom_end = min(img_h, bottom_y + search_radius); % 下方搜索结束位置 if bottom_start <= bottom_end % 确保搜索范围有效 bottom_region = inverted_img(bottom_start:bottom_end, ... max(1, center_x - search_radius):min(img_w, center_x + search_radius)); else bottom_region = []; % 如果搜索范围无效,则置为空 end % 检查上下是否存在白色的小方块 top_white_pixels = sum(top_region(:)) > numel(top_region)*0.01; % 至少1%为白色 bottom_white_pixels = sum(bottom_region(:)) > numel(bottom_region)*0.01; if top_white_pixels && bottom_white_pixels division_sign_indices = [division_sign_indices; idx]; % 扩展边界框以包含上下白色区域 (扩展倍数增加到3.0) extended_top = max(1, bbox(2) - round(bbox(4)*3.0)); % 增加到3.0倍 extended_bottom = min(img_h, bbox(2) + bbox(4) + round(bbox(4)*3.0)); % 增加到3.0倍 extended_bbox = [bbox(1), extended_top, bbox(3), extended_bottom - extended_top]; % 更新区域信息 row_stats(idx).BoundingBox = extended_bbox; row_bboxes(idx,:) = extended_bbox; % 创建新的除号区域属性结构体 division_region = struct(... 'BoundingBox', extended_bbox, ... 'Area', extended_bbox(3)*extended_bbox(4), ... 'Eccentricity', 0.8, ... % 除号通常是细长的 'Orientation', 0, ... % 水平方向 'Solidity', 0.6, ... % 中等实心度 'IsEqual', 2 ,... 'Row',row_num); % 标记为非等号区域 % 确保字段顺序一致 if ~isempty(row_stats) division_region = orderfields(division_region, fieldnames(row_stats)); end % 替换原始减号区域 row_stats(idx) = division_region; % 标记为除号 rectangle('Position', extended_bbox, 'EdgeColor', [0.2 0.8 0.2], 'LineWidth', 1.5); text(bbox(1)+5, bbox(2)-10, '÷', ... 'Color', [0 0.6 0], 'FontSize', 12, 'FontWeight', 'bold'); log_entries{end+1} = sprintf('区域 %d: 被标记为除号', idx); end end if isempty(division_sign_indices) log_entries{end+1} = '未找到有效的除号'; else % % 同步更新 handles 中的区域信息 % handles.region_row_stats = row_stats; % handles.region_row_bboxes = row_bboxes; % guidata(gcbf, handles); % 保存更新后的 handles end % 更新日志 set(handles.equal_log, 'String', log_entries); handles.equal_sign_index = equal_sign_index; guidata(gcbf, handles); % ================= 第三步:字符识别 ================= updateProgress(30+row_num*30, '字符识别...'); % % 关键修复:使用更新后的区域列表 % row_stats = handles.region_row_stats; % row_bboxes = handles.region_row_bboxes; ocr_results = cell(1, length(row_stats)); for i = 1:length(row_stats) % 更新进度 updateProgress(30+row_num*30 + round(40*i/length(row_stats)), sprintf('识别字符 %d/%d', i, length(row_stats))); % 根据 IsEqual 字段直接读取符号 switch row_stats(i).IsEqual case 1 % 等号 ocr_results{i} = '='; log_entries{end+1} = sprintf('区域 %d: 已标记为等号', i); case 2 % 除号 ocr_results{i} = '÷'; log_entries{end+1} = sprintf('区域 %d: 已标记为除号', i); case 3 % 减号 ocr_results{i} = '-'; log_entries{end+1} = sprintf('区域 %d: 已标记为减号', i); otherwise % 其他字符 % 提取字符区域 bbox = row_stats(i).BoundingBox; % 放大裁剪框(增加5%的边距) pad_ratio = 0.05; % 边距比例 x = floor(max(1, bbox(1) - bbox(3)*pad_ratio)); y = floor(max(1, bbox(2) - bbox(4)*pad_ratio)); w = ceil(min(img_w - x, bbox(3)*(1 + 2*pad_ratio))); h = ceil(min(img_h - y, bbox(4)*(1 + 2*pad_ratio))); % 安全提取区域图像 y1 = max(1, y); y2 = min(img_h, y+h-1); x1 = max(1, x); x2 = min(img_w, x+w-1); if y2 < y1 || x2 < x1 ocr_results{i} = '?'; continue; end region_img = inverted_img(y1:y2, x1:x2); % 调用改进的 OCR 函数 [char, ~] = improvedOCR(region_img); ocr_results{i} = char; % 实时更新日志和进度提示 log_entries{end+1} = sprintf('区域 %d: 识别结果为 "%s"', i, char); end % 更新进度条下方的文本提示 set(handles.progress_text, 'String', sprintf('识别字符 %d/%d: 结果为 "%s"', i, length(row_stats), ocr_results{i})); drawnow; % 立即刷新界面 end % ================= 第四步:构建表达式 ================= updateProgress(30+row_num*65, '构建表达式...'); % 分割表达式和答案 expr_str = ''; answer_str = ''; % 找到等号位置 eq_idx = []; if ~isempty(handles.equal_sign_index) eq_idx = handles.equal_sign_index; % 验证索引有效性 if eq_idx < 1 || eq_idx > length(ocr_results) log_entries = get(handles.equal_log, 'String'); log_entries{end+1} = sprintf('警告: 等号索引 %d 超出范围 [1-%d]', eq_idx, length(ocr_results)); set(handles.equal_log, 'String', log_entries); eq_idx = round(length(ocr_results)/2); % 使用备用方案 end else % 如果没有找到等号,使用位置最中间的区域作为等号 [~, eq_idx] = min(abs(1:length(row_stats) - length(row_stats)/2)); log_entries = get(handles.equal_log, 'String'); log_entries{end+1} = sprintf('未找到等号,使用区域 %d 作为等号', eq_idx); set(handles.equal_log, 'String', log_entries); ocr_results{eq_idx} = '='; end % 关键修复:确保等号区域被正确识别 if ~strcmp(ocr_results{eq_idx}, '=') log_entries = get(handles.equal_log, 'String'); log_entries{end+1} = sprintf('警告: 区域 %d 不是等号,强制设为等号', eq_idx); set(handles.equal_log, 'String', log_entries); ocr_results{eq_idx} = '='; 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'); expr_str = strrep(expr_str, 'z', '2'); try correct_result = eval(expr_str); catch ME log_entries = get(handles.equal_log, 'String'); log_entries{end+1} = sprintf('计算错误: %s', ME.message); set(handles.equal_log, 'String', log_entries); correct_result = NaN; end 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); if row_num==1 imshow(inverted_img); end hold on; for i = 1:length(row_stats) bbox = row_stats(i).BoundingBox; % 等号区域用绿色标注 if row_stats(i).IsEqual switch row_stats(i).IsEqual case 1 % 等号 rect_color = [0.2 0.8 0.2]; % 绿色 text_color = [0 0.5 0]; % 深绿色 symbol = '='; case 2 % 除号 rect_color = [0.8 0.2 0.5]; % 粉色 text_color = [0.6 0 0.3]; % 深粉色 symbol = '÷'; case 3 % 减号 rect_color = [0.8 0.5 0.2]; % 橙色 text_color = [0.6 0.3 0]; % 深橙色 symbol = '-'; otherwise % 其他字符 rect_color = [1 0 0]; % 红色 text_color = [0 0 1]; % 蓝色 symbol = ocr_results{i}; end % 绘制边界框 rectangle('Position', bbox, 'EdgeColor', rect_color, 'LineWidth', 1.5); % 添加文本标注 if ~isempty(symbol) text(bbox(1)+5, bbox(2)-10, symbol, ... 'Color', text_color, 'FontSize', 10, 'FontWeight', 'bold'); end 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]; % 深橙色 elseif strcmp(ocr_results{i}, '*') || strcmp(ocr_results{i}, '×') rect_color = [0.2 0.5 0.8]; % 蓝色 text_color = [0.1 0.3 0.6]; % 深蓝色 elseif strcmp(ocr_results{i}, '/') || strcmp(ocr_results{i}, '÷') rect_color = [0.8 0.2 0.5]; % 粉色 text_color = [0.6 0 0.3]; % 深粉色 else rect_color = [1 0 0]; % 红色 text_color = [0 0 1]; % 蓝色 end rectangle('Position', bbox, 'EdgeColor', rect_color, 'LineWidth', 1.5); if ~isempty(ocr_results{i}) text(bbox(1)+5, bbox(2)-10, ocr_results{i}, ... 'Color', text_color, 'FontSize', 10, 'FontWeight', 'bold'); end end end hold off; % 将当前行的 stats 副本存入 all_row_stats all_row_stats{end+1} = row_stats; all_row_bboxes{end+1} = row_bboxes; all_row_ocr_results{end+1} = ocr_results; end title('最终识别结果'); % 正确合并所有行 - 创建新的结构体数组 if ~isempty(all_row_stats) % 方法1: 使用 vertcat 直接合并 try stats_combined = vertcat(all_row_stats{:}); catch % 方法2: 处理不同尺寸结构体的替代方案 stats_combined = struct(); for i = 1:numel(all_row_stats) if ~isempty(all_row_stats{i}) stats_combined = [stats_combined; all_row_stats{i}(:)]; end end end else stats_combined = struct([]); % 空结构体 end % 替换原始 stats stats = stats_combined; % 如果需要将所有行的 Bboxes 副本按顺序连接起来 reconstructed_ocr_results = []; for i = 1:length(all_row_bboxes) bboxes = [bboxes; all_row_bboxes{i}]; end for i = 1:length(all_row_ocr_results) reconstructed_ocr_results = [reconstructed_ocr_results, all_row_ocr_results{i}]; end % 保存处理后的数据 handles.ocr_results = ocr_results; guidata(gcbf, handles); updateProgress(30+row_num*100, '公式识别完成'); catch ME updateProgress(0, sprintf('错误: %s', ME.message)); errordlg(sprintf('识别失败: %s', ME.message), '错误'); end end function clearResults(~, ~) handles = guidata(gcbf); % 清除结果文本 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('等号检测'); % 清除进度条 set(handles.progress_bar, 'Position', [0,0,0,1]); set(handles.progress_text, 'String', '就绪'); % 如果有原始图像,重新显示 if ~isempty(handles.current_image) axes(handles.ax_original); imshow(handles.current_image); title('原始图像'); end % 保存handles状态 guidata(gcbf, handles); end % 更新进度条函数 function updateProgress(percent, message) handles = guidata(gcbf); % 获取当前 figure 的 handles % 确保进度条存在 if isfield(handles, 'progress_bar') && ishandle(handles.progress_bar) set(handles.progress_bar, 'Position', [0,0,percent,1]); end if isfield(handles, 'progress_text') && ishandle(handles.progress_text) set(handles.progress_text, 'String', message); end drawnow; % 立即更新显示 % 保存 handles 状态 guidata(gcbf, handles); end end % ======================== 运算符检测函数 ======================== function [is_operator, operator_type] = detectOperator(region_img) [h, w] = size(region_img); % 计算形状特征 aspect_ratio = w / h; eccentricity = regionprops(region_img, 'Eccentricity'); if ~isempty(eccentricity) eccentricity = eccentricity.Eccentricity; else eccentricity = 0; end 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 = '*'; return; end end end % 除号检测(点) if aspect_ratio > 0.8 && aspect_ratio < 1.2 % 检测中心点 center_y = round(h/2); center_x = round(w/2); if center_x > 0 && center_y > 0 && center_x <= w && center_y <= h if region_img(center_y, center_x) is_operator = true; operator_type = '/'; return; end end end % 等号检测(特殊情况) if aspect_ratio > 2.5 && eccentricity > 0.95 is_operator = true; operator_type = '='; 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 = false; if w > 1 && h > 1 right_top_corner = region_img(1, end) && region_img(2, end); end % 0的特征:封闭轮廓 filled_area = bwarea(region_img); convex_area = regionprops(region_img, 'ConvexArea'); if ~isempty(convex_area) convex_area = convex_area.ConvexArea; solidity = filled_area / convex_area; else solidity = 0; end % 8的特征:两个封闭区域 holes = regionprops(region_img, 'EulerNumber'); if ~isempty(holes) num_holes = 1 - holes.EulerNumber; % 欧拉数 = 1 - 孔洞数 else num_holes = 0; end switch original_char case '7' % 检查是否为1的特征 if aspect_ratio > 3 && top_pixels < numel(top_region)*0.2 && bottom_pixels > numel(bottom_region)*0.5 char = '1'; return; end case '1' % 检查是否为7的特征 if aspect_ratio < 2 && top_line && right_top_corner char = '7'; return; end case '0' % 检查是否为8的特征(有孔洞) if num_holes > 0 char = '8'; return; end case '8' % 检查是否为0的特征(无孔洞) if num_holes == 0 && solidity > 0.9 char = '0'; return; end case '5' % 检查是否为6的特征(底部封闭) bottom_closed = sum(region_img(end, :)) > w*0.6; if bottom_closed char = '6'; return; end case '6' % 检查是否为5的特征(底部开放) bottom_open = sum(region_img(end, :)) < w*0.4; if bottom_open char = '5'; return; end end char = original_char; % 保持原识别结果 end function [char, confidence] = improvedOCR(region_img) % 确保输入图像为二值图像 if ~islogical(region_img) error('输入图像必须是二值图像'); end % 获取图像尺寸 [height, width] = size(region_img); % 定义目标尺寸 target_size = 200; % 计算填充量 pad_top = max(0, floor((target_size - height) / 2)); pad_bottom = max(0, ceil((target_size - height) / 2)); pad_left = max(0, floor((target_size - width) / 2)); pad_right = max(0, ceil((target_size - width) / 2)); % 对图像进行填充 region_img_padded = padarray(region_img, [pad_top, pad_left], 'post'); region_img_padded = padarray(region_img_padded, [pad_bottom, pad_right], 'pre'); % 确保填充后的图像大小符合要求 if size(region_img_padded, 1) < target_size || size(region_img_padded, 2) < target_size error('填充后的图像大小不符合要求'); end %region_img_processed = thinningPreprocessing(region_img_padded); region_img_processed = edgeEnhancement(region_img_padded); % 定义扩展字符集; char_set = '0123456789+-*/=Xx'; % 设置置信度阈值 confidence_threshold = 0.3; % 使用 OCR 函数 try ocr_result = ocr(region_img_processed, 'CharacterSet', char_set, 'TextLayout', 'Block' ); % 检查 OCR 返回值是否为有效的 ocrText 对象 if ~isa(ocr_result, 'ocrText') || isempty(ocr_result.Text) % 如果 OCR 未能识别出任何字符,则尝试基于几何特征进行校正 char = detectCharacterByGeometry(region_img); confidence = 0; else % 提取 Text 的第一个字符 first_char = ocr_result.Text(1); % 直接提取第一个字符 % 检查第一个字符是否在定义的字符集中 if contains(char_set, first_char) char = first_char; % 如果有效,则记录该字符 else char = '?'; % 如果无效,则返回问号 end % 优先使用 WordConfidence word_confidence = ocr_result.WordConfidences; if word_confidence >= confidence_threshold confidence = word_confidence; else char = '?'; % 如果无法获取任何置信度信息,返回问号 confidence = 0; end end catch ME % 记录错误日志 fprintf('OCR 错误: %s\n', ME.message); fprintf('OCR 返回类型: %s\n', class(ocr_result)); char = '?'; % 如果 OCR 失败,返回问号 confidence = 0; end % 特殊字符校正 char = strrep(char, 'x', '*'); char = strrep(char, 'X', '*'); char = strrep(char, '÷', '/'); char = strrep(char, ' ', ''); % 针对 '6' 的检测与校正逻辑 if strcmp(char, '?') % 如果 OCR 未能识别出字符,则尝试基于几何特征进行校正 char = detectCharacterByGeometry(region_img); end end % ======================== 字符检测函数 ======================== function detected_char = detectCharacterByGeometry(region_img) % 计算形状特征 stats = regionprops(region_img, 'BoundingBox', 'Eccentricity', 'Solidity', 'Orientation', 'EulerNumber', 'Extent'); if isempty(stats) detected_char = '?'; return; end bbox = stats.BoundingBox; aspect_ratio = bbox(4) / bbox(3); % 高宽比 eccentricity = stats.Eccentricity; % 离心率 solidity = stats.Solidity; % 实心度 orientation = stats.Orientation; % 方向 euler_number = stats.EulerNumber; % 欧拉数 extent = stats.Extent; % 填充比例 % 判断是否为 '6' if aspect_ratio > 1.2 && aspect_ratio < 2.5 && ... eccentricity < 0.8 && solidity > 0.4 && ... abs(orientation) > 80 && euler_number ~= 1 detected_char = '6'; % 判断是否为 '3' elseif aspect_ratio > 0.8 && aspect_ratio < 1.8 && ... eccentricity > 0.7 &&eccentricity < 0.9... && solidity > 0.4 && ... euler_number == 1 && ... % '3' 的欧拉数通常为 1(有两个连通区域) abs(orientation) > 80&&... extent > 0.35 &&extent < 0.40 detected_char = '3'; % 判断是否为 '7' elseif aspect_ratio > 1.5 && ... % '7' 的高宽比较大 eccentricity > 0.9 && ... % '7' 的离心率较高 solidity > 0.45 && ... % '7' 的实心度较高 extent < 0.35 && ... % '7' 的填充比例较高 abs(orientation) > 75 &&abs(orientation) < 85 % '7' 的方向接近水平 detected_char = '7'; % 判断是否为 '5' elseif aspect_ratio > 1.0 && aspect_ratio < 2.0 && ... eccentricity < 0.85 && solidity > 0.45 && ... euler_number == 1 && ... % '5' 的欧拉数通常为 1(有两个连通区域) abs(orientation) > 85 &&...% '5' 的方向接近水平 extent > 0.40 detected_char = '5'; else detected_char = '?'; end end function [thinned_img] = thinningPreprocessing(img) % 确保输入为二值图像 if ~islogical(img) img = imbinarize(rgb2gray(im2double(img))); end % 字符瘦身(Thinning) thinned_img = bwmorph(img, 'thin', Inf); % 确保输出为二值图像 thinned_img = imbinarize(thinned_img); end function [sharpened_img] = edgeEnhancement(img) % 确保输入为二值图像 if ~islogical(img) img = imbinarize(rgb2gray(im2double(img))); end % 定义结构元素 se = strel('disk', 1); % 腐蚀操作 eroded_img = imerode(img, se); % 提取边缘 edge_img = img - eroded_img; % 边缘增强:将边缘信息加回到原始图像中 sharpened_img = img + edge_img; % 确保输出为二值图像 sharpened_img = imbinarize(sharpened_img); end function se_radius = calculateDilationRadius(inverted_img, img_h, img_w) % 计算连通域 cc = bwconncomp(inverted_img, 4); % 使用 4 连通性 stats = regionprops(cc, 'Area', 'BoundingBox'); % 提取所有连通域的面积 areas = [stats.Area]; bboxes = vertcat(stats.BoundingBox); % 去除小面积噪点 min_area_threshold = img_h * img_w * 0.0005; % 动态最小面积阈值 valid_idx = areas > min_area_threshold; valid_areas = areas(valid_idx); % 有效区域的面积 (1xN 数组) valid_bboxes = bboxes(valid_idx, :); % 有效区域的包围盒 (Nx4 数组) % 如果没有有效区域,则设置默认半径 if isempty(valid_areas) se_radius = 2; % 默认半径 return; end % 计算平均宽度和高度 widths = valid_bboxes(:,3); % 宽度 (Nx1 数组) heights = valid_bboxes(:,4); % 高度 (Nx1 数组) avg_width = mean(widths); % 平均宽度 avg_height = mean(heights); % 平均高度 % 计算有效区域的平均分辨率 avg_res = sqrt(avg_width * avg_height); % 平均分辨率 % 调整维度以确保逐元素运算正确 compactness = valid_areas ./ (widths' .* heights'); % 每个连通域的紧凑性 avg_compactness = 20 * mean(compactness(:)); % 紧凑性的平均值 % 边缘检测以获取字符边缘分布 edge_img = edge(inverted_img, 'Canny'); edge_density = 20 * sum(edge_img(:)) / numel(edge_img); % 边缘密度 % 综合计算字符粗细 char_thickness = sqrt(avg_width * avg_height * avg_compactness * edge_density); % 定义幂次参数 p p = 1.8; % 幂次参数,可以调整以控制非线性程度 % 使用对数函数结合幂次增强非线性度 adjusted_radius = (log(1 + avg_res / (char_thickness + 1e-6)))^p * 5; % 根据字符粗细动态调整膨胀半径 se_radius = round(max(1, adjusted_radius)); % 根据字符粗细动态调整膨胀半径 % se_radius = round(max(1, 20 * char_thickness / avg_res)); % 限制膨胀半径范围 if se_radius > 10 se_radius = 10; % 最大半径限制 elseif se_radius < 1 se_radius = 1; % 最小半径限制 end end 请参考我这里面使用的方法,预处理图像部分原样保留到二值化处理,之后的图像分割全部不要,然后把这种预处理放到刚才的无预处理代码中
最新发布
06-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值