Skip to content

Latest commit

 

History

History
337 lines (300 loc) · 12.2 KB

Blood-Cell-Detection.md

File metadata and controls

337 lines (300 loc) · 12.2 KB

Download images and annotations: BCCD dataset.

Split dataset into train, validation and test set

library(tidyverse)
library(platypus)
library(abind)
library(here)

BCCD_path <- here("development/BCCD/")
annot_path <- file.path(BCCD_path, "Annotations/")
images_path <- file.path(BCCD_path, "JPEGImages/")

c("train", "valid", "test") %>%
  walk(~ {
    dir.create(file.path(BCCD_path, .))
    dir.create(file.path(BCCD_path, ., "Annotations/"))
    dir.create(file.path(BCCD_path, ., "JPEGImages/"))
  })

annot_paths <- list.files(annot_path, full.names = TRUE)
images_paths <- list.files(images_path, full.names = TRUE)
n_samples <- length(annot_paths)
set.seed(111)
train_ids <- sample(1:n_samples, round(0.8 * n_samples))
valid_ids <- sample(setdiff(1:n_samples, train_ids), round(0.19 * n_samples))
test_ids <- setdiff(1:n_samples, c(train_ids, valid_ids))

walk2(c("train", "valid", "test"), list(train_ids, valid_ids, test_ids), ~ {
  annots <- annot_paths[.y]
  images <- images_paths[.y]
  dir_name <- .x
  annots %>% walk(~ file.copy(., gsub("BCCD", paste0("BCCD/", dir_name), .)))
  images %>% walk(~ file.copy(., gsub("BCCD", paste0("BCCD/", dir_name), .)))
})

Generate custom anchor boxes:

blood_labels <- c("Platelets", "RBC", "WBC")
n_class <- length(blood_labels)
net_h <- 416 # Must be divisible by 32
net_w <- 416 # Must be divisible by 32
anchors_per_grid <- 3

blood_anchors <- generate_anchors(
  anchors_per_grid = anchors_per_grid, # Number of anchors (per one grid) to generate
  annot_path = annot_path, # Annotations directory
  labels = blood_labels, # Class labels
  n_iter = 10, # Number of k-means++ iterations
  annot_format = "pascal_voc", # Annotations format
  seed = 55, # Random seed
  centroid_fun = mean # Centroid function
)
##       label    n
## 1 Platelets  361
## 2       RBC 4153
## 3       WBC  372

blood_anchors
## [[1]]
## [[1]][[1]]
## [1] 0.3552235 0.4417515
## 
## [[1]][[2]]
## [1] 0.2911290 0.3292675
## 
## [[1]][[3]]
## [1] 0.1971296 0.2346442
## 
## 
## [[2]]
## [[2]][[1]]
## [1] 0.1757463 0.1592062
## 
## [[2]][[2]]
## [1] 0.1652637 0.2065506
## 
## [[2]][[3]]
## [1] 0.1630269 0.2439239
## 
## 
## [[3]]
## [[3]][[1]]
## [1] 0.1391842 0.1769376
## 
## [[3]][[2]]
## [1] 0.1245985 0.2258089
## 
## [[3]][[3]]
## [1] 0.06237392 0.08062560

Build YOLOv3 model (you can load YOLOv3 Darknet weights trained on COCO dataset. Download pre-trained weights from here):

blood_yolo <- yolo3(
  net_h = net_h, # Input image height
  net_w = net_w, # Input image width
  grayscale = FALSE, # Should images be loaded as grayscale or RGB
  n_class = n_class, # Number of object classes (80 for COCO dataset)
  anchors = blood_anchors # Anchor boxes
)
blood_yolo %>% load_darknet_weights(here("development/yolov3.weights")) # Optional

blood_yolo
## Model
## Model: "yolo3"
## ________________________________________________________________________________
## Layer (type)              Output Shape      Param #  Connected to               
## ================================================================================
## input_img (InputLayer)    [(None, 416, 416, 0                                   
## ________________________________________________________________________________
## darknet53 (Functional)    [(None, None, Non 40620640 input_img[0][0]            
## ________________________________________________________________________________
## yolo3_conv1 (Functional)  (None, 13, 13, 51 11024384 darknet53[0][2]            
## ________________________________________________________________________________
## yolo3_conv2 (Functional)  (None, 26, 26, 25 2957312  yolo3_conv1[0][0]          
##                                                      darknet53[0][1]            
## ________________________________________________________________________________
## yolo3_conv3 (Functional)  (None, 52, 52, 12 741376   yolo3_conv2[0][0]          
##                                                      darknet53[0][0]            
## ________________________________________________________________________________
## grid1 (Functional)        (None, 13, 13, 3, 4747288  yolo3_conv1[0][0]          
## ________________________________________________________________________________
## grid2 (Functional)        (None, 26, 26, 3, 1194008  yolo3_conv2[0][0]          
## ________________________________________________________________________________
## grid3 (Functional)        (None, 52, 52, 3, 302104   yolo3_conv3[0][0]          
## ================================================================================
## Total params: 61,587,112
## Trainable params: 61,534,504
## Non-trainable params: 52,608
## ________________________________________________________________________________

Compile the model with correct loss and metrics:

blood_yolo %>% compile(
  optimizer = optimizer_adam(lr = 1e-5),
  loss = yolo3_loss(blood_anchors, n_class = n_class),
  metrics = yolo3_metrics(blood_anchors, n_class = n_class)
)

Create data generators:

train_blood_yolo_generator <- yolo3_generator(
  annot_path = file.path(BCCD_path, "train", "Annotations/"),
  images_path = file.path(BCCD_path, "train", "JPEGImages/"),
  net_h = net_h,
  net_w = net_w,
  batch_size = 8,
  shuffle = TRUE,
  labels = blood_labels
)
## 291 images with corresponding annotations detected!
## Set 'steps_per_epoch' to: 37
valid_blood_yolo_generator <- yolo3_generator(
  annot_path = file.path(BCCD_path, "valid", "Annotations/"),
  images_path = file.path(BCCD_path, "valid", "JPEGImages/"),
  net_h = net_h,
  net_w = net_w,
  batch_size = 8,
  shuffle = TRUE,
  labels = blood_labels
)
## 69 images with corresponding annotations detected!
## Set 'steps_per_epoch' to: 9

Fit the model:

history <- blood_yolo %>%
  yolo3_fit_generator(
    generator = train_blood_yolo_generator,
    epochs = 5,
    steps_per_epoch = 37,
    validation_generator = valid_blood_yolo_generator,
    validation_steps_per_epoch = 9,
    model_filepath = "development/BCCD/blood_w.hdf5",
    save_best_only = TRUE,
    monitor = "val_loss"
  )

Predict on new images:

blood_yolo <- yolo3(
  net_h = net_h,
  net_w = net_w,
  grayscale = FALSE,
  n_class = n_class,
  anchors = blood_anchors
)
blood_yolo %>% load_model_weights_hdf5(here("development/BCCD/blood_w.hdf5"))

test_blood_yolo_generator <- yolo3_generator(
  annot_path = file.path(BCCD_path, "test", "Annotations/"),
  images_path = file.path(BCCD_path, "test", "JPEGImages/"),
  net_h = net_h,
  net_w = net_w,
  batch_size = 4,
  shuffle = FALSE,
  labels = blood_labels
)
## 4 images with corresponding annotations detected!
## Set 'steps_per_epoch' to: 1
test_preds <- predict_generator(blood_yolo, test_blood_yolo_generator, 1)

test_boxes <- get_boxes(test_preds, blood_anchors, blood_labels,
                        obj_threshold = 0.6)

test_boxes
## [[1]]
## # A tibble: 17 x 7
##      xmin   ymin  xmax  ymax p_obj label_id label
##     <dbl>  <dbl> <dbl> <dbl> <dbl>    <int> <chr>
##  1 0.617  0.0137 0.884 0.543  1           2 RBC  
##  2 0.299  0      0.500 0.140  1.00        2 RBC  
##  3 0.479  0      0.687 0.203  1.00        2 RBC  
##  4 0.709  0      0.870 0.206  1.00        2 RBC  
##  5 0.0199 0      0.163 0.250  1.00        2 RBC  
##  6 0.0516 0.190  0.196 0.336  1           2 RBC  
##  7 0.406  0.164  0.564 0.316  1.00        2 RBC  
##  8 0.472  0.165  0.635 0.326  1.00        2 RBC  
##  9 0.865  0.0795 1     0.411  1.00        2 RBC  
## 10 0.0799 0.308  0.296 0.494  1.00        2 RBC  
## 11 0.414  0.359  0.562 0.506  1.00        2 RBC  
## 12 0.819  0.328  1     0.563  1.00        2 RBC  
## 13 0.482  0.395  0.623 0.544  1.00        2 RBC  
## 14 0.250  0.464  0.420 0.679  1           2 RBC  
## 15 0.868  0.500  1     0.704  1.00        2 RBC  
## 16 0.0584 0.531  0.290 0.821  1.00        3 WBC  
## 17 0.440  0.584  0.746 0.906  1.00        3 WBC  
## 
## [[2]]
## # A tibble: 16 x 7
##       xmin  ymin  xmax  ymax p_obj label_id label
##      <dbl> <dbl> <dbl> <dbl> <dbl>    <int> <chr>
##  1 0.133   0     0.409 0.287  1.00        2 RBC  
##  2 0.427   0.275 0.704 0.779  1.00        2 RBC  
##  3 0.831   0.104 0.970 0.285  1.00        2 RBC  
##  4 0.489   0.278 0.680 0.461  1.00        2 RBC  
##  5 0.00549 0.279 0.106 0.509  1.00        2 RBC  
##  6 0.579   0.306 0.757 0.496  1.00        2 RBC  
##  7 0.0502  0.341 0.204 0.572  1.00        2 RBC  
##  8 0.769   0.436 0.945 0.592  1.00        2 RBC  
##  9 0.360   0.536 0.510 0.683  1.00        2 RBC  
## 10 0.239   0.595 0.436 0.771  1.00        2 RBC  
## 11 0.626   0.678 0.812 0.878  1.00        2 RBC  
## 12 0.700   0.740 0.890 0.939  1.00        2 RBC  
## 13 0.805   0.715 0.985 0.915  1.00        2 RBC  
## 14 0.191   0.692 0.389 1      1.00        2 RBC  
## 15 0.347   0.756 0.544 1      1.00        2 RBC  
## 16 0.217   0.252 0.442 0.507  1.00        3 WBC  
## 
## [[3]]
## # A tibble: 13 x 7
##     xmin  ymin  xmax  ymax p_obj label_id label
##    <dbl> <dbl> <dbl> <dbl> <dbl>    <int> <chr>
##  1 0.593 0.106 0.838 0.617 0.642        2 RBC  
##  2 0.212 0.577 0.434 1     0.660        2 RBC  
##  3 0.280 0     0.453 0.236 1.00         2 RBC  
##  4 0.637 0     0.801 0.237 1.00         2 RBC  
##  5 0.780 0.184 0.972 0.399 0.996        2 RBC  
##  6 0.588 0.263 0.773 0.481 0.809        2 RBC  
##  7 0     0.342 0.214 0.514 0.931        2 RBC  
##  8 0.836 0.468 1     0.661 1.00         2 RBC  
##  9 0.778 0.686 0.969 0.873 1.00         2 RBC  
## 10 0.441 0.770 0.624 0.958 0.939        2 RBC  
## 11 0.610 0.783 0.806 0.969 1.00         2 RBC  
## 12 0.257 0.195 0.592 0.523 0.999        3 WBC  
## 13 0.225 0.259 0.512 0.559 0.999        3 WBC  
## 
## [[4]]
## # A tibble: 19 x 7
##       xmin   ymin   xmax  ymax p_obj label_id label    
##      <dbl>  <dbl>  <dbl> <dbl> <dbl>    <int> <chr>    
##  1 0.0478  0.239  0.219  0.336 0.982        1 Platelets
##  2 0.185   0.512  0.305  0.685 0.978        1 Platelets
##  3 0.318   0.578  0.496  0.689 0.998        1 Platelets
##  4 0       0.510  0.0972 0.777 1.00         1 Platelets
##  5 0.158   0      0.343  0.281 0.947        2 RBC      
##  6 0.874   0.0645 1      0.235 0.962        2 RBC      
##  7 0.372   0.151  0.567  0.325 1.00         2 RBC      
##  8 0.549   0.155  0.709  0.478 1.00         2 RBC      
##  9 0.445   0.251  0.607  0.566 0.984        2 RBC      
## 10 0.458   0.339  0.639  0.514 0.988        2 RBC      
## 11 0.00327 0.466  0.193  0.642 1.00         2 RBC      
## 12 0.463   0.562  0.642  0.789 1.00         2 RBC      
## 13 0.178   0.671  0.331  0.847 0.997        2 RBC      
## 14 0.148   0.671  0.370  0.885 0.975        2 RBC      
## 15 0.358   0.696  0.546  1     0.994        2 RBC      
## 16 0.535   0.806  0.710  0.969 1.00         2 RBC      
## 17 0.692   0.705  0.892  1     0.999        2 RBC      
## 18 0.837   0.783  1      1     0.994        2 RBC      
## 19 0.615   0.408  0.904  0.748 1.00         3 WBC

Plot / save images with predicted bounding boxes:

plot_boxes(
  images_paths = list.files(file.path(BCCD_path, "test", "JPEGImages/"), full.names = TRUE),
  boxes = test_boxes,
  labels = blood_labels,
  save_dir = BCCD_path)