OPEN CV & DEEP LEARNING ในการตรวจจับโรคผิวหนัง และคัดกรองโดย model Xception
Tags: opencv, deeplearning, python, ai
Published: 2024-04-16 02:45:42

มะเร็งผิวหนังเป็นหนึ่งในมะเร็งที่พบบ่อยที่สุดในโลก โดยมีผู้ป่วยใหม่มากกว่า 2 ล้านคนในสหรัฐอเมริกาในแต่ละปี https://www.webmd.com/melanoma-skin-cancer/default.htm มะเร็งผิวหนังมีสองประเภทหลักคือ Actinic keratosis (AK) และ Basal cell carcinoma (BCC)

AK เป็นโรคก่อนมะเร็งที่พบบ่อยซึ่งเกิดจากการสัมผัสกับแสงแดดเป็นเวลานาน มักปรากฏเป็นรอยแผลเรียบสีชมพูหรือสีน้ำตาลบนผิวหนัง BCC เป็นมะเร็งผิวหนังที่พบบ่อยที่สุดและเติบโตช้า มักปรากฏเป็นก้อนเล็ก ๆ บนผิวหนังที่มีสีชมพู, สีแดง, สีน้ำตาล, หรือสีดำ


การวินิจฉัย AK และ BCC มักทำโดยแพทย์ผู้เชี่ยวชาญด้านผิวหนัง (แพทย์ผิวหนัง) อย่างไรก็ตาม การวินิจฉัยโรคเหล่านี้อาจเป็นเรื่องยาก เนื่องจากอาจมีลักษณะคล้ายกับสภาพผิวอื่นๆ

ปัญหานี้สามารถแก้ไขได้โดยใช้ระบบการวินิจฉัยที่ช่วยด้วยคอมพิวเตอร์ (CAD)  CAD ใช้ปัญญาประดิษฐ์ (AI) ในการวิเคราะห์รูปภาพผิวหนังเพื่อระบุโรคที่อาจก่อให้เกิดเป็นมะเร็ง

Projectนี้มุ่งเน้นไปที่การพัฒนาระบบ CAD สำหรับการวินิจฉัย AK และ BCC โดยใช้ OpenCV และการเรียนรู้เชิงลึก  ระบบนี้ใช้โมเดล Xception ซึ่งเป็นโมเดล CNN (Convolutional Neural Network) ที่ได้รับการฝึกฝนบนชุดข้อมูลรูปภาพผิวหนัง


**<u>ขั้นตอนที่ 1</u>**
โหลดdatasetที่ใช้สำหรับการ train และ Test 
โดยdatasetที่ใช้สำหรับ Project ในครั้งนี้เราจะใช้model ที่ได้จาก kaggle
Download [Here](https://www.kaggle.com/datasets/riyaelizashaju/skin-disease-image-dataset-balanced?fbclid=IwAR1_AgnOD0Te9uNJ-UqgymNu6qxzo4NGhS3oT3FLMWVc9CsBqxmOkKGCB4s_aem_AcZdXsADik4WpuXvhyFzQjXw4ypPjPvqvRFKn7ZauDB7aLm5yz2NChA3B5veHHu1xOkjLCQGYXypkYSSTBcsxR5k) 

**<u>ขั้นตอนที่ 2</u>**
ทำการสร้างโฟเดอร์ใน google drive เพื่อใช้สำหรับการทำ model ในครั้งนี้ และทำการ upload folder ที่ใช้ในการ test และ train model ในครั้งนี้

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxu2qgtxq92hc37fifh8.png)

**<u>ขั้นตอนที่ 3</u>**
ทำการ Import libraries ทั้งหมดที่ต้องการใช้ลงไป

```
import tensorflow as tf
import numpy as np
import cv2
import os
from tqdm import tqdm
import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPool2D
from os import listdir
from os.path import isfile, join
from tensorflow.keras.applications import Xception
import matplotlib.pyplot as plt
```
โดย libraries ที่ใช้ก็จะมี
**1.TensorFlow** ช่วยให้สร้างและฝึกโมเดลแมชชีนเลิร์นนิง
**2.NumPy** รองรับการคำนวณทางคณิตศาสตร์และการจัดการข้อมูล
**3.OpenCV** ช่วยให้ทำงานกับภาพและวิดีโอ
**4.Keras** ช่วยให้สร้างโมเดลเครือข่ายประสาทเทียมได้ง่าย
**5.matplotlib.pyplot** ช่วยให้สร้างกราฟและแผนภูมิ
**6.โมเดล Keras** อื่นๆ ช่วยให้สร้างสถาปัตยกรรมเครือข่ายประสาทเทียมที่ซับซ้อน
**7.ฟังก์ชันจาก os** ช่วยจัดการไฟล์และไดเรกทอรี
**8.โมเดล Xception** ช่วยจำแนกภาพจากชุดข้อมูลขนาดใหญ่

**<u>ขั้นตอนที่ 4</u>**
ทำการเชื่อมgoogle colab กับ google drive ที่เก็บ dataset ของเรา

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tz1tg3icux7injvoi4t6.png)
```
FOLDERNAME = 'Skin/Xception_Model2'
%cd /content/drive/My\ Drive/Skin
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j99r1anjlqegoitxrity.png)

**<u>ขั้นตอนที่ 5</u>**
ทำการสร้างพารามิตเตอร์และโหลดภาพสำหรับงานจำแนกภาพผิวหนัง

```
width = 128
num_classes = 2  
trainpath = 'train/'
testpath = 'test/'
trainImg = [trainpath + f for f in listdir(trainpath) if not f.startswith('.')]
testImg = [testpath + f for f in listdir(testpath) if not f.startswith('.')]

trainImg , testImg
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lc1l57xv7xjl4zgpyd32.png)

ในขั้นตอนนี้เรากำหนดค่า
**width:** กำหนดความกว้างของรูปภาพที่จะใช้ (ในพิกเซล) ในกรณีนี้ตั้งค่าเป็น 128 เพื่อปรับให้ขนาดของรูปภาพทั้งหมดมีขนาดเท่ากันเพื่่วยลดจำนวนพารามิตเตอร์ใน model ได้
**num_classes:** กำหนดจำนวนคลาสสำหรับงานจำแนกภาพ ในกรณีนี้ตั้งค่าเป็น 2 (คือ Actinic keratosis และ Basal cell carcinoma)
**trainpath:** กำหนดเส้นทางไปยังไดเรกทอรีที่เก็บรูปภาพสำหรับฝึกโมเดล
**testpath:** กำหนดเส้นทางไปยังไดเรกทอรีที่เก็บรูปภาพสำหรับทดสอบโมเดล
**trainImg:** รายการชื่อfolderรูปภาพสำหรับฝึกโมเดล
**testImg:** รายการชื่อfolderรูปภาพสำหรับทดสอบโมเดล

**<u>ขั้นตอนที่ 6</u>**
สร้างฟังก์ชัน img2data ทำหน้าที่โหลดรูปภาพจากfolderที่กำหนด ปรับขนาดรูปภาพ และแปลงเป็นข้อมูลสำหรับการฝึกโมเดล

```
def img2data(path):
  rawImgs = []
  labels = []

  for imagePath in (path):
      for item in tqdm(listdir(imagePath)):
          file = join(imagePath, item)
          if file[-1] in ('g', 'jpg', 'png'):  
              try:
                  img = cv2.imread(file , cv2.COLOR_BGR2RGB)
                  img = cv2.resize(img ,(width,width))
                  if img.shape == (width, width, 3):  
                      rawImgs.append(img)
                      l = imagePath.split('/')[1]
                      if l == 'Actinickeratosis':
                          labels.append([1,0])         
                      elif l == 'BasalCellCarcinoma':
                          labels.append([0,1])
              except:
                  pass  

  return rawImgs, labels
```
**rawImgs:** มีหน้าที่เก็บข้อมูลรูปภาพดิบ (NumPy arrays)
**labels:** มีหน้าที่เก็บข้อมูลlayerของแต่ละรูปภาพเอาไว้
**path:** มีหน้าที่ในการวบลูปรูปภาพใน folder test และ train และในแต่ละ path เราจะใช้ tqdm เพื่อวนลูปไปยังไฟล์ทั้งหมด
**<u>และนอกจากนี้</u>**
เรายังใช้ **cv2.imread** ในการอ่านไฟล์รูปภาพออกมาเป็น Array เพื่อที่จะสามารถอ่านค่าออกมาได้เป็นค่า 3 มิติ หรือ BGR ได้และเราได้ทำการแปลงจากค่า BGR เป็นค่า RGB เพื่อใช้ในการประมาณผลของรูปภาพได้ และ เราได้ทำการกำหนด One-Hot encoded ด้วยว่า ถ้า l = 1,0 จะมีค่าเป็น Actinic keratosis(AK) ถ้า l = 0,1 จะมีค่าเป็น Basal cell carcinoma (BCC) กรณีที่เกิดการ Error เราได้ทำการใส่ except เพื่อป้องกันไม่ให้โปรแกรมหยุดทำงานในการโหลดรูปภาพได้ และเราจะทำการส่งค่ากลับไปrawImgs และ labelsด้วยคำสั่ง return 

**<u>ขั้นตอนที่ 7</u>**
ทำการโหลดข้อมูลของโมเดลในการ train และ test

```
x_train, y_train = img2data(trainImg)
x_test, y_test = img2data(testImg)
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lpcqcywrdt1zd2wmqt54.png)
ในขั้นตอนนี้เราได้ทำการโหลดรูปภาพที่ใช้สำหรับ train และ test model เอาไว้
**x_train:** มีหน้าที่ในการเก็บข้อมูลของรูปภาพไว้ที่ใช้ในการ train model
**y_train:** มีหน้าที่ในการเก็บข้อมูลlayerของรูปภาพไว้ที่ใช้ในการ train model

**x_test:** มีหน้าที่ในการเก็บข้อมูลของรูปภาพไว้ที่ใช้ในการ test model
**y_test:** มีหน้าที่ในการเก็บข้อมูลlayerของรูปภาพไว้ที่ใช้ในการ test model

**<u>ขั้นตอนที่ 8</u>**
แปลงข้อมูลรูปภาพและข้อมูลlayerของรูปภาพ

```
x_train = np.array(x_train)
y_train = np.array(y_train)
x_test = np.array(x_test)
y_test = np.array(y_test)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /=255
x_test /=255
```
โดยขั้นตอนนี้เราจะทำการ แปลงข้อมูลรูปภาพและข้อมูลlayerเป็น NumPy arrays และ Normalize ข้อมูลรูปภาพก่อนป้อนข้อมูลเข้าสู่model Convolutional Neural Network
โดยเราจะแปลง **x_train** , **y_train** , **x_test** , **y_test**  เป็น NumPy arrays และ **x_train** , **x_test**  ให้เป็นชนิดข้อมูล float32 และทพการแบ่งกลุ่มให้เป็น 255

**<u>ขั้นตอนที่ 9</u>**
สร้างmodel Xception สำหรับงานจำแนกภาพ Actinic keratosis (AK) และ Basal cell carcinoma (BCC)
```
def create_xception_model(input_shape=(width, width, 3), num_classes=2):
  base_model = Xception(weights='imagenet', include_top=False, input_shape=input_shape)

  base_model.trainable = False

  x = base_model.output
  x = Flatten()(x)
  x = Dense(128, activation='relu')(x)
  x = Dropout(0.5)(x)  # Regularization
  predictions = Dense(num_classes, activation='softmax')(x)

  model = Sequential([
    base_model,
    Flatten(),
    Dense(128, activation='relu'),
    Dropout(0.5),
    Dense(num_classes, activation='softmax')
])

  return model


model = create_xception_model(input_shape=(width, width, 3), num_classes=num_classes)
```
<u>**ฟังก์ชัน create_xception_model:**</u> ฟังก์ชันนี้รับสองค่า
**input_shape:** รูปร่างของข้อมูลรูปภาพ (width, width, 3)
**num_classes:** จำนวนคลาสสำหรับงานจำแนกภาพ
**<u>ฟังก์ชันในการสร้าง model xception:</u>**
ใช้ Xception(weights='imagenet', include_top=False, input_shape=input_shape)โหลดmodel Xception ที่ผ่านการtrainชุดข้อมูล ImageNet และทำการ ตั้งค่า include_top=False เพื่อไม่รวมชั้นสุดท้าย (Fully-connected) และทำการระบุ input_shape ของmodel และ base_model.trainable = False เพื่อป้องกันไม่ให้model Xception ที่โหลดมาถูกปรับพารามิเตอร์ระหว่างการฝึกmodelใหม่
**base_model.output:** รับoutputจากmodel Xception
**Flatten()(x):** แปลงข้อมูลจากโครงสร้าง 3 มิติ (width, height, channels) เป็น 1 มิติ
**Dense(128, activation='relu')(x):** เพิ่มชั้น Fully-connected ใหม่ที่มี 128 neurons network
**Dropout(0.5)(x):**เพิ่มชั้น Dropout ช่วยลดปัญหา overfitting โดยตัดการเชื่อมต่อ neurons network บางส่วน
**Dense(num_classes, activation='softmax')(x):** เพิ่มชั้น Fully-connected ใหม่ที่มีจำนวน neurons network เท่ากับจำนวนคลาส และ แปลงค่าเอาต์พุตเป็นค่าความน่าจะเป็นสำหรับแต่ละคลาส
และทำการส่งค่ากลับไปด้วย return
และทำการสร้างโมเดลด้วยคำสั่ง create_xception_model(input_shape=(width, width, 3), num_classes=num_classes)

**<u>ขั้นตอนที่ 10</u>**
ตั้งค่าการ train model สำหรับ xception

```
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), 
              loss='categorical_crossentropy', 
              metrics= ['accuracy'])
batch_size = 32
epochs = 10
```
โค้ดนี้ตั้งค่าhyperparameterที่สำคัญสำหรับtrain model Xception
โดยเราได้ตั้งค่า epochs ในการ train model จำนวน 10 ครั้ง และตั้ง จำนวนตัวอย่างข้อมูลที่model xception จะใช้ในการคำนวณการไล่ระดับสี แต่ละครั้งระหว่างการtrain อยู่ที่ 32 

**<u>ขั้นตอนที่ 11</u>**
เริ่มการtrain model 

```
history = model.fit(x_train, y_train ,batch_size=batch_size, epochs=epochs ,validation_data=(x_test, y_test))

```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65x8omdtep6f17epoesh.png)
คำสั่ง model.fit คือเริ่มทำการ train model โดยเราจะ train model ด้วย x_train และ y_train validation_data ใช้สำหรับประเมินประสิทธิภาพของModelย้อนหลังระหว่าง Train model ตามด้วยจำนวน batch_size ที่32 และกำหนดค่า epochs อยู่ที่10 

**<u>ขั้นตอนที่ 12</u>**
plot graph model ดูค่า accuracy(ความแม่นยำ)และค่า loss(ค่าความผิดพลาด) ของ model ที่เปรียบเทียบActinic keratosis (AK) และ Basal cell carcinoma (BCC)

```
    plt.figure(figsize=(10,4))
    plt.subplot(121),
    plt.title('model accuracy');plt.ylabel('accuracy');plt.xlabel('epoch')
    plt.legend(['train', 'validation'], loc='upper left')
    plt.plot(history.history['accuracy']);plt.plot(history.history['val_accuracy'])

    plt.subplot(122)
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train', 'validation'], loc='upper left')
    plt.plot(history.history['loss']);plt.plot(history.history['val_loss'])


    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    avg_acc = np.mean(acc) * 100
    avg_val_acc = np.mean(val_acc) * 100


    print("Average training accuracy:", avg_acc, "%")
    print("Average validation accuracy:", avg_val_acc, "%")

```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx65h43m487lsc6k7q28.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds6llsv4flcg9t06ai2o.png)
**<u>ผลลัพธ์</u>**
จากกราฟเมื่อได้ทดลองในการ train 10รอบพบว่า
**มีความแม่นยำในการtrain อยู่ที่ 89.84%**
**มีความแม่นยำในการทดสอบ อยู่ที่ 76.12%**

**<u>ขั้นตอนที่ 13</u>**
ทดสอบmodel

```
testpath = 'test/'
testImg = [testpath+f for f in listdir(testpath) if listdir(join(testpath, f))]
rimg = []
for imagePath in (testImg):
    for item in (os.listdir(imagePath)):
        file = os.path.join(imagePath, item)
        if item.split('.')[0] != "":
           
          img = cv2.imread(file , cv2.COLOR_BGR2RGB)
          ori = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
          img = cv2.resize(img ,(width,width))
          rimg = np.array(img)
          rimg = rimg.astype('float32')
          rimg /= 255
          rimg = np.reshape(rimg ,(1,128,128,3))
          predict = model.predict(rimg)
          label = ['Actinickeratosis','BasalCellCarcinoma']
          result = label[np.argmax(predict)]
          print(predict)
          print('real:'+str(item))
          print('predict:'+str(result))
          plt.imshow(ori)
          plt.show()


```
จากการทดสอบนี้พบว่า สามารถทำนายแยกโรคผิวหนังระหว่าง Actinic keratosis (AK) และ Basal cell carcinoma (BCC) ได้โดยใช้ model ที่ถูก Train มาจากข้างต้น

**Result**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbb3olbxs1yg80adz57x.png)
จากการทดสอบรูปนี้พบว่าค่า One-Hot encoded [ 0.11 , 0.88 ] สังเกตได้ว่า 0.88 มีค่าเข้าใกล้เลข 1 มากกว่า 0.11 ดังนั้นผลลัพธ์จึงเป็น Basal cell carcinoma (BCC) โดยถ้านำ 0.88 เปลี่ยนเป็นเปอร์เซ็นต์แล้วผลลัพธ์ที่ได้คือ 88%


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dn087v25epi6wsgk3v9.png)
จากการทดสอบรูปนี้พบว่าค่า One-Hot encoded [ 0.99 , 0.00 ] สังเกตได้ว่า 0.99 มีค่าเข้าใกล้เลข 1 มากกว่า 0.00 ดังนั้นผลลัพธ์จึงเป็น Actinic keratosis (AK)โดยถ้านำ 0.99 เปลี่ยนเป็นเปอร์เซ็นต์แล้วผลลัพธ์ที่ได้คือ 99%

สรุปผล

ผลการทดลองแสดงให้เห็นว่าการใช้ OPEN CV กับ model Xception สามารถจำแนก Actinic keratosis (AK) และ Basal cell carcinoma (BCC) ได้ความแม่นยำอยู่ที่ 76.12% อย่างไรก็ตามถ้าเพิ่มประสิทธิภาพในการประมาณผล เช่น เพิ่มความละเอียดของรูปภาพ หรือ ลองเปลี่ยน model มาใช้ VGG16 หรือ MobileNetV2 อาจจะเพิ่มประสิทธิภาพได้การประมวลผลได้

อ้างอิง
https://opencv.org/
https://www.tensorflow.org/
https://keras.io/api/applications/xception/
https://www.kaggle.com/datasets/riyaelizashaju/skin-disease-image-dataset-balanced/data
https://www.webmd.com/melanoma-skin-cancer/default.htm