首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >忽略边界的彩色反褶积后的正像素识别

忽略边界的彩色反褶积后的正像素识别
EN

Stack Overflow用户
提问于 2020-04-08 01:37:55
回答 4查看 583关注 0票数 2

我正在分析组织学组织图像,用一个特定的蛋白质标记染色,我想识别该标记的阳性像素。我的问题是,在图像上进行阈值处理会产生太多的假阳性,这是我想要排除的。

我正在使用颜色反褶积(separate_stainsskimage.color)来获得AEC通道(对应于红色标记),将它从背景(苏木精蓝色)中分离出来,并使用cv2 Otsu阈值来使用cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)来识别正像素,但它也是在提取组织边界(见示例图片中的白线,有时甚至有白色以外的随机颜色),有时甚至是非阳性细胞(示例图片中的蓝色区域)。它也缺少了一些微弱的正像素,我想要捕捉。

总体上:(1)如何过滤假阳性组织边界和蓝色像素?以及(2)如何调整Otsu阈值以捕捉微弱的红色正向?

添加一个修改后的示例图像-

  1. 在使用HistoQC识别组织区域并在组织上应用它识别的掩膜后,左上方的原始图像使得所有的非组织区域都是黑色的。我应该调整它的参数,以排除折叠的组织区域,这些区域看起来更暗(向这个图像的左下角)。欢迎对其他识别组织区域的工具提出建议。
  2. 反褶积后右上角苏木精
  3. 反褶积后左下角AEC
  4. 右下角Otsu阈值不应用原始RGB图像,只试图捕获AEC阳性像素,但也显示假阳性和假阴性。

谢谢

EN

回答 4

Stack Overflow用户

发布于 2020-04-19 07:48:04

有几个问题会导致不正确的量化。我将详细介绍如何推荐您处理这些幻灯片。

我使用DIPlib,因为我最熟悉它(我是一个作者)。它有Python绑定,我在这里使用,可以与pip install diplib一起安装。但是,所有这些都不是复杂的图像处理,您应该能够与其他库进行类似的处理。

加载图像

这里没有什么特别之处,除了图像有很强的JPEG压缩伪影,这会干扰污迹分解。我们用一个小高斯滤波器对图像进行平滑处理,对处理过程有一点帮助。

代码语言:javascript
运行
复制
import diplib as dip
import numpy as np

image = dip.ImageRead('example.png')
image = dip.Gauss(image, [1]) # because of the severe JPEG compression artifacts

染色解混

[个人注意:我发现很不幸的是,http://helios.mi.parisdescartes.fr/%7Elomn/Data/2017/Color/Quantification_of_histochemical_staining.pdf的作者Ruifrok和Johnston称它为“反褶积”,因为这个词在图像处理中已经有了既定的含义,特别是与显微镜相结合的含义。我总是把这称为“污点分解”,而不是“反褶积”。

这应该始终是任何尝试量化从bightfield图像量化的第一步。这里需要确定三个重要的RGB三重奏:背景的RGB值(即光源的亮度)和每个色斑的RGB值。分解过程有两个组成部分:

首先,我们应用啤酒-兰伯特映射。这种映射是非线性的.它将透射光(如显微镜所记录的)转换为吸光度值。吸光度表示幻灯片上的每一点吸收不同波长的光的强度。这些污渍吸收光,并不同的相对吸光度在每一个R,G和B通道的相机。

代码语言:javascript
运行
复制
background_intensity = [209, 208, 215]
image = dip.BeerLambertMapping(image, background_intensity)

我手动确定了背景强度,但是如果你有完整的幻灯片图像,你可以很好地自动处理:在整个幻灯片图像中,图像的边缘总是与背景相对应的,所以你可以在那里寻找强度。

第二步是实际拆解。吸光度的混合是一个线性过程,因此解混合是在每个像素处求解一组线性方程组。为此,我们需要知道每个通道中每个污迹的吸光度值。使用标准值(如在skimage.color.hax_from_rgb中)可能会给出一个很好的第一近似,但很少会提供最佳的量化。

染色颜色因化验而异(例如,苏木精有不同的颜色,取决于制作它的人、什么组织被染色等),也取决于用来拍摄幻灯片的照相机(每种型号都有不同的RGB滤镜)。确定这些颜色的最佳方法是为每一种染色准备一张幻灯片,使用所有相同的方法,但不使用其他染料。从这些幻灯片中,您可以很容易地获得对您的分析和幻灯片扫描仪有效的染色颜色。然而,在实践中很少这样做。

一个更实际的解决方案是从幻灯片本身估计颜色。通过在幻灯片上找到一个点,您可以单独看到每个污迹(其中的污迹不是混合的),就可以手动确定相当好的值。可以自动确定适当的值,但要复杂得多,很难找到现有的实现。有几篇论文展示了如何在具有稀疏约束的非负矩阵因式分解中实现这一点,而IMO是我们现有的最佳方法。

代码语言:javascript
运行
复制
hematoxylin_color = np.array([0.2712, 0.2448, 0.1674])
hematoxylin_color = (hematoxylin_color/np.linalg.norm(hematoxylin_color)).tolist()
aec_color = np.array([0.2129, 0.2806, 0.4348])
aec_color = (aec_color/np.linalg.norm(aec_color)).tolist()
stains = dip.UnmixStains(image, [hematoxylin_color, aec_color])
stains = dip.ClipLow(stains, 0) # set negative values to 0
hematoxylin = stains.TensorElement(0)
aec = stains.TensorElement(1)

注意线性解混合如何导致负值。这是由于不正确的颜色矢量、噪声、JPEG伪影和幻灯片上吸收光的东西,而这些不是我们定义的两种颜色。

组织面积识别

您已经有了一个很好的方法,可以应用到原始的RGB映像中。但是,在进行上述分解之前,不要将掩码应用于原始图像,将掩码保持为单独的图像。我写了下一段基于苏木精染色的组织区域的代码。这不是很好,也不难改进,但我不想浪费太多的时间在这里。

代码语言:javascript
运行
复制
tissue = dip.MedianFilter(hematoxylin, dip.Kernel(5))
tissue = dip.Dilation(tissue, [20])
tissue = dip.Closing(tissue, [50])
area = tissue > 0.2

组织褶皱识别

你也在问这一步。组织皱褶在图像中通常以较大的深色区域出现。找到一种自动识别它们的方法并不容易,因为许多其他的东西也会在图像中产生更暗的区域。手动注释是一个很好的开始,如果您收集了足够多的手工注释示例,您可以训练一个深度学习模型来帮助您。我这样做只是作为一个位置持有人,再次它不是很好,并确定一些积极的区域为褶皱。从组织面罩中减去褶皱。

代码语言:javascript
运行
复制
folds = dip.Gauss(hematoxylin - aec, [20])
area -= folds > 0.2

识别正像素

为此使用固定的阈值是很重要的。只有病理学家才能告诉你阈值应该是什么,它们是构成积极和消极的黄金标准。

请注意,幻灯片必须是按照相同的协议编写的。在临床环境中,这是相对容易的,因为所使用的检测是标准化和验证的,并产生已知的,有限的染色变化。在实验环境中,检测不那么严格的控制,你可能会看到更多的变化染色质量。不幸的是,你甚至会看到染色颜色的变化。您至少可以使用自动阈值处理方法来获取一些数据,但是会有一些您无法控制的偏差。我不认为有出路:不一致的污点,不一致的数据出来。

使用基于图像内容的方法,例如Otsu,会使阈值因样本而异。例如,在正像素较少的样本中,阈值会比其他样本低,从而导致对正向百分比的相对高估。

代码语言:javascript
运行
复制
positive = aec > 0.1 # pick a threshold according to pathologist's idea what is positive and what is not

pp = 100 * dip.Count(dip.And(positive, area)) / dip.Count(area)
print("Percent positive:", pp)

我在这个样本中得到了1.35%。请注意,%正像素不一定与%正像素相关,不应用作替代。

票数 2
EN

Stack Overflow用户

发布于 2020-05-15 23:31:22

@cris-luengo感谢您对scikit图像的投入!我是核心开发人员之一,基于@assafb输入,我们试图在color/colorconv/separate_stains上重写代码。

@Assafb:负log10变换是Beer映射。在这段代码中,我不明白的是行rgb += 2。我不知道那是从哪里来的,也不知道他们为什么要用它。我百分之百肯定这是错误的。我想他们是在努力避免log10(0),但应该采取不同的做法。不过,我敢打赌这就是你负价值的来源。

是的,显然(我不是这段代码的原作者)我们使用rgb += 2来避免log10(0)。我检查了斐济的彩色反褶积插件,他们在输入中添加了1。我测试了几个输入数字来帮助解决这个问题,而~2将使我们更接近预期的结果。

@Assafb:将skimage中的实现与原始文件中所描述的进行比较。您将看到实现中出现了几个错误,最重要的是缺少了最大强度的除法。他们应该使用-np.log10(rgb/255) (假设255个是光照强度),比-np.log10(rgb)更重要。

我们的输入数据是浮动的;在这种情况下,最大强度是1。我要说,这就是我们没有除以某物的原因。

此外,我还打开了一篇关于科学图片的文章讨论了这些问题,并指定了解决方案。我已经做了一些研究--我甚至检查了DIPlib的文档-,并实现了这个特定功能的不同版本。然而,污渍不是我的主要专业领域,如果您能帮助评估代码--也许可以提供更好的解决方案--我们会很高兴的。再次感谢您的帮助!

票数 2
EN

Stack Overflow用户

发布于 2020-04-18 17:39:55

最后,我将Chris提供的一些反馈意见纳入了以下可能的非常规解决方案,我希望得到反馈(针对以下具体问题,但也包括关于改进或更有效/更准确的工具或策略的一般性建议):

  1. 在优化HistoQC脚本后定义(但尚未应用)组织掩膜(HistoQC),以便在不去除正常组织区域的情况下尽可能去除组织皱褶
  2. 利用hax_from_rgb对原始RGB图像进行反褶积
  3. 使用应该对应于红色染色像素的第二个通道,并从中减去第三个通道,据我所见,第三个通道对应于图像的背景非红色/蓝色像素。此步骤移除第二通道中的高值,因为组织折叠或其他在第一步中未被移除的伪影(第三通道对应于什么?RGB的绿色元素
  4. 模糊调整后的图像和阈值,基于图像的中值加上20 (半任意,但它工作。有没有更好的选择?Otsu在这里根本不工作)
  5. 在阈值图像上应用组织区域掩膜,只产生红色/红色的正像素,而不产生非组织区域。
  6. 计算相对于组织掩膜区域的正像素百分比。

我一直在尝试应用,如上所述,组织面膜上的反褶积红色通道输出,然后使用Otsu阈值。但是它失败了,因为应用组织区域掩膜产生的黑色背景使得Otsu阈值检测到整个组织为阳性。因此,我开始在调整后的红色通道上应用阈值,然后在计数正像素之前应用组织掩膜。我有兴趣了解我在这里做错了什么。

除此之外,LoG转换似乎不太有效,因为它产生了许多拉伸明亮的片段,而不仅仅是细胞所在的圆形圆点。我不知道为什么会这样。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61092050

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档