临床试验中的 DICOM 数据通常不只在元数据标签里包含敏感信息。患者姓名、检查编号、日期或机构名称,也可能已经被“烧录”到影像像素中。Clario 使用 Amazon Bedrock 和 Amazon Textract,自动检查数千张 DICOM 切片,同时覆盖 DICOM 元数据与影像内嵌文字,这为大规模医学影像脱敏提供了一条可自动化的路径。
DICOM 脱敏不能只看元数据
DICOM 文件包含结构化标签,例如患者姓名、患者 ID、出生日期、检查日期和机构信息。这些字段适合通过 DICOM 解析器直接读取和匹配规则。
但另一类信息不在标签中,而是出现在像素内容里。放射科工作站可能会把姓名、检查号或时间戳渲染到影像边缘。删除 DICOM 标签并不能清理这些文字,必须对每张切片进行 OCR。
因此,一个完整的检测流程通常包括两条路径:
- 使用 DICOM 解析器遍历元数据标签,识别敏感字段和可疑值。
- 将影像帧转换为 Textract 支持的格式,提取烧录文字,再使用 Bedrock 根据业务策略判断文本是否属于 PHI 或 PII。
这个划分也有助于控制成本:结构化标签可以本地快速扫描,OCR 和语义判断只对需要检查的图像执行。
Bedrock 与 Textract 如何分工
Textract 适合做文字识别。它负责回答“图像中写了什么”,输出文字内容、置信度以及文本位置。
Bedrock 则适合做后续语义判断。它可以根据组织的检测策略,对 OCR 结果进行分类,例如判断某段文本是否像患者姓名、医疗记录号、日期、地址或其他受保护信息。应用不应把模型输出当作唯一事实来源,而应保留原始 OCR 结果、模型判断、置信度和审计信息。
可以把每张切片的结果设计成类似下面的结构:
{
"sop_instance_uid": "1.2.840.example",
"metadata_findings": [
{"tag": "PatientName", "value": "REDACTED", "category": "PHI"}
],
"ocr_text": "Patient ID: A12345",
"pixel_findings": [
{"text": "A12345", "category": "PHI", "confidence": 0.96}
],
"needs_review": false
}
在临床试验场景中,保留这些中间结果很重要。它们既支持人工复核,也便于解释某张切片为什么被标记为风险。
一个可改造的处理示例
下面的 Python 示例展示最小处理链路:读取 DICOM 元数据,把第一帧转换为 PNG,调用 Amazon Textract OCR,再调用 Amazon Bedrock 对 OCR 文本进行分类。示例假设 DICOM 文件能够通过 pydicom 读取像素数据,并且 AWS 凭证、区域和 IAM 权限已经配置好。
运行前安装依赖,并将 DICOM_PATH 改成实际文件路径:
python -m pip install boto3 pydicom pillow numpy
export AWS_REGION=us-east-1
export DICOM_PATH=/data/example.dcm
python detect_phi.py
将下面内容保存为 detect_phi.py。Bedrock 模型 ID 需要替换为目标区域中已启用的模型:
import io
import json
import os
import boto3
import numpy as np
import pydicom
from PIL import Image
REGION = os.getenv("AWS_REGION", "us-east-1")
DICOM_PATH = os.environ["DICOM_PATH"]
MODEL_ID = os.getenv("BEDROCK_MODEL_ID", "anthropic.claude-3-haiku-20240307-v1:0")
textract = boto3.client("textract", region_name=REGION)
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
def metadata_findings(ds):
sensitive_keywords = {
"PatientName", "PatientID", "PatientBirthDate", "PatientAddress",
"InstitutionName", "AccessionNumber", "StudyDate", "SeriesDate"
}
findings = []
for element in ds.iterall():
if element.keyword in sensitive_keywords and element.value not in (None, ""):
findings.append({
"tag": element.keyword,
"value": str(element.value),
"category": "PHI/PII"
})
return findings
def dicom_frame_as_png(ds):
pixels = ds.pixel_array
if pixels.ndim > 2:
pixels = pixels[0]
pixels = pixels.astype(np.float32)
pixels -= pixels.min()
maximum = pixels.max()
if maximum:
pixels = pixels / maximum * 255.0
image = Image.fromarray(pixels.astype(np.uint8)).convert("L")
output = io.BytesIO()
image.save(output, format="PNG")
return output.getvalue()
def ocr(image_bytes):
response = textract.detect_document_text(Document={"Bytes": image_bytes})
return "\n".join(
block["Text"]
for block in response["Blocks"]
if block["BlockType"] == "LINE"
)
def classify_with_bedrock(text):
if not text.strip():
return {"contains_sensitive_data": False, "findings": []}
prompt = (
"Classify possible PHI or PII in the following OCR text. "
"Return JSON only with keys contains_sensitive_data and findings. "
"Each finding must contain text, category, and reason.\n\n"
f"OCR text:\n{text}"
)
response = bedrock.converse(
modelId=MODEL_ID,
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"temperature": 0}
)
answer = response["output"]["message"]["content"][0]["text"]
return json.loads(answer)
def main():
ds = pydicom.dcmread(DICOM_PATH, force=True)
metadata = metadata_findings(ds)
text = ocr(dicom_frame_as_png(ds))
pixel = classify_with_bedrock(text)
result = {
"sop_instance_uid": str(getattr(ds, "SOPInstanceUID", "")),
"metadata_findings": metadata,
"ocr_text": text,
"pixel_findings": pixel,
}
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
这是一个验证思路的最小实现,不是直接用于生产临床数据的脱敏程序。生产环境还需要处理多帧 DICOM、压缩传输语法、窗宽窗位、图像方向、OCR 误识别,以及 Bedrock 返回非严格 JSON 的情况。对于多切片研究,应按 Study、Series 和 SOP Instance 建立任务状态,避免重复处理并支持失败重试。
生产化时要重点控制什么
准确率与人工复核。 OCR 可能把字符 0 识别成 O,也可能漏掉低对比度文字。可以结合 Textract 置信度、Bedrock 分类结果和规则引擎设置复核阈值。高风险数据宁可进入人工队列,也不要只依赖一次模型调用。
数据最小化。 发送到云服务前应明确数据流边界、加密方式、区域选择、访问控制和日志策略。不要把完整患者信息写入普通应用日志。S3 对象、Textract 响应、Bedrock 请求和人工复核记录都应按照组织的保留策略管理。
可审计性。 记录输入文件哈希、DICOM UID、处理版本、模型 ID、OCR 输出、分类结果和最终处置。模型或规则升级后,可以用固定样本集重新评估,而不必凭感觉判断效果是否变好。
性能与成本。 元数据扫描可以批量执行,像素 OCR 则按切片并发,但并发度需要受到服务配额、网络带宽和预算约束。对没有像素文字风险的图像,可以依据设备类型、Series 描述或历史评估结果设计受控跳过策略,但这种优化必须有验证数据支撑。
落地检查清单
- 明确需要检测的 PHI/PII 类别和组织内部规则。
- 分别覆盖 DICOM 标签与像素烧录文字。
- 用固定的已标注样本评估 OCR、分类和脱敏效果。
- 为低置信度、模型解析失败和服务调用失败设计人工或自动重试路径。
- 保存可审计的处理记录,但避免在日志中扩散敏感原文。
- 在正式处理临床试验数据前完成区域、权限、加密和合规评审。
Amazon Bedrock 和 Amazon Textract 的价值不在于替代整个脱敏流程,而在于把文字提取和语义判断接入一个可扩展的流水线。真正可靠的方案,仍然需要 DICOM 规则、图像处理、人工复核、审计记录和合规控制共同构成闭环。