วิธีสร้างระบบนับเหรียญไทยด้วย LINE และ Roboflow
ระบบนี้เป็นโปรแกรมที่ให้ผู้ใช้ส่งรูปภาพเหรียญไทยผ่านแอป LINE แล้วโปรแกรมจะวิเคราะห์ภาพเพื่อนับจำนวนเหรียญและคำนวณเงินรวม โดยใช้ Roboflow ซึ่งเป็นเครื่องมือ AI สำหรับการตรวจจับวัตถุในภาพ มาดูขั้นตอนการทำงานแบบเข้าใจง่ายกัน!
1. ภาพรวมของระบบ
- เป้าหมาย: สร้างระบบที่รับภาพเหรียญจาก LINE วิเคราะห์ด้วย AI และตอบกลับจำนวนเหรียญและยอดเงิน
- เครื่องมือที่ใช้:
- LINE Messaging API: ใช้รับภาพจากผู้ใช้และส่งข้อความตอบกลับ
- Roboflow: ใช้ AI วิเคราะห์ภาพเพื่อระบุประเภทและจำนวนเหรียญ
- Google Apps Script: รันโค้ดเพื่อเชื่อมต่อ LINE กับ Roboflow
- Google Drive: เก็บภาพชั่วคราวเพื่อให้ Roboflow ใช้งาน
2. ขั้นตอนการทำงานของระบบ
- ผู้ใช้ส่งภาพเหรียญผ่าน LINE: ผู้ใช้ถ่ายรูปเหรียญ (เช่น 1 บาท, 2 บาท, 5 บาท, 10 บาท) ส่งมาในแชท LINE
- รับภาพจาก LINE: โปรแกรมดึงภาพจาก LINE โดยใช้ LINE API
- บันทึกภาพลง Google Drive: ภาพจะถูกอัปโหลดไป Google Drive เพื่อให้ Roboflow เข้าถึงได้
- วิเคราะห์ภาพด้วย Roboflow: ส่งภาพไปยัง Roboflow ซึ่งจะตรวจจับประเภทและจำนวนเหรียญ
- คำนวณผลลัพธ์: นับจำนวนเหรียญแต่ละประเภทและคำนวณยอดเงินรวม
- ส่งผลลัพธ์กลับไปที่ 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]
})
});
}