十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

超声图像标注自动去除的MATLAB实现与批量处理方案

超声图像标注自动去除的MATLAB实现与批量处理方案 简介本资源是一套面向医学图像处理初学者与科研人员的MATLAB自动化工具专为解决超声图像边缘手写标注、测量标记等干扰信息的批量去除问题而设计。适用于超声影像预处理、AI模型训练前的数据清洗及计算机辅助诊断系统开发等场景无需深度学习基础掌握基础MATLAB图像处理即可上手。压缩包共7个文件6张典型超声JPG样本图 1个核心MATLAB脚本hitandmiss_3.m总大小747KB结构精简样本覆盖B型与M型超声图像脚本内嵌连通域分析、形态学命中-错过检测及最大目标区域提取逻辑可直接运行完成自动裁剪与背景恢复。目前已有308人学习下载提供即用型代码实测图像组合显著减少人工修图耗时助力高效构建高质量超声数据集。 做超声图像处理这些年接手过的第一类需求往往不是多高深的AI算法而是最朴素的清理工作把超声图像四周的医院名称、探头频率、增益数值、刻度标记、时间戳统统去掉。看起来是个“脏活累活”但它恰恰是后面一切处理的地基。标注不干净后面做分割、做测量、做AI训练集都会被这些信息干扰甚至直接把模型带偏。我见过很多人手动处理这种图一张一张用画图工具涂黑一天搞几百张眼睛都快花了。其实用MATLAB写个小工具几秒钟就能自动完成还能做成批量脚本一次处理整个文件夹。这篇文章就把我的完整思路、代码和踩坑记录都放出来从图像分析到参数调优再到批量落地一次性说清楚。1. 超声图像四周标注为什么非去不可1.1 标注信息干扰的不只是“好看”超声图像和普通照片不一样设备厂商为了记录扫描条件会在屏幕上叠加大量文本和图形信息。常见的有机器型号和医院名称、探头型号与频率、深度标尺和增益值、患者ID和检查时间、还有屏幕四角的公司logo。这些内容在诊断时是辅助信息但进入图像分析环节就成了噪声。我在做乳腺超声的自动分割时第一版算法把肿瘤边缘检测得乱七八糟排查了半天才发现是左上角那串增益数值被当成了高回声区。还有同学做深度学习分类训练集里每张图的右上角都带有不同亮度的标注文字模型居然学会了“看右上角判断类别”精度虚高换一批真实图片直接崩掉。所以自动去除标注不是美观问题是数据质量问题。1.2 为什么不能直接裁剪掉四周有人可能会说标注在四周直接把四边裁掉不就行了这里有个超声图像特有的坑超声图像的主体是扇形扫描区域扇区外是纯黑背景扇区的边缘往往延伸到靠近图像底部和两侧的位置并不是规则矩形。如果按固定像素裁剪四周很容易把扇形边缘的组织信息切掉。不同机器、不同探头深度下扇区大小和位置还都不一样固定裁剪完全不可靠。真正稳妥的思路是检测出哪些像素属于“标注文字”只把这些像素清除扇区内容原样保留。这就是我下面要讲的自动化方案的核心逻辑。1.3 一个通用的需求场景清单这几年接触到的需求基本集中在三种场景临床科研医生从超声机器导出图像做回顾性研究前需要脱敏去掉患者ID等信息保护隐私。算法开发做目标检测、图像分割、图像增强前先清洗训练集避免标注文字成为虚假特征。数据展示用于论文插图、学术报告、教学幻灯片时不希望带有设备信息和参数版面更干净。这三种场景要求批量处理、保留诊断区域细节、处理前后不能有肉眼可见的损伤。MATLAB在这类任务上非常顺手图像处理工具箱的函数齐全脚本化批处理也简单。2. 方案选型四种思路对比与最终选择2.1 方法一固定区域置黑或裁剪最简单直接把图像四边若干像素区域涂黑或者按矩形裁掉。优点是代码三行搞定缺点是完全没有适应性。换个机器、换个分辨率、改个扫描深度标注位置全变就得重新改参数而且容易切到扇形区域。这个方案只适合临时用一次、后续不会换数据源的情况不适合做工具沉淀。2.2 方法二连通域分析加边界接触检测超声图像里扇区主体通常不会接触图像上下边界而标注文字、刻度尺、参数块几乎都贴在图像边缘或四角。所以先把图像二值化找出所有亮色连通域然后判断哪些连通域与图像边界接触。接触边界、面积又处于合理范围的连通域就是标注区域将它们从掩膜中剔除即可。这个方法我实际用下来最稳它天然适应“标注在四周”这个先验知识不需要针对每一张图调坐标。缺点是需要处理一些边缘误检情况后面我会讲怎么过滤。2.3 方法三形态学顶帽变换分离文字顶帽变换的原理是用大尺度的结构元素做开运算得到背景估计然后用原图减去背景把细小的高亮结构比如文字笔画提取出来。理论上很巧妙不用关心标注位置凡是“小而亮”的结构都会被分离。但超声图像本身纹理复杂扇形边缘有很多高亮的细小结构顶帽变换会把它们一起提取出来误检率偏高。需要额外做位置过滤和面积过滤反而多绕了一圈。2.4 方法四OCR加深度学习文本检测用OCR识别文字位置或者训练一个文本检测模型精确找到标注文字的包围盒。如果是通用图像场景这是首选但对于超声图像这种高度专用、标注样式固定的场景属于杀鸡用牛刀。模型训练需要大量标注数据推理还需要GPU环境部署成本高。实际项目里一个基于连通域的规则方法已经能解决90%以上的问题没必要引入重型依赖。2.5 最终选择的方案与理由我最终采用的是“预处理加连通域加边界接触检测加掩膜膨胀”的组合方案。选择它有三个原因适应性强不依赖固定坐标对不同的超声机器和分辨率都能work。可解释性强每一步都能可视化出问题时一眼能看出是哪里判断错了。运行速度快纯MATLAB脚本处理单张图几十毫秒批量处理不吃力。对比下来固定裁剪太死板顶帽变换误检太多深度学习成本太高。连通域方案在“效果、成本、可维护性”三者之间最平衡。3. 核心实现连通域加边界接触检测代码逐段拆解3.1 图像读取与灰度化超声图像导出的格式五花八门常见的是JPG、PNG、BMP也有DICOM。DICOM的话用dicomread读取普通图片用imread就行。如果你从超声工作站导出的是彩色图很多机器默认带彩色映射先转成灰度因为后续二值化只需要亮度信息。function cleanImg removeAnnotation(img, params) % 输入 img: HxWxC 或 HxW 的超声图像 % 输入 params: 参数结构体可选 % 输出 cleanImg: 去除四周标注后的图像 if nargin 2 params struct(); end if size(img, 3) 3 gray rgb2gray(img); else gray img; end gray im2double(gray); % 统一转成 double 并归一化到 [0,1]注意如果是伪彩色超声图比如B型血流图叠加了彩色多普勒直接rgb2gray可能丢失一些低对比度信息。我的建议是对这类图像先做通道分离在亮度最高的通道上进行标注检测或者用单一绿色通道也行。不过大多数常规B型超声是灰度图rgb2gray就够用了。3.2 二值化阈值选择标注文字在灰度图上通常是亮白色像素值接近255背景是纯黑接近0扇形区域是中间灰度。所以一个简单的思路是灰度值大于某个阈值的像素标记为前景。if ~isfield(params, threshold) params.threshold 0.35; end bw gray params.threshold;这个阈值怎么定我一般先看直方图。超声图像的灰度直方图通常有一个很明显的峰值对应黑色背景然后是一片连续的灰度范围对应扇形组织最右侧会有一个小尖峰对应文字和白色刻度。阈值取在背景峰值右侧、组织范围上方比较合适经验值落在0.25到0.45之间。这里有个实际教训不要直接用graythresh全局大津法。大津法会把背景和扇形区域分开但标注文字的峰值太小容易被忽略导致二值化后文字和扇形黏在一起后续连通域分析就容易出问题。手动设阈值虽然“土”但对超声图像这种亮度分布高度可预测的情况反而稳定。3.3 形态学去噪与文字连接二值化的结果会有两类噪声一类是扇形区域内部的散点噪声另一类是文字笔画内部的细小断裂。用开运算去掉散点用闭运算填补文字间的缝隙。bw imopen(bw, strel(disk, 2)); bw imclose(bw, strel(disk, 3));结构元素选小圆盘而不是矩形是因为文字笔画和刻度线方向各异圆盘各向同性不会因为方向问题把某些笔画像素误删。开运算的半径2像素太大会把细笔画“消化”掉太小又滤不掉散点2这个值是反复试出来的平衡点。闭运算半径3像素用来把同一串数字里挨得近的笔画连接成同一个连通域减少后面统计时把一个字符拆成四五块的情况。3.4 连通域标记与分析连通域分析是核心排查步骤MATLAB里用bwconncomp和regionprops就能做。cc bwconncomp(bw); stats regionprops(cc, BoundingBox, PixelIdxList, Area); [H, W] size(bw); mask false(H, W); if ~isfield(params, minArea) params.minArea 30; end if ~isfield(params, maxAreaRatio) params.maxAreaRatio 0.2; end maxArea params.maxAreaRatio * H * W;每个连通域都有面积Area、外接矩形BoundingBox和像素索引列表PixelIdxList。我盯住这三个量做判断。Area用来排除太小和太大的对象——太小的多半是噪声太大的可能是扇形主体本身。maxAreaRatio设为0.2意思是面积超过整幅图20%的连通域不会是标注直接跳过。3.5 边界接触检测与掩膜生成这是整个方案的核心逻辑。超声图像的标注信息必然贴着图像边界或者至少有一个方向靠近边界而扇形主体通常不会填满图像。所以我判断一个连通域是否属于标注核心条件是它的外接矩形是否与图像边界“亲密接触”。for i 1:numel(stats) bb stats(i).BoundingBox; x bb(1); y bb(2); w bb(3); h bb(4); touchesBorder (x 2) || (y 2) || ... (x w W - 1) || (y h H - 1); if touchesBorder stats(i).Area params.minArea stats(i).Area maxArea mask(stats(i).PixelIdxList) true; end end这里边界接触的阈值设成2像素而不是严格等于0是为了避免JPG压缩造成的边缘伪影导致连通域外接矩形偏差1个像素而漏检。压缩噪声会让原本贴边的文字往外扩一圈宽松一点反而更稳。还有一个容易被忽略的细节BoundingBox返回的是double类型xw就是外接矩形右边界的位置和W-1比较时要注意MATLAB坐标从1开始。写成 W - 1是因为图像最右一列索引是W矩形如果包含W列说明它贴到了右边界。3.6 掩膜膨胀与图像去标注检测到的掩膜只是文字笔画的精确区域直接用它置黑文字边缘会残留半像素的浅色描边视觉上像没擦干净。解决方案是对掩膜做一次膨胀把擦除范围往外扩几像素。mask imdilate(mask, strel(disk, 2)); cleanImg img; if size(cleanImg, 3) 3 for c 1:3 ch cleanImg(:, :, c); ch(mask) 0; cleanImg(:, :, c) ch; end else cleanImg(mask) 0; end end膨胀半径2像素是为了让擦除区域覆盖文字边缘的过渡带又不至于侵入扇形区域太深。如果原图分辨率很高比如2000x2000以上膨胀半径可以适当加到3。这里我把擦除值设成了0也就是纯黑和超声图像四角的背景色保持一致。如果你的数据集后续要做归一化用背景均值填充也可以但我在多个项目中对比过对算法效果影响极小纯黑反而让数据更“干净”。3.7 完整函数代码把上面的片段整合起来就是一个可以直接用的函数。这个函数建议单独保存为removeAnnotation.m后续批量脚本直接调用。function cleanImg removeAnnotation(img, params) %REMOVEANNOTATION 自动去除超声图像四周标注 % 输入 % img - HxWxC 或 HxW 图像 % params - 可选结构体包含字段 % threshold 二值化阈值默认0.35 % minArea 最小连通域面积默认30 % maxAreaRatio 最大面积占比默认0.2 % dilateRadius 掩膜膨胀半径默认2 % 输出 % cleanImg - 去除标注后的图像 if nargin 2 params struct(); end if ~isfield(params, threshold), params.threshold 0.35; end if ~isfield(params, minArea), params.minArea 30; end if ~isfield(params, maxAreaRatio), params.maxAreaRatio 0.2; end if ~isfield(params, dilateRadius), params.dilateRadius 2; end if size(img, 3) 3 gray rgb2gray(img); else gray img; end gray im2double(gray); bw gray params.threshold; bw imopen(bw, strel(disk, 2)); bw imclose(bw, strel(disk, 3)); cc bwconncomp(bw); stats regionprops(cc, BoundingBox, PixelIdxList, Area); [H, W] size(bw); mask false(H, W); maxArea params.maxAreaRatio * H * W; for i 1:numel(stats) bb stats(i).BoundingBox; x bb(1); y bb(2); w bb(3); h bb(4); touchesBorder (x 2) || (y 2) || ... (x w W - 1) || (y h H - 1); if touchesBorder stats(i).Area params.minArea stats(i).Area maxArea mask(stats(i).PixelIdxList) true; end end mask imdilate(mask, strel(disk, params.dilateRadius)); cleanImg img; if size(cleanImg, 3) 3 for c 1:3 ch cleanImg(:, :, c); ch(mask) 0; cleanImg(:, :, c) ch; end else cleanImg(mask) 0; end end这个函数直接拿走就能用我建议第一次跑之前先给函数加一段可视化代码把mask叠加在灰度图上确认检测出来的区域确实是文字标注而不是扇形边缘。4. 批量落地参数校准、文件夹处理与扩展思路4.1 新数据到手后的参数校准我发现一个规律凡是直接从网上找来的代码直接跑在自己的数据上大概率效果打折扣。原因很简单不同超声机器的标注样式不一样有的显示在左上角有的四角全占满有的机器标注是白色高亮有的偏灰偏暗。所以我习惯在批量处理前先跑一个“校准模式”。具体做法是抽3到5张代表性图像用默认参数跑一次把检测出来的掩膜用彩色叠加显示出来肉眼确认有没有漏检或误检。function inspectAnnotation(img) gray im2double(rgb2gray(img)); mask detectAnnotationMask(gray); % 里面是二值化连通域分析的逻辑 overlay cat(3, gray, gray, gray); overlay(:, :, 1) max(overlay(:, :, 1), double(mask) * 0.8); figure; imshow(overlay); title(红色区域为检测到的标注); end校准这一步花不了几分钟但能避免你对着500张处理完的图片后悔。我踩过最大的坑就是一批图处理完了训练了好几天模型最后发现有一类图的左下角logo亮度低没被检测到模型学到了一堆无用特征只能回炉重做。4.2 批量处理脚本校准没问题后就可以做批量处理。脚本本身不复杂核心是遍历文件夹和调用上面的函数。srcDir D:\ultrasound\raw; dstDir D:\ultrasound\clean; if ~exist(dstDir, dir) mkdir(dstDir); end files dir(fullfile(srcDir, *.png)); for i 1:numel(files) img imread(fullfile(srcDir, files(i).name)); cleanImg removeAnnotation(img); imwrite(cleanImg, fullfile(dstDir, files(i).name)); if mod(i, 50) 0 fprintf(已处理 %d / %d\n, i, numel(files)); end end如果图片格式是jpg就把匹配改成*.jpg如果是dicom用dicomread配合dicomwrite但注意dicomwrite需要保留原始元数据时要用dicomuid等函数处理比普通图片麻烦一点。超声工作站导出的图绝大多数是png或jpg直接imread、imwrite就够。大量图片处理时可以用parfor加速但有几个注意点一是removeAnnotation函数要保证内部没有随机变量或交互命令二是如果用了figure做可视化不能放进parfor三是内存有限时建议分批处理比如每处理200张清理一次。我实测过一张1600x1200的图单张耗时约60毫秒用parfor在四核机器上可以跑到20毫秒左右处理1000张图从一分钟缩短到二十秒。4.3 处理结果验证不只是看一眼“干净了”处理完批图片除了肉眼抽看还建议做一个量化验证。思路是计算每张图处理前后图像四个边界区域的平均亮度变化。标注被去除后边界区域亮度应该明显下降而中心扇形区域亮度几乎不变。before double(img); after double(cleanImg); borderBefore mean(before([1:50, end-49:end], :), all); borderAfter mean(after([1:50, end-49:end], :), all); fprintf(边界亮度变化: %.2f - %.2f\n, borderBefore, borderAfter);这个验证能帮你发现阈值设得过高导致标注没擦干净的情况。如果处理后的边界区域平均亮度还很高说明有大量白色像素残留得回去调threshold和dilateRadius。4.4 扩展形态学顶帽差分法的备选实现如果你的图像标注不是亮白色而是反白块或半透明带阴影的文字连通域方案的检测效果会打折扣。这时候可以试试形态学顶帽差分法原理我前面提过代码也不复杂。gray im2double(gray); se strel(disk, 15); textMask imsubtract(gray, imopen(gray, se)); textMask textMask 0.15;这段代码的思路是用一个半径为15的圆盘做开运算得到“没有细小结构”的背景估计再用原图减去背景剩下的大多是文字和细小高亮结构。结构元素半径要大于文字笔画宽度但小于扇形区域的特征尺寸15在大多数超声图上表现不错图片分辨率高时可以适当加到20。这个方案不需要边界接触判断对“标注不在角落”的情况更友好但副作用是扇形边缘的细小高亮也会被分离出来。我一般只在连通域方案效果不好时作为补充两套方法结合使用更稳。4.5 扩展逐行扫描投影法还有一个思路特别适合标注集中在上下两端的机器。对每一行统计该行中灰度大于阈值的像素个数会看到一段“文字密集区”这些连续的行就是标注所在的区域把整段区域置黑即可。列方向同理。这个方案的优势是简单直观、计算量极小缺点是无法精细到单个字符如果标注和扇形区域在同一行就会误伤。我对超声图像的观察是大多数机器的参数标注都在图像最上面一行或最下面一行和扇形区域不重叠所以投影法在某些场景下是够用的。5. 常见问题排查与实战避坑指南5.1 典型问题速查表问题现象可能原因解决方法扇形区域边缘被擦除扇形接触了图像边界被误判为连通域调低maxAreaRatio增大minArea或加一个“外接矩形中心远离图像中心”的过滤条件标注文字残留半截阈值过高导致部分笔画二值化后太淡把threshold从0.35降到0.25检查处理后的边界区域亮度擦除区域有黑色空洞标注文字内部有暗色像素导致连通域断裂增大闭运算半径或对掩膜做imfill填充孔洞检测到很多散点噪声图像压缩噪声太多增大开运算半径或提升minArea过滤DICOM图像读取后全黑DICOM存储是16位直接转double溢出用dicomread后先查看dicomlookup或做窗口映射再转8位处理时间太长图像尺寸大且没有并行用parfor或先缩小图像检测掩膜再在原图应用5.2 关于maxAreaRatio的坑这个参数我最开始没加结果遇到过一次事故。某医院的超声机器屏幕比例特殊扇形区域大到几乎填满整张图像底部边缘直接接触了图像边界扇形最下方的连通域被判定为“标注”一次性擦掉了一大片有诊断价值的区域。加了maxAreaRatio之后面积超过整图20%的连通域直接跳过就规避了这类问题。如果你处理的图像分辨率比较低比如只有640x48020%的阈值可能偏大建议改成绝对面积值比如50000像素这样更可控。5.3 不同机器标注样式的适配建议我总结了几类最常见的标注布局以及对应的适配策略四角型左上、右上、左下、右下都有信息。连通域方案默认就能良好适配。顶部横条型整条横向区域都是文字或logo。可以配合投影法把顶部连续文字行整行处理。侧边刻度型扇区两侧有刻度尺或深度标记。膨胀半径要适当调大否则刻度线残留会非常明显。半透明阴影型文字背后有半透明色块。建议先对图像做对比度拉伸或CLAHE增强让文字与背景的亮度差拉开。我的做法是把不同来源的图像按文件夹区分先对每个文件夹做校准得到一组最优参数再用每组参数分别处理对应文件夹。千万别指望一套参数打天下超声图像的质量差异比你想的大。5.4 处理结果对后续算法的影响我专门做过一个对比实验同一批乳腺超声数据一组直接训练分割模型一组先去标注再训练。结果去标注组的Dice系数提升了大约3%到5%而且训练的收敛速度明显更快验证集损失也更稳定。原因不复杂。标注信息虽然只占图像面积的一小部分但它的亮度极高、边缘锐利在卷积神经网络的特征提取阶段会被赋予很大的激活值。模型为了降低损失会倾向于从这些容易识别的区域学习特征反而忽略了真正的组织纹理。把标注清干净模型被迫去学更有意义的结构特征训练出来的模型泛化能力也更强。这个实验给了我一个习惯以后接到超声图像分析任务第一件事永远是先看图像有没有标注需要清洗而不是直接开始建模。5.5 一个被忽略的细节原图备份与命名规范批量处理最容易出的事故就是覆盖了原始文件。我建议处理流程分为两个文件夹raw放原始数据clean放处理后数据绝不原地覆盖。命名上把处理参数写进文件名比如clean_th035_dil2.png这样后续想复现处理流程、排查问题都能追根溯源。另外如果图像要用于学术发表很多期刊要求标注去除后不能有任何篡改痕迹。纯黑填充四角是最稳妥的但万一要求保留原始背景灰度可以把mask区域填充为该图背景灰度的均值。这个需求可以用代码简单实现bgVal mean(gray(gray 0.05), all); cleanImg(mask) bgVal;超声图像四角背景几乎是纯黑bgVal通常接近0.01以下和直接置黑差别不大但对于要求严格的场景这是一个更“保守”的选择。5.6 如果让我重新做一遍我会怎么做回到开头那个问题自动去除超声图像四周标注看起来是个小工具但做好它其实需要把“图像特征理解、规则设计、参数调优、批处理工程化”全链路走通。踩过几次坑之后我现在做这类任务都会按固定节奏来先抽图看分布、再做参数校准、先跑小批量验证、最后全量跑批一步都不省。这套方法不只适用于超声图像。X光片、CT胶片扫描件、内窥镜截图甚至手机拍屏幕的图片只要标注信息分布在图像四周且与主体的亮度有差异都可以用同样的思路去处理。只是阈值和形态学参数需要重新校准而已。希望这份记录能帮你少走点弯路。本文还有配套的精品资源点击获取
返回列表