一、时间

5.4 - 5.7

二、工作目标

基于上一阶段历史人物轨迹可视化功能成果,重点完成坐标精度优化与功能迭代升级:通过与CHGIS官方数据手动校对,提升轨迹坐标匹配精度,完善坐标匹配逻辑;优化HistoricalMap.vue组件核心功能,新增轨迹合并显示、手动控制、多人物对比等实用功能,提升交互体验;扩展历史人物轨迹数据至278人,丰富可视化内容;最后完成OCR上传识别功能的需求分析、技术选型与开发规划,为后续功能落地奠定基础,进一步完善历史地理可视化系统,助力古文沉浸式智慧阅读平台建设。

核心要求:坐标精度符合CHGIS学术标准,轨迹功能流畅无卡顿,OCR功能规划贴合项目需求,可直接落地开发。

三、详细内容

本阶段工作围绕坐标精度校对→轨迹功能优化→人物数据扩展→OCR功能规划四大核心环节展开,兼顾数据质量、功能体验与后续开发落地,确保系统实用性与完整性进一步提升。

(一)核心工作:CHGIS坐标手动校对与匹配逻辑优化

上一阶段增强版清洗脚本实现了78.36%的CHGIS坐标匹配率,但部分匹配结果存在坐标偏差、层级不对应等问题,不符合学术展示要求。本阶段重点通过手动校对结合代码优化,提升坐标匹配精度,确保每一个轨迹节点的坐标都能精准对应历史地理位置。

1. 手动校对核心流程

以CHGIS官方地名数据为基准,对清洗后的数据(update2.json)进行逐点手动校对,重点处理以下三类问题:

  • 坐标偏差:部分模糊匹配、反向匹配的坐标与实际历史地点存在轻微偏差,手动调整经纬度至精准位置;
  • 层级错误:纠正“县级地名匹配到省级坐标”“点状地名匹配到区域坐标”的问题,确保地名层级与坐标精度对应;
  • 匹配错误:删除误匹配的坐标,补充未匹配成功但有明确CHGIS记录的地名坐标,提升高可信度坐标占比。

2. 校对辅助代码实现

为提升手动校对效率,开发坐标校对辅助脚本,实现地名索引快速构建、层级识别、多模式匹配校验等功能,核心代码如下(基于Node.js):

const fs = require('fs');

// 行政层级映射(从具体到抽象)
const HIERARCHY_LEVELS = {
    // 最具体 - 点状地名
    'point': ['镇', '村', '寨', '堡', '驿', '铺', '关', '口', '渡', '桥', '山', '岭', '峰', '陵', '台', '亭', '庙', '寺', '观', '宫', '殿', '阁', '楼', '塔', '坛'],
    // 县级
    'county': ['县', '邑', '城', '侯国', '道'],
    // 州郡级
    'prefecture': ['州', '郡', '府', '路', '王国', '属国', '都护府'],
    // 省级
    'province': ['省', '行省', '布政使司', '道']
};

// 构建地名索引(用于快速查找)
function buildPlaceIndex(historicalPlaces) {
    const exactIndex = new Map();      // 精确匹配索引
    const fuzzyIndex = new Map();       // 模糊匹配索引(小地名)
    const pinyinIndex = new Map();      // 拼音索引

    for (const feature of historicalPlaces) {
        const props = feature.properties;
        const coords = feature.geometry.coordinates;

        // 标准地名
        if (props.name_ch) {
            const name = props.name_ch.trim();
            exactIndex.set(name, { feature, matchType: 'exact', score: 100 });

            // 构建模糊索引:提取核心地名(去掉行政区划后缀)
            const coreName = extractCoreName(name);
            if (coreName && coreName !== name) {
                if (!fuzzyIndex.has(coreName) || fuzzyIndex.get(coreName).score < 90) {
                    fuzzyIndex.set(coreName, { feature, matchType: 'fuzzy', score: 90, originalName: name });
                }
            }
        }

        // 繁体地名
        if (props.name_ft) {
            const name = props.name_ft.trim();
            if (!exactIndex.has(name)) {
                exactIndex.set(name, { feature, matchType: 'exact', score: 100 });
            }
        }

        // 拼音地名
        if (props.name_py) {
            const pinyin = props.name_py.trim().toLowerCase();
            pinyinIndex.set(pinyin, { feature, matchType: 'pinyin', score: 85 });
        }

        // 今地名描述
        if (props.pres_loc) {
            const presLoc = props.pres_loc.trim();
            // 提取今地名中的关键地名
            const placesInPresLoc = extractPlacesFromPresLoc(presLoc);
            for (const place of placesInPresLoc) {
                if (!fuzzyIndex.has(place) || fuzzyIndex.get(place).score < 80) {
                    fuzzyIndex.set(place, { feature, matchType: 'pres_loc', score: 80, originalName: props.name_ch });
                }
            }
        }
    }

    console.log(`  索引构建完成: 精确索引 ${exactIndex.size} 条, 模糊索引 ${fuzzyIndex.size} 条, 拼音索引 ${pinyinIndex.size} 条`);
    return { exactIndex, fuzzyIndex, pinyinIndex };
}

// 提取核心地名(去掉行政区划后缀)
function extractCoreName(placeName) {
    if (!placeName) return '';

    // 去掉常见后缀
    const suffixes = /(?:县|邑|城|州|郡|府|路|省|镇|村|寨|堡|关|山|岭|峰|陵|台|亭|庙|寺|观|宫|殿|阁|楼|塔|坛|国|属国|王国|都护府|道|行省|布政使司)$/;
    let core = placeName.replace(suffixes, '');

    // 如果去掉后缀后太短,保留原样
    if (core.length >= 2) {
        return core;
    }
    return placeName;
}

// 从今地名描述中提取地名
function extractPlacesFromPresLoc(presLoc) {
    const places = [];

    // 匹配模式:今河南临颍东南二十五里 -> 河南临颍、临颍
    const match1 = presLoc.match(/今([^东南西北0-9]+)/);
    if (match1) {
        let place = match1[1].trim();
        places.push(place);

        // 提取最后一部分
        const lastPart = place.match(/([^省市县区]+?[市县]?)$/);
        if (lastPart && lastPart[1].length >= 2) {
            places.push(lastPart[1]);
        }
    }

    // 匹配模式:江苏邳县北艾山南 -> 邳县、艾山
    const match2 = presLoc.match(/([^东南西北]+?)(?:县|山)/);
    if (match2) {
        places.push(match2[1] + match2[0].slice(-1));
    }

    return [...new Set(places)];
}

// 智能拆分地名(按行政层级从大到小)
function smartSplitPlaceName(placeName) {
    if (!placeName) return [];

    const parts = [];
    let remaining = placeName;

    // 按层级从大到小拆分
    const allKeywords = [...HIERARCHY_LEVELS.province, ...HIERARCHY_LEVELS.prefecture, ...HIERARCHY_LEVELS.county, ...HIERARCHY_LEVELS.point];

    for (const keyword of allKeywords) {
        const regex = new RegExp(`(.+?${keyword})`, 'g');
        let match;
        while ((match = regex.exec(remaining)) !== null) {
            const part = match[1];
            if (part.length >= 2 && !parts.includes(part)) {
                parts.push(part);
                remaining = remaining.replace(part, '');
            }
        }
    }

    // 添加剩余的纯地名(通常是核心地名)
    if (remaining.trim().length >= 2) {
        parts.push(remaining.trim());
    }

    // 如果没有拆出任何部分,返回原地名
    if (parts.length === 0 && placeName.length >= 2) {
        parts.push(placeName);
    }

    // 按长度从小到大排序(具体→抽象)
    parts.sort((a, b) => a.length - b.length);

    return [...new Set(parts)];
}

// 生成所有可能的搜索变体
function generateSearchVariants(placeName) {
    const variants = new Set();

    // 1. 原地名
    variants.add(placeName);

    // 2. 提取核心地名
    const core = extractCoreName(placeName);
    if (core && core !== placeName) variants.add(core);

    // 3. 智能拆分后的各个部分
    const parts = smartSplitPlaceName(placeName);
    parts.forEach(p => variants.add(p));

    // 4. 去掉常见前缀(朝代名)
    let withoutDynasty = placeName.replace(/^(秦|汉|唐|宋|元|明|清|周|春秋|战国|魏|蜀|吴|晋|隋|大秦帝国|新朝)/, '');
    if (withoutDynasty !== placeName && withoutDynasty.length >= 2) variants.add(withoutDynasty);

    // 5. 去掉"城"字
    if (placeName.endsWith('城')) {
        variants.add(placeName.slice(0, -1));
    }

    // 6. 提取双字地名(常见模式)
    const twoCharMatch = placeName.match(/([^路府州县镇城郡尹关岭山峰口渡桥村堡寨驿铺]{2,4})/g);
    if (twoCharMatch) {
        twoCharMatch.forEach(m => {
            if (m.length >= 2 && m.length <= 4 && !variants.has(m)) {
                variants.add(m);
            }
        });
    }

    // 过滤太短的
    return Array.from(variants).filter(v => v.length >= 2);
}

// 尝试匹配地点(最高概率版本)
function findMatchingPlaceHighest(placeName, indices, context = '') {
    if (!placeName || placeName === 'undefined' || placeName === 'null') {
        return null;
    }

    const { exactIndex, fuzzyIndex, pinyinIndex } = indices;

    // 生成所有搜索变体
    const searchVariants = generateSearchVariants(placeName);

    // 按优先级和得分排序的匹配结果
    const matches = [];

    // 1. 精确匹配(最高分)
    for (const variant of searchVariants) {
        if (exactIndex.has(variant)) {
            const match = exactIndex.get(variant);
            matches.push({
                ...match,
                searchTerm: variant,
                finalScore: match.score
            });
        }
    }

    // 2. 模糊索引匹配
    for (const variant of searchVariants) {
        if (fuzzyIndex.has(variant)) {
            const match = fuzzyIndex.get(variant);
            // 根据匹配长度调整分数
            let score = match.score;
            if (variant.length === match.feature.properties.name_ch?.length) {
                score += 5; // 完全匹配加分
            }
            matches.push({
                ...match,
                searchTerm: variant,
                finalScore: score
            });
        }
    }

    // 3. 拼音匹配(针对特殊地名)
    const pinyinVariants = searchVariants.map(v => convertToPinyin(v));
    for (const pinyin of pinyinVariants) {
        if (pinyinIndex.has(pinyin)) {
            const match = pinyinIndex.get(pinyin);
            matches.push({
                ...match,
                searchTerm: pinyin,
                finalScore: match.score
            });
        }
    }

    // 4. 包含匹配(遍历精确索引)
    for (const variant of searchVariants) {
        if (variant.length < 2) continue;

        for (const [key, match] of exactIndex) {
            // 搜索词包含在索引词中
            if (key.includes(variant) && variant.length >= 2) {
                let score = 70;
                // 如果搜索词就是索引词的一部分且长度相近,提高分数
                if (key.length - variant.length <= 2) score = 85;
                matches.push({
                    ...match,
                    searchTerm: variant,
                    finalScore: score,
                    matchDetail: `${key} 包含 ${variant}`
                });
            }
            // 索引词包含在搜索词中
            else if (variant.includes(key) && key.length >= 2) {
                let score = 75;
                // 如果索引词就是搜索词的核心部分,提高分数
                if (variant.replace(key, '').length <= 2) score = 88;
                matches.push({
                    ...match,
                    searchTerm: variant,
                    finalScore: score,
                    matchDetail: `${variant} 包含 ${key}`
                });
            }
        }
    }

    // 去重并按分数排序
    const uniqueMatches = new Map();
    for (const match of matches) {
        const key = match.feature.properties.id || match.feature.properties.name_ch;
        if (!uniqueMatches.has(key) || uniqueMatches.get(key).finalScore < match.finalScore) {
            uniqueMatches.set(key, match);
        }
    }

    const sortedMatches = Array.from(uniqueMatches.values()).sort((a, b) => b.finalScore - a.finalScore);

    // 返回最佳匹配
    if (sortedMatches.length > 0) {
        const best = sortedMatches[0];
        const props = best.feature.properties;
        return {
            coordinates: best.feature.geometry.coordinates,
            matchedPlace: props.name_ch,
            matchedField: best.matchType,
            matchType: best.matchType,
            matchScore: best.finalScore,
            searchTerm: best.searchTerm,
            matchDetail: best.matchDetail || ''
        };
    }

    return null;
}

// 简单的拼音转换(仅处理常见情况)
function convertToPinyin(chinese) {
    // 简单处理:对于罕见字,直接返回原字符串
    // 实际使用中可以接入完整的拼音库
    return chinese.toLowerCase();
}

// 主处理函数
function processMatchWithHighestProbability() {
    console.log('\n' + '='.repeat(70));
    console.log('历史地名匹配程序 - 最高匹配概率版本');
    console.log('策略:智能拆分 + 多级索引 + 相似度评分');
    console.log('='.repeat(70));

    // 读取数据文件
    console.log('\n读取数据文件...');
    const historicalPlacesPath = './historical_places.geojson';
    const personalTrackPath = './personal_track.geojson';

    const historicalPlaces = readJSONFile(historicalPlacesPath);
    const personalTrack = readJSONFile(personalTrackPath);

    if (!historicalPlaces || !personalTrack) {
        console.error('无法读取数据文件,程序退出');
        return;
    }

    console.log(`✓ historical_places.geojson: ${historicalPlaces.length} 条记录`);
    console.log(`✓ personal_track.geojson: ${personalTrack.length} 个人物\n`);

    // 构建索引
    console.log('构建多级索引...');
    const indices = buildPlaceIndex(historicalPlaces);

    // 统计变量
    let totalTrajectories = 0;
    let matchedTrajectories = 0;
    let unmatchedTrajectories = 0;
    let errorTrajectories = 0;
    const unmatchedPlaces = new Set();
    const matchDetails = [];
    const matchScores = { high: 0, medium: 0, low: 0 };

    console.log('\n开始匹配处理...');
    console.log('='.repeat(70));

    for (const person of personalTrack) {
        const personName = person.人物 || '未知人物';

        if (!person.轨迹 || !Array.isArray(person.轨迹)) {
            console.log(`⚠ ${personName}: 无轨迹数据`);
            continue;
        }

        console.log(`\n处理人物: ${personName} (${person.轨迹.length}条轨迹)`);

        for (let i = 0; i < person.轨迹.length; i++) {
            const trajectory = person.轨迹[i];
            totalTrajectories++;

            const placeName = trajectory.地点;
            if (!placeName || placeName === 'undefined' || placeName === 'null') {
                errorTrajectories++;
                continue;
            }

            console.log(`\n  [${i + 1}] 地名: "${placeName}"`);

            // 使用最高概率匹配
            const match = findMatchingPlaceHighest(placeName, indices, personName);

            if (match) {
                trajectory.coordinates = match.coordinates;
                trajectory.coordinate_source = 'historical_places';
                trajectory.coordinate_confidence = match.matchScore >= 90 ? 'high' : (match.matchScore >= 75 ? 'medium' : 'low');
                trajectory.match_type = match.matchType;
                trajectory.match_score = match.matchScore;

                console.log(`    ✓ 匹配成功: ${match.matchedPlace}`);
                console.log(`      匹配类型: ${match.matchType}, 得分: ${match.matchScore}`);
                console.log(`      搜索词: "${match.searchTerm}"`);
                if (match.matchDetail) console.log(`      详情: ${match.matchDetail}`);
                console.log(`      坐标: [${match.coordinates.join(', ')}]`);

                matchedTrajectories++;

                // 统计匹配质量
                if (match.matchScore >= 90) matchScores.high++;
                else if (match.matchScore >= 75) matchScores.medium++;
                else matchScores.low++;

                matchDetails.push({
                    person: personName,
                    originalPlace: placeName,
                    matchedPlace: match.matchedPlace,
                    matchType: match.matchType,
                    matchScore: match.matchScore,
                    searchTerm: match.searchTerm
                });
            } else {
                console.log(`    ✗ 未找到匹配`);
                unmatchedTrajectories++;
                unmatchedPlaces.add(placeName);
            }
        }
    }

    // 保存更新后的数据
    const outputPath = './personal_track_matched.geojson';
    writeJSONFile(outputPath, personalTrack);

    // 输出统计信息
    console.log('\n' + '='.repeat(70));
    console.log('匹配结果统计:');
    console.log('='.repeat(70));
    console.log(`总轨迹数: ${totalTrajectories}`);
    console.log(`成功匹配: ${matchedTrajectories}`);
    console.log(`未匹配: ${unmatchedTrajectories}`);
    console.log(`错误记录: ${errorTrajectories}`);

    const validTrajectories = totalTrajectories - errorTrajectories;
    if (validTrajectories > 0) {
        const matchRate = (matchedTrajectories / validTrajectories * 100).toFixed(2);
        console.log(`\n匹配成功率: ${matchRate}%`);

        console.log(`\n匹配质量分布:`);
        console.log(`  高质量匹配(≥90分): ${matchScores.high}`);
        console.log(`  中等质量匹配(75-89分): ${matchScores.medium}`);
        console.log(`  低质量匹配(<75分): ${matchScores.low}`);
    }

    // 输出未匹配的地点
    if (unmatchedPlaces.size > 0) {
        console.log(`\n未匹配的地点列表(共${unmatchedPlaces.size}个,显示前30个):`);
        console.log('-'.repeat(70));
        let index = 1;
        for (const place of unmatchedPlaces) {
            if (index <= 30) {
                console.log(`  ${index}. ${place}`);
            }
            index++;
        }
    }

    console.log('\n' + '='.repeat(70));
    console.log(`结果已保存到: ${outputPath}`);
}

// 辅助函数
function readJSONFile(filePath) {
    try {
        const data = fs.readFileSync(filePath, 'utf8');
        const parsed = JSON.parse(data);
        if (parsed.type === 'FeatureCollection' && Array.isArray(parsed.features)) {
            return parsed.features;
        }
        if (Array.isArray(parsed)) return parsed;
        return null;
    } catch (error) {
        console.error(`读取文件失败:`, error.message);
        return null;
    }
}

function writeJSONFile(filePath, data) {
    try {
        fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
        console.log(`文件已保存: ${filePath}`);
    } catch (error) {
        console.error(`写入文件失败:`, error.message);
    }
}

// 运行
if (require.main === module) {
    console.log('最高匹配概率程序启动...\n');
    processMatchWithHighestProbability();
}

module.exports = { findMatchingPlaceHighest, buildPlaceIndex };

3. 校对成果

通过手动校对结合辅助脚本校验,完成2247条高可信度轨迹坐标的优化,将高可信度坐标占比从81.23%提升至 91.19%,坐标精度完全符合CHGIS学术标准,为轨迹可视化的准确性提供了核心保障。

未匹配地名为国外或者太宽、偏僻的地区:

(二)功能优化:HistoricalMap.vue组件迭代升级

在上一阶段轨迹可视化功能基础上,针对用户体验与功能完整性进行优化,新增轨迹合并、手动控制、多人物对比等功能,完善交互逻辑,优化样式细节,核心优化内容如下:

1. 新增轨迹合并显示功能

针对部分人物轨迹中存在重复地点(如多次在咸阳城活动)的问题,新增轨迹合并模式,自动识别同一地点的多个轨迹节点,合并为单个标记,标记上显示节点数量,点击可查看该地点的所有轨迹事件,避免地图标记过于密集、遮挡视线。核心实现代码片段如下:

const calculateMergeGroups = (): MergeGroup[] => {
    if (!selectedTrackDetail.value) return [];
    const points = selectedTrackDetail.value.points;
    const locationMap = new Map<string, number[]>();
    points.forEach((point, index) => {
        const loc = (point.location || '未知地点').trim();
        if (!locationMap.has(loc)) locationMap.set(loc, []);
        locationMap.get(loc)!.push(index);
    });
    const groups: MergeGroup[] = [];
    locationMap.forEach((indexes, location) => {
        if (indexes.length > 1) {
            const sortedIndexes = [...indexes].sort((a, b) => a - b);
            const namePrefix = selectedPerson.value ? selectedPerson.value + ' ' : '';
            let label: string;
            if (sortedIndexes.length === 2 && sortedIndexes[1] - sortedIndexes[0] === 1) {
                label = `${sortedIndexes[0] + 1}-${sortedIndexes[1] + 1}`;
            } else {
                label = sortedIndexes.map(i => i + 1).join('、');
            }
            groups.push({ label: `${namePrefix}${label}`, location, indexes: sortedIndexes, points: sortedIndexes.map(i => points[i]) });
        }
    });
    return groups;
};
const isIndexInMergeGroup = (index: number): boolean => {
    if (!mergeMode.value) return false;
    return currentMergeGroups.value.some(g => g.indexes.includes(index));
};

const isFirstInMergeGroup = (index: number): boolean => {
    if (!mergeMode.value) return false;
    const group = currentMergeGroups.value.find(g => g.indexes.includes(index));
    return group ? group.indexes[0] === index : false;
};

const getMergeGroupLabel = (index: number): string => {
    const group = currentMergeGroups.value.find(g => g.indexes.includes(index));
    return group ? group.label : '';
};

const getMergeGroupIndexesCount = (index: number): string => {
    const group = currentMergeGroups.value.find(g => g.indexes.includes(index));
    if (!group) return '';
    return group.label.replace(selectedPerson.value ? selectedPerson.value + ' ' : '', '');
};

const findOriginalIndex = (place: TrackPoint): number => {
    if (!selectedTrackDetail.value) return -1;
    return selectedTrackDetail.value.points.findIndex(p => p === place) + 1;
};

2. 完善轨迹手动控制功能

在上一阶段自动播放功能基础上,新增轨迹手动控制功能,支持暂停/继续播放、跳转至指定节点、播放倍速调整,解决原有“只能自动播放、无法手动干预”的问题,提升用户交互体验。核心功能包括:

  • 暂停/继续播放:点击暂停按钮停止播放,再次点击从当前节点继续播放;
  • 节点跳转:点击轨迹时间轴的任意节点,地图自动定位至该节点坐标,显示对应事件信息;
  • 倍速调整:支持1x、1.5x、2x三种倍速,适配不同用户的查看需求。

3. 优化多模块交互与样式

进一步优化轨迹模块与历史地名、时间轴、AI问答的联动逻辑,调整渲染层级与交互细节:

  • 交互优化:解决轨迹播放与时间轴筛选的冲突,点击历史地名可查看对应人物轨迹,点击轨迹点可联动AI问答,查询该地点相关历史信息;
  • 样式优化:统一古风视觉风格,调整轨迹线渐变效果、标记点样式、弹窗布局,提升移动端适配效果,确保小屏幕下轨迹面板、弹窗无错乱;
  • 性能优化:优化轨迹渲染逻辑,减少大数据量下的卡顿问题,278位人物的轨迹加载时间控制在1秒内,播放流畅无延迟。

4. 完整组件代码

优化后的HistoricalMap.vue组件整合了坐标校验、轨迹合并、手动控制、多模块联动等所有功能,代码逻辑连贯、注释完整,可直接用于系统部署,核心代码如下:

5. 功能优化成果

完成HistoricalMap.vue组件全量优化后,轨迹可视化功能体验显著提升:解决了原有标记密集、无法手动控制的核心痛点,新增的轨迹合并功能可有效减少80%以上的重复标记,手动控制功能满足不同用户的查看习惯;多模块交互更流畅,278位人物轨迹加载速度≤1秒,播放无卡顿、无异常;样式适配移动端与PC端,古风视觉风格与项目整体定位高度契合,完全满足用户交互与学术展示需求。

(三)数据扩展:历史人物轨迹数据补充

为丰富轨迹可视化内容,提升系统实用性,本阶段将历史人物轨迹数据从原有数量扩展至278人,覆盖先秦至明清各个朝代,涵盖政治家、文学家、军事家等多个领域,确保数据的多样性与代表性。

数据扩展核心工作包括:筛选符合项目需求的历史人物,优先选择有明确历史活动轨迹、史料记载完整的人物;补充人物基础信息(姓名、朝代、核心事迹),完善轨迹节点(时间、地点、事件、坐标),所有新增轨迹节点均经过CHGIS坐标手动校对,确保坐标精度符合学术标准;对新增数据进行分类整理,按朝代、领域划分标签,便于后续用户筛选与查看。

新增数据已同步至项目数据库,与原有数据统一管理,支持按姓名、朝代、领域等多维度筛选,为后续多人物对比、轨迹分析功能奠定数据基础。

、总结与下一步计划

(一)本阶段总结

本阶段(5.4-5.7)顺利完成坐标精度校对、轨迹功能优化、人物数据扩展,达成预期工作目标:CHGIS坐标高可信度占比提升至 91.19%,符合学术标准;HistoricalMap.vue组件新增3项核心功能,交互体验与性能显著优化;人物轨迹数据扩展至278人,内容更丰富;可多人轨迹对比展示;完善了deepseek接口对接。

通过本阶段工作,历史地理可视化系统的实用性、准确性与完整性得到进一步提升,为后续功能落地与系统部署奠定了坚实基础,同时积累了坐标校对、组件优化的相关经验,为后续工作提供了参考。

(二)下一步计划

1.  重点推进OCR图像识别功能的开发准备工作,规划细节,梳理开发所需资源与依赖,为功能落地开发做好充分铺垫;

2.  推进OCR上传识别功能开发),严格按照规划分阶段落地,确保识别精度与交互体验符合需求。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐