Part 4: Optical Character Recognition with TensorFlow and Keras
Usage of handwriting recognition model
Blog ตอนที่ 4 แล้วสำหรับ Optical Character Recognition (OCR) เนื้อหาของตอนนี้เป็นเรื่องเกี่ยวกับการนำโมเดลที่ Trained เรียบร้อยแล้ว มาลองใช้งานกับข้อมูลจริง อาจยังไม่ได้ Scale ให้เป็นประโยคยาว ๆ หรือเป็นหน้ากระดาษ แต่ก็เป็นข้อมูล Input ที่เป็นรูปภาพที่เกิดจากการลายมือของตัวเองจริง ๆ
สำหรับเนื้อหาของทั้ง 3 ตอนก่อนหน้านี้ อยากให้ลองย้อนกลับไปอ่าน เพราะมีรายละเอียดตั้งแต่ 1.การเตรียมข้อมูล 2.การใช้ CNN พัฒนาโมเดลขึ้นมาเอง และ 3.การใช้ Pre-trained model ทำ Transfer learning เพื่อสร้างโมเดล
Input
Input ในที่นี้ไม่ได้หมายถึงแค่รูปภาพ แต่ยังเป็น OCR Model ที่พัฒนามาจากตอนก่อนหน้านี้ทั้ง CNN Model หรือ ResNet50 Model ให้ load_model() เพื่อใช้เป็น Predictor สำหรับการทำ OCR
เขียนเป็นฟังก์ชั่นสำหรับการแสดงผลรูปภาพในแต่ละขั้นตอน เพื่อให้สามารถเรียกใช้งานได้ง่าย และลดปริมาณการเขียน Code
อ่านรูปภาพที่ต้องการทดสอบใช้งานกับ Model ที่สร้างมาจาก Public dataset ภาพนี้เป็นข้อความที่เขียนด้วยลายมือ บนกระดาษ และถ่ายด้วยกล้อง iPhone XR สำหรับใช้ในการทดสอบ
Image processing
ขั้นตอนต่อมาเป็นการทำ Image processing ภาพรวมของขั้นตอนนี้คือการ Process รูปภาพที่รับเข้ามา เพื่อให้ทำงานกับ Model ได้ แต่ขั้นตอนแรกสุดคือ การทำ Data cleansing เพื่อให้แสดงเฉพาะข้อมูลที่ต้องการในที่นี้คือตัวอักษร โดยไม่สนใจข้อมูลอื่น ๆ เช่น เงา ฝุ่น หรือใด ๆ ที่ไม่ได้เป็นตัวอักษร
Image processing ขั้นแรกคือการเปลี่ยนรูปภาพ Input จากภาพสี RGB ให้เป็น Gray scale หรือภาพขาวดำ เพราะ Deep learning ทำงานได้ดีกับภาพขาวดำมากกว่า
ต่อมาเป็นการใส่ Blur effect ให้กับรูปภาพ เหตุผลคือต้องการลด Noise ในรูปภาพให้ได้ในระดับหนึ่ง
เมื่อได้รูปภาพที่ Noise ลดลงแล้ว ให้เปลี่ยนรูปภาพนั้นจาก Gray scale ให้เป็น Binary ที่มีเฉพาะขาวหรือดำ สิ่งที่ต้องระวังในขั้นตอนนี้คือ การกำหนด Threshold เพราะถ้ากำหนดมากเกินไป ส่วนที่เป็น Shadow ในภาพจะโดนทับด้วยสีดำ หรือถ้ากำหนดน้อยไป ส่วนที่เป็น Highlight ในภาพจะโดนแทนที่ด้วยสีขาว
Threshold ที่ดีควรเป็นตัวเลขที่ทำให้ตัวอักษรทั้งหมดปรากฎชัดเจน อาจมีขาดหายไปบ้าง แต่ตราบใดที่ยังสามารถอ่านข้อความได้ ถือว่าเป็นค่าที่ใช้ได้
เมื่อได้ภาพที่เป็น Binary แล้ว ต่อไปเป็นการหา Object ที่อยู่ในภาพทั้งหมด ไม่ว่าจะเป็นตัวอักษร หรือ Noise การที่จะได้มาซึ่งรูปร่าง Object สามารถใช้วิธีการ “หาขอบ” ของ Object นั้น ๆ
แสดงตัวอย่างจากรูปข้อมูลดิบ และ Object ที่หาเจอทั้งหมดจากขั้นตอนการทำ Image processing ขั้นตอนต่อไปเป็นการ Extract เอา Object เหล่านี้ เพื่อนำไปใช้งานในโมเดลต่อ
Features extraction
การทำ Features extraction ในภาพรวมทั้งหมดคือ การแยกสิ่งที่ต้องการออกจากสิ่งที่ไม่ต้องการ และนำสิ่งที่ต้องการนั้นไป Processes ต่อเพื่อให้ใช้งานกับ Model ได้
- เขียนเป็น Iteration ใน Array ที่เก็บข้อมูลขอบของ Object เอาไว้
- หาพิกัด
(x, y, w, h)
ของแต่ละ Object ด้วยcv2.boundRect()
- จากนั้นกำหนดขนาดเพื่อป้องกัน Noise ที่อาจมีอยู่ในรูปภาพ เพื่อผ่านเงื่อนไขดังกล่าวแล้ว ให้เก็บพิกัดของ Object นั้นไว้
- **ใช้รูป Gray scale ในการ Crop เอารูป Object นั้นออกมา ไม่จำเป็นต้องใช้รูป Blur effect หรือ Binary เพราะทั้ง 2 Processes นั้นมีไว้เพื่อการหาขอบ Object ที่แม่นยำขึ้นเท่านั้น แต่ข้อมูลที่จะใส่เข้าใน Model ยังคงเป็น Gray scale
- นำรูป Cropped ที่ได้ Convert เป็น Binary ที่มี Threshold ระหว่าง 0–255 รูป Binary ก่อนหน้าใช้ Threshold นี้ไม่ได้ เพราะยังมี Noise อยู่ในรูปเยอะเกินไป (นี่คือเหตุผลว่าทำไมต้องใช้จากรูป Gray scale)
- Resize ให้ Binary object มีขนาด 32x32 สำหรับใช้ใน ResNet50 Trained model
- สร้างกรอบรูปให้ Resized object ด้วย
cv2.copyMakeBorder()
ให้มีระยะจากขอบเท่า ๆ กันทุกด้าน cv2.resize()
ให้ทั้งกรอบรูปมีขนาด 32x32- จากนั้นเปลี่ยนข้อมูลเป็น
np.float32
และ Normalised data/ 255.0
- เนื่องจาก ResNet50 ต้องรับข้อมูลเป็น Array 3-Channels (32, 32, 3) แต่ข้อมูลที่มีตอนนี้มีมิติเท่ากับ (32, 32, 1) ดังนั้นจึงต้องเพิ่มมิติข้อมูลให้เป็น 3-Channels แม้ว่าเป็นรูปขาวดำก็ตาม ใช้
np.stack(*3)
- สุดท้ายเป็นเก็บข้อมูลแต่ละ Object ไว้ใน List
- สำหรับข้อมูลที่ไม่ผ่านเงื่อนไขที่ตั้งไว้ตอนแรก อาจ Extract ออกมาด้วยเช่นกัน แต่ไม่ต้องผ่าน Process ใด ๆ เพราะต้องการแสดงแค่ Noise ที่มีในรูปเท่านั้น
ข้อมูลที่ได้ออกมาคือตัวอักษรที่อยู่ในรูปภาพทั้งหมดคือ HILENGYI โดยถูกเปลี่ยนให้มีขนาด 32x32 พร้อมเข้า ResNet50 Model แล้ว
แสดงรูปภาพ Nosie ที่หาไม่เจอแล้วด้วยสายตา แต่ Code หาเจอทั้งหมด 9 Pixels ด้วยกัน
Result
เปลี่ยน List ให้เป็น Numpy array และสร้างคำตอบที่เป็นไปได้ทั้งหมด โดยต้องมีลำดับที่เหมือนจาก Development process จากนั้นใช้ model.predict()
และผ่าน Array ของ Characters ส่วนที่เป็น batch_size
ควรใช้ขนาดเดียวกับช่วงที่ Training model ขึ้นมา สุดท้ายใช้ .max()
เพื่อหา Index ที่มี Probability มากที่สุด เพื่อนำไปเทียบกับ labelNames
และได้เป็นคำตอบออกมา
วาดคำตอบที่ได้ลงในภาพ พร้อมกับวาดกรอบเพื่อบอกว่ากำลัง Prediction ที่ตัวอักษรใด โดยตำแหน่งของกรอบคือ พิกัดที่ได้จาก Iteration ก่อนหน้านี้
Predict label: H
Predict label: I
Predict label: L
Predict label: E
Predict label: N
Predict label: G
Predict label: 4
Predict label: I
แสดงผลลัพธ์สุดท้ายบนรูปที่เป็น Input data
จากผลลัพธ์เห็นได้ว่า Model สามารถทำได้ค่อนข้างแม่งยำ แต่มีหลุดที่ตัว Y แต่โมเดลมองว่าเป็นเลข 4 แต่ตัวอักษรที่เหลือ Model สามารถทายผลได้ถูกต้องทั้งหมด
Conclusion
จบแล้วสำหรับ Blog ตอนที่ควรจะต่อเนื่องแต่กลับลากยาวข้ามปี ถือว่าเป็นแบบการฝึกใช้ TensorFlow ในการทำ Model ที่ดีอันนึง นอกจากนี้ยังได้ฝึกใช้งาน OpenCV สำหรับงานที่เป็น Image processing เพิ่มเติมด้วย
สำหรับ Colab notebook ของเนื้อหาทั้งตอน สามารถดูได้ที่ Link ด้านบน