รูปปกบทความ

1. 🎯 ตอนที่ 14: จับการเคลื่อนไหวของเมาส์ด้วย HighGUI สร้างหน้าต่างที่ตอบสนองได้ดั่งใจ

2. 📖 เปิดฉาก (The Hook)

น้องๆ เคยทำโปรเจกต์ AI แล้วเจอปัญหาแบบนี้ไหมครับ? สมมติว่าเราเขียนโปรแกรมตรวจจับรอยร้าวบนชิ้นงานสำเร็จแล้ว แต่หน้างานจริงมีแสงสะท้อนรบกวน AI เลยทำงานพลาด เราอยากจะสร้างระบบให้ User หรือ Operator สามารถ “ใช้เมาส์ลากคลุม” (Draw Bounding Box) เพื่อตีกรอบบอก AI ว่า “เฮ้ย สนใจแค่ตรงนี้นะ!” หรืออยากให้เอาเมาส์ไปจิ้มที่พิกเซลบนหน้าจอเพื่อดูค่าสีตรงนั้น

ถ้าเราใช้แค่ฟังก์ชัน imshow ธรรมดา หน้าต่างภาพของเราก็จะนิ่งสนิทเหมือนรูปถ่ายที่ตายแล้วครับ การจะทำให้โปรแกรมของเรามีชีวิตและโต้ตอบกับมนุษย์ได้ เราต้องพึ่งพาโมดูลที่ชื่อว่า HighGUI ซึ่งมีเวทมนตร์ในการดักจับเหตุการณ์ (Events) จากเมาส์ วันนี้พี่จะมาชงกาแฟ แล้วพาน้องๆ ไปรู้จักกับสถาปัตยกรรมแบบ Event-Driven ผ่านการสร้าง Callback Function รับรองว่าทำเป็นแล้ว Vision App ของเราจะดูโปรขึ้นอีกสิบระดับเลยครับ!

3. 🧠 แก่นวิชา (Core Concepts)

การจัดการกับเมาส์ใน OpenCV อาศัยกลไกที่เรียกว่า Callback Function หลักการทำงานคือ เราจะเขียนฟังก์ชันเตรียมไว้ แล้วเอาไปฝากไว้กับระบบของ OpenCV เหมือนเราจ้างยามไปเฝ้าหน้าต่าง (Window) แล้วสั่งยามไว้ว่า “ถ้ามีคนมาคลิกเมาส์ หรือขยับเมาส์ในหน้าต่างนี้ ให้โทรมาบอกที่ฟังก์ชันนี้นะ!”

เพื่อให้ทำงานได้สมบูรณ์ เราต้องรู้จักองค์ประกอบ 3 ส่วนนี้ครับ:

  • 1. ฟังก์ชัน setMouseCallback (การจ้างยาม): เป็นคำสั่งที่เราใช้บอก OpenCV ว่าหน้าต่างไหนที่จะให้ดักจับเมาส์ และจะให้เรียกใช้ Callback Function ตัวไหน โดยมีพารามิเตอร์สำคัญ 3 ตัวคือ ชื่อหน้าต่าง, ชื่อฟังก์ชัน Callback, และ userdata (เอาไว้แอบส่งข้อมูลไปให้ฟังก์ชันโดยไม่ต้องประกาศตัวแปร Global)
  • 2. รูปแบบของ Callback Function: ฟังก์ชันที่เราจะให้ OpenCV เรียกใช้งาน “ต้อง” มีโครงสร้าง (Signature) ที่กำหนดไว้เป๊ะๆ คือรับค่า 5 ตัว ได้แก่ event, x, y, flags, และ userdata โดยที่:
    • event: ชนิดของเหตุการณ์ที่เกิดขึ้น (เช่น คลิกซ้าย, คลิกขวา, เลื่อนเมาส์)
    • x, y: พิกัดที่เมาส์ชี้อยู่บน Image Matrix (ไม่ได้อิงตามพิกัดหน้าจอคอมพิวเตอร์ แต่เป็นพิกัดบนรูปภาพ)
    • flags: ใช้เช็กว่ามีการกดปุ่มพิเศษค้างไว้ไหม (เช่น ลากเมาส์ค้างไว้ หรือกด Shift/Ctrl ค้าง)
    • userdata: ข้อมูลที่เราแนบมา (มักจะใช้ Pointer ชี้ไปที่ cv::Mat เพื่อให้เราแก้ภาพได้)
  • 3. ประเภทของ Events ที่ใช้บ่อย:
    • EVENT_MOUSEMOVE: เมื่อเมาส์ถูกขยับ
    • EVENT_LBUTTONDOWN: เมื่อผู้ใช้ “กด” เมาส์ปุ่มซ้าย
    • EVENT_LBUTTONUP: เมื่อผู้ใช้ “ปล่อย” เมาส์ปุ่มซ้าย
    • EVENT_FLAG_LBUTTON: เป็น Flag ที่บอกว่าตอนนี้เมาส์ปุ่มซ้ายกำลังถูก “กดค้างไว้” (ลากเมาส์)
รูปประกอบ

4. 💻 ร่ายมนต์คำสั่ง (Show me the Code)

เรามาดูตัวอย่างการเขียนโค้ด C++ สำหรับแอปพลิเคชันวาดรูปด้วยเมาส์กันครับ โค้ดนี้จะอนุญาตให้เราคลิกเพื่อวาดวงกลม และถ้าคลิกค้างแล้วลาก จะเป็นการวาดเส้นต่อเนื่องครับ

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

// 1. สร้าง Callback Function ที่มีโครงสร้างตามที่ OpenCV กำหนดเป๊ะๆ
void myMouseCallback(int event, int x, int y, int flags, void* userdata) {
    // แปลง userdata ที่เป็น void pointer กลับมาเป็น Mat pointer เพื่อให้เราวาดรูปลงไปได้
    Mat* img = (Mat*)userdata; 

    // ดักจับเหตุการณ์: ถ้ามีการคลิกเมาส์ซ้าย 1 ครั้ง
    if (event == EVENT_LBUTTONDOWN) {
        // วาดวงกลมสีเขียวทึบ ขนาดรัศมี 5 พิกเซล ลงบนพิกัด x,y ที่คลิก
        circle(*img, Point(x, y), 5, Scalar(0, 255, 0), FILLED);
    }
    // ดักจับเหตุการณ์: ถ้ามีการ "ขยับเมาส์" และ "กำลังกดปุ่มซ้ายค้างไว้" (ลากเมาส์)
    else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) {
        // วาดวงกลมเล็กๆ สีน้ำเงิน เพื่อสร้างเส้นต่อเนื่อง
        circle(*img, Point(x, y), 2, Scalar(255, 0, 0), FILLED);
    }
}

int main() {
    // 2. สร้างผืนผ้าใบ (Image Matrix) สีดำขนาด 500x500 พิกเซล
    Mat image = Mat::zeros(500, 500, CV_8UC3);
    String windowName = "Interactive Mouse Tracking";

    // 3. สร้างหน้าต่าง HighGUI ขึ้นมารอ
    namedWindow(windowName, WINDOW_AUTOSIZE);

    // 4. ผูกเวทมนตร์! ติดตั้ง Callback function ลงบนหน้าต่าง
    // สังเกตว่าเราส่ง &image เข้าไปที่พารามิเตอร์ userdata เพื่อให้ฟังก์ชันเข้าถึงรูปภาพได้
    setMouseCallback(windowName, myMouseCallback, &image);

    cout << "ลองคลิกเมาส์ซ้ายเพื่อวาดวงกลม หรือคลิกค้างเพื่อวาดเส้นดูสิครับ!" << endl;

    // 5. ลูปแสดงผลภาพแบบ Real-time
    while (true) {
        imshow(windowName, image);
        
        // รอรับค่าคีย์บอร์ด 15 มิลลิวินาที ถ้ากด ESC (รหัส 27) ให้ออกจากลูป
        char key = (char)waitKey(15);
        if (key == 27) break;
    }

    destroyAllWindows();
    return 0;
}

5. 🛡️ เคล็ดลับจากคัมภีร์ลับ (Under the Hood / Pro-Tips)

  • ศิลปะแห่งการ Casting Pointer (The void* Magic): ใน C++ ฟังก์ชัน Callback รับค่าข้อมูลเสริมผ่านพารามิเตอร์ void* userdata ครับ ข้อดีคือมันทำให้เราส่งอ็อบเจ็กต์อะไรเข้าไปก็ได้ (ในโค้ดเราส่งที่อยู่ของรูปภาพ &image ไป) แต่เวลาจะใช้งานในฟังก์ชัน เราต้องทำ Type Casting แปลงมันกลับมาเป็น Mat* เสมอ (Mat* img = (Mat*)userdata;) วิธีนี้คือท่ามาตรฐานที่วิศวกรระดับโปรใช้ เพื่อหลีกเลี่ยงการประกาศตัวแปรภาพแบบ Global ซึ่งทำให้โค้ดรันได้ปลอดภัยและนำไปสเกลต่อได้ง่ายครับ!
  • ตรวจสอบสถานะด้วย Bitwise AND (&): สังเกตตรงเงื่อนไข (flags & EVENT_FLAG_LBUTTON) ไหมครับ? พารามิเตอร์ flags เป็นตัวแปรแบบ Bit-field ที่สามารถเก็บสถานะหลายๆ อย่างพร้อมกันได้ (เช่น กดทั้งเมาส์ซ้ายและปุ่ม Shift) การใช้เครื่องหมาย & (Bitwise AND) เป็นการทะลวงเช็กบิตว่าสถานะ “กดปุ่มซ้ายค้าง” ถูกเปิดใช้งานอยู่หรือไม่ มันทำงานได้เร็วกว่าการเขียนเช็ก == ทั่วไปหลายเท่าครับ
  • ระวังพิกัดทะลุขอบ: ตัวแปร x และ y ที่ส่งเข้ามาคือพิกัดบนรูปภาพ ถ้าเราเขียนโปรแกรมดึงค่าสี (เช่น .at<Vec3b>(y,x)) อย่าลืมเขียน if เพื่อเช็กก่อนเสมอว่า x และ y ไม่ได้หลุดออกไปนอกขนาดของภาพ (img->cols, img->rows) ไม่งั้นถ้า User เผลอลากเมาส์ทะลุขอบหน้าต่าง โปรแกรมอาจจะ Crash ดับไปดื้อๆ ได้ครับ!

6. 🏁 บทสรุป (To be continued…)

ยอดเยี่ยมมากครับน้องๆ! ตอนนี้แอปพลิเคชัน AI ของเราไม่ใช่แค่ตัวรับภาพนิ่งๆ อีกต่อไป แต่กลายเป็นโปรแกรมที่สามารถสื่อสารโต้ตอบกับผู้ใช้งานผ่านการคลิกเมาส์และลากวาด Bounding Box ได้แล้ว เทคนิคนี้คือจุดเริ่มต้นสำคัญหากเราต้องการสร้างเครื่องมือสำหรับทำ Data Annotation ไว้เทรนโมเดล Deep Learning ในอนาคตเลยล่ะครับ

ในตอนต่อไป พี่จะพาน้องๆ ไปรู้จักกับอีกหนึ่งเครื่องมือโต้ตอบบน HighGUI ที่ยอดฮิตไม่แพ้กัน นั่นก็คือ “Trackbars” (แถบเลื่อน) ที่จะช่วยให้เราปรับค่าพารามิเตอร์ของ Filter ต่างๆ ได้แบบ Real-time โดยไม่ต้องแก้โค้ดคอมไพล์ใหม่ให้เสียเวลา เตรียมตัวพบกับความสะดวกสบายได้เลยครับ!


ต้องการที่ปรึกษาด้านการพัฒนาระบบ AI Camera หรือ Machine Vision ให้กับโรงงานของคุณ? ทีมงาน WP Solution พร้อมให้บริการออกแบบและติดตั้งระบบแบบครบวงจร ดูรายละเอียดบริการของเราได้ที่: www.wpsolution2017.com หรือพูดคุยปรึกษาเบื้องต้นได้ที่ Line: wisit.p