ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MATLAB 图像标注实战:用 textarrow 与 annotation 精准标记数据点

MATLAB 图像标注实战:用 textarrow 与 annotation 精准标记数据点 1. 为什么 MATLAB 科研绘图需要代码化标注做科研绘图时很多人习惯用 Data Cursor 手动点一下曲线把关键点的坐标读出来再截图放进论文或笔记里。这个流程在单张图上没问题但一旦遇到下面几种情况就会很痛苦图要批量生成、图要反复修改、图要用 MATLAB 自带的 Publish 导出成带说明的文档或者图要交给合作者复现。手动标注没法写进脚本换一台机器、换一组数据就得重新点一遍。textarrow和annotation就是解决这个问题的。它们属于 MATLAB 的 annotation 系列函数作用是在 figure 的归一化坐标系里画箭头、文本框、椭圆、矩形等注释对象。和text()直接按数据坐标放文字不同annotation用的是 figure 归一化坐标范围固定在 0 到 1 之间横纵都相对整个绘图窗口所以它不会随坐标轴缩放而漂移适合做“钉死”在画面某个位置的标注。这篇内容面向的是需要把标注写进代码、能一键复现的科研绘图场景。核心要解决三件事箭头位置怎么定、textarrow参数怎么填、标注偏了怎么调。我会给出一套可以直接复制的代码骨架再演示怎么验证箭头是否真的指向了目标数据点。如果你平时用 MATLAB 画 sin、实验曲线、频谱图并需要标出峰值或特定采样点这套方法能直接套用。2. 前置准备TaoToken 与 MATLAB 环境标注本身是纯 MATLAB 功能不需要联网。但如果你在写脚本、查文档、让模型帮你生成或改写标注代码时想更顺手可以先把 TaoToken 的接入配好。它的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。下面这些 deep link 在需要时可以直接用模型对话https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewriteCoding Planhttps://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite控制台https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keyshttps://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteClaudeCodeAnthropichttps://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewriteMATLAB 这边只需要基础环境R2016b 及以上都支持annotation(textarrow,...)我实测 R2021b 和 R2023a 行为一致。不需要额外工具箱annotation属于 MATLAB 核心图形函数。建议把 figure 的Units保持默认避免归一化坐标和像素坐标混用导致箭头跑偏。3. 可复制配置textarrow 标注代码骨架先给一个最小可运行骨架。思路是画曲线找到要标注的数据点索引算出它在 figure 归一化坐标里的位置再用annotation(textarrow, x, y, String, ...)画箭头。close all; x 0:0.01:2*pi; y sin(x); figure(Color,w); plot(x, y, LineWidth, 1.5); grid on; % 目标点x 2.66 处的采样点 targetX 2.66; index find(abs(x - targetX) 1e-6, 1); px x(index); py y(index); % 把数据坐标转成 figure 归一化坐标 ax gca; axPos ax.Position; % 坐标轴在 figure 中的位置 [left bottom width height] xNorm axPos(1) axPos(3) * (px - ax.XLim(1)) / diff(ax.XLim); yNorm axPos(2) axPos(4) * (py - ax.YLim(1)) / diff(ax.YLim); % 箭头起点稍微偏移避免压住曲线 arrowStart [xNorm 0.08, yNorm 0.10]; annotation(textarrow, ... [arrowStart(1), xNorm], ... [arrowStart(2), yNorm], ... String, sprintf(x %.2f, y %.3f, px, py), ... FontSize, 11, ... Color, r);这段代码的关键在坐标转换。annotation的坐标不是数据坐标而是 figure 归一化坐标所以不能直接把px、py丢进去。ax.Position给出坐标轴在 figure 里的位置ax.XLim、ax.YLim给出数据范围两者一组合就能把任意数据点映射到 0 到 1 的归一化空间。这样即使你改了坐标轴范围或 figure 大小箭头依然指向同一个数据点。textarrow的前两个参数分别是 x 和 y 的坐标数组格式是[起点, 终点]。终点就是箭头尖指向的位置起点是文字框所在的位置。String是显示在起点附近的文本。把起点放在终点的右上方箭头就会从右上指向左下视觉上比较自然。4. 验证请求与成功结果跑完上面的代码你会看到一条正弦曲线在 x 约等于 2.66 的位置有一个红色箭头箭头尖精确落在曲线上旁边显示x 2.66, y 0.457。这就是成功结果。验证标注是否准确可以用一个简单办法在箭头终点位置再画一个标记点看两者是否重合。hold on; plot(px, py, ko, MarkerSize, 8, LineWidth, 1.5);如果黑色圆圈和箭头尖完全重合说明坐标转换没问题。如果箭头尖偏了通常是两个原因一是ax.Position在plot之后被改动过二是用了axis tight或axis equal改变了坐标轴比例。解决办法是在所有坐标轴设置完成之后再计算归一化坐标顺序不能反。再给一个多标注点的例子标出正弦曲线的峰值和零点close all; x 0:0.01:2*pi; y sin(x); figure(Color,w); plot(x, y, LineWidth, 1.5); grid on; ax gca; axPos ax.Position; points [pi/2, 1; pi, 0; 3*pi/2, -1]; labels {峰值 y1, 零点 y0, 谷值 y-1}; for k 1:size(points, 1) px points(k, 1); py points(k, 2); xNorm axPos(1) axPos(3) * (px - ax.XLim(1)) / diff(ax.XLim); yNorm axPos(2) axPos(4) * (py - ax.YLim(1)) / diff(ax.YLim); annotation(textarrow, ... [xNorm 0.06, xNorm], ... [yNorm 0.08, yNorm], ... String, labels{k}, ... FontSize, 10, ... Color, [0.85 0.1 0.1]); end运行后三个箭头分别指向峰值、零点和谷值文字不重叠。如果文字框互相压住可以给每个点设置不同的起点偏移量比如峰值往右上、谷值往右下。5. 本篇常见错排查第一个高频错误是箭头位置整体偏移。原因通常是annotation在plot之前调用或者ax.Position读取的是旧值。正确顺序是先plot再grid、axis、xlim、ylim全部设置完最后读ax.Position并计算归一化坐标。只要坐标轴还在变归一化坐标就得重算。第二个错误是textarrow报“坐标超出范围”。annotation的坐标必须在 0 到 1 之间如果你直接把数据坐标传进去比如[2.66, 2.66]就会报错或箭头跑到画面外。记住一定要做归一化转换。第三个坑是String里的数值格式。用num2str默认会输出很多位小数图里显得很乱。建议用sprintf(%.2f, value)控制精度。如果标注的是整数采样点用%d更干净。第四个问题是箭头被坐标轴裁剪。annotation对象默认在 figure 层不会被 axes 裁剪但如果起点坐标算到了 0 以下或 1 以上箭头就看不见了。检查方法是把起点坐标打印出来确认都在 0 到 1 之间。第五个常见误解是以为textarrow会随数据缩放自动更新。不会。annotation是静态的你改了xlim或ylim之后必须重新计算并重画。如果图要频繁改范围建议把标注逻辑封装成函数每次改完范围调用一次。6. 继续深入把标注写进可复用函数实际科研里标注逻辑最好封装起来避免每次手算坐标。下面这个函数接收数据点坐标和标签自动完成归一化和标注function addTextArrow(ax, px, py, label, offset) if nargin 5 offset [0.06, 0.08]; end axPos ax.Position; xNorm axPos(1) axPos(3) * (px - ax.XLim(1)) / diff(ax.XLim); yNorm axPos(2) axPos(4) * (py - ax.YLim(1)) / diff(ax.YLim); annotation(textarrow, ... [xNorm offset(1), xNorm], ... [yNorm offset(2), yNorm], ... String, label, ... FontSize, 10, ... Color, [0.85 0.1 0.1]); end调用时先画图、设好坐标轴再传gca和目标点close all; x 0:0.01:2*pi; y sin(x); figure(Color,w); plot(x, y, LineWidth, 1.5); grid on; xlim([0, 2*pi]); ylim([-1.2, 1.2]); addTextArrow(gca, pi/2, 1, 峰值); addTextArrow(gca, pi, 0, 零点, [0.06, -0.12]); addTextArrow(gca, 3*pi/2, -1, 谷值, [0.06, -0.10]);这样每次只需要传数据点和标签坐标转换和箭头绘制都自动完成。如果标注点很多还可以把points和labels做成数组循环调用。实测下来这套骨架在批量生成几十张图时特别省事改数据不用重新点标注。如果你在写脚本时需要让模型帮忙生成或改写这类标注代码可以用模型对话入口 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 快速验证长期做科研编码和 Agent 工作流的话Coding Plan https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 更合适需要管理密钥就去 API Keys https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入细节看文档 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。
返回列表