訂閱
糾錯
加入自媒體

使用簡單方法在圖像中檢測血細胞

對象檢測問題的基礎是數(shù)據(jù)的外觀,F(xiàn)在,本文將介紹可用于解決對象檢測問題的不同深度學習架構。讓我們首先討論我們將要處理的問題陳述。

目錄

1. 了解問題陳述:血細胞檢測

2. 數(shù)據(jù)集鏈接

3. 解決對象檢測問題的簡單方法

4. 實施簡單方法的步驟

· 加載數(shù)據(jù)集

· 數(shù)據(jù)探索

· 為簡單方法準備數(shù)據(jù)集

· 創(chuàng)建訓練和驗證集

· 定義分類模型架構

· 訓練模型

· 作出預測

5. 結(jié)論

了解問題陳述血細胞檢測

問題陳述

對于一組給定的血細胞圖像,我們必須檢測圖像中的白細胞。現(xiàn)在,這是來自數(shù)據(jù)集的示例圖像。如你所見,你可以看到有一些紅色陰影區(qū)域和藍色或紫色區(qū)域。

在上圖中,紅色陰影區(qū)域是紅細胞(RBC),紫色陰影區(qū)域是白細胞(WBC),還有一些小的黑色突出部分是血小板。

正如你在此圖像中看到的那樣,我們有多個對象和多個類。

為簡單起見,我們將其轉(zhuǎn)換為單類單對象問題。這意味著我們將只考慮白細胞。

因此,只有一個類,即白細胞,而忽略其余的類。此外,我們將只保留具有單個白細胞的圖像。

因此,具有多個白細胞的圖像將從該數(shù)據(jù)集中刪除。

以下是我們將從該數(shù)據(jù)集中選擇圖像的方法。

因此,我們刪除了圖像 2 和圖像 5,因為圖像 5 沒有白細胞,而圖像 2 有 2 個白細胞,其他圖像保留在數(shù)據(jù)集中。同樣,測試集也將只有一個白細胞。

現(xiàn)在,對于每個圖像,我們在白細胞周圍都有一個邊界框。正如你在這張圖片中看到的,我們的文件名為 1.jpg,這些是白細胞周圍邊界框的邊界框坐標。

在下一節(jié)中,我們將介紹解決此對象檢測問題的簡單方法。

解決對象檢測問題的簡單方法

在本節(jié)中,我們將討論一種解決對象檢測問題的簡單方法。所以讓我們首先了解任務,我們必須在血細胞圖像中檢測白細胞,可以看到下圖。

現(xiàn)在,最簡單的方法是將圖像劃分為多個塊,因此對于此圖像,將圖像劃分為四個塊。

我們對這些塊中的每一個進行分類,因此第一個塊沒有白細胞,第二個塊有一個白細胞,同樣第三個和第四個沒有任何白細胞。

我們已經(jīng)熟悉分類過程以及如何構建分類算法。因此,我們可以輕松地將這些單獨的塊中的每一個分類為 yes 和 no,以表示白細胞。

現(xiàn)在,在下圖中,具有白細胞的塊(綠色框)可以表示為邊界框,因此在這種情況下,我們將取這個塊的坐標值,并將其返回為白細胞的邊界框。

現(xiàn)在為了實施這種方法,我們首先需要準備我們的訓練數(shù)據(jù)。

現(xiàn)在可能有一個問題,為什么我們需要準備訓練數(shù)據(jù)?我們已經(jīng)有了這些圖像和邊界框。

我們的訓練數(shù)據(jù)采用以下格式,其中我們有白細胞邊界框和邊界框坐標。

現(xiàn)在,請注意我們有完整圖像的這些邊界框坐標,但我們將把這個圖像分成四個塊。我們需要所有這四個塊的邊界框坐標。下一個問題是我們?nèi)绾巫龅竭@一點?

我們必須定義一個新的訓練數(shù)據(jù),我們有文件名,如下圖所示。

我們有不同的塊,對于每個塊,我們有 Xmin、Xmax、Ymin 和 Ymax 值,它們表示這些塊的坐標,最后,我們的目標變量是白細胞。圖像中是否存在白細胞?

現(xiàn)在在這種情況下,它將成為一個簡單的分類問題。因此,對于每個圖像,我們將其劃分為四個不同的塊,并為每個塊創(chuàng)建邊界框坐標。

現(xiàn)在下一個問題是我們?nèi)绾蝿?chuàng)建這些邊界框坐標?這真的很簡單。

考慮到我們有一個大小為 (640*480) 的圖像。所以原點是(0,0)。上圖有 x 軸和 y 軸,這里我們的坐標值為 (640, 480)。

現(xiàn)在,我們找出中點,它是 (320,240)。一旦我們有了這些值,我們就可以很容易地找出每個塊的坐標。所以對于第一個塊,我們的 Xmin 和 Ymin 將是 (0,0) ,而 Xmax, Ymax 將是 (320,240)。

同樣,我們可以在第二個、第三個和第四個塊中找到它。一旦我們有了這些塊中的每一個的坐標值或邊界框值。下一個任務是確定此塊中是否存在白細胞。

在這里我們可以清楚地看到塊 2 有白細胞,而其他塊沒有,但是我們不能在數(shù)據(jù)集中的每個塊上對每個圖像都手動標注白細胞。

現(xiàn)在在下一節(jié)中,我們將實現(xiàn)簡單的方法。

實施簡單方法的步驟

在上一節(jié)中,我們討論了用于對象檢測的簡單方法,F(xiàn)在讓我們定義在血細胞檢測問題上實施這種方法的步驟。

這些是將要遵循的步驟:

1. 加載數(shù)據(jù)集

2. 數(shù)據(jù)探索

3. 為簡單方法準備數(shù)據(jù)集

4. 創(chuàng)建訓練和驗證集

5. 定義分類模型架構

6. 訓練模型

7. 作出預測

讓我們進入下一節(jié),實現(xiàn)上述步驟。

1.加載所需的庫和數(shù)據(jù)集

因此,讓我們首先從加載所需的庫開始。numpy和pandas,matplotlib用來可視化數(shù)據(jù),我們已經(jīng)加載了一些庫來處理圖像并調(diào)整圖像大小,最后是torch庫。

# Importing Required Libraries

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

%matplotlib inline

import os

from PIL import Image

from skimage.transform import resize

import torch

from torch import nn

現(xiàn)在我們將修復一個隨機種子值。

# Fixing a random seed values to stop potential randomness
seed = 42

rng = np.random.RandomState(seed)

在這里,我們將安裝驅(qū)動器,因為數(shù)據(jù)集存儲在驅(qū)動器上。

# mount the drive

from google.colab import drive

drive.mount('/content/drive')

現(xiàn)在因為驅(qū)動器上的數(shù)據(jù)以 zip 格式提供。我們必須解壓縮這些數(shù)據(jù),在這里我們將解壓縮數(shù)據(jù)。

所以我們可以看到所有的圖像都被加載并存儲在一個名為 images 的文件夾中。在這個文件夾的末尾,我們有一個 CSV 文件,它是trained.csv。

# unzip the dataset from drive

!unzip /content/drive/My Drive/train_zedkk38.zip

2.數(shù)據(jù)探索

閱讀 CSV 文件并找出存儲在這個“train.csv”文件中的信息是什么。

## Reading target file

data = pd.read_csv('train.csv')

data.shape

打印 CSV 文件的前幾行,我們可以看到該文件具有 image_names 以及 cell_type,它將表示紅細胞或白細胞等等。最后是此特定圖像中此特定對象的邊界框坐標。data.head()

因此,如果我們檢查紅細胞、白細胞和血小板的計數(shù)值。我們將看到紅細胞具有最大計數(shù)值,其次是白細胞和血小板。

data.cell_type.value_counts()

現(xiàn)在為簡單起見,我們將只考慮白細胞。因此,我們選擇了只有白細胞的數(shù)據(jù)。

現(xiàn)在我們針對這些圖像有 image_names和 cell_type WBC。還有邊界框坐標。

(data.loc[data['cell_type'] =='WBC']).head()

讓我們看看原始數(shù)據(jù)集中的幾張圖像以及這些圖像的形狀。

我們可以看到這些圖像的形狀是(480,640,3)。這是一個具有三個通道的 RGB 圖像,這是數(shù)據(jù)集中的第一張圖像。

image = plt.imread('images/' + '1.jpg')

print(image.shape)

plt.imshow(image)

下一步是用這個圖像創(chuàng)建塊。我們要學習如何把這張圖片分成四個塊,F(xiàn)在我們知道圖像的形狀是 (640, 480)。因此這張圖片的中間點是 (320,240),中心是 (0, 0)。

因此,我們有圖像中所有這些塊的坐標,在這里我們將利用這些坐標并創(chuàng)建塊。

這些坐標的格式將是 Ymin、Ymax、Xmin 和 Xmax。這里我們的 (Ymin, Ymax) 是 (0, 240) 并且 (Xmin, Xmax) 是 (0,320)。這基本上表示第一個塊。

同樣,對于隨后的第二個第三個和第四個塊,我們有 image_2、image_3、image_4。這是一個我們可以從圖像創(chuàng)建塊的過程。

# creating 4 patches from the image

# format ymin, ymax, xmin, xmax

image_1 = image[0:240, 0:320, :]

image_2 = image[0:240, 320:640, :]

image_3 = image[240:480, 0:320, :]

image_4 = image[240:480, 320:640, :]

現(xiàn)在我們需要為這些塊分配一個目標值。為了做到這一點,我們計算并集的交集,我們必須找出交集區(qū)域和并集區(qū)域。

所以交集區(qū)域就是這個特定的矩形,要找出面積,我們需要找出這個矩形的 Xmin、Xmax 和 Ymin、Ymax 坐標。

def iou(box1, box2):

   Irect_xmin, Irect_ymin = max(box1[0],box2[0]), max(box1[2],box2[2])

   Irect_xmax, Irect_ymax = min(box1[1],box2[1]), min(box1[3],box2[3])

   if Irect_xmax < Irect_xmin or Irect_ymax < Irect_ymin:

       target = inter_area = 0

   else:

     inter_area = np.a(chǎn)bs((Irect_xmax - Irect_xmin) * (Irect_ymax - Irect_ymin))

     box1_area = (box1[1]-box1[0])*(box1[3]-box1[2])

     box2_area = (box2[1]-box2[0])*(box2[3]-box2[2])

     union_area = box1_area+box2_area-inter_area

     iou = inter_area/union_area

     target = int(iou > 0.1)

   return target

我們有來自訓練 CSV 文件的原始邊界框坐標。當我將這兩個值用作我們定義的“ iou”函數(shù)的輸入時,目標為 1。

你也可以嘗試使用不同的塊,也可以基于你將得到的目標值。

box1= [320, 640, 0, 240]

box2= [93,    296, 1, 173]

iou(box1, box2)

輸出為 0,F(xiàn)在下一步是準備數(shù)據(jù)集。

3.為簡單方法準備數(shù)據(jù)集

我們只考慮并探索了數(shù)據(jù)集中的單個圖像。因此,讓我們對數(shù)據(jù)集中的所有圖像執(zhí)行這些步驟。這里是我們擁有的完整數(shù)據(jù)。

data.head()

現(xiàn)在,我們正在轉(zhuǎn)換這些細胞類型,紅細胞為0,白細胞為 1,血小板為 2。

data['cell_type'] = data['cell_type'].replace({'RBC': 0, 'WBC': 1, 'Platelets': 2})

現(xiàn)在我們必須選擇只有一個白細胞的圖像。

因此,首先我們創(chuàng)建數(shù)據(jù)集的副本,然后僅保留白細胞并刪除任何具有多個白細胞的圖像。

## keep only Single WBCs

data_wbc = data.loc[data.cell_type == 1].copy()

data_wbc = data_wbc.drop_duplicates(subset=['image_names', 'cell_type'], keep=False)

現(xiàn)在我們已經(jīng)選擇了圖像。我們將根據(jù)輸入圖像大小設置塊坐標。

我們正在逐一讀取圖像并存儲該特定圖像的白細胞邊界框坐標,使用我們在此處定義的塊坐標從該圖像中提取塊。

然后我們使用自定義的 IoU 函數(shù)找出每個塊的目標值。最后,在這里我們將塊大小調(diào)整為標準大小 (224, 224, 3)。在這里,我們正在為每個塊創(chuàng)建最終輸入數(shù)據(jù)和目標數(shù)據(jù)。

# create empty lists

X = []

Y = []

# set patch co-ordinates

patch_1_coordinates = [0, 320, 0, 240]

patch_2_coordinates = [320, 640, 0, 240]

patch_3_coordinates = [0, 320, 240, 480]

patch_4_coordinates = [320, 640, 240, 480]

for idx, row in data_wbc.iterrows():

   # read image

   image = plt.imread('images/' + row.image_names)

   bb_coordinates = [row.xmin, row.xmax, row.ymin, row.ymax]

   # extract patches

   patch_1 = image[patch_1_coordinates[2]:patch_1_coordinates[3],
                            patch_1_coordinates[0]:patch_1_coordinates[1], :]

   patch_2 = image[patch_2_coordinates[2]:patch_2_coordinates[3],
                              patch_2_coordinates[0]:patch_2_coordinates[1], :]

   patch_3 = image[patch_3_coordinates[2]:patch_3_coordinates[3],
                           patch_3_coordinates[0]:patch_3_coordinates[1], :]

   patch_4 = image[patch_4_coordinates[2]:patch_4_coordinates[3],
                           patch_4_coordinates[0]:patch_4_coordinates[1], :]

   # set default values

   target_1 = target_2 = target_3 = target_4 = inter_area = 0

   # figure out if the patch contains the object

   ## for patch_1

   target_1 = iou(patch_1_coordinates, bb_coordinates )

   ## for patch_2

   target_2 = iou(patch_2_coordinates, bb_coordinates)

   ## for patch_3

   target_3 = iou(patch_3_coordinates, bb_coordinates)

   ## for patch_4

   target_4 = iou(patch_4_coordinates, bb_coordinates)

   # resize the patches

   patch_1 = resize(patch_1, (224, 224, 3), preserve_range=True)

   patch_2 = resize(patch_2, (224, 224, 3), preserve_range=True)

   patch_3 = resize(patch_3, (224, 224, 3), preserve_range=True)

   patch_4 = resize(patch_4, (224, 224, 3), preserve_range=True)

   # create final input data

   X.extend([patch_1, patch_2, patch_3, patch_4])

   # create target data

   Y.extend([target_1, target_2, target_3, target_4])

# convert these lists to single numpy array

X = np.a(chǎn)rray(X)

Y = np.a(chǎn)rray(Y)

現(xiàn)在,讓我們打印原始數(shù)據(jù)和剛剛創(chuàng)建的新數(shù)據(jù)的形狀。我們可以看到我們最初有 240 張圖像。

現(xiàn)在我們將這些圖像分成四部分, 即(960,224,224,3)。這是圖像的形狀。

# 4 patches for every image

data_wbc.shape, X.shape, Y.shape

讓我們快速看一下我們剛剛創(chuàng)建的這些圖像之一。這是我們的原始圖像,這是原始圖像的最后一個塊或第四個塊。我們可以看到分配的目標是1。

image = plt.imread('images/' + '1.jpg')

plt.imshow(image)

如果我們檢查任何其他塊,假設我要檢查此圖像的第一個塊,這里會將目標設為0。你將獲得第一個塊。

同樣,你可以確保將所有圖像轉(zhuǎn)換為塊并相應地分配目標。

plt.imshow(X[0].a(chǎn)stype('uint8')), Y[0]

4.準備訓練和驗證集

現(xiàn)在我們有了數(shù)據(jù)集。我們將準備我們的訓練和驗證集。現(xiàn)在請注意,這里我們的圖像形狀為 (224,224,3)。

# 4 patches for every image

data_wbc.shape, X.shape, Y.shape

輸出是:

((240, 6), (960, 224, 224, 3), (960,))

在 PyTorch 中,我們首先需要擁有通道。因此,我們將移動具有形狀 (3,224,224) 的軸。

X = np.moveaxis(X, -1, 1)

X.shape

輸出是:

(960, 3, 224, 224)

現(xiàn)在,我們對圖像像素值進行歸一化。

X = X / X.max()

使用訓練測試拆分功能,我們將創(chuàng)建一個訓練集和驗證集。

from sklearn.model_selection import train_test_split

X_train, X_valid, Y_train, Y_valid=train_test_split(X, Y, test_size=0.1,
                                           random_state=seed)

X_train.shape, X_valid.shape, Y_train.shape, Y_valid.shape

上述代碼的輸出是:

((864, 3, 224, 224), (96, 3, 224, 224), (864,), (96,))

現(xiàn)在,我們要將訓練集和驗證集都轉(zhuǎn)換為張量,因為它們是“ numpy”數(shù)組。

X_train = torch.FloatTensor(X_train)

Y_train = torch.FloatTensor(Y_train)

X_valid = torch.FloatTensor(X_valid)

Y_valid = torch.FloatTensor(Y_valid)

5.模型構建

現(xiàn)在,我們要構建我們的模型,在這里我們安裝了一個庫,它是 PyTorch 模型摘要。

!pip install pytorch-model-summary

這僅用于在 PyTorch 中打印模型摘要。現(xiàn)在我們從這里導入?yún)R總函數(shù)。

from pytorch_model_summary import summary

這是我們?yōu)榉椒ǘx的架構。

我們定義了一個順序模型,其中有Conv2d 層,輸入通道數(shù)為 3,過濾器數(shù)量為 64,過濾器的大小為 5,步幅設置為 2。對于這個 Conv2d 層有 ReLU 激活函數(shù)。一個池化層,窗口大小為 4,步幅為 2,然后是卷積層。

現(xiàn)在展平 Conv2d 層的輸出,最后是全連接層和 sigmoid 激活函數(shù)。

## model architecture

model = nn.Sequential(

       nn.Conv2d(in_channels=3, out_channels=64, kernel_size=5, stride=2),  

       nn.ReLU(),  

       nn.MaxPool2d(kernel_size=4,stride=2),  

       nn.Conv2d(in_channels=64, out_channels=64, kernel_size=5, stride=2),    

       nn.Flatten(),

       nn.Linear(40000, 1),

       nn.Sigmoid()

在這里打印模型,將是我們定義的模型架構。

print(model)

使用summary函數(shù),我們可以查看模型摘要。因此,這將為我們返回每個層的輸出形狀,每個層的可訓練參數(shù)的數(shù)量,F(xiàn)在我們的模型已經(jīng)準備好了。

print(summary(model, X_train[:1]))

現(xiàn)在模型已經(jīng)準備好訓練了。

6.訓練模型

讓我們訓練這個模型。所以我們要定義我們的損失函數(shù)和優(yōu)化函數(shù)。我們將二元交叉熵定義為損失和Adam優(yōu)化器。然后我們將模型傳輸?shù)?GPU。

在這里,我們從輸入圖像中提取批次來訓練這個模型。

## loss and optimizer

criterion = torch.nn.BCELoss()

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

## GPU device

if torch.cuda.is_available():

 model = model.cuda()

 criterion = criterion.cuda()

因此,我們從 x_train 中提取了批次并使用了這些批次。我們將對該模型進行總共 15 個 epoch 的訓練。我們還設置了一個 optimizer.Zero_grad() 并將輸出存儲在這里。

現(xiàn)在我們正在計算損失并存儲所有損失并執(zhí)行反向傳播和更新參數(shù)。此外,我們在每個 epoch 之后打印損失。

在輸出中,我們可以看到每個時期的損失都在減少。所以這個模型的訓練就完成了。

# batch size of the model

batch_size = 32

# defining the training phase

model.train()

for epoch in range(15):

   # setting initial loss as 0

   train_loss = 0.0        

   # to randomly pick the images without replacement in batches

   permutation = torch.randperm(X_train.size()[0])

   # to keep track of training loss

   training_loss = []

   # for loop for training on batches

   for i in range(0,X_train.size()[0], batch_size):

       # taking the indices from randomly generated values

       indices = permutation[i:i+batch_size]

      # getting the images and labels for a batch

       batch_x, batch_y = X_train[indices], Y_train[indices]

       if torch.cuda.is_available():

           batch_x, batch_y = batch_x.cuda().float(), batch_y.cuda().float()

       # clearing all the accumulated gradients

       optimizer.zero_grad()

       # mini batch computation

       outputs = model(batch_x)

       # calculating the loss for a mini batch

       loss = criterion(outputs.squeeze(),batch_y)

       # storing the loss for every mini batch

       training_loss.a(chǎn)ppend(loss.item())

       # calculating the gradients

       loss.backward()

       # updating the parameters

       optimizer.step()

   training_loss = np.a(chǎn)verage(training_loss)

   print('epoch: t', epoch, 't training loss: t', training_loss)

7.做出預測

現(xiàn)在讓我們使用這個模型來進行預測。所以在這里我只從驗證集中獲取前五個輸入并將它們傳輸?shù)?Cuda。

output = model(X_valid[:5].to('cuda')).cpu().detach().numpy()

這是我們拍攝的前五張圖像的輸出,F(xiàn)在我們可以看到前兩個的輸出是沒有白細胞或有白細胞。

output

這是輸出:

array([[0.00641595],

      [0.01172841],

      [0.99919134],

      [0.01065345],

      [0.00520921]], dtype=float32)

繪制圖像。我們可以看到這是第三張圖片,這里的模型說有一個白細胞,我們可以看到這張圖片中有一個白細胞。

plt.imshow(np.transpose(X_valid[2]))

同樣,我們可以檢查另一張圖像,因此將獲取第一張圖像。

你可以看到輸出圖像,這個圖像是我們的輸入塊,這個塊中沒有白細胞。

plt.imshow(np.transpose(X_valid[1]))

這是一種非常簡單的方法,可以進行預測或識別具有白細胞的圖像的塊或部分。

結(jié)論

使用簡單方法了解使用圖像數(shù)據(jù)集進行血細胞檢測的實際實現(xiàn)。這是解決業(yè)務問題和開發(fā)模型的真正挑戰(zhàn)。

在處理圖像數(shù)據(jù)時,你必須分析一些任務,例如邊界框、計算 IoU 值、評估指標。本文的下一個級別(未來任務)是一個圖像可以有多個對象。任務是檢測每個圖像中的對象。希望這些文章能幫助你了解如何使用圖像數(shù)據(jù)檢測血細胞,如何建立檢測模型,我們將使用這種技術,并將其應用于醫(yī)學分析領域。

       原文標題 : 使用簡單方法在圖像中檢測血細胞

聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關注公眾號
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯
x
*文字標題:
*糾錯內(nèi)容:
聯(lián)系郵箱:
*驗 證 碼:

粵公網(wǎng)安備 44030502002758號