ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

扫描电镜报告PDF解析:从pdfplumber到颗粒尺寸自动统计

扫描电镜报告PDF解析:从pdfplumber到颗粒尺寸自动统计 简介这份资料是扫描电镜SEM实验报告的完整PDF版本面向材料科学、生物、地质等专业需掌握电子显微分析技术的学生与实验人员可作为课程实验报告撰写、考试复习或实验课前预习的参考。报告包含实验目的、要求、原理、步骤、数据总结及教师意见栏系统梳理了真空系统、电子束系统、成像系统三大核心组成并介绍形貌衬度与原子序数衬度的实际应用帮助读者理解电子束与样品相互作用的次级电子、背散射电子、X射线等信号产生机制。文档中对纤维区、放射区、瞬断区等断口特征的观察记录展示了如何借助SEM区分不同微观组织结构对撰写实验报告或准备相关课程答辩具有直接参考价值。资源为单份PDF文件共1个文件包体约402KB轻量易用。目前已有166人学习下载可作为扫描电镜实验教学与自学的配套材料。1. 扫描电镜实验报告.pdf数据躺在 PDF 里取不出来就是白做实验室拿到一份扫描电镜实验报告 PDF关心的是能不能交差、能不能发文章IT 这边真正头疼的是后面的事加速电压、工作距离、放大倍数散在页面表格里显微图被封成压缩位图颗粒尺寸只能对着标尺数像素。材料数据入库、批量质检、把图像喂给下游分析脚本时这份 PDF 就是黑盒。下面这条路径能把扫描电镜实验报告.pdf 拆成结构化字段、原始显微图、标尺换算系数和颗粒统计结果拆完还能交叉验证。适合批量处理 SEM 报告、被 PDF 解析反复折磨的工程师和数据工程师。2. 解析扫描电镜报告 PDF先拆文本参数表再拆图像2.1 先弄清 SEM 报告 PDF 的版式和参数落点ZEISS、Thermo Fisher、Hitachi、JEOL 各家生成的扫描电镜报告版式差异不小但信息落点有共性第一页通常是样品信息加成像参数表下面排一到四张显微图后面是能谱或更大倍率的附图。成像参数表里高频出现的键是「加速电压 EHT/HV」「工作距离 WD」「放大倍数 Mag」「探测器 DetectorSE2/BSD/InLens」「扫描速度」。键名和值之间可能有冒号、制表符也可能被拆进表格的两个单元格解析必须兼容这两种布局。解析路径上建议直接在上游 PDF 上做先别想着 pdf转word。转 Word 会把表格打散、图片二次压缩标尺文字变成文本框还会错位转曲再导出的路线损失更大。PDF 原生文本层只要不是扫描件字段就是完整保真的直接解析效率和准确率都高得多。2.2 用 pdfplumber 优先抽参数表无边框时退回文本行解析参数表我习惯分三步pdfplumber 抽表格、无边框时退回文本行、字段级正则收敛。第一步先直接看表格解析的实际效果import pdfplumber with pdfplumber.open(扫描电镜实验报告.pdf) as pdf: for pno, page in enumerate(pdf.pages, 1): for row in page.extract_tables(): print(第{}页:.format(pno), row)extract_tables 默认策略对无边框表格不友好返回空列表时先别急着换库显式传 table_settings 往往能救回来专门处理竖线存在、横线缺失的半线框表格settings { vertical_strategy: lines, horizontal_strategy: text, snap_tolerance: 3, join_tolerance: 3, } rows page.extract_tables(settings)参数说明vertical_strategy 指定竖线来源lines 表示只认线条horizontal_strategy 用 text 是让 pdfplumber 按文字行推断横线位置。snap_tolerance 和 join_tolerance 控制线段吸附与合并的像素容差单位是 pt报告缩放比例异常、线对不齐时把 3 调到 5~8 一般能解决。pdfplumber 逐页解析几十页会明显偏慢所以它只负责把表格结构看清字段提取交给下一节。2.3 用 PyMuPDF 按块取文本正则收敛字段我一般第二遍用 PyMuPDFfitz按块取文本再用正则把字段收敛成字典速度比 pdfplumber 快一个量级import fitz import re FIELD_PATTERNS { accelerating_voltage_kv: re.compile( r(?:加速电压|EHT|HV)\s*[:]?\s*([\d.])\s*k?V, re.I), working_distance_mm: re.compile( r(?:工作距离|WD|Work\s*Dist\.?)\s*[:]?\s*([\d.])\s*mm, re.I), magnification: re.compile( r(?:放大倍数|Mag)\s*[:]?\s*([\d,])\s*[×xX]?, re.I), } doc fitz.open(扫描电镜实验报告.pdf) page doc[0] text page.get_text(text) for field, pat in FIELD_PATTERNS.items(): m pat.search(text) if m: print(field, , m.group(1).replace(,, ))逻辑说明get_text(text) 返回整页纯文本正则先匹配键名再取数值避免正文里恰好出现相似单位group(1) 里的千分位逗号统一去掉再转 float后续 JSON 序列化才不会有字符串混入。常用字段对照表直接按这个建字典字段常见键名单位正则要点加速电压加速电压 / EHT / HVkV键名后允许全半角冒号与空格工作距离工作距离 / WD / Work Dist.mm必须先锁键名再取值放大倍数放大倍数 / Mag×兼容 1,000× 千分位写法探测器探测器 / Detector无只取 2~6 位纯字母提示少数报告把参数表做成了图片get_text 抽不到任何内容。这种情况只能对图像区域做 OCR用 Tesseract 的中英文混合模型识别后再过一遍同一张字段表。3. 提取扫描电镜图像并做歪斜校正纠偏先拿到干净位图3.1 用 extract_image 取原始位图先算有效 DPIpdfplumber 也能取图但要绕页面对象和裁剪矩阵批量场景效率不如 PyMuPDF。取图首选 doc.extract_imageimport fitz doc fitz.open(扫描电镜实验报告.pdf) page doc[0] for i, info in enumerate(page.get_images(fullTrue)): xref, smask, w, h info[0], info[1], info[2], info[3] base doc.extract_image(xref) fname micrograph_{}.{}.format(i, base[ext]) with open(fname, wb) as fh: fh.write(base[image]) eff_dpi w / (page.rect.width / 72.0) print(fname, base[ext], w, h, 有效DPI约, round(eff_dpi))这里的关键是有效 DPI的计算思路。PDF 页面尺寸单位是 pt1 pt 等于 1/72 英寸一张 900×800 像素的图被排版成 4.5×4 英寸有效 DPI 就是 200 上下。很多 SEM 报告的显微图嵌入时接近 300 DPI但页面上显示宽度只有七八厘米直接导出页面图片会把分辨率压到 150 DPI 以下颗粒边界发糊标尺像素长度也跟着失真。所以取图必须走 extract_image拿嵌入的原始 JPEG/PNG 流而不是页面截图。参数说明get_images(fullTrue) 返回的 info 里xref 是图像对象编号smask 是透明度遮蔽层的 xrefw、h 是像素宽高。extract_image(xref) 返回 dict二进制内容在 [image]格式后缀在 [ext]。有效 DPI 低于 150 的报告图后面测出来的尺寸只当参考别进正式结论。3.2 歪斜校正纠偏旋转角小于 0.15 度就别动PDF 歪斜校正纠偏这个操作落到扫描电镜报告上通常有两个来源纸质报告二次扫描时页面转了零点几度以及 PDF 编辑器导出图片时带的旋转残差。角度虽小但标尺像素长度会按 cos 缩水颗粒直径同样带偏差。用 OpenCV 做整体纠偏import cv2 import numpy as np def deskew(gray): th cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)[1] coords np.column_stack(np.where(th 0)) if coords.shape[0] 3: return gray angle cv2.minAreaRect(coords)[-1] if angle -45: angle 90 angle if abs(angle) 0.15: return gray h, w gray.shape M cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1.0) return cv2.warpAffine(gray, M, (w, h), flagscv2.INTER_CUBIC, borderModecv2.BORDER_REPLICATE)逻辑说明Otsu 二值化后把所有前景像素坐标收集起来minAreaRect 算最小外接矩形的倾斜角负角换算到 -45~45 度区间小于 0.15 度直接跳过避免重采样把本来就正的图搞糊。重采样用 INTER_CUBIC 比 INTER_NEAREST 平滑borderMode 用 REPLICATE 复制边缘像素防止旋转后四角变黑——黑边会被后面的标尺检测误判成干扰线。干扰项过滤维度按像素面积和宽高比来干扰对象典型尺寸过滤规则仪器 logo100×100 以下宽或高低于 160 直接丢弃页眉水印文字细长条宽高比大于 6 丢弃能谱图与显微图接近按页面显示位置过滤正常 SEM 显微图方形或 4:3保留进入标尺校准3.3 取图时记录显示矩形和旋转标志取图顺手把 page.get_image_rects(xref) 的显示矩形也存下来。标尺文字通常紧贴图像下边缘用矩形 Y 坐标加偏移去匹配文本块比全局 OCR 定位省事得多。旋转标志同样落盘第 5 章人工抽检要靠它定位问题报告。4. 标尺校准与颗粒尺寸统计像素怎么换算成微米4.1 标尺识别的两种做法OCR 读数值Hough 找线段标尺识别两条路OCR 读出 10 µm 这类数值文字Tesseract 单行模式 psm 7 命中率较好图像底部区域用 Hough 直线检测找最长水平线段作为像素长度。两条路结合用数值走 OCR长度走直线检测h, w gray.shape roi gray[int(h * 0.9):, :] # 标尺常驻图底边只看底部10% edges cv2.Canny(roi, 50, 150) lines cv2.HoughLinesP(edges, 1, np.pi / 180, threshold80, minLineLengthint(w * 0.08), maxLineGap2) longest max(lines, keylambda l: abs(l[0][2] - l[0][0])) x1, y1, x2, y2 longest[0] scale_bar_pix abs(x2 - x1) # 标尺像素长度参数说明ROI 只取底部 10% 是经验值SEM 标尺几乎总在图下边缘或右下角这样能避开颗粒纹理被误认为水平线。minLineLength 设为图像宽度的 8%短于这个长度不可能是标尺。maxLineGap2 允许标尺中间的细微断线拼回一条。若检出的最长线段明显短于标尺文字宽度多半是标尺颜色偏灰、Canny 阈值太紧把 50/150 改成 30/100 再看。4.2 像素到微米的换算系数以及闭环校验怎么做换算关系只有一句像素到微米的系数等于标尺物理长度除以像素长度。OCR 数值和 Hough 像素长度都拿到后换算和合理性检查一次算完scale_text ocr_scale_bar_text(roi) # 复用Tesseract封装返回单行文本 physical_um float(re.search(r([\d.])\s*(?:µm|μm|um), scale_text).group(1)) um_per_px physical_um / scale_bar_pix # 像素到微米的换算系数 fov_width_um w * um_per_px # 整幅图的物理视场宽度 print(换算系数:, round(um_per_px, 4), µm/px, 视场宽度:, round(fov_width_um, 2), µm)参数说明OCR 读出的标尺文字要事先做单位归一化报告里 μm、µm 甚至全角符号都有正则写宽容易误吃相邻字符建议先替换再匹配。视场宽度是副产品用来做快速合理性检查放大倍数标 5000× 时视场宽度一般落在 20~100 µm超出量级就要怀疑 OCR 把 5.0 读成了 50。真正的闭环校验不靠公式反推而是拿已知周期的标准样品报告做端到端对比同一条处理链导出标准样品的统计结果和厂商标称值比误差 5% 以内说明下载-纠偏-标尺-分割整条链路是通的。4.3 Otsu 分割加等效直径做颗粒尺寸统计分割和统计的骨架代码def measure_particles(gray, um_per_px, min_area_px80): blur cv2.medianBlur(gray, 5) th cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)[1] contours, _ cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) out [] for c in contours: area_px cv2.contourArea(c) if area_px min_area_px: continue out.append({ area_um2: round(area_px * um_per_px ** 2, 2), eq_diameter_um: round(2 * (area_px / 3.14159) ** 0.5 * um_per_px, 3), }) return out逻辑说明二次电子像里颗粒相对背景是亮目标用 BINARY_INV 反转要统计孔洞时改成 BINARY 即可。Otsu 自动选阈值同批次报告通常不用手调。min_area_px80 用来滤噪点300 DPI 下 80 像素约为 0.014 平方微米正好压掉电荷噪声。粘连颗粒会被当成一个大目标导致直径偏大纯粒径分布场景要加分水岭分割先记面积分布反而是更稳的中间产物。测量常见误判与对策现象原因对策直径整体偏大颗粒粘连成团改报面积分布或加 watershed直径整体偏小min_area 切掉小颗粒下调到 40~60 重跑只有大块没细节JPEG 压缩太狠回 3.1 查有效 DPI标尺像素和文字对不上底部 ROI 混入页码ROI 改右下 20% 区域重检5. 批量解析扫描电镜报告 PDF 的收尾技巧JSON 落地、抽检、旋转角留痕5.1 一条命令从 PDF 目录到 JSON把前面的逻辑收进 sem_report_parser.py暴露 CLI命令行用 --in 指定 PDF 目录、--out 指定输出目录import argparse, json, pathlib ap argparse.ArgumentParser() ap.add_argument(--in, destin_dir, requiredTrue) ap.add_argument(--out, destout_dir, default./parsed) args ap.parse_args() results [] for pdf_path in sorted(pathlib.Path(args.in_dir).glob(*.pdf)): result parse_one(str(pdf_path)) # 返回字段/图像/换算系数/统计 results.append(result) pathlib.Path(args.out_dir).mkdir(exist_okTrue) out_json pathlib.Path(args.out_dir) / reports.json out_json.write_text(json.dumps(results, ensure_asciiFalse, indent2), encodingutf-8) print(已生成, out_json, 共, len(results), 份报告)python sem_report_parser.py --in E:/SEM/2025/pdf --out E:/SEM/2025/jsonreports.json 每条记录包含报告名、解析字段、图像文件名、标尺像素长度、um_per_px 换算系数和颗粒统计数组。字段与统计分层存放Pandas 读的时候不用做二次清洗。5.2 人工抽检和旋转角留痕验证时把等效直径画成红圈叠加回原图用任一 pdf 编辑器打开原报告对照手工量三五个颗粒误差 ±5% 以内通过。歪斜校正生效时把 deskew 返回的 angle 写进 JSON抽检对不上的报告先查 angle 是否非零再看 OCR 读出的标尺数值有没有把 5 µm 读成 50 µm。批量收尾最值得留的就是中间产物有效 DPI、angle、标尺像素长度全部落盘别只存最终统计。下次复跑或下游复核时直接从 JSON 看每一步的取值不用重新解析一遍 PDF。angle 非零的报告单独列一个清单作为人工复核队列的入口这个清单配合报告名和图像序号命名长期归档也不怕丢上下文。本文还有配套的精品资源点击获取
返回列表