OCR คืออะไรและทำไมภาพถึงสำคัญ
เวลาเราถ่ายรูปเอกสารแล้วอยากให้คอมพิวเตอร์อ่านข้อความออกมาเป็นไฟล์ที่เราแก้ไขได้ เราจะใช้เทคนิคที่เรียกว่า OCR (Optical Character Recognition หรือ การแปลงภาพตัวอักษรให้เป็นข้อความที่แก้ไขได้) ซึ่งเปรียบเสมือนดวงตาของโปรแกรมที่คอยจ้องมองภาพแล้วเดาว่าลายเส้นเหล่านั้นคือตัวอักษรตัวไหน
ลองนึกภาพว่าคุณกำลังพยายามอ่านหนังสือในที่มืดสนิทหรือในห้องที่มีควันฟุ้งกระจาย คุณย่อมอ่านไม่ออกแน่นอน OCR ก็เช่นกัน ถ้าภาพที่ส่งเข้าไปมีคุณภาพแย่ ต่อให้ใช้โปรแกรมที่ฉลาดแค่ไหน ผลลัพธ์ที่ได้ก็มักจะผิดพลาดหรืออ่านไม่ออกเลย
ปัญหาที่พบบ่อยคือภาพเอียง แสงน้อย หรือมีจุดรบกวนเต็มไปหมด สิ่งเหล่านี้คือตัวการที่ทำให้โปรแกรมทำงานพลาด นักพัฒนาซอฟต์แวร์ ส่วนใหญ่มักจะพยายามแก้ปัญหาด้วยการหาโมเดลใหม่ที่เก่งกว่าเดิม ทั้งที่จริงแล้ววิธีแก้ที่ง่ายและได้ผลดีที่สุดคือการทำความสะอาดภาพก่อนส่งไปให้โปรแกรมอ่าน
รู้จักกับ PrepOCR ตัวช่วยทำความสะอาดภาพ
ผมสร้าง PrepOCR ขึ้นมาเพื่อเป็นเครื่องมือเล็กๆ ที่ช่วยจัดการภาพก่อนส่งเข้ากระบวนการอ่านข้อความ หน้าที่หลักของมันคือการปรับแต่งภาพให้ชัดเจนที่สุด เพื่อให้โปรแกรมอ่านตัวอักษรทำงานได้แม่นยำขึ้นโดยไม่ต้องพึ่งพาระบบที่ซับซ้อน
มันถูกออกแบบมาให้เน้นทำสิ่งที่จำเป็นจริงๆ เช่น การหมุนภาพให้ตรง การเพิ่มความคมชัด และการเปลี่ยนภาพให้เป็นขาวดำสนิท ซึ่งขั้นตอนสุดท้ายนี้ถือเป็นกุญแจสำคัญที่ทำให้ OCR ทำงานได้แม่นยำขึ้นแบบก้าวกระโดดสำหรับเอกสารที่เป็นกระดาษ
เครื่องมือนี้เหมาะสำหรับคนที่ต้องจัดการกับเอกสารจำนวนมาก หรือคนที่กำลังสร้างระบบอ่านฟอร์มต่างๆ คุณสามารถอัปโหลดไฟล์ภาพเข้าไป เลือกการปรับแต่งที่ต้องการ แล้วระบบจะส่งไฟล์ที่ถูกปรับปรุงคุณภาพแล้วกลับมาให้ในรูปแบบไฟล์บีบอัด (Zip)
ขั้นตอนการทำงานของระบบหลังบ้าน
ในส่วนของ Backend (ระบบหลังบ้านที่ทำงานบนเซิร์ฟเวอร์) ผมใช้ภาษา Scala ร่วมกับเฟรมเวิร์ก http4s ซึ่งเป็นเครื่องมือที่จัดการการรับส่งข้อมูลได้รวดเร็ว โดยกระบวนการเริ่มต้นจากการรับไฟล์ผ่านคำสั่ง Multipart Request (การส่งข้อมูลหลายส่วนในคราวเดียว)
เมื่อได้ไฟล์มา ระบบจะตรวจสอบความถูกต้องทันที ไม่ใช่แค่ดูที่นามสกุลไฟล์ แต่เราเช็คไปถึง Magic Bytes (เลขรหัสเฉพาะที่บอกประเภทไฟล์จริงๆ) เพื่อให้มั่นใจว่าเป็นไฟล์รูปภาพที่ถูกต้องจริงๆ หลังจากนั้นจึงเข้าสู่ขั้นตอนการประมวลผลตามที่ผู้ใช้เลือกไว้
ความท้าทายหนึ่งคือการจัดการไฟล์จำนวนมากพร้อมกัน เพราะการหมุนภาพหรือปรับสีใช้พลังของ CPU (หน่วยประมวลผลกลาง) ค่อนข้างสูง ผมจึงเลือกใช้การประมวลผลแบบขนานเพื่อให้งานเสร็จเร็วขึ้น แทนที่จะรอทำทีละไฟล์ตามลำดับ
// ตัวอย่างการประมวลผลภาพแบบขนานใน Scala
val images = List(img1, img2, img3, img4)
// ใช้ parTraverseN เพื่อรันคำสั่งหลายตัวพร้อมกัน
images.parTraverseN(4)(processImage)
บรรทัดแรกคือการสร้างรายการรูปภาพที่ต้องการประมวลผล ส่วนบรรทัดที่สองใช้ฟังก์ชัน parTraverseN เพื่อบอกให้ระบบแบ่งงานออกเป็น 4 ส่วนทำงานพร้อมกันในเวลาเดียวกัน ผลลัพธ์ที่ได้คือเวลาในการประมวลผลลดลงจาก 7 วินาที เหลือเพียง 2 วินาทีบนเครื่องที่มี 14 คอร์
บทเรียนสำคัญเรื่องทรัพยากรเซิร์ฟเวอร์
ตอนที่ผมเอาไปรันบนโฮสต์ฟรี ผมเจอปัญหาที่น่าสนใจคือความเร็วไม่ได้เร็วขึ้นตามที่คาดหวังไว้ เพราะเซิร์ฟเวอร์จำกัดการใช้งาน CPU ไว้ค่อนข้างเข้มงวด ทำให้คำว่า "ทำงานพร้อมกัน" กลายเป็นแค่การสลับคิวงานทำงานไปมาอย่างรวดเร็วเท่านั้น
บทเรียนราคาแพงที่ผมได้รับคือคำสั่ง Runtime.getRuntime.availableProcessors() (คำสั่งเช็คจำนวนหน่วยประมวลผลที่มี) มักจะหลอกเราถ้าเราใช้งานในสภาพแวดล้อมที่แชร์ทรัพยากรร่วมกับคนอื่น ระบบจะบอกว่ามีคอร์เยอะ แต่ในความเป็นจริงเราถูกจำกัดการใช้งานไว้ไม่ให้เกินขีดจำกัดที่กำหนด
สำหรับมือใหม่ที่กำลังทำโปรเจกต์ อย่าเพิ่งตกใจถ้าโค้ดที่เขียนว่าเร็วบนเครื่องตัวเอง แต่พอเอาขึ้น Deploy (การเอาโค้ดขึ้นเซิร์ฟเวอร์จริง) แล้วช้าลง ให้จำไว้ว่าสภาพแวดล้อมที่ต่างกันส่งผลกระทบต่อประสิทธิภาพการทำงานได้มหาศาลเสมอ
แนวทางการพัฒนาเพื่อมือใหม่
การสร้าง PrepOCR ครั้งนี้เป็นเพียง MVP (สินค้าที่มีฟีเจอร์น้อยที่สุดพอที่จะใช้งานได้จริง) ผมสร้างขึ้นมาเพราะผมต้องการใช้เองจริงๆ ไม่ใช่เพราะมีงานวิจัยรองรับความต้องการของตลาด การเริ่มทำโปรเจกต์จากปัญหาที่เราเจอเองเป็นวิธีฝึกทักษะที่ดีที่สุด
ถ้าคุณกำลังฝึกเขียนโค้ด ผมแนะนำให้ลองสร้างเครื่องมือเล็กๆ ที่แก้ปัญหาในชีวิตประจำวันของคุณดู ไม่ว่าจะเป็นการย่อขนาดไฟล์ การเปลี่ยนชื่อไฟล์อัตโนมัติ หรือการดึงข้อมูลจากหน้าเว็บ การทำโปรเจกต์เล็กๆ แบบนี้จะทำให้คุณเข้าใจพื้นฐานของ HTTP (โปรโตคอลหลักในการรับส่งข้อมูลบนเว็บ) และการจัดการไฟล์ได้ดีกว่าการอ่านตำรา
อย่ากลัวที่จะเริ่มทำอะไรที่ดูเหมือนจะมีคนทำไปแล้ว เพราะหัวใจสำคัญไม่ใช่การสร้างสิ่งใหม่ที่ไม่เคยมีมาก่อน แต่คือการได้ลงมือเขียนโค้ดจริงๆ ได้เจอบั๊ก ได้ลองแก้ปัญหา และได้เรียนรู้ว่าเครื่องมือแต่ละอย่างทำงานร่วมกันอย่างไรในโลกความเป็นจริง
สรุป: การนำไปใช้จริง
หากคุณมีโปรเจกต์ที่ต้องจัดการเอกสารสแกน แล้วพบว่าโปรแกรมอ่านข้อความของคุณทำงานได้ไม่แม่นยำ ลองย้อนกลับมาดูที่ต้นทางคือ "คุณภาพของภาพ" ก่อนเสมอ การใช้การทำ Binarization (การเปลี่ยนภาพให้เป็นขาวดำสนิท) จะช่วยให้ตัวอักษรโดดเด่นขึ้นมาทันที
ตัวอย่างการนำไปใช้จริง: สมมติคุณทำแอปพลิเคชันเก็บใบเสร็จรับเงิน คุณสามารถใช้ PrepOCR เป็นขั้นตอนแรก เพื่อปรับภาพใบเสร็จให้ตรงและคมชัด ก่อนส่งภาพนั้นเข้าโมเดลอ่านข้อความ Google Vision หรือ Tesseract จะทำให้แอปของคุณอ่านยอดเงินได้แม่นยำขึ้นหลายเท่าตัว
จำไว้ว่าโปรแกรมเมอร์ที่เก่ง ไม่ได้ดูที่ว่าเขียนโค้ดซับซ้อนแค่ไหน แต่ดูที่ว่าแก้ปัญหาให้ผู้ใช้ได้ตรงจุดและเรียบง่ายเพียงใด ลองเอาแนวคิดการเตรียมข้อมูลก่อนนำไปประมวลผลไปปรับใช้กับทุกงานที่คุณทำ แล้วคุณจะเห็นความเปลี่ยนแปลงของผลลัพธ์อย่างชัดเจน
ที่มา: I built a free image preprocessor to get better OCR results (PrepOCR) — DEV Community