Reviews

  • 1
  • 2
  • 3
  • 4
  • 5
Editor Rating
  • 1
  • 2
  • 3
  • 4
  • 5
User Ratings
Based on 0 reviews

Major Concepts

Articles Home » Predictive and Prescriptive Modeling » Machine Learning » Blood Cells Detection with YOLOV3 Darknet

Blood Cells Detection with YOLOV3 Darknet

What is YOLO?


YOLO is a state-of-the-art object detection system. It is used to detect objects in an image and also draw a bounding box around the object. In other object detection systems like Fast RCNN & Faster RCNN, separate networks are used to detect the objects and predict the bounding boxes whereas in YOLO, a single convolutional network predicts the bounding boxes and the class probabilities for these boxes, hence the name You Only Look Once.The original yolo research paper is available here https://pjreddie.com/media/files/papers/yolo.pdf


 



 


YOLO is originally implemented in darknet which is an open source neural network framework written in C and CUDA. Later, it is implemented in other libraries like keras, pytorch, tensorflow. In this article, we will see how to train yolo darknet on a custom dataset. The following process is implemented in google colab which is useful as it provides free GPU and also to avoid the hassle of several installations and setups.


 


Anchor boxes


Anchor boxes are used to help the model in drawing the bounding boxes around each object. YOLO divides an entire image into grids. Sometimes a single image may contain more than one object to detect. By default, 5 anchor boxes are used which means the model detects a maximum of 5 objects in each grid.


 


The dataset can be downloaded from here:


Images: https://github.com/Shenggan/BCCD_Dataset/tree/master/BCCD/JPEGImages

Original Annotations: https://github.com/Shenggan/BCCD_Dataset/tree/master/BCCD/Annotations


YOLO Format Annotations: https://github.com/DatascienceAuthority/Bloodcells-Detection-YOLOV3/tree/master/annotations-yolo 


 


This is a dataset for blood cells detection(RBC, WBC, Platelets). It contains 2 folders - Images folder and annotations folder which consists of bounding box values for objects in each image in xml format. Our goal here is to train the model to detect WBC in an image.


 


Consider the first image ‘BloodImage_00000.jpg’, its corresponding annotation file is in annotations folder ‘BloodImage_00000.xml’. The xml file has the locations of all RBC, WBC, Platelets present in that image in the following format,


Original Image:



 


 


Few Annotations of Above Image :


Gist: https://gist.github.com/DatascienceAuthority/3a5f7d75583f55b73bd9f629f1e9860a 


<annotation>
<folder>JPEGImages</folder>
<filename>BloodImage_00000.jpg</filename>
<path>/home/pi/detection_dataset/JPEGImages/BloodImage_00000.jpg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>640</width>
<height>480</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>WBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>260</xmin>
<ymin>177</ymin>
<xmax>491</xmax>
<ymax>376</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>78</xmin>
<ymin>336</ymin>
<xmax>184</xmax>
<ymax>435</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>63</xmin>
<ymin>237</ymin>
<xmax>169</xmax>
<ymax>336</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>214</xmin>
<ymin>362</ymin>
<xmax>320</xmax>
<ymax>461</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>414</xmin>
<ymin>352</ymin>
<xmax>506</xmax>
<ymax>445</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>555</xmin>
<ymin>356</ymin>
<xmax>640</xmax>
<ymax>455</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>469</xmin>
<ymin>412</ymin>
<xmax>567</xmax>
<ymax>480</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>1</xmin>
<ymin>333</ymin>
<xmax>87</xmax>
<ymax>437</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>4</xmin>
<ymin>406</ymin>
<xmax>95</xmax>
<ymax>480</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>155</xmin>
<ymin>74</ymin>
<xmax>247</xmax>
<ymax>174</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>11</xmin>
<ymin>84</ymin>
<xmax>104</xmax>
<ymax>162</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>534</xmin>
<ymin>39</ymin>
<xmax>639</xmax>
<ymax>139</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>547</xmin>
<ymin>195</ymin>
<xmax>640</xmax>
<ymax>295</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>388</xmin>
<ymin>11</ymin>
<xmax>481</xmax>
<ymax>111</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>171</xmin>
<ymin>175</ymin>
<xmax>264</xmax>
<ymax>275</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>260</xmin>
<ymin>1</ymin>
<xmax>374</xmax>
<ymax>83</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>229</xmin>
<ymin>91</ymin>
<xmax>343</xmax>
<ymax>174</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>69</xmin>
<ymin>144</ymin>
<xmax>184</xmax>
<ymax>235</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>482</xmin>
<ymin>131</ymin>
<xmax>594</xmax>
<ymax>230</ymax>
</bndbox>
</object>
<object>
<name>RBC</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>368</xmin>
<ymin>89</ymin>
<xmax>464</xmax>
<ymax>176</ymax>
</bndbox>
</object>
</annotation>


 


Annotated Image:



 


Each object’s label is in the <name> tag and its corresponding location is represented by the tags <xmin>, <ymin>, <xmax>, <ymax>. This is called Pascal VOC. But to train images with Darknet and YOLOV3, the annotations should be in YOLO format. There is a project on github which can be used to convert annotations from Pascal VOC to YOLO format -  https://github.com/ssaru/convert2Yolo . As we want our model to detect only WBCs; RBCs and Platelets should be omitted from the annotations. So, some custom changes should be made to that project to achieve this task. Instead, you can download the YOLO format annoatations directly from here  https://github.com/DatascienceAuthority/Bloodcells-Detection-YOLOV3/tree/master/annotations-yolo


 


During conversion of annotations, another .txt file is generated which has locations of all the images. I have excluded few images (408, 409, 410) so that they can be used to test the trained model. So, there are train.txt and test.txt files.


train.txt - https://gist.github.com/DatascienceAuthority/4c980e45501639a1d067eabb6f4e1352


test.txt - https://gist.github.com/DatascienceAuthority/1505ab8bcf1e291177f04cdc2e6763b7


 


Move all the train images and annotations to same folder in your Google Drive and correct the locations in train.txt using a text editor.


 


WBC.names


Create *.names file which consists of class name  to detect. In our case, only one class (WBC)


WBC

https://gist.github.com/DatascienceAuthority/c3641fd2f1277ebea76e9b143c6b8a76


 


WBC-obj.data


Create *.data file which consist of  locations of other files or folders


Gist: https://gist.github.com/DatascienceAuthority/a8624a789e3235c1da2a8af8f29bf353


classes = 1
train = /content/drive/My\ Drive/Projects/BCCD/train_annotations_yolo/train.txt
valid = /content/drive/My\ Drive/Projects/BCCD/test_annotations_yolo/test.txt
names = /content/drive/My\ Drive/Projects/BCCD/WBC.names
backup = /content/drive/My\ Drive/Projects/darknet_bloodcells/backup/

train, valid, names are the files we created above and backup is the location where the trained model will be saved. The model is saved for every 100 steps (Processing of 1 batch of images i.e batch size is 1 step. Note that the ‘My Drive’ is written as ‘My\ Drive’ in the above file but it is written as ‘My Drive’ in annotation files.


 


Configuration file (.cfg)


The original yolo is trained on ImageNet data which has many number of classes. Here, we will be training our model to detect only WBCs, so we need to create a custom configuration file.


Original yolov3.cfg


https://gist.github.com/DatascienceAuthority/6dab60a98f650dc234b6957b2ab20623


 


yolo_custom.cfg


We should change the batch and subdivisions based on the memory available. Change the classes in all the  [yolo] layers to 1 as we are training the model for 1 class (WBC). Above all the [yolo] layers, change the number of filters in the [convolution layer] to 3*(classes+5). The number 3 is the number of masks in the [yolo] layer, classes is the number of classes to detect and the number 5 is due to the parameters in prediction output (center_x, center_y, width, height, confidence). To be specific, create a copy of the configuration file, rename it to yolo_custom.cfg and make the following changes.


 


Line 6: batch=64


Line 7: subdivisions=64


Line 603, 689, 776: filters=18


Line 610, 696, 783: classes=1


Optionally, you can change the width and height (line 8 and 9) to increase resolution.


 


The new configuration file after making the above changes


Gist: https://gist.github.com/DatascienceAuthority/9cb763a57f58c7e5fd9726ff9d9f23b5 


 


Install Darknet


Create a new python notebook in Google Colab and clone the darknet repository from github and setup darknet. The original darknet repository is at https://github.com/pjreddie/darknet but there is another darknet implementation which is suitable for Google Colab https://github.com/kriyeng/darknet/. The Google Drive folder structure includes a space “My Drive” which will raise an error if we use original implementation and it is handled by the git branch ‘feature/google-colab’ in this version of darknet.


!git clone https://github.com/kriyeng/darknet/
%cd darknet
!git checkout feature/google-colab

 


Compile Darknet


!make


Download Pre-Trained Darknet Weights


!wget https://pjreddie.com/media/files/darknet53.conv.74


File Locations


Upload the files created above to your Google Drive and save their locations in variables


objdata = '/content/drive/My Drive/Projects/BCCD/WBC-obj.data'
yolov3cfg = '/content/drive/My Drive/Projects/BCCD/yolo_custom.cfg'
preweights = '/content/darknet/darknet53.conv.74'



Mount Drive


Authenticate so that Colab can access the files uploaded to drive


from google.colab import drive
drive.mount('/content/drive')



Train the model


!./darknet detector train '$objdata' '$yolov3cfg' '$preweights' -dont_show

Training will take few hours, it took about 6 hours for me and I stopped training after 1000 steps and the loss is about 0.3. The training weights will be saved for every 100 steps at the location specified (backup).


 


Start training from last saved weights


saved_weights = '/content/drive/My Drive/Projects/darknet_bloodcells/backup/yolo_custom_last.weights'
!./darknet detector train '$objdata' '$yolov3cfg' '$saved_weights' -dont_show

 


 Predicting with trained model


%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import cv2
import pandas as pd
from matplotlib import patches

objdata = '/content/drive/My Drive/Projects/BCCD/WBC-obj.data'
yolov3cfg = '/content/drive/My Drive/Projects/BCCD/yolo_custom.cfg'
saved_weights = '/content/drive/My Drive/Projects/darknet_bloodcells/backup/yolo_custom_last.weights'

def PredictandShow(image_loc):
original_img = cv2.imread(image_loc)
original_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB)
result = !./darknet detector test '$objdata' 'yolov3cfg' 'saved_weights' '$image_loc' -dont_show
confidence = ''
if(('WBC' in result[-1]) & ('%' in result[-1])):
confidence = result[-1]
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(20, 10))
newImage = cv2.imread('predictions.jpg')
new_img = cv2.cvtColor(newImage, cv2.COLOR_BGR2RGB)
ax[0].imshow(original_img)
ax[0].axis('off')
ax[1].imshow(new_img)
ax[1].axis('off')
ax[1].set_title(confidence, fontsize=30, color="green")

 


Let us see how the model detects on an image that is not used during training


PredictandShow('/content/drive/My Drive/Projects/fast-rcnn/BCCD/test_images/BloodImage_00410.jpg')

Output



 On the left is the original image and on the right is the predicted image. The model is predicting WBC with 97% confidence. The difference in the original bounding box and predicted bounding box can be seen below



 


GitHub: https://github.com/DatascienceAuthority/Bloodcells-Detection-YOLOV3


Original Data: https://github.com/Shenggan/BCCD_Dataset


Colab Notebook: https://colab.research.google.com/drive/1E2kM1M63DlYhbe4z--jg0cF6My8EQDbS


User Reviews