วิธีสร้างระบบนับเหรียญไทยด้วย LINE และ Roboflow

วิธีสร้างระบบนับเหรียญไทยด้วย LINE และ Roboflow

ระบบนี้เป็นโปรแกรมที่ให้ผู้ใช้ส่งรูปภาพเหรียญไทยผ่านแอป LINE แล้วโปรแกรมจะวิเคราะห์ภาพเพื่อนับจำนวนเหรียญและคำนวณเงินรวม โดยใช้ Roboflow ซึ่งเป็นเครื่องมือ AI สำหรับการตรวจจับวัตถุในภาพ มาดูขั้นตอนการทำงานแบบเข้าใจง่ายกัน!

1. ภาพรวมของระบบ

  • เป้าหมาย: สร้างระบบที่รับภาพเหรียญจาก LINE วิเคราะห์ด้วย AI และตอบกลับจำนวนเหรียญและยอดเงิน
  • เครื่องมือที่ใช้:
    • LINE Messaging API: ใช้รับภาพจากผู้ใช้และส่งข้อความตอบกลับ
    • Roboflow: ใช้ AI วิเคราะห์ภาพเพื่อระบุประเภทและจำนวนเหรียญ
    • Google Apps Script: รันโค้ดเพื่อเชื่อมต่อ LINE กับ Roboflow
    • Google Drive: เก็บภาพชั่วคราวเพื่อให้ Roboflow ใช้งาน

2. ขั้นตอนการทำงานของระบบ

  1. ผู้ใช้ส่งภาพเหรียญผ่าน LINE: ผู้ใช้ถ่ายรูปเหรียญ (เช่น 1 บาท, 2 บาท, 5 บาท, 10 บาท) ส่งมาในแชท LINE
  2. รับภาพจาก LINE: โปรแกรมดึงภาพจาก LINE โดยใช้ LINE API
  3. บันทึกภาพลง Google Drive: ภาพจะถูกอัปโหลดไป Google Drive เพื่อให้ Roboflow เข้าถึงได้
  4. วิเคราะห์ภาพด้วย Roboflow: ส่งภาพไปยัง Roboflow ซึ่งจะตรวจจับประเภทและจำนวนเหรียญ
  5. คำนวณผลลัพธ์: นับจำนวนเหรียญแต่ละประเภทและคำนวณยอดเงินรวม
  6. ส่งผลลัพธ์กลับไปที่ LINE: ส่งข้อความกลับไปบอกผู้ใช้ว่ามีเหรียญกี่เหรียญและยอดเงินเท่าไร

3. การตั้งค่าเริ่มต้น

ก่อนเริ่มเขียนโค้ด ต้องตั้งค่าสิ่งต่อไปนี้:

  • LINE Channel Access Token: ได้จาก LINE Developers Console ใช้สำหรับเชื่อมต่อกับ LINE
  • Roboflow API Key: ได้จาก Roboflow ใช้สำหรับส่งภาพไปวิเคราะห์
  • Google Apps Script: สร้างโปรเจกต์ใน Google Apps Script เพื่อรันโค้ด

4. อธิบายโค้ดแบบง่าย ๆ

โค้ดนี้เขียนด้วย Google Apps Script และแบ่งเป็น 4 ส่วนหลัก:

ส่วนที่ 1: รับข้อมูลจาก LINE
  • ฟังก์ชัน doPost(e): ทำงานเมื่อ LINE ส่งข้อมูลมา (เช่น รูปภาพ)
  • ดึงข้อมูลจาก LINE เช่น replyToken (ใช้ตอบกลับ) และ messageId (ระบุรูปภาพ)
  • ตรวจสอบว่าเป็นภาพหรือไม่ ถ้าใช่ก็ไปขั้นตอนต่อไป
ส่วนที่ 2: ดึงภาพจาก LINE และอัปโหลดไป Google Drive
  • ฟังก์ชัน getImageUrl(messageId):
    • ใช้ messageId เพื่อขอ URL รูปภาพจาก LINE API
    • อัปโหลดภาพไป Google Drive และตั้งค่าให้ทุกคนที่มีลิงก์เข้าถึงได้
    • ส่ง URL ของภาพใน Google Drive ไปให้ Roboflow
ส่วนที่ 3: วิเคราะห์ภาพด้วย Roboflow
  • ฟังก์ชัน processImageWithRoboflow(imageUrl):
    • ส่ง URL ภาพไปยัง Roboflow พร้อม API Key
    • Roboflow วิเคราะห์ภาพและส่งผลลัพธ์กลับมา (เช่น มีเหรียญ 1 บาท 2 เหรียญ, 5 บาท 1 เหรียญ)
    • นับเฉพาะเหรียญที่มีความมั่นใจ (confidence) มากกว่า 60%
    • คำนวณยอดเงินรวม (เช่น 1 บาท × 2 + 5 บาท × 1 = 7 บาท)
    • สร้างข้อความสรุปผล เช่น:
ผลการนับเหรียญ
1 บาท: 2 เหรียญ
10 บาท: 0 เหรียญ
2 บาท: 0 เหรียญ
5 บาท: 1 เหรียญ
จำนวนเหรียญทั้งหมด: 3 เหรียญ
จำนวนเงินทั้งหมด: 7 บาท

// Channel Access Token จาก LINE
const CHANNEL_ACCESS_TOKEN = 'xxxxxx';

// Roboflow API
const ROBOFLOW_API_URL = 'https://serverless.roboflow.com/thai-coins-model/4';
const ROBOFLOW_API_KEY = 'kPUMWSZnttvttboPNusd';

// ฟังก์ชันหลักที่รับ webhook จาก LINE
function doPost(e) {
  try {
    const replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
    const messageType = JSON.parse(e.postData.contents).events[0].message.type;
    const messageId = JSON.parse(e.postData.contents).events[0].message.id;

    if (messageType === 'image') {
      // ดึง URL ของภาพจาก LINE
      const imageUrl = getImageUrl(messageId);
      
      // ส่งภาพไปประมวลผลที่ Roboflow
      const roboflowResult = processImageWithRoboflow(imageUrl);
      
      // สร้างข้อความตอบกลับ
      const replyMessage = {
        type: 'text',
        text: roboflowResult
      };
      
      // ส่งข้อความตอบกลับ
      replyToLine(replyToken, replyMessage);
    }
    
    return ContentService.createTextOutput(JSON.stringify({status: 'success'})).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    Logger.log('Error: ' + error);
    return ContentService.createTextOutput(JSON.stringify({status: 'error', message: error.toString()})).setMimeType(ContentService.MimeType.JSON);
  }
}

// ฟังก์ชันดึง URL รูปภาพจาก LINE
function getImageUrl(messageId) {
  const url = 'https://api-data.line.me/v2/bot/message/' + messageId + '/content';
  const response = UrlFetchApp.fetch(url, {
    headers: {
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN
    }
  });
  
  // อัปโหลดภาพไป Google Drive เพื่อใช้ใน Roboflow
  const blob = response.getBlob();
  const file = DriveApp.createFile(blob);
  file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
  return file.getDownloadUrl();
}

// ฟังก์ชันประมวลผลภาพด้วย Roboflow และนับเหรียญ
function processImageWithRoboflow(imageUrl) {
  const url = ROBOFLOW_API_URL + '?api_key=' + ROBOFLOW_API_KEY + '&image=' + encodeURIComponent(imageUrl);
  
  const response = UrlFetchApp.fetch(url, {
    method: 'POST',
    muteHttpExceptions: true
  });
  
  const data = JSON.parse(response.getContentText());
  const predictions = data.predictions;
  
  // นับจำนวนเหรียญแต่ละประเภทที่มี confidence >= 0.80
  let coinCount = {
    '1 bahts': 0,
    '10 bahts': 0,
    '2 bahts': 0,
    '5 bahts': 0
  };
  
  predictions.forEach(prediction => {
    const coinClass = prediction.class;
    const confidence = prediction.confidence;
    if (confidence >= 0.60 && coinCount.hasOwnProperty(coinClass)) {
      coinCount[coinClass]++;
    }
  });
  
  // คำนวณจำนวนเงิน
  const totalAmount = 
    coinCount['1 bahts'] * 1 +
    coinCount['10 bahts'] * 10 +
    coinCount['2 bahts'] * 2 +
    coinCount['5 bahts'] * 5;
  
  // คำนวณจำนวนเหรียญทั้งหมด
  const totalCoins = 
    coinCount['1 bahts'] +
    coinCount['10 bahts'] +
    coinCount['2 bahts'] +
    coinCount['5 bahts'];
  
  // สร้างข้อความผลลัพธ์
  let resultMessage = 'ผลการนับเหรียญ\n';
  resultMessage += `1 บาท: ${coinCount['1 bahts']} เหรียญ\n`;
  resultMessage += `10 บาท: ${coinCount['10 bahts']} เหรียญ\n`;
  resultMessage += `2 บาท: ${coinCount['2 bahts']} เหรียญ\n`;
  resultMessage += `5 บาท: ${coinCount['5 bahts']} เหรียญ\n`;
  resultMessage += `จำนวนเหรียญทั้งหมด: ${totalCoins} เหรียญ\n`;
  resultMessage += `จำนวนเงินทั้งหมด: ${totalAmount} บาท`;
  
  return resultMessage;
}

// ฟังก์ชันส่งข้อความตอบกลับไปยัง LINE
function replyToLine(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN
    },
    method: 'POST',
    payload: JSON.stringify({
      replyToken: replyToken,
      messages: [message]
    })
  });
}

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *