choosehappy / HistoQC

HistoQC is an open-source quality control tool for digital pathology slides
BSD 3-Clause Clear License
266 stars 106 forks source link

Issue with bright/dark area removal somehow affecting the spur step #172

Closed asmagen closed 4 years ago

asmagen commented 4 years ago

I was trying to exclude the background of the slide better using the bright removal step LightDarkModule.getIntensityThresholdPercent:tissue and the dark tissue folds using the dark tissue detection step LightDarkModule.getIntensityThresholdPercent:darktissue. The issue is that playing with these parameters somehow affects the output of BasicModule.finalProcessingSpur significantly, where the spur is removing huge parts of the actual tissue. Here's an example of modifying the bright parameter upper_threshold from .85 to 0.75, showing that it is indeed improving the exclusion of the slide background (in the middle right between the two tissues), but the spur is also removing too much although the description of spur filter doesn't seem to have anything related to this behavior.

Bright .85: Li57TCD68 ndpi_bright LightDarkModule getIntensityThresholdPercent tissue upper_threshold  85 Spur (not changing disk_radius: 10 default param): Li57TCD68 ndpi_spur LightDarkModule getIntensityThresholdPercent tissue upper_threshold  85 Bright .75: Li57TCD68 ndpi_bright LightDarkModule getIntensityThresholdPercent tissue upper_threshold  75 Spur (not changing disk_radius: 10 default param): Li57TCD68 ndpi_spur LightDarkModule getIntensityThresholdPercent tissue upper_threshold  75

choosehappy commented 4 years ago

what does the original image look like?

choosehappy commented 4 years ago

also, what does your config file look like? this pattern seems more related to the removeFatlikeTissue module and not the finalProcessingSpur module

asmagen commented 4 years ago

Here it is: stain_example

And the config, based on the config example here with minor modifications:

[pipeline]
steps= BasicModule.getBasicStats
    ClassificationModule.byExampleWithFeatures:coverslip_edge
    LightDarkModule.getIntensityThresholdPercent:tissue
    BubbleRegionByRegion.detectSmoothness
    #MorphologyModule.fillSmallHoles
    MorphologyModule.removeSmallObjects
    BlurDetectionModule.identifyBlurryRegions
    BasicModule.finalProcessingSpur
    BasicModule.finalProcessingArea
    HistogramModule.compareToTemplates
    HistogramModule.getHistogram
    BrightContrastModule.getContrast
    BrightContrastModule.getBrightnessGray
    BrightContrastModule.getBrightnessByChannelinColorSpace:RGB
    BrightContrastModule.getBrightnessByChannelinColorSpace:YUV
    DeconvolutionModule.seperateStains
    SaveModule.saveFinalMask
    SaveModule.saveThumbnails
    BasicModule.finalComputations

[BaseImage.BaseImage]
image_work_size = 1.25x

#not yet implemented
confirm_base_mag: False

#three options: relative2mask, absolute, relative2image
mask_statistics = relative2mask

[BasicModule.getBasicStats]
image_work_size = 1.25x

area_threshold: 100
features:  frangi
           laplace
           rgb
           #lbp
           #gabor
           #median
           #gaussian

laplace_ksize: 3

frangi_scale_range: (1,10)
frangi_scale_step: 2
frangi_beta1: .5
frangi_beta2: 15
frangi_black_ridges: True

gabor_theta: 4
gabor_sigma: (1,3)
gabor_frequency: (0.05, 0.25)

lbp_radius: 3
lbp_points: 24
lbp_method: default

median_disk_size: 3

#gaussian_sigma: 1
#gaussian_multichan: False

[ClassificationModule.byExampleWithFeatures:coverslip_edge]
name: coverslip_edge
threshold: 0.9

examples: ./models/coverslip_edge_he/coverslip_edge.png:./models/coverslip_edge_he/coverslip_edge_mask.png

area_threshold: 15
features:  frangi
           laplace
           rgb

dilate_kernel_size: 5

[LightDarkModule.getIntensityThresholdPercent:bubble]
name: bubble
upper_threshold: .94
lower_threshold: .82
upper_variance: 11
invert: true

[LightDarkModule.getIntensityThresholdPercent:tissue]
name: bright
upper_threshold: .85
lower_var: 10

[LightDarkModule.getIntensityThresholdPercent:darktissue]
name: dark
upper_threshold: .25
invert: true

[LightDarkModule.getTissuePercent]
threshold: .75

[LightDarkModule.getDarkTissuePercent]
threshold: .5

[MorphologyModule.removeSmallObjects]
min_size: 250

[MorphologyModule.removeFatlikeTissue]
kernel_size: 10
max_keep_size: 1000
fat_cell_size: 64

[MorphologyModule.fillSmallHoles]
min_size: 1000

[HistogramModule.compareToTemplates]
limit_to_mask: True
bins: 20
templates= ./templates/template1.png
           ./templates/template2.png
           ./templates/template3.png
           ./templates/template4.png

[HistogramModule.getHistogram]
limit_to_mask: True
bins: 20

[BrightContrastModule.getContrast]
limit_to_mask: True

[BrightContrastModule.getBrightnessGray]
limit_to_mask: True

[BrightContrastModule.getBrightnessByChannelinColorSpace:RGB]
limit_to_mask: True

[BrightContrastModule.getBrightnessByChannelinColorSpace:YUV]
limit_to_mask: True
#pick a color space in the list from 'RGB', 'HSV', 'RGB CIE', 'XYZ', 'YUV', 'YIQ', 'YPbPr', 'YCbCr'  : http://scikit-image.org/docs/dev/api/skimage.color.html#skimage.color.convert_colorspace
to_color_space: YUV

[SaveModule.saveFinalMask]
overlay: True

[SaveModule.saveThumbnails]
image_work_size: 1.25x
small_dim: 500

[BlurDetectionModule.identifyBlurryRegions]
image_work_size = 2.5x
blur_radius: 50
blur_threshold: .05

[BasicModule.finalComputations]

[BasicModule.finalProcessingSpur]
disk_radius: 10

[BasicModule.finalProcessingArea]
#area_threshold: 90000
area_threshold:  10000

[DeconvolutionModule.seperateStains]
;hed_from_rgb: Hematoxylin + Eosin + DAB
;hdx_from_rgb: Hematoxylin + DAB
;fgx_from_rgb: Feulgen + Light Green
;bex_from_rgb: Giemsa stain : Methyl Blue + Eosin
;rbd_from_rgb: FastRed + FastBlue + DAB
;gdx_from_rgb: Methyl Green + DAB
;hax_from_rgb: Hematoxylin + AEC
;bro_from_rgb: Blue matrix Anilline Blue + Red matrix Azocarmine + Orange matrix Orange-G
;bpx_from_rgb: Methyl Blue + Ponceau Fuchsin
;ahx_from_rgb: Alcian Blue + Hematoxylin
;hpx_from_rgb: Hematoxylin + PAS
stain: hed_from_rgb
use_mask: True

[BubbleRegionByRegion.detectSmoothness]
threshold: .01
kernel_size: 10
min_object_size: 500
choosehappy commented 4 years ago

can you take a screenshot of all the thumbnails created for this image, for the Bright = .75 setting? or alternatively, can you share the WSI? its not obvious to me why this is happening

asmagen commented 4 years ago

You mean the output from each step? Here it is: Li57TCD68 ndpi_fuse Li57TCD68 ndpi_mask_use Li57TCD68 ndpi_deconv_c2 Li57TCD68 ndpi_deconv_c1 Li57TCD68 ndpi_deconv_c0 Li57TCD68 ndpi_hist Li57TCD68 ndpi_areathresh Li57TCD68 ndpi_spur Li57TCD68 ndpi_blurry Li57TCD68 ndpi_small_remove Li57TCD68 ndpi_flat Li57TCD68 ndpi_bright Li57TCD68 ndpi_coverslip_edge

choosehappy commented 4 years ago

got it, just realized you commented out "MorphologyModule.fillSmallHoles".

what does the output look like when this is used?

because of the lower threshold, you can see a lot more small islands of pixels inside of result from LightDarkModule.getIntensityThresholdPercent:tissue

since these holes aren't being filled by the fillsmallholes module, the spur module identifies them as potential places for trimming, and then performs the trimming

asmagen commented 4 years ago

I see. I wanted to avoid using that because I wanted the mask to ignore empty areas of any size since I want it to represent the precise tissue cellular area, but I guess filling very small holes isn't going to make much difference. If there is a way to maintain the small holes I would be glad to use it.

choosehappy commented 4 years ago

completely understand

if you look at this section of the config:

[MorphologyModule.fillSmallHoles] min_size: 1000

this parameter is equal to the "area_threshold" parameter used here:

https://scikit-image.org/docs/dev/api/skimage.morphology.html#skimage.morphology.remove_small_holes

Basically: The maximum area, in pixels, of a contiguous hole that will be filled. Replaces min_size.

Although unintuitive, it describes the maximum size hole which will be filled in pixel size. looking at your image, you can likely get away with something on the order of 10

On Thu, Apr 16, 2020 at 2:30 PM Assaf Magen notifications@github.com wrote:

I see. I wanted to avoid using that because I wanted the mask to ignore empty areas of any size since I want it to represent the precise tissue cellular area, but I guess filling very small holes isn't going to make much difference. If there is a way to maintain the small holes I would be glad to use it.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/choosehappy/HistoQC/issues/172#issuecomment-614622159, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACJ3XTEN2A4ZQJJWX56IPWLRM3245ANCNFSM4MIEAZIA .

asmagen commented 4 years ago

Yeah, that's the threshold I ended up using. Thanks