删除 dicom 图像中的像素注释
remove pixel annotations in dicom image
我正在分析医学图像。所有图像都有一个带有位置的标记。看起来像这样
在这张图片中是"TRH RMLO"标注,但在其他图片中可以不同。大小也不同。图像被裁剪,但您会看到组织从右侧开始。
我发现这些标记的存在扭曲了我的分析。
如何删除它们?
我像这样在 python 中加载图像
import dicom
import numpy as np
img = dicom.read_file(my_image.dcm)
img_array = img.pixel_array
图像是一个 numpy 数组。白色文本总是被一个大的黑色区域包围(黑色的值为零)。标记在每个图像中的位置不同。
如何在不损坏组织数据的情况下删除白色文本。
更新
添加了第二张图片
更新2:
这是两个原始的 dicom 文件。所有个人信息已被 removed.edit:删除
好消息是,这些水印可能在一个孤立的全黑区域中,这使得它更容易(尽管是否根据指示的用途删除它是有问题的;license-stuff)。
虽然不是专家,但这里有一个 想法。它可能是针对此问题量身定制的一些非常非常强大的方法的草图,但您必须决定 implementation-complexity 和 algorithmic-complexity(非常依赖于 image-statistics)是否值得:
基本思路
- 检测 semi-cross 样边框 (4)
- 根据这些计算定义的矩形
- Black-out 这个矩形
步骤
0
二值化
1
- 使用一些gradient-based edge-detector得到所有的水平边
- 可能有多个;你可以尝试给出 min-length(也许需要一些形态来连接基于源或算法中的噪声未连接的像素)
2
- 使用一些gradient-based edge-detector得到所有的水平边
- 和上面一样,但是方向不同
3
做一些connected-component计算得到一些垂直和水平线的对象
现在你可以根据以下知识尝试candidate-components(8个真实的)的不同选择
- 这些组件中的两个可以用同一条线描述(slope-intercept 形式;线性回归问题)-> 与矩形接壤的线
- 可能最好的4个pair-chosings(根据linear-regression损失)是这个矩形的有效边界
- 您可以添加假设,即垂直边界和水平边界彼此正交
4
- 从这些边界计算矩形
- 将其加宽几个像素 (hyper-parameter)
- Black-out那个矩形
这是基本方法。
备选
这个工作少得多,使用更专业的工具并假设开头的事实:
- 要删除的内容位于图像的某个全黑部分
- 有点偏僻;到 medical-data 的距离很远
步骤
- 运行 一些通用的 OCR 检测字符
- 以某种方式获取占用的像素/边框(我不确定是什么 OCR 工具 return)
- 计算一些外矩形和black-out(使用一些预定义的widening-gap;这个需要比上面那个大很多)
备选方案 2
仅草图:想法是在图像上使用类似 binary-closing 的东西以某种方式构建完整的 connected-components ouf 源像素(同时填充小的 gaps/holes),这样我们就得到了一个描述 medical-data 的大组件和一个用于水印的组件。然后把小的拿掉。
如果这些注释在 DICOM 文件中,则可以通过多种方式存储它们(参见 )。只需从文件中删除 60xx 组属性即可清除当前支持的方法。
对于已弃用的方法(仍然常用),您可以手动清除未使用的高位注释,而不会弄乱其他图像数据。类似于:
int position = object.getInt( Tag.OverlayBitPosition, 0 );
if( position == 0 ) return;
int bit = 1 << position;
int[] pixels = object.getInts( Tag.PixelData );
int count = 0;
for( int pix : pixels )
{
int overlay = pix & bit;
pixels[ count++ ] = pix - overlay;
}
object.putInts( Tag.PixelData, VR.OW, pixels );
如果这些真的被刻录到图像数据中,您可能无法使用此处的其他建议之一。
我相信这可以优化,但是......您可以创建 4 个大小为 3x3 或 4x4 的补丁,并使用围绕帧的每个角的像素值的确切内容初始化它们注释文本。然后您可以遍历整个图像(或者只在黑色区域进行一些智能初始化)并找到这些补丁的精确匹配。你不太可能在组织中有相同的规则结构(90 度角被接近 0 包围),所以这可能会给你边界框。
查看您提供的图像的实际像素值,您可以看到标记几乎 (99.99%) 是纯白色,而这不会出现在图像的其他地方,因此您可以使用简单的 99.99 将其隔离% 阈值。
我更喜欢 ImageMagick 在 command-line,所以我会这样做:
convert sample.dcm -threshold 99.99% -negate mask.png
convert sample.dcm mask.png -compose darken -composite result.jpg
当然,如果样本图像不具有代表性,您可能还需要更加努力。让我们看看那个...
如果简单的阈值不适用于您的图像,我会查看 "Hit and Miss Morphology"。基本上,您将图像阈值设为纯黑白 - 大约 90%,然后您寻找特定的形状,例如标签上的角标记。所以,如果我们想在黑色背景上寻找白色矩形的 top-left 角,我们使用 0
表示 "this pixel must be black", 1
表示"this pixel must be white",-
表示"we don't care",我们会用这个模式:
0 0 0 0 0
0 1 1 1 1
0 1 - - -
0 1 - - -
0 1 - - -
希望你能在那里看到一个白色矩形的左上角。在终端中会是这样的:
convert sample.dcm -threshold 90% \
-morphology HMT '5x5:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' result.png
现在我们还要寻找top-right、bottom-left和bottom-right角,所以我们需要旋转图案,即ImageMagick 当你添加 >
标志时很容易做到:
convert sample.dcm -threshold 90% \
-morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' result.png
希望您现在可以看到标定标志角的点,所以我们可以请求 ImageMagick 到 trim 所有无关黑色的图像,只留下白色点,然后告诉我们边界框:
cconvert sample.dcm -threshold 90% \
-morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' -format %@ info:
308x198+1822+427
因此,如果我现在在这些坐标周围绘制一个红色框,您可以看到检测到标签的位置 - 当然在实践中我会绘制一个黑色框来覆盖它,但我正在解释这个想法:
convert sample.dcm -fill "rgba(255,0,0,0.5)" -draw "rectangle 1822,427 2130,625" result.png
如果您希望脚本自动执行此操作,我会使用类似的东西,将其保存为 HideMarker
:
#!/bin/bash
input=""
output=""
# Find corners of overlaid marker using Hit and Miss Morphology, then get crop box
IFS="x+" read w h x1 y1 < <(convert "$input" -threshold 90% -morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' -format %@ info:)
# Calculate bottom-right corner from top-left and dimensions
((x1=x1-1))
((y1=y1-1))
((x2=x1+w+1))
((y2=y1+h+1))
convert "$input" -fill black -draw "rectangle $x1,$y1 $x2,$y2" "$output"
然后你会这样做以使其可执行:
chmod +x HideMarker
而运行是这样的:
./HideMarker someImage.dcm result.png
我有另一个想法。此解决方案在 OpenCV 中使用 python。这是一个相当的解决方案。
首先获取图像的二值阈值
ret,th = cv2.threshold(img,2,255, 0)
进行形态学膨胀:
dilate = cv2.morphologyEx(th, cv2.MORPH_DILATE, kernel, 3)
- 为了弥补差距,我使用了中值过滤:
median = cv2.medianBlur(dilate, 9)
现在您可以使用轮廓属性来消除最小的轮廓并保留包含图像的其他轮廓。
它也适用于第二张图片:
更简单的还是可以的!!!
在 (img_array = img.pixel_array)
之后执行以下操作
img_array[img_array > X] = Y
其中X是之后要消除的强度阈值。 Y 也是您要考虑的强度值。
例如:
img_array[img_array > 4000] = 0
用黑色强度 0 替换大于 4000 的白质。
我正在分析医学图像。所有图像都有一个带有位置的标记。看起来像这样
在这张图片中是"TRH RMLO"标注,但在其他图片中可以不同。大小也不同。图像被裁剪,但您会看到组织从右侧开始。 我发现这些标记的存在扭曲了我的分析。
如何删除它们?
我像这样在 python 中加载图像
import dicom
import numpy as np
img = dicom.read_file(my_image.dcm)
img_array = img.pixel_array
图像是一个 numpy 数组。白色文本总是被一个大的黑色区域包围(黑色的值为零)。标记在每个图像中的位置不同。
如何在不损坏组织数据的情况下删除白色文本。
更新
添加了第二张图片
更新2: 这是两个原始的 dicom 文件。所有个人信息已被 removed.edit:删除
好消息是,这些水印可能在一个孤立的全黑区域中,这使得它更容易(尽管是否根据指示的用途删除它是有问题的;license-stuff)。
虽然不是专家,但这里有一个 想法。它可能是针对此问题量身定制的一些非常非常强大的方法的草图,但您必须决定 implementation-complexity 和 algorithmic-complexity(非常依赖于 image-statistics)是否值得:
基本思路
- 检测 semi-cross 样边框 (4)
- 根据这些计算定义的矩形
- Black-out 这个矩形
步骤
0
二值化
1
- 使用一些gradient-based edge-detector得到所有的水平边
- 可能有多个;你可以尝试给出 min-length(也许需要一些形态来连接基于源或算法中的噪声未连接的像素)
2
- 使用一些gradient-based edge-detector得到所有的水平边
- 和上面一样,但是方向不同
3
做一些connected-component计算得到一些垂直和水平线的对象
现在你可以根据以下知识尝试candidate-components(8个真实的)的不同选择
- 这些组件中的两个可以用同一条线描述(slope-intercept 形式;线性回归问题)-> 与矩形接壤的线
- 可能最好的4个pair-chosings(根据linear-regression损失)是这个矩形的有效边界
- 您可以添加假设,即垂直边界和水平边界彼此正交
4 - 从这些边界计算矩形 - 将其加宽几个像素 (hyper-parameter) - Black-out那个矩形
这是基本方法。
备选
这个工作少得多,使用更专业的工具并假设开头的事实:
- 要删除的内容位于图像的某个全黑部分
- 有点偏僻;到 medical-data 的距离很远
步骤
- 运行 一些通用的 OCR 检测字符
- 以某种方式获取占用的像素/边框(我不确定是什么 OCR 工具 return)
- 计算一些外矩形和black-out(使用一些预定义的widening-gap;这个需要比上面那个大很多)
备选方案 2
仅草图:想法是在图像上使用类似 binary-closing 的东西以某种方式构建完整的 connected-components ouf 源像素(同时填充小的 gaps/holes),这样我们就得到了一个描述 medical-data 的大组件和一个用于水印的组件。然后把小的拿掉。
如果这些注释在 DICOM 文件中,则可以通过多种方式存储它们(参见 )。只需从文件中删除 60xx 组属性即可清除当前支持的方法。
对于已弃用的方法(仍然常用),您可以手动清除未使用的高位注释,而不会弄乱其他图像数据。类似于:
int position = object.getInt( Tag.OverlayBitPosition, 0 );
if( position == 0 ) return;
int bit = 1 << position;
int[] pixels = object.getInts( Tag.PixelData );
int count = 0;
for( int pix : pixels )
{
int overlay = pix & bit;
pixels[ count++ ] = pix - overlay;
}
object.putInts( Tag.PixelData, VR.OW, pixels );
如果这些真的被刻录到图像数据中,您可能无法使用此处的其他建议之一。
我相信这可以优化,但是......您可以创建 4 个大小为 3x3 或 4x4 的补丁,并使用围绕帧的每个角的像素值的确切内容初始化它们注释文本。然后您可以遍历整个图像(或者只在黑色区域进行一些智能初始化)并找到这些补丁的精确匹配。你不太可能在组织中有相同的规则结构(90 度角被接近 0 包围),所以这可能会给你边界框。
查看您提供的图像的实际像素值,您可以看到标记几乎 (99.99%) 是纯白色,而这不会出现在图像的其他地方,因此您可以使用简单的 99.99 将其隔离% 阈值。
我更喜欢 ImageMagick 在 command-line,所以我会这样做:
convert sample.dcm -threshold 99.99% -negate mask.png
convert sample.dcm mask.png -compose darken -composite result.jpg
当然,如果样本图像不具有代表性,您可能还需要更加努力。让我们看看那个...
如果简单的阈值不适用于您的图像,我会查看 "Hit and Miss Morphology"。基本上,您将图像阈值设为纯黑白 - 大约 90%,然后您寻找特定的形状,例如标签上的角标记。所以,如果我们想在黑色背景上寻找白色矩形的 top-left 角,我们使用 0
表示 "this pixel must be black", 1
表示"this pixel must be white",-
表示"we don't care",我们会用这个模式:
0 0 0 0 0
0 1 1 1 1
0 1 - - -
0 1 - - -
0 1 - - -
希望你能在那里看到一个白色矩形的左上角。在终端中会是这样的:
convert sample.dcm -threshold 90% \
-morphology HMT '5x5:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' result.png
现在我们还要寻找top-right、bottom-left和bottom-right角,所以我们需要旋转图案,即ImageMagick 当你添加 >
标志时很容易做到:
convert sample.dcm -threshold 90% \
-morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' result.png
希望您现在可以看到标定标志角的点,所以我们可以请求 ImageMagick 到 trim 所有无关黑色的图像,只留下白色点,然后告诉我们边界框:
cconvert sample.dcm -threshold 90% \
-morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' -format %@ info:
308x198+1822+427
因此,如果我现在在这些坐标周围绘制一个红色框,您可以看到检测到标签的位置 - 当然在实践中我会绘制一个黑色框来覆盖它,但我正在解释这个想法:
convert sample.dcm -fill "rgba(255,0,0,0.5)" -draw "rectangle 1822,427 2130,625" result.png
如果您希望脚本自动执行此操作,我会使用类似的东西,将其保存为 HideMarker
:
#!/bin/bash
input=""
output=""
# Find corners of overlaid marker using Hit and Miss Morphology, then get crop box
IFS="x+" read w h x1 y1 < <(convert "$input" -threshold 90% -morphology HMT '5x5>:0,0,0,0,0 0,1,1,1,1 0,1,-,-,- 0,1,-,-,- 0,1,-,-,-' -format %@ info:)
# Calculate bottom-right corner from top-left and dimensions
((x1=x1-1))
((y1=y1-1))
((x2=x1+w+1))
((y2=y1+h+1))
convert "$input" -fill black -draw "rectangle $x1,$y1 $x2,$y2" "$output"
然后你会这样做以使其可执行:
chmod +x HideMarker
而运行是这样的:
./HideMarker someImage.dcm result.png
我有另一个想法。此解决方案在 OpenCV 中使用 python。这是一个相当的解决方案。
首先获取图像的二值阈值
ret,th = cv2.threshold(img,2,255, 0)
进行形态学膨胀:
dilate = cv2.morphologyEx(th, cv2.MORPH_DILATE, kernel, 3)
- 为了弥补差距,我使用了中值过滤:
median = cv2.medianBlur(dilate, 9)
现在您可以使用轮廓属性来消除最小的轮廓并保留包含图像的其他轮廓。
它也适用于第二张图片:
更简单的还是可以的!!!
在 (img_array = img.pixel_array)
之后执行以下操作img_array[img_array > X] = Y
其中X是之后要消除的强度阈值。 Y 也是您要考虑的强度值。
例如: img_array[img_array > 4000] = 0
用黑色强度 0 替换大于 4000 的白质。