From 1dd429bb745bce00b87b6719fda90444ef5375c4 Mon Sep 17 00:00:00 2001 From: Nick Tustison Date: Tue, 23 Apr 2024 16:42:13 -0700 Subject: [PATCH] ENH: Add TB dx. --- R/chexnet.R | 79 ++++++++++++++++++++++++++----------- R/getPretrainedNetwork.R | 2 + man/cerebellumMorphology.Rd | 3 +- man/chexnet.Rd | 11 +++++- man/deepFlash.Rd | 3 +- man/getPretrainedNetwork.Rd | 7 ++-- 6 files changed, 74 insertions(+), 31 deletions(-) diff --git a/R/chexnet.R b/R/chexnet.R index 43501cca..0b59d9b0 100644 --- a/R/chexnet.R +++ b/R/chexnet.R @@ -166,8 +166,14 @@ checkXrayLungOrientation <- function( image, #' template and model weights. Since these can be resused, if #' \code{is.null(antsxnetCacheDirectory)}, these data will be downloaded to the #' subdirectory ~/.keras/ANTsXNet/. +#' @param useANTsXNetVariant Use an extension of the original chexnet approach +#' by adding a left/right lung masking and including those two masked regions +#' in the red and green channels, respectively. +#' @param includeTuberculosisDiagnosis Include the output of an additional network +#' trained on TB data but using the ANTsXNet variant chexnet data as the initial +#' weights. #' @param verbose print progress. -#' @return classification scores for each of the 14 categories. +#' @return classification scores for each of the 14 or 15 categories. #' @author Tustison NJ #' @examples #' \dontrun{ @@ -179,6 +185,7 @@ chexnet <- function( image, lungMask = NULL, checkImageOrientation = FALSE, useANTsXNetVariant = TRUE, + includeTuberculosisDiagnosis = FALSE, antsxnetCacheDirectory = NULL, verbose = FALSE ) { @@ -227,12 +234,52 @@ chexnet <- function( image, resampledImage <- antsImageClone( image ) } + if( is.null( lungMask ) ) + { + if( verbose ) + { + cat( "No lung mask provided. Estimating using antsxnet." ) + } + lungExtract <- lungExtraction( image, modality = "xray", + antsxnetCacheDirectory = antsxnetCacheDirectory, + verbose = verbose ) + lungMask <- lungExtract$segmentationImage + } + + if( any( dim( lungMask ) != imageSize ) ) + { + resampledLungMask <- resampleImage( lungMask, imageSize, useVoxels = TRUE, interpType = 1 ) + } else { + resampledLungMask <- antsImageClone( lungMask ) + } + # use imagenet mean,std for normalization imagenetMean <- c( 0.485, 0.456, 0.406 ) imagenetStd <- c( 0.229, 0.224, 0.225 ) numberOfChannels <- 3 + tbPrediction <- NULL + if( includeTuberculosisDiagnosis ) + { + modelFileName <- getPretrainedNetwork( "tb_antsxnet_model", + antsxnetCacheDirectory = antsxnetCacheDirectory ) + model <- tensorflow::tf$keras$models$load_model( modelFileName, compile = FALSE ) + + batchX <- array( data = 0, dim = c( 1, imageSize, numberOfChannels ) ) + imageArray <- as.array( resampledImage ) + imageArray <- ( imageArray - min( imageArray ) ) / ( max( imageArray ) - min( imageArray ) ) + + batchX[1,,,1] <- ( imageArray - imagenetMean[1] ) / ( imagenetStd[1] ) + batchX[1,,,2] <- ( imageArray - imagenetMean[2] ) / ( imagenetStd[2] ) + batchX[1,,,2] <- batchX[1,,,2] * as.array( thresholdImage( resampledLungMask, 1, 1, 1, 0 ) ) + batchX[1,,,3] <- ( imageArray - imagenetMean[3] ) / ( imagenetStd[3] ) + batchX[1,,,3] <- batchX[1,,,3] * as.array( thresholdImage( resampledLungMask, 2, 2, 1, 0 ) ) + + batchY <- model %>% predict( batchX, verbose = verbose ) + tbPrediction <- batchY[1] + } + if( ! useANTsXNetVariant ) { modelFileName <- getPretrainedNetwork( "chexnetClassificationModel", @@ -250,7 +297,10 @@ chexnet <- function( image, batchY <- model %>% predict( batchX, verbose = verbose ) diseaseCategoryDf = data.frame( batchY ) colnames( diseaseCategoryDf ) <- diseaseCategories - + if( includeTuberculosisDiagnosis ) + { + diseaseCategoryDf$Tuberculosis <- c( tbPrediction ) + } return( diseaseCategoryDf ) } else { @@ -258,26 +308,6 @@ chexnet <- function( image, antsxnetCacheDirectory = antsxnetCacheDirectory ) model <- tensorflow::tf$keras$models$load_model( modelFileName, compile = FALSE ) - resampledLungMask <- NULL - if( is.null( lungMask ) ) - { - if( verbose ) - { - cat( "No lung mask provided. Estimating using antsxnet." ) - } - lungExtract <- lungExtraction( image, modality = "xray", - antsxnetCacheDirectory = antsxnetCacheDirectory, - verbose = verbose ) - resampledLungMask <- lungExtract$segmentationImage - } else { - resampledLungMask <- antsImageClone( lungMask ) - } - - if( any( dim( resampledLungMask ) != imageSize ) ) - { - resampledLungMask <- resampleImage( resampledLungMask, imageSize, useVoxels = TRUE, interpType = 1 ) - } - batchX <- array( data = 0, dim = c( 1, imageSize, numberOfChannels ) ) imageArray <- as.array( resampledImage ) imageArray <- ( imageArray - min( imageArray ) ) / ( max( imageArray ) - min( imageArray ) ) @@ -291,7 +321,10 @@ chexnet <- function( image, batchY <- model %>% predict( batchX, verbose = verbose ) diseaseCategoryDf = data.frame( batchY ) colnames( diseaseCategoryDf ) <- diseaseCategories - + if( includeTuberculosisDiagnosis ) + { + diseaseCategoryDf$Tuberculosis <- c( tbPrediction ) + } return( diseaseCategoryDf) } } diff --git a/R/getPretrainedNetwork.R b/R/getPretrainedNetwork.R index 4fc1a815..c914434b 100644 --- a/R/getPretrainedNetwork.R +++ b/R/getPretrainedNetwork.R @@ -126,6 +126,7 @@ getPretrainedNetwork <- function( "xrayLungExtraction", "chexnetClassificationModel", "chexnetClassificationANTsXNetModel", + "tb_antsxnet_model", "wholeHeadInpaintingFLAIR", "wholeHeadInpaintingPatchBasedT1", "wholeHeadInpaintingPatchBasedFLAIR", @@ -248,6 +249,7 @@ getPretrainedNetwork <- function( xrayLungExtraction = "https://figshare.com/ndownloader/files/41965818", chexnetClassificationModel = "https://figshare.com/ndownloader/files/42460332", chexnetClassificationANTsXNetModel = "https://figshare.com/ndownloader/files/42460335", + tb_antsxnet_model = "https://figshare.com/ndownloader/files/45820599", wholeHeadInpaintingT1 = "https://figshare.com/ndownloader/files/39255422", wholeHeadInpaintingFLAIR = "https://figshare.com/ndownloader/files/39255419", wholeHeadInpaintingPatchBasedT1 = "https://figshare.com/ndownloader/files/39337442", diff --git a/man/cerebellumMorphology.Rd b/man/cerebellumMorphology.Rd index e72af096..83e43af9 100644 --- a/man/cerebellumMorphology.Rd +++ b/man/cerebellumMorphology.Rd @@ -41,8 +41,7 @@ Perform cerebellum segmentation using a modification of the set of MaGET cerebellum atlases --- \url{https://www.cobralab.ca/cerebellum-lobules}. } \details{ -Current citation: -\url{https://www.medrxiv.org/content/10.1101/2023.01.17.23284693v4} +\url{https://www.nature.com/articles/s41598-024-59440-6} The tissue labeling is as follows: \itemize{ diff --git a/man/chexnet.Rd b/man/chexnet.Rd index 1ae3bbb9..0cdbcf68 100644 --- a/man/chexnet.Rd +++ b/man/chexnet.Rd @@ -9,6 +9,7 @@ chexnet( lungMask = NULL, checkImageOrientation = FALSE, useANTsXNetVariant = TRUE, + includeTuberculosisDiagnosis = FALSE, antsxnetCacheDirectory = NULL, verbose = FALSE ) @@ -21,6 +22,14 @@ chexnet( \item{checkImageOrientation}{Check the correctness of image orientation, i.e., flipped left-right, up-down, or both. If TRUE, attempts to correct before prediction.} +\item{useANTsXNetVariant}{Use an extension of the original chexnet approach +by adding a left/right lung masking and including those two masked regions +in the red and green channels, respectively.} + +\item{includeTuberculosisDiagnosis}{Include the output of an additional network +trained on TB data but using the ANTsXNet variant chexnet data as the initial +weights.} + \item{antsxnetCacheDirectory}{destination directory for storing the downloaded template and model weights. Since these can be resused, if \code{is.null(antsxnetCacheDirectory)}, these data will be downloaded to the @@ -29,7 +38,7 @@ subdirectory ~/.keras/ANTsXNet/.} \item{verbose}{print progress.} } \value{ -classification scores for each of the 14 categories. +classification scores for each of the 14 or 15 categories. } \description{ ANTsXNet reproduction of https://arxiv.org/pdf/1711.05225.pdf. This includes diff --git a/man/deepFlash.Rd b/man/deepFlash.Rd index e074e5c3..58dd2080 100644 --- a/man/deepFlash.Rd +++ b/man/deepFlash.Rd @@ -39,8 +39,7 @@ Perform hippocampal/entorhinal segmentation in T1 and T1/T2 images using labels from Mike Yassa's lab---\url{https://faculty.sites.uci.edu/myassa/} } \details{ -Current citation: -\url{https://www.medrxiv.org/content/10.1101/2023.01.17.23284693v4} +\url{https://www.nature.com/articles/s41598-024-59440-6} The labeling is as follows: \itemize{ diff --git a/man/getPretrainedNetwork.Rd b/man/getPretrainedNetwork.Rd index ca2f54d8..d2f592c0 100644 --- a/man/getPretrainedNetwork.Rd +++ b/man/getPretrainedNetwork.Rd @@ -46,9 +46,10 @@ getPretrainedNetwork( "sysuMediaWmhFlairT1Model2", "tidsQualityAssessment", "xrayLungOrientation", "xrayLungExtraction", "chexnetClassificationModel", - "chexnetClassificationANTsXNetModel", "wholeHeadInpaintingFLAIR", - "wholeHeadInpaintingPatchBasedT1", "wholeHeadInpaintingPatchBasedFLAIR", - "wholeTumorSegmentationT2Flair", "wholeLungMaskFromVentilation"), + "chexnetClassificationANTsXNetModel", "tb_antsxnet_model", + "wholeHeadInpaintingFLAIR", "wholeHeadInpaintingPatchBasedT1", + "wholeHeadInpaintingPatchBasedFLAIR", "wholeTumorSegmentationT2Flair", + "wholeLungMaskFromVentilation"), targetFileName, antsxnetCacheDirectory = NULL )