รีวิว EasyOCR: ผลลัพธ์ข้อความคมชัดดีมาก แต่ติดเพดานเรื่องขนาด และกิน RAM CPU ราว 1 GB

อัปเดตล่าสุดเมื่อ August 17, 2026
รีวิว EasyOCR: ผลลัพธ์ข้อความคมชัดดีมาก แต่ติดเพดานเรื่องขนาด และกิน RAM CPU ราว 1 GB
สรุปด้วย AI

EasyOCR คือไลบรารี OCR แบบพร้อมใช้ของ JaidedAI สำหรับ Python: แค่ pip install easyocr แล้วเขียนโค้ดเพียงสองบรรทัด ข้อความที่ฝังอยู่ในรูปก็ถูกดึงกลับมาเป็นสตริงได้ มันใช้สัญญาอนุญาต Apache-2.0 รองรับมากกว่า 80 ภาษา และทำงานเป็นไปป์ไลน์ของโมเดล PyTorch 2 ตัว — ตัวตรวจจับ CRAFT ที่วาดกรอบรอบบริเวณที่คิดว่าเป็นข้อความ จากนั้นตัวรู้จำจะอ่านตัวอักษรภายในกรอบนั้น น้ำหนักโมเดลที่ฝึกไว้จะดาวน์โหลดเองอัตโนมัติเมื่อเรียกใช้ครั้งแรก ในภาพรวมมันคือทางเลือกแบบ self-hosted แทน Tesseract และ PaddleOCR ไม่ใช่ OCR แบบ cloud ต่อหน้าทีละหน้า

EasyOCR คือไลบรารี OCR แบบพร้อมใช้ของ JaidedAI สำหรับ Python: แค่ pip install easyocr แล้วเขียนโค้ดไม่กี่บรรทัด ข้อความที่ฝังอยู่ในรูปก็ถูกดึงกลับมาเป็นสตริงได้เลย ตัวนี้ใช้สัญญาอนุญาต Apache-2.0 รองรับมากกว่า 80 ภาษา และทำงานเป็นไปป์ไลน์ของโมเดล PyTorch 2 ตัว — ตัวตรวจจับ CRAFT ที่วาดกรอบรอบบริเวณที่คิดว่าเป็นข้อความ จากนั้นตัวรู้จำจะอ่านตัวอักษรภายในกรอบนั้น น้ำหนักโมเดลที่ฝึกไว้จะดาวน์โหลดเองอัตโนมัติเมื่อเรียกใช้ครั้งแรก โดยรวมแล้วมันคือทางเลือกแบบ self-hosted แทน Tesseract และ PaddleOCR ไม่ใช่ OCR แบบ cloud ทีละหน้า

API พื้นฐานสั้นมาก: Reader(['en']) แล้วตามด้วย readtext() แต่ฝั่งการติดตั้งและใช้งานจริงไม่เบา — ในสภาพแวดล้อมนี้กิน PyTorch เกือบ 2 GB และใช้หน่วยความจำ resident สูงสุดราว 1 GB ตอนเริ่มโปรเซสใหม่ที่วัดไว้ ผมเรนเดอร์ไฟล์ PNG ภาษาอังกฤษ 36 ชิ้น แล้วรัน easyocr 1.7.2 บน CPU จากนั้นให้คะแนนการอ่านแบบตัวอักษรต่อ ตัวอักษรเทียบกับ ground truth ที่สร้างขึ้นเอง ขนาดตัวอักษรและการหมุนเป็นตัวทำให้ค่า CER ดิ่งแรงที่สุด; แดชบอร์ดยังเผยให้เห็นปัญหาการจับ token สั้น ๆ และการอ่านสัญลักษณ์ดอลลาร์ผิดด้วย

ความพังที่ชัดที่สุดเกี่ยวข้องกับพารามิเตอร์เดียวที่ทุกคนมักแนะนำกัน rotation_info มีชื่อเสียงใน issue tracker ว่าเป็น ตัวแก้ สำหรับภาพที่หมุนเอียง จึงนำมาทดสอบกับสำเนาประโยคเดียวกันที่หมุนตั้งฉาก 3 แบบ ที่ 270° มันทำงานตามที่โฆษณาไว้จริง ลด character error rate จาก 0.83 เหลือ 0.10 ที่ 180° มันช่วยได้แค่ครึ่งเดียว: 0.85 เหลือ 0.67 โดยประโยคหนึ่งหายไป ที่ 90° มันกลับแย่ลง: 0.81 ไปเป็น 0.92 และตัวรู้จำเริ่มคืนข้อความกลับด้าน พารามิเตอร์เดียว รายการมุมเดียวกัน แต่ได้ผลลัพธ์ 3 แบบ — ดังนั้นการเปิดมันไม่ได้แปลว่า “รองรับการหมุนแล้ว” ชุด fixture ทั้ง 36 ชิ้นยังทำให้เห็นกำแพงเรื่องขนาดฟอนต์อย่างชัดเจน การอ่านสัญลักษณ์ดอลลาร์ผิดแบบเป็นระบบ และมีหนึ่งข้อสรุปของผมเองที่ผิดไปแบบตรง ๆ

สองโมเดลที่ทำงานซ้อนกันเหมือนใส่เสื้อโค้ตทับกัน

EasyOCR ไม่ได้มีโมเดลเดียว แต่มันคือไปป์ไลน์ของสองโมเดล และการรู้ว่าพังตรงขั้นไหนจะเปลี่ยนวิธี debug ไปเลย

ขั้นแรกคือ CRAFT ตัว detector หน้าที่เดียวของมันคือการระบุตำแหน่ง — ตัดสินว่าในภาพตรงไหนมีข้อความบ้าง แล้วส่งกรอบกลับมา มันไม่ได้อ่านตัวอักษรแม้แต่ตัวเดียว ขั้นที่สองคือ CRNN ตัว recognizer — ใช้ ResNet สกัดฟีเจอร์, ตามด้วย BiLSTM, แล้วปิดด้วย CTC greedy decoding เพื่ออ่านตัวอักษรในแต่ละกรอบ ทั้งสองรันบน PyTorch บน CPU ตัว recognizer จะถูก quantize แบบไดนามิกเป็น int8 โดยค่าเริ่มต้น จึงเร็วและเบากว่าที่จำนวนพารามิเตอร์ดิบ ๆ จะทำให้คิด

System diagram: Detection Before Recognition

ผลทางปฏิบัติคือ EasyOCR มีโหมดล้มเหลวสองแบบที่ต่างกันสิ้นเชิง และต้องแก้คนละวิธี ถ้าตัวตรวจจับไม่วาดกรอบให้เลย ต่อให้ปรับตัวรู้จำยังไงก็ไม่ช่วย — เพราะตัวอักษรไม่เคยเข้าไปอยู่ในไปป์ไลน์ตั้งแต่แรก ถ้ามีกรอบแต่ข้อความผิด นั่นคือปัญหาของ recognizer และ preprocessing อาจช่วยได้ เกือบทุกกระทู้แนว “EasyOCR มองไม่เห็นข้อความฉัน” ที่ผมอ่านมา มักเอาสองกรณีนี้มารวมกัน

สถานะปัจจุบันของโปรเจกต์ ตรวจเมื่อ 27 กรกฎาคม 2026: ดาว 29,825, 528 issues ที่ยังเปิดอยู่, สัญญาอนุญาต Apache-2.0 และ v1.7.2 จากกันยายน 2024 โดย push ล่าสุดไป master อยู่ในเดือนธันวาคม 2025 วันที่เหล่านี้ไม่ได้พิสูจน์ความเสถียรของสถาปัตยกรรมหรือสุขภาพการดูแลโปรเจกต์ ก่อนใช้งานจริงควรเช็กให้แน่ใจว่าเข้ากับสแต็ก Python/PyTorch ของคุณได้ ดูการตอบสนองของผู้ดูแลล่าสุด และตรวจ issues ที่เกี่ยวกับ input ของคุณโดยตรง

OCR มักถูกโยงกับการแก้ CAPTCHA แต่ไม่ใช่กรณีใช้งานในบทความนี้ ไม่มีการทดสอบเพื่อ และไม่มีสิ่งใดในที่นี้สนับสนุนการเอาชนะกลไก bot-detection งานที่พูดถึงคือการอ่านข้อความจากภาพและสกรีนช็อตที่คุณมีสิทธิ์อ่าน

ผมวัดอะไร และตัวเลขพวกนี้ไม่ได้ครอบคลุมอะไรบ้าง

ชุดทดสอบคือ 36 PNG ที่ผมเรนเดอร์เอง: ภาพข้อความบรรทัดเดียว 35 ภาพ ไล่ครบ 7 ฟอนต์ 8 ขนาด 7 ระดับคอนทราสต์ 7 มุม skew 3 การหมุนตั้งฉาก และ 3 พื้นหลัง — บวกอีก 1 dashboard แบบสังเคราะห์ที่มี 19 องค์ประกอบติดป้ายกำกับแยกกัน ทุกภาพถูกสร้างจากสตริงเดียวกัน (Sphinx of black quartz, judge my vow. 1234567890 — 48 ตัวอักษร มีทั้งตัวพิมพ์ใหญ่เล็ก ตัวเลข และเครื่องหมายวรรคตอน) ในรอบเดียวกับที่เขียนป้าย ground truth จึงไม่มีทางที่ภาพกับฉลากจะเพี้ยนแยกจากกันได้

ความแม่นยำใช้ character error rate (CER): ระยะ Levenshtein แบบตัวอักษรหารด้วยความยาวของ ground truth CER 0 คืออ่านได้สมบูรณ์แบบ CER 0.10 คือประมาณ 1 ตัวอักษรใน 10 ตัวผิด ผมรายงาน CER แบบ sensitive ต่อ case เป็นตัวหลัก และแถมแบบไม่สน case ไว้ด้วย เพราะจุดที่เกิด “error” ส่วนใหญ่จริง ๆ อยู่ที่ตัวพิมพ์ใหญ่เล็ก

ขอบเขตสำคัญกว่าตัวเลขเสียอีก:

  • เฉพาะภาษาอังกฤษ ใช้ recognizer english_g2 EasyOCR โฆษณารองรับมากกว่า 80 ภาษา แต่ผมทดสอบแค่ภาษาเดียว สิ่งนี้ไม่ได้บอกอะไรเกี่ยวกับสคริปต์ที่ไม่ใช่ละติน ซึ่งเป็นจุดที่งานวิจัย OCR เปรียบเทียบกันจริง ๆ
  • เฉพาะภาพสังเคราะห์ เป็นข้อความที่เรนเดอร์ขึ้น ไม่ใช่ภาพถ่าย ไม่มี noise จากกล้อง ไม่มี JPEG artifact ไม่มีแสง ไม่มี perspective
  • ไม่ทดสอบลายมือ ตัวโปรเจกต์เองก็ระบุว่ายังไม่รองรับ
  • CPU เท่านั้น macOS arm64, gpu=False เครื่องนี้มี MPS แต่ EasyOCR จะใช้ CPU กับทุกอย่างที่ไม่ใช่ CUDA จึงไม่ได้วัด performance บน GPU และไม่มีตัวเลข GPU ปรากฏที่ไหนเลย
  • เครื่องเดียว เวอร์ชันเดียว easyocr 1.7.2, torch 2.13.0, Python 3.12

สรุปคือ นี่คือกราฟแบบควบคุมตัวแปรทีละตัวที่แสดงให้เห็นชัดว่า fidelity พังตรงไหนบนตัวอักษรละตินที่เรนเดอร์คม ๆ ไม่ใช่คะแนนจากคอร์ปัสโลกจริง และไม่ได้แทนที่มันได้

หลักฐานทั้งหมดตรวจสอบได้ ไม่ได้ถูกขังอยู่ในโน้ตบุ๊ก ตัวสร้าง fixture และสตริง exact อยู่ใน tests/build_fixtures.py และ tests/fixtures/ground_truth.json; ส่วนการรู้จำ เวลา และการจับ resource อยู่ใน tests/run_easyocr.py; และ tests/metrics.py คำนวณค่า error ที่รายงานจากผลลัพธ์ดิบ บันทึกการรู้จำและค่าเมตริกสรุปเก็บไว้ใน artifacts/raw/ การรันซ้ำโซ่นี้มีประโยชน์ในการเช็กเครื่องและเวอร์ชันนี้ แต่มันก็ยังไม่ตอบว่า EasyOCR จะทำงานกับรูปถ่ายมือถือ ภาษาของคุณ เลย์เอาต์ของคุณ หรือ pipeline preprocess ของคุณอย่างไร ดังนั้นถ้าจะใช้งานจริงควรเพิ่ม input ที่เหมือนของจริงเข้าไป ไม่ใช่เอา fixture ชุดนี้ไปใช้เหมือนใบรับรอง

กับดักหนึ่งของ harness ควรพูดถึง เพราะมันเกือบทำให้ผมรายงานตัวเลขหัวเรื่องผิด ตอนแรก metrics pass แรกบอก CER 0.375 บน Arial สีดำบนพื้นขาวที่สะอาด ซึ่งแย่มากสำหรับ input ที่ง่ายที่สุด ปัญหาไม่ได้มาจาก EasyOCR ตัว detector แยกบรรทัดเดียวออกเป็นกรอบของคำกับกรอบของตัวเลข และการ join แบบ naive ที่เรียงตาม y แล้วค่อย x ของผมดันเอาตัวเลขไปไว้ก่อน แก้โดย group ตามการซ้อนทับในแนวตั้งแล้วอ่านซ้ายไปขวา หลังจากนั้นฟอนต์สะอาด ๆ ก็ลงมาอยู่ราว 0.04–0.10 ถ้าคุณกำลังทำ evaluation OCR ของตัวเอง กับดักนี้ก็รอคุณอยู่เหมือนกัน

การติดตั้ง: ตัวแพ็กเกจเล็ก แต่ dependency ไม่เล็ก

System diagram: Setup: the install is small, the dependency is not

pip install easyocr

มีแค่นี้จริง ๆ แต่ความจริงไม่ได้เบาอะไรเลย ตัวแพ็กเกจเองจิ๋วมาก สิ่งที่มันลากมาด้วยคือ PyTorch ราว 2 GB และเมื่อคุณเรียก readtext() ครั้งแรก EasyOCR จะดาวน์โหลด weights ไปไว้ที่ ~/.EasyOCR/model/ แบบเงียบ ๆ — รวม 93.7 MiB แยกเป็น 79.30 MiB สำหรับ detector CRAFT (craft_mlt_25k.pth) และ 14.44 MiB สำหรับ recognizer ภาษาอังกฤษ (english_g2.pth)

ไม่มีใครบอกสิ่งนี้ชัด ๆ ใน tutorial ดังนั้นการรันครั้งแรกต้องมีอินเทอร์เน็ต และจะหยุดรอการดาวน์โหลด ส่วนการ deploy แบบ container ต้องเลือกเอาว่าจะ bake weights ไว้ใน image หรือยอมเสียเวลาตอน cold start ไปกับการดาวน์โหลด เมื่อ cache แล้วก็ใช้งานแบบ offline ได้

การเริ่มต้นแบบ cold ของ Reader() — เอาโมเดลจากดิสก์เข้ามาใน RAM พร้อมผ่านขั้น quantization แบบ int8 — ใช้เวลา 1.3–1.7 วินาที ในหลายรอบที่ทดสอบ หลังจากนั้น:

import easyocr
reader = easyocr.Reader(['en'], gpu=False)
result = reader.readtext('screenshot.png')

แล้วมันก็ใช้งานได้เลย สองบรรทัด ไม่ต้องตั้งค่า ไม่ต้องไปหาดาวน์โหลด checkpoint เอง คำว่า “easy” ในชื่อโปรแกรมถือว่าตรงในจุดนี้ — ความฝืดอยู่ที่น้ำหนักของ dependency ไม่ใช่ API

พื้นต่ำสุดของข้อความคมชัด: อ่านได้แทบสมบูรณ์ แต่ตัวพิมพ์ใหญ่เล็กยังสะดุด

ฟอนต์ระบบ 7 แบบ ตัวดำบนพื้นขาว ขนาด 32 px ใช้สตริงเดิมทุกครั้ง:

ฟอนต์CER (คำนึงถึงตัวพิมพ์ใหญ่เล็ก)CER (ไม่คำนึงถึงตัวพิมพ์ใหญ่เล็ก)
Georgia0.06250.0000
Times0.04170.0417
Comic Sans0.04170.0208
Arial0.08330.0208
Verdana0.08330.0208
Impact0.08330.0208
Courier0.10420.0417
เฉลี่ย0.07140.0238

CER เฉลี่ยอยู่ที่ 0.071 และลดลงเหลือ 0.024 เมื่อไม่สน case ช่องว่างระหว่างสองค่านี้คือข้อค้นพบ EasyOCR ไม่ได้ทำตัวอักษรหายบนข้อความละตินที่สะอาด แต่มันอ่าน “รูปร่าง” ถูก แล้ว “ตัวพิมพ์ใหญ่เล็ก” ผิด

เจาะลงไปคือ มันอ่านคำตัวเล็ก vow ออกมาเป็น VOW ใน 6 จาก 7 ฟอนต์ (Comic Sans ใจดีหน่อย เปลี่ยนเป็น Vow) ส่วน Impact แถม ofOf มาอีก ทีนี้ข้อผิดพลาดที่เกิดบ่อยอีกอย่างคือเครื่องหมายวรรคตอน: จุดท้ายประโยคถูกอ่านเป็น : หรือ _ ในหลายฟอนต์ Georgia คือ อ่านได้สมบูรณ์แบบ ถ้าคุณไม่แคร์เรื่องตัวพิมพ์ใหญ่เล็ก

นี่เป็นรูปแบบที่มีประโยชน์มาก ถ้าขั้นถัดไปของคุณคือ fuzzy matching, ค้นหาคีย์เวิร์ด หรือส่งข้อความเข้า LLM การสลับ case แทบไม่ทำให้เสียหายอะไร แต่ถ้าขั้นถัดไปคือเทียบสตริงแบบ exact กับ key ในฐานข้อมูล มันเสียหายทั้งหมด ดังนั้น normalize case ก่อนเปรียบเทียบ แล้วครึ่งหนึ่งของ error rate ที่ดูเหมือนว่า EasyOCR ทำพังจะหายไปเอง

ที่ Courier แย่สุด (0.1042) ก็สมเหตุสมผลเหมือนกัน: ฟอนต์ monospace เว้นตัวอักษรกว้างผิดธรรมชาติ ซึ่งยากกับ CTC decoder ที่เรียนรู้ spacing แบบทั่วไปของตัวอักษร

กำแพงเรื่องขนาดอยู่ตรงตำแหน่งที่เอกสารบอกไว้เป๊ะ

Measured results chart: Character error rate by glyph height

readtext() มีพารามิเตอร์ที่ระบุไว้ชื่อ min_size=10 ซึ่งจะทิ้งกรอบที่ตรวจพบซึ่งมีความสูงน้อยกว่า 10 พิกเซล คนส่วนใหญ่มักเลื่อนผ่านมันไป แต่นี่คือค่าที่สำคัญที่สุดใน API สำหรับคนที่ดึงข้อความจาก screenshot หรือ PDF และนี่คือสิ่งที่มันทำเมื่อไล่ความสูงของ glyph:

ขนาดที่เรนเดอร์ (px)CERเกิดอะไรขึ้น
80.7708พังยับ — กรอบตกใต้ฟิลเตอร์ min_size แล้วถูกทิ้ง เหลือเพียงเศษข้อความ
100.1458แย่ลง — ตรงกับขอบล่างพอดี ตัวตรวจจับแตกบรรทัดออกเป็น 3 กรอบ
120.0417กลับมาดีขึ้น
160.0000อ่านได้สมบูรณ์แบบ
200.0208ปกติ
280.0208ปกติ
400.0625ปกติ (ปัญหาสลับ case กลับมา)
640.0625ปกติ (สลับ case)

การกระโดดจาก 0.04 ไปเป็น 0.77 ระหว่าง 12 px กับ 8 px ไม่ใช่การเสื่อมแบบค่อยเป็นค่อยไป แต่มันคือฟิลเตอร์ที่ทำงานตามที่ระบุไว้เป๊ะ และผลลัพธ์คือข้อความที่สูงราว ๆ ต่ำกว่า 10 px แทบจะมองไม่เห็นสำหรับ EasyOCR ค่าเริ่มต้น

ช่วงที่ดีที่สุดคือ 12–28 px และอ่านได้สมบูรณ์แบบที่ 16 px เมื่อเกิน 40 px ไป CER จะขยับขึ้นอีก — ไม่ใช่เพราะตัวอักษรอ่านยากขึ้น แต่เพราะการสลับ vowVOW กลับมา ข้อความขนาดใหญ่ไม่ได้ยากกว่าเดิม เพียงแต่ไม่ได้รับประโยชน์จาก spacing แบบที่ทำให้ 16 px พอดีเป๊ะ

สำหรับใครที่ดึงข้อความจาก screenshot: เช็กความสูงของ glyph ก่อนโทษโมเดล Dashboard ที่จับจาก 1× บนหน้าจอ HiDPI หรือ PDF ที่ rasterize ที่ 72 DPI มักทำให้ body text ต่ำกว่า 10 px อยู่บ่อย ๆ ถ้าจับที่ 2× หรือ upscale ก่อน OCR คุณจะข้าม bug report ประเภท “EasyOCR มองข้ามครึ่งหน้า” ไปได้เลย ถ้าทำแบบนั้นไม่ได้จริง ๆ ค่อยลด min_size — แต่ต้องยอมรับ noise ที่เพิ่มขึ้น เพราะฟิลเตอร์นี้มีไว้คัดการตรวจจับขยะออก

การหมุน: ทนเอียงได้ 10° และวิธีแก้ที่ไม่สมมาตร

เริ่มจาก skew ก่อน มุมเล็ก ๆ, Arial 32 px, ตั้งค่าเริ่มต้นเทียบกับ rotation_info=[90,180,270]:

มุม skewCER (ค่าเริ่มต้น)CER (เมื่อใช้ rotation_info)
0.08330.0833
0.04170.0417
10°0.02080.0833
15°0.29170.3750
20°0.75000.7708
30°0.89580.8750
45°0.89580.8750

EasyOCR ค่าเริ่มต้นรับ skew ได้ประมาณ 10° โดยแทบไม่สะดุด (CER ≤ 0.083), เริ่มแกว่งที่ 15° และพังยับที่ 20° rotation_info ไม่ช่วยกับ skew ซึ่งก็สมเหตุสมผลเมื่อรู้ว่ามันทำอะไร — มันลองใหม่เฉพาะมุมที่คุณใส่ไว้ และ 15° skew ไม่ใช่ 90, 180 หรือ 270 ที่ 10° มันกลับทำให้แย่นิดหน่อยเสียด้วยซ้ำ (0.021 → 0.083) เพราะการลองมุมผิดอาจชนะคะแนนความมั่นใจในระบบโหวต

ส่วนการหมุนตั้งฉากคือจุดที่แปลกขึ้น:

การหมุนCER (ค่าเริ่มต้น)CER (เมื่อใช้ rotation_info)กู้คืนได้ไหม?
90°0.81250.9167ไม่ — แย่ลง
180°0.85420.6667ได้บางส่วน
270°0.83330.1042ได้

พารามิเตอร์เดียว รายการมุมเดียวกัน แต่ผลลัพธ์ 3 แบบ

ที่ 270° rotation_info ทำงานตรงตามที่ issue thread สัญญาไว้: CER ลดจาก 0.83 เหลือ 0.10 ซึ่งอ่านใช้จริงได้ ที่ 180° มันช่วยได้ครึ่งเดียว — CER ดีขึ้นเป็น 0.67 แต่ประโยค my vow. หายไปทั้งก้อน ที่ 90° มันถอยหลัง จาก 0.81 ไปเป็น 0.92 และ raw output ก็อธิบายได้ว่าเพราะอะไร: ตัวรู้จำคืนสตริงที่ กลับด้านในกระจก VOW กลายเป็น MOA quartz กลายเป็น zuuenb ถ้าเอาไปอ่านในกระจกมันก็ถูก แต่ใน pipeline ข้อมูลมันไร้ประโยชน์

ผมเช็กเรื่องนี้จาก prediction ดิบ ไม่ใช่แค่มองเมตริกสรุป เพราะตอนแรกผมสงสัยว่าอาจเป็นการ join ลำดับผิด แต่ไม่ใช่ — นั่นคือสิ่งที่ EasyOCR ส่งกลับมาจริง ๆ

กลไกเบื้องหลังเป็นเพียงสมมติฐาน ไม่ใช่การวัดจริง; ผมไม่ได้รันทดลองเรื่อง convention ของการหมุน Pillow แสดงมุมบวกเป็นทวนเข็มนาฬิกา ดังนั้นมีแค่ภาพที่เรนเดอร์ที่ 270° เท่านั้นที่ดันไปตรงกับ orientation ของการ retry ที่ recognizer รับได้ดี ส่วนกรณี 90° ดันไปตกใน orientation กลับด้านที่คะแนนดีที่สุด ไม่ว่ากลไกจะเป็นอะไร บทเรียนเชิงปฏิบัติไม่เปลี่ยน

ชุด fixture นี้พิสูจน์ว่า rotation_info ไม่ควรถูกคาดหวังให้ทำงานสมมาตรทุกทิศทาง ตรวจสอบการหมุนที่คุณคาดว่าจะเจอใน input ของคุณก่อน และการ normalize orientation ที่ต้นทางคือทางแก้ที่ควรพิจารณา ไม่ใช่ข้อกำหนดที่พิสูจน์ได้จากตัวอย่างเรนเดอร์ 3 ภาพ

ข้อคาดเดาที่ผมคิดผิด

ตอนเริ่มผมคิดว่า low contrast น่าจะเป็นจุดอ่อนของ EasyOCR แสงตัวอักษรสีเทาจางบนพื้นขาวคือเรื่องคลาสสิกของความพัง OCR และมันก็มีเส้นทางกู้คืนที่ชัดเจนอยู่แล้ว: contrast_ths=0.1 คู่กับ adjust_contrast=0.5 ซึ่งจะรันกรอบที่คอนทราสต์ต่ำใหม่ด้วยสำเนาที่เพิ่มคอนทราสต์แล้วเลือกผลที่มั่นใจกว่า

แต่สุดท้ายมันไม่เคยถูกเรียกใช้ เพราะมันไม่จำเป็นต้องใช้เลย

เทา foregroundWeber contrastCER (ค่าเริ่มต้น)CER (adjust_contrast=1.0)
0 (ดำ)1.0000.08330.0833
640.7490.08330.0833
1100.5690.08330.0833
1500.4120.10420.1042
1800.2940.06250.0625
2000.2160.04170.0417
2200.1370.04170.0417

CER ไม่เคยหลุดจากช่วงที่ถือว่าดีเลย แม้จะลงไปถึง Weber 0.14 — เทา 220 บนพื้นขาว ซึ่งจางพอที่ผมต้องหรี่ตาดู fixture เพื่อยืนยันว่ามีข้อความอยู่จริง และคอลัมน์เพิ่มคอนทราสต์ก็ เหมือนกันเป๊ะ กับคอลัมน์เริ่มต้นทุกขั้น เพราะค่าเริ่มต้นก็สำเร็จอยู่แล้ว

พื้นหลังก็บอกเรื่องเดียวกัน ตัวอักษรเป็นสีดำตลอด:

พื้นหลังCER
พื้นสีฟ้าอ่อนแบบทึบ0.083
กราดิเอนต์แนวตั้ง0.021
Gaussian noise (μ200, σ22)0.000

อ่านได้สมบูรณ์แบบแม้บน fixture ที่ noisy ที่สุดในชุด

ขอบเขตตรงนี้แคบ: นี่คือคอนทราสต์ต่ำแบบสีทึบ ไม่มี noise ไม่ใช่ใบเสร็จจากกล้องที่มี sensor noise และ JPEG compression ในชุด fixture นี้ รูปทรงและ token สั้น ๆ คือสาเหตุหลักของความพัง ส่วนตัวแปรสีและ synthetic noise ที่ทดสอบไม่ได้ทำให้แย่ลง

สถานการณ์จริง: ดึงตัวเลขจากสกรีนช็อตแดชบอร์ด

นี่คือเคสที่งาน OCR ใน Python มักลงเอยจริง ๆ มีคนส่งสกรีนช็อตแดชบอร์ดภายในมาให้ หรือคุณกำลังรัน Python scraping pipeline บนหน้า analytics ที่เต็มไปด้วยกราฟ ซึ่งตัวเลขมีอยู่แค่ในรูปพิกเซล แล้วคุณต้องการเอาค่านั้นมาเป็นข้อมูล

ผมเรนเดอร์หน้าต่าง “Sales Dashboard” — หัวแถบสีเข้มพร้อมชื่อเรื่องและ badge วงกลมรูปอวาตาร์, แผง KPI 3 ชุด, ปุ่ม 3 ปุ่ม, ตาราง 2×3 — แล้วติดป้ายทุก 19 องค์ประกอบข้อความ ด้วยสตริงที่ตรงเป๊ะและกรอบพิกเซลของมัน จากนั้นจับ output ของ EasyOCR มาเทียบกับแต่ละชิ้นด้วยการทับซ้อนของกรอบ

Detection recall: 16 จาก 19 จุดที่พลาดมี 3 จุด:

  • badge ตัวอักษรเดี่ยว "A"
  • เซลล์ตาราง "Q1"
  • เซลล์ตาราง "Q2"

และ "Q3" ถูกตรวจเจอ ฟอนต์เดียวกัน ขนาดเดียวกัน คอลัมน์เดียวกัน — ตัวตรวจจับเลือก token สองตัวอักษรไว้หนึ่งอัน แต่กลับทิ้งอีกสองอัน การตรวจจับไม่สม่ำเสมอแม้ในเซลล์ที่หน้าตาคล้ายกันมาก และเพราะผลลัพธ์ในรอบนี้เป็นแบบ deterministic จึงไม่ใช่หลักฐานของพฤติกรรมสุ่มแบบ “โยนเหรียญ” ปัญหาคุณภาพ screenshot ที่เกี่ยวข้องถูกติดตามไว้ใน #460

ใน 16 องค์ประกอบที่จับได้ ข้อความเกือบสมบูรณ์แบบ: CER เฉลี่ย 0.027, และ 13 จาก 16 อ่านได้ตรงทั้งหมด ชื่อเรื่อง ป้ายกำกับ ปุ่ม ("Save", "Cancel", "Export CSV") หัวคอลัมน์ และตัวเลขที่คั่นด้วย comma ล้วนกลับมาได้ CER 0 1,284 ถูกอ่านถูกต้อง รวม comma ด้วย

ผลอ่านที่ไม่ตรงทั้งสามกรณีเป็นความผิดแบบเดียวกันหมด คือจำนวนเงินดอลลาร์:

Ground truthEasyOCR read
$57,912S57,912
$18,330S18,330
$25,178S25,178
$12,004$12,004 (ถูกต้อง)

สัญลักษณ์ดอลลาร์ 3 ใน 4 ตัวกลายเป็นตัว S ใหญ่ ซึ่งถ้ามองด้วยตาเปล่าก็พอเข้าใจได้ แต่แปลว่าทุกฟิลด์สกุลเงินที่คุณดึงออกมาจะเหลือห่างจากขยะไปแค่ตัวอักษรเดียว และ float() แบบ naive จะพังกับทุกค่า

ถ้า pipeline ของคุณมี label สั้น ๆ หรือสกุลเงิน ให้ตรวจสอบวิธีแก้ที่เป็นไปได้ เช่น การ upscale, ครอปแบบมี padding, กำหนดรูปแบบฟิลด์ให้แคบลง หรือ post-process ที่รู้จักสัญลักษณ์ แต่ละวิธีในที่นี้ไม่ได้ถูก benchmark และ regex ที่แก้ S ต้นคำอาจทำลายค่าที่ถูกต้องได้ ใช้การแก้เฉพาะเมื่อ schema และกฎ validation รับประกันความปลอดภัยเท่านั้น

ต้นทุนการรัน

ตัวเลขบนเครื่องเดียวกัน (macOS arm64, CPU, โฮสต์เดียว, วัดภายใต้ภาระงานพร้อมกันที่อาจเกิดขึ้น — ให้มองเป็นลักษณะ ไม่ใช่ benchmark สากล):

เมตริกค่า
น้ำหนักโมเดลบนดิสก์93.7 MiB (79.30 detector + 14.44 recognizer)
Peak resident memory, โปรเซส CPU ใหม่984.5 MiB
Cold init ของ Reader()1.3–1.7 s
Warm latency, หนึ่งบรรทัดคมชัด 48 ตัวอักษร (p50)~0.062 s (p25–p75: 0.059–0.067 s, n=20)
detail=0 เทียบกับ detail=1ใกล้เคียงกัน (~0.062 vs 0.063 s median)

ต้นทุนหลักไม่ใช่ weights 94 MB — แต่คือ resident memory ราว 1 GB ต่อ worker process บน top ของ torch ประมาณ 2 GB ตัวเลขนี้แหละที่เป็นตัวตัดสินว่ามันจะอยู่ใน container ของคุณได้ไหม และเป็นตัวเลขที่ไม่มีใครชอบพูดถึง

ความเร็วถือว่าโอเคในเคสง่าย ๆ warm ต่ำกว่า 0.1 วินาทีสำหรับหนึ่งบรรทัดคมชัดบน CPU ใช้งานได้สบาย แต่ก็ต้องย้ำว่านั่นคือเคสง่าย: หนึ่งบรรทัดสั้น คอนทราสต์สูง ข้อร้องเรียนที่เจอบ่อยอย่าง “EasyOCR ใช้เวลาหลายสิบวินาทีบน CPU” มักเกี่ยวกับเอกสารขนาดใหญ่ที่มีหลายพื้นที่บน canvas เต็ม ซึ่งผมไม่ได้ทำซ้ำเคสนั้น — บริบทงานต่างกัน และผมอ้างถึงมันแทนที่จะอ้างว่าทำซ้ำได้

มี myth เล็ก ๆ อย่างหนึ่งที่ควรลบ: detail=0 ไม่ทำให้ EasyOCR เร็วขึ้น มันแค่ตัด bounding box และ confidence score ออกจากค่าที่คืนกลับมา งานคำนวณเกิดไปแล้ว เวลามัธยฐานต่างกันแค่ระดับมิลลิวินาที ซึ่งเป็น noise

ข้อดีและข้อเสีย

ข้อดี

  • การจับตัวอักษรบนข้อความละตินที่เรนเดอร์คมชัดแทบสมบูรณ์แบบ — CER เฉลี่ย 0.071 เมื่อคำนึงถึง case และ 0.024 เมื่อไม่สน case โดยสามารถได้ CER 0 ที่ความสูง 16 px
  • API สั้นจริง ๆ สองบรรทัด Reader(['en']) แล้ว readtext() ไม่ต้องตั้งค่า ไม่ต้องเลือกโมเดล
  • ทนคอนทราสต์ได้ดีกว่าที่เล่าต่อ ๆ กันมา: ไม่พังแม้ Weber 0.14 บนข้อความสะอาด และพื้นหลังแบบ noise/gradient/สีไม่ได้ทำให้แย่ลง (fixture Gaussian-noise อ่านได้สมบูรณ์แบบ)
  • อ่านองค์ประกอบใน screenshot ได้เกือบสมบูรณ์แบบ: CER เฉลี่ย 0.027, 13 จาก 16 ตรงทั้งหมด รวมถึงตัวเลขที่มี comma
  • ผลลัพธ์ deterministic ทุกตัวเลขความแม่นยำในที่นี้ได้ byte-identical ระหว่างการรันโปรเซสแยกกันสองครั้ง สิ่งที่ขยับมีแค่เวลา
  • Apache-2.0 และ self-hosted ไม่มีค่าธรรมเนียมการใช้งานจากผู้ให้บริการ ต้นทุนอยู่ที่ compute, memory, storage และ queueing เท่านั้น

ข้อเสีย

  • ดิ่งหนักทันทีเมื่อขนาดต่ำกว่าค่า min_size=10 ตามเอกสาร — CER 0.77 ที่ 8 px ข้อความ UI ขนาดเล็กจึงมองไม่เห็นโดยค่าเริ่มต้น
  • ทน skew ได้แค่ราว 10° และพังเมื่อถึง 20°
  • rotation_info ไม่ใช่วิธีแก้แบบสมมาตร: 270° กู้คืนได้, 180° ได้แค่บางส่วน, 90° แย่ลง และคืนข้อความกลับด้าน
  • detector ทิ้ง token สั้น ๆ ที่โดดเดี่ยว — badge ตัวอักษรเดี่ยวและเซลล์ 2 ตัวอักษร 2 อัน แต่กลับเก็บเซลล์อีกอันที่รูปแบบเหมือนกันไว้
  • อ่าน $ เป็น S อย่างเป็นระบบในค่าที่เป็นสกุลเงิน (3 จาก 4)
  • ใช้ resident memory ราว 1 GB ต่อโปรเซส บวก dependency torch ราว 2 GB
  • release ล่าสุดมาจากกันยายน 2024; โปรเจกต์ค่อนข้างนิ่งมากกว่าจะพัฒนาเร็ว

ใครควรใช้ และใครควรเดินหนี

ควรประเมิน EasyOCR เมื่อ input ของคุณเป็นข้อความที่สะอาด ตั้งตรง เรนเดอร์มาในขนาดที่เหมาะสม—เช่น screenshot, UI capture, PDF ที่ rasterize แล้ว หรือรายงานที่สร้างขึ้น—และคุณต้องการ pipeline Python แบบ self-hosted ที่ไม่มีค่าธรรมเนียมจาก vendor ผลภาษาอังกฤษบน CPU ที่เป็น synthetic ในบทความนี้ใช้กับเส้นทางงานแบบนั้น ส่วนรูปถ่าย ลายมือ และสคริปต์อื่น ๆ ต้องทดสอบแยกต่างหาก

ควรเดินหนีหาก input ของคุณเข้าข่ายข้อใดข้อหนึ่งต่อไปนี้ ภาพถ่าย — ตัวเลขที่ผมมีเป็นข้อความที่เรนเดอร์สังเคราะห์ และไม่ได้บอกอะไรเกี่ยวกับ noise ของกล้อง, perspective, หรือแสง ลายมือ — ตัวโปรเจกต์เองไม่ได้อ้างว่ารองรับ สคริปต์ที่ไม่ใช่ละติน — EasyOCR รองรับมากกว่า 80 ภาษา แต่ผมทดสอบแค่ภาษาเดียว และงานเปรียบเทียบเชิงวิชาการที่ตีพิมพ์คือข้อมูลอ้างอิงจริงในจุดนั้น ไม่ใช่การไล่ตรวจ English synthetic แบบนี้ input ที่หมุนได้อิสระ — เว้นแต่คุณจะทำ correction ของ orientation เองก่อน ระบบที่จำกัดหน่วยความจำ — worker ละ 1 GB เพิ่มขึ้นเร็วมาก

ก่อนจะเลือก OCR ให้ดู DOM และ network response ก่อน ถ้าค่าที่ต้องการมีอยู่แล้วเป็น structured text การดึงจากแหล่งนั้นจะหลีกเลี่ยงทั้ง error จาก detector และ recognizer ของ OCR ได้ OCR ควรใช้เมื่อ pixels คือรูปแบบข้อมูลเดียวที่มีให้

ตัวเลือกอื่น และสแต็กของเราเหมาะตรงไหน

EasyOCR ใช้ Apache-2.0 และ self-hosted ไม่มีค่าธรรมเนียมการใช้งานจากผู้ให้บริการ แต่มีต้นทุน compute และการปฏิบัติการจริง ๆ PaddleOCR, Tesseract และ vision-language models ไม่ได้ถูกนำมารันใน benchmark นี้ จึงไม่มีข้อสรุปแบบ head-to-head

การเปรียบเทียบที่น่าสนใจกว่าไม่ใช่ OCR เทียบ OCR แต่คือคุณควรทำ OCR หรือไม่ตั้งแต่แรก

งานดึงข้อมูลจาก screenshot ที่ผมเจอส่วนใหญ่จริง ๆ คือ workaround ของเว็บเพจที่ scrape ยาก — ตารางที่เรนเดอร์ด้วย JavaScript, dashboard หลังล็อกอิน, หรือเว็บที่ตั้งใจต้าน คุณจับภาพหน้าจอแล้วทำ OCR มันดูเหมือนทางลัดที่ง่ายที่สุด แต่สุดท้ายคุณกำลังทิ้ง structured text ที่ดีอยู่แล้ว แล้วจ่ายภาษีสัญลักษณ์ดอลลาร์เพื่อได้เวอร์ชันที่แย่กว่ากลับมา

หมายเหตุผู้เขียน: Thunderbit คือทางเลือกแบบ managed ของเราสำหรับดึงข้อมูลจากเว็บเพจ เครื่องมือนี้ไม่ได้ถูกนำไปรันกับชุด fixture ภาพในบทความนี้ ประเด็นสำคัญคือรูปแบบของแหล่งข้อมูล: ใช้ DOM/network extraction เมื่อมีข้อมูลเว็บแบบมีโครงสร้างอยู่แล้ว และใช้ OCR เมื่อ pixels คือแหล่งข้อมูลเดียว

อ่านต่อที่เกี่ยวข้องจากชุดทดสอบเดียวกัน: full open-source scraper comparison, รีวิว Crawl4AI และมุมมองกว้างขึ้นเกี่ยวกับ AI-driven extraction สำหรับหน้าที่ต้าน selector

ลองใช้ Thunderbit สำหรับดึงข้อมูลเว็บ

บทสรุป

ควรใช้ EasyOCR ไหม? ควร ถ้าภาพของคุณตั้งตรง glyph สูงอย่างน้อย 12 พิกเซล และคุณกำลังอ่านสคริปต์ละติน ภายในเงื่อนไขเหล่านี้มันทำได้ดีมาก — CER เฉลี่ย 0.071 บนข้อความสะอาด, 0.024 เมื่อ normalize case, อ่านได้สมบูรณ์แบบที่ 16 px และทนคอนทราสต์ได้ดีกว่าชื่อเสียงที่คนพูดกัน API สองบรรทัดจริง และ output ก็ deterministic ซึ่งสำคัญกว่าที่หลายคนยอมรับเวลาต้อง debug pipeline

นอกเหนือจากนั้นมันจะล้มในรูปแบบที่เฉพาะเจาะจงและเรียนรู้ได้ ข้อความต่ำกว่า 10 px หายไปในฟิลเตอร์ min_size การ skew เกิน 20° ทำให้การอ่านพัง rotation_info ช่วยแก้ได้แค่องศาตั้งฉากบางแบบ อีกแบบช่วยได้ครึ่งเดียว และอีกแบบยิ่งแย่พร้อมคืนข้อความกลับด้าน token ตัวอักษรเดี่ยวและ token สองตัวอักษรหลุดจาก detector ทั้งที่เพื่อนข้าง ๆ ยังอยู่ ดอลลาร์กลายเป็นตัว S

ความพังที่พบใน fixture มาจากทั้งสองขั้น: กรอบที่หายหรือหันผิดในฝั่ง geometry และการสับสน $ ในฝั่ง recognizer ให้มอง upscaling, orientation normalization, padded crop และการซ่อมสัญลักษณ์ตาม schema เป็นตัวเลือกที่ต้องตรวจสอบ ไม่ใช่วิธีแก้ที่ปลอดภัยเสมอไป

แต่อย่าทดสอบมันแบบที่ผมเกือบทำ ด้วย harness ที่พังและตัวเลขที่คุณไม่เข้าใจ สร้าง fixture ของตัวเอง ให้รู้ ground truth แบบเป๊ะ ๆ แล้วหากำแพงของคุณเองให้เจอ

ลองใช้ Thunderbit สำหรับดึงข้อมูลเว็บ Get Started Free

คำถามที่พบบ่อย

EasyOCR แม่นพอสำหรับการดึงข้อความจาก screenshot ในงาน production ไหม? บน synthetic dashboard องค์ประกอบที่ match ได้มี mean CER 0.027 และ 13 จาก 16 ตรงทั้งหมด มี 3 จาก 19 องค์ประกอบที่ตรวจไม่เจอ และดอลลาร์ 3 ใน 4 ตัวถูกอ่านเป็น S ว่าสิ่งนี้รับได้ไหม — และ upscaling หรือการซ่อมตาม schema ช่วยได้ไหม — ต้องทดสอบกับ layout เป้าหมายของคุณเอง

EasyOCR อ่านฟอนต์เล็กสุดได้กี่พิกเซล? ในทางปฏิบัติอยู่ที่ความสูงของ glyph ราว 12 พิกเซล พารามิเตอร์ min_size=10 ของ readtext() จะทิ้งกรอบที่สูงต่ำกว่า 10 px และผลที่ได้ไม่ใช่การค่อย ๆ แย่ลงแต่เป็นหน้าผา: CER คือ 0.77 ที่ 8 px, 0.15 ที่ 10 px, 0.04 ที่ 12 px และ 0 ที่ 16 px ช่วงที่สะอาดใน sweep ของผมคือ 12–28 px ถ้าแหล่งข้อมูลเป็น screenshot จาก HiDPI ที่จับ 1× หรือ PDF ที่ rasterize ที่ 72 DPI ควร upscale ก่อน OCR แทนที่จะลด min_size เพราะฟิลเตอร์นี้มีไว้กันการตรวจจับขยะ

rotation_info แก้ภาพหมุนใน EasyOCR ได้ไหม? ไม่เสมอไป และไม่สมมาตรด้วย เมื่อใช้ rotation_info=[90,180,270] กับสำเนาที่หมุนตั้งฉาก 3 แบบของประโยคเดียวกัน ภาพ 270° กู้คืนได้ดี (CER 0.83 → 0.10), ภาพ 180° ได้แค่บางส่วน (0.85 → 0.67 และมีวลีหายไป), ส่วนภาพ 90° แย่ลงเป็น 0.92 พร้อมคืนข้อความกลับด้านอย่าง VOWMOA มันยังไม่ช่วยกับ skew มุมเล็ก ๆ เพราะมันลองใหม่แค่ตามมุมที่ใส่ไว้ ควรแก้ orientation ให้ถูกก่อนเรียก EasyOCR มากกว่าพึ่งพาพารามิเตอร์นี้

EasyOCR ใช้หน่วยความจำและพื้นที่ดิสก์เท่าไร? weights มีขนาด 93.7 MiB และจะดาวน์โหลดไปไว้ที่ ~/.EasyOCR/model/ ตอนใช้งานครั้งแรก—79.30 MiB สำหรับ detector บวก 14.44 MiB สำหรับ recognizer อังกฤษ Peak resident memory ในโปรเซส CPU ใหม่ที่วัดได้คือ 984.5 MiB บน torch ที่กินพื้นที่ประมาณ 2 GB การเริ่ม reader แบบ cold ใช้เวลา 1.3–1.7 วินาที จากนั้นบรรทัดเดียวที่คมชัดจะรันได้ p50 ราว 0.062 วินาทีบนเครื่องนี้ detail=0 เปลี่ยนแค่รูปทรงของผลลัพธ์ ไม่ได้เปลี่ยนเวลารันที่วัดได้

EasyOCR ใช้เชิงพาณิชย์ได้ฟรีไหม และยังดูแลอยู่หรือเปล่า? มันใช้สัญญาอนุญาต Apache-2.0 ซึ่งค่อนข้างเสรีและเหมาะกับงานเชิงพาณิชย์ ณ วันที่ 27 กรกฎาคม 2026 repository มี 29,825 ดาว กับ 528 issues ที่เปิดอยู่ release ล่าสุดคือ v1.7.2 จากกันยายน 2024 และ push ล่าสุดไป master คือธันวาคม 2025 มองภาพนี้ว่าโปรเจกต์ค่อนข้างนิ่งมากกว่าจะถูกทิ้ง—สถาปัตยกรรมไม่ค่อยเปลี่ยนมาระยะหนึ่งแล้ว และ activity ย้ายไปอยู่ที่ issue tracker มากกว่า ตรวจดู license และสถานะ release ปัจจุบันด้วยตัวเองก่อนนำไปสร้างต่อ

Ke
Ke
CTO ที่ Thunderbit | นักวิทยาศาสตร์ข้อมูลอาวุโสและผู้เชี่ยวชาญด้านแมชชีนเลิร์นนิง ด้วยประสบการณ์เกือบสิบปีในด้านแมชชีนเลิร์นนิงและวิทยาศาสตร์ข้อมูล เคเฉินเป็นศิษย์เก่ามหาวิทยาลัยโคลัมเบีย และอดีตนักวิทยาศาสตร์ข้อมูลอาวุโสที่ Walmart Labs ด้วยความเชี่ยวชาญลึกซึ้งที่ได้รับการยอมรับจากเพื่อนร่วมสายงานใน Python, R, Java และสถิติ เขาจึงแบ่งปันมุมมองที่ผ่านการพิสูจน์มาแล้วในการพัฒนาอัลกอริทึม AI ที่ซับซ้อนจากแนวคิดไปสู่สถาปัตยกรรมระดับใช้งานจริง
สารบัญ
Thunderbit · เอเจนต์ข้อมูลเว็บด้วย AI

ดึงข้อมูลจากทุกหน้าได้ใน 1 คลิก

ได้รับความไว้วางใจจากผู้ใช้กว่า 250,000+ คน
มีแผนใช้ฟรี
จากหน้าเว็บสู่สเปรดชีต
บอกสิ่งที่ต้องการ — Thunderbit’s AI Agent จะดึงข้อมูลและส่งออกไปยัง Excel, Google Sheets, Airtable หรือ Notion เริ่มต้นได้ฟรี
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week