รูปปกบทความ

1. 🎯 ตอนที่ 11: คณิตศาสตร์ของภาพ - การบวก ลบ และความทึบแสง (Image Mathematics & Alpha Blending)

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

น้องๆ เคยอยากทำระบบ AI ที่มีลายน้ำ (Watermark) โลโก้บริษัทแปะทับลงไปบนภาพจากกล้อง Webcam แบบโปร่งแสงไหมครับ? หรือบางทีภาพในโรงงานมันมืดเกินไป เราเลยอยากจะ “บวก” ค่าความสว่างเข้าไปดื้อๆ ให้ภาพมันสว่างขึ้น

ถ้าเราคิดแบบโปรแกรมเมอร์ทั่วไป ภาพก็คือตารางตัวเลข (Matrix) งั้นเราก็แค่เอาตัวเลขพิกเซลมาบวกกันตรงๆ สิ เช่น ค่าสีเดิมคือ 150 เราอยากเพิ่มความสว่างไปอีก 150 ผลลัพธ์ก็คือ 150 + 150 = 300 ถูกไหมครับ?

แต่เดี๋ยวก่อน! ในโลกของภาพ 8-bit (CV_8U) ค่าสีมันเก็บได้สูงสุดแค่ 255 ครับ ถ้าเราเขียนโปรแกรมบวกเลขเองแบบไม่ระวัง ค่า 300 มันจะเกิดการล้น (Overflow) และวนกลับไปเริ่มต้นใหม่กลายเป็น 300 - 256 = 44! จากภาพที่ควรจะสว่างจ้า ดันกลายเป็นจุดดำมืดซะงั้น นี่คือบั๊กคลาสสิกที่ทำเอาเด็ก Vision ร้องไห้มานักต่อนักแล้วครับ

วันนี้พี่จะพามาจิบกาแฟ แล้วดูวิธีแก้ปัญหานี้ด้วยคณิตศาสตร์ของ OpenCV ที่เรียกว่า Saturated Operation รวมถึงการผสมภาพแบบโปร่งแสงด้วยฟังก์ชันระดับเทพอย่าง addWeighted กันครับ!

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

ใน OpenCV มีฟังก์ชันสำหรับทำคณิตศาสตร์ของภาพ (Image Mathematics) ที่ถูกปรับแต่งความเร็วมาถึงขีดสุด และที่สำคัญมันมาพร้อมกับระบบป้องกันบั๊กที่เรียกว่า Saturation Arithmetics

  • 1. Saturated Operation (เวทมนตร์กันน้ำล้น): ใน OpenCV เมื่อเราใช้ฟังก์ชันบวกหรือลบภาพ มันจะใช้กลไกการปัดเศษแบบ Saturation เปรียบเทียบง่ายๆ เหมือนเราเทน้ำลงในแก้วที่มีความจุ 255 มิลลิลิตร ถ้าเราเทน้ำลงไป 300 มิลลิลิตร น้ำมันก็แค่ปริ่มหยุดอยู่ที่ขอบแก้ว (255) ไม่ทะลุไปไหน และถ้าเราดูดน้ำออกจนติดลบ มันก็จะหยุดอยู่ที่ก้นแก้ว (0) ครับ สมการเบื้องหลังคือ: I(x, y) = min(max(round(r), 0), 255)
  • 2. การบวกภาพ (cv::add): เป็นการนำภาพ 2 ภาพ (หรือภาพกับค่าคงที่ Scalar) มาบวกกันแบบจุดต่อจุด (Per-element sum) ฟังก์ชันนี้เหมาะมากเวลาเราต้องการเพิ่มความสว่างให้ภาพ โดยถ้าค่าพิกเซลบวกกันแล้วเกิน 255 มันจะถูกตรึงไว้ที่ 255 ทันที
  • 3. การลบภาพ (cv::subtract): ทำงานตรงข้ามกับการบวก คือเอาพิกเซลมาลบกัน ถ้าผลลัพธ์ติดลบก็จะถูกตรึงไว้ที่ 0 ฟังก์ชันนี้คือหัวใจสำคัญของการทำ Background Subtraction เพื่อหาวัตถุที่เคลื่อนไหว (เช่น เอาภาพแบ็กกราวด์เปล่าๆ มาลบกับภาพที่มีคนเดินผ่าน เราจะได้ตัวคนโผล่ขึ้นมา)
  • 4. การผสมภาพแบบโปร่งแสง (cv::addWeighted): นี่คือฟังก์ชันสำหรับทำ Alpha Blending หรือการทำภาพซ้อนทับแบบกำหนดความทึบแสง (Opacity) ได้ มันจะนำภาพ 2 ภาพมาผสมกันตามน้ำหนักเปอร์เซ็นต์ที่เรากำหนด (เช่น เอาภาพ A มา 70% ผสมกับภาพ B 30%) สมการของมันคือ: $dst = src1 \times \alpha + src2 \times \beta + \gamma$
รูปประกอบ

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

เรามาดูโค้ด C++ ในการสร้างระบบบวกภาพเพื่อปรับความสว่าง และการใส่ลายน้ำแบบโปร่งแสงด้วย addWeighted กันครับ

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

using namespace cv;
using namespace std;

int main() {
    // 1. โหลดภาพต้นฉบับจากโรงงาน และภาพโลโก้ลายน้ำ
    Mat factoryImg = imread("factory_machine.jpg", IMREAD_COLOR);
    Mat logoImg = imread("wp_logo.jpg", IMREAD_COLOR);

    if (factoryImg.empty() || logoImg.empty()) return -1;

    // กฎเหล็ก: การจะบวกภาพได้ ทั้งสองภาพต้องมีขนาด (Size) และชนิด (Type) เท่ากันเป๊ะๆ
    // เราจึงต้อง Resize โลโก้ให้เท่ากับภาพโรงงานก่อน
    Mat resizedLogo;
    resize(logoImg, resizedLogo, factoryImg.size());

    // ==========================================
    // 1. การใช้ cv::add เพื่อเพิ่มความสว่าง 
    // ==========================================
    Mat brightImg;
    // สร้างตารางสี (Scalar) ที่มีค่า B=50, G=50, R=50
    Scalar brightnessOffset(50, 50, 50); 
    
    // เอาภาพโรงงาน + ค่าความสว่าง (ใช้เวทมนตร์ Saturated กันค่าล้น 255)
    add(factoryImg, brightnessOffset, brightImg); 

    // ==========================================
    // 2. การใช้ cv::addWeighted ทำลายน้ำโปร่งแสง
    // ==========================================
    Mat blendedImg;
    double alpha = 0.7; // ให้น้ำหนักภาพโรงงาน 70%
    double beta = 0.3;  // ให้น้ำหนักภาพโลโก้ 30%
    double gamma = 0.0; // ไม่ต้องการบวกแสงเพิ่มตอนท้าย

    // ร่ายคาถาผสมภาพ
    addWeighted(factoryImg, alpha, resizedLogo, beta, gamma, blendedImg);

    // แสดงผลงาน
    imshow("Original Factory", factoryImg);
    imshow("Brighter Factory (add)", brightImg);
    imshow("Watermark (addWeighted)", blendedImg);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

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

  • กฎของการแต่งงาน (Size & Depth Must Match): ข้อควรระวังสูงสุดเวลาใช้ cv::add, cv::subtract หรือ cv::addWeighted คือ Matrix ทั้งสองตัวจะต้องมีขนาด (Width x Height) และชนิดความลึก (เช่น CV_8UC3) “เหมือนกันเป๊ะ” ถ้าน้องๆ โยนภาพ 4K ไปบวกกับภาพ 1080p โปรแกรมจะพ่น Error ตัวแดงและแครชทันทีครับ! อย่าลืมครอบด้วย ROI หรือ cv::resize ก่อนเสมอ
  • ใช้ C++ Operator Overloading ก็ได้นะ: ถ้าน้องๆ ขี้เกียจพิมพ์ฟังก์ชัน add() ยาวๆ OpenCV ยุคใหม่รองรับการเขียนแบบสมการคณิตศาสตร์ตรงๆ เช่น Mat dst = src1 + src2; หรือ Mat dst = src1 * alpha + src2 * beta + gamma; ได้เลย ซึ่งเบื้องหลังของมัน (Matrix Expressions) ก็แอบเรียกใช้ saturate_cast เพื่อป้องกันค่าล้นให้อัตโนมัติเหมือนกันครับ ฉลาดสุดๆ!
  • อย่าใช้ for loop บวกเลขเองถ้าไม่จำเป็น: แม้น้องๆ จะเก่ง Pointer แค่ไหน แต่ถ้าต้องเขียน loop ปรับแสงทีละพิกเซล พี่แนะนำให้ใช้ cv::add หรือ cv::Mat::convertTo ดีกว่าครับ เพราะ OpenCV มีการทำ Hardware Optimization (เช่นใช้คำสั่ง SSE2 แบบ SIMD) ทำให้การบวกภาพทั้งเฟรมทำงานได้เร็วกว่าเราเขียน Loop เองหลายเท่าตัว!

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

ด้วยความเข้าใจเรื่อง Saturated Operation และการใช้ฟังก์ชัน add / addWeighted ตอนนี้น้องๆ ก็สามารถปรับแสงของภาพ ผสมภาพ ทำลายน้ำ หรือแม้แต่วางพื้นฐานเพื่อทำระบบลบภาพพื้นหลัง (Background Subtraction) ได้แล้วครับ คณิตศาสตร์เบื้องหลัง Computer Vision ไม่ได้น่ากลัวอย่างที่คิดเลยใช่ไหมล่ะ?

ในตอนต่อไป พี่จะพาเข้าสู่โลกของการ “กรองภาพ” (Image Filtering) เราจะมาดูวิธีใช้ Convolution Kernel ในการเบลอภาพเพื่อลบสัญญาณรบกวน (Noise) ออกไปให้หมด เตรียมตัวให้พร้อมครับ!


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