ตอนที่ 7: เจาะลึกโครงสร้างภาพ - ขนาด, จำนวนช่องสี และความลึก

1. 🎯 ตอนที่ 7: เจาะลึกโครงสร้างภาพ - ขนาด, จำนวนช่องสี และความลึก (Size, Channels, Depth)
2. 📖 เปิดฉาก (The Hook)
น้องๆ เคยเจอปัญหาแบบนี้ไหมครับ? เขียนโค้ดดึงภาพจากกล้องมาซะดิบดี กะว่าจะเอาภาพสองภาพมาลบกันเพื่อหาของที่ขยับบนสายพาน (Motion Detection) หรือกะจะส่งภาพเข้าไปรันในโมเดล Deep Learning ปรากฏว่าพอกดรันปุ๊บ โปรแกรมเด้งหน้าจอแครช (Crash) แจ้งเตือน Error ตัวแดงเถือก หรือบางทีรันผ่าน แต่พอใช้ imshow โชว์ภาพออกมา ภาพดันกลายเป็นสีขาวโพลนทั้งจอซะงั้น!
ปัญหาคลาสสิกนี้ไม่ได้เกิดจากอัลกอริทึมที่ผิดพลาดครับ แต่เกิดจากการที่เรา “ไม่เข้าใจโครงสร้างข้อมูลของภาพ” อย่างถ่องแท้ ในวงการ Computer Vision ถ้าเราเอาน้ำหนึ่งแกลลอนไปเทใส่แก้วน้ำใบเล็กๆ ข้อมูลมันก็ล้น (Overflow) ใช่ไหมครับ? วันนี้พี่จะมาชงกาแฟ แล้วพาน้องๆ มุดลงไปดูไส้ในของคลาส cv::Mat ว่าตาราง Excel ขนาดยักษ์นี้ มันบอก “ขนาด” “ความหนาของสี” และ “ความจุของแต่ละพิกเซล” ให้เราทราบได้อย่างไร รับรองว่าเข้าใจเรื่องนี้แล้ว บั๊กกวนใจจะหายไปเกินครึ่งครับ!
3. 🧠 แก่นวิชา (Core Concepts)
ใน OpenCV ภาพทุกภาพจะถูกเก็บในรูปแบบของ cv::Mat (Image Matrix) ซึ่งมีคุณสมบัติ (Properties) หลักๆ ที่วิศวกรสาย Vision ต้องเช็กให้ชัวร์ก่อนนำไปประมวลผลเสมอ ได้แก่,:
1. ขนาดของภาพ (Dimensions:
rowsและcols)rows(จำนวนแถว): คือ “ความสูง” (Height) ของภาพในหน่วยพิกเซลcols(จำนวนคอลัมน์): คือ “ความกว้าง” (Width) ของภาพในหน่วยพิกเซล- เปรียบเทียบ: เหมือนเรากางตาราง Excel ถ้าภาพขนาด 1920x1080 ก็คือมี 1080 แถว และ 1920 คอลัมน์ครับ
2. จำนวนช่องสี (Channels:
channels())- ถ้าภาพเป็น Grayscale (ขาวดำ) จะมีเพียง 1 Channel (เก็บแค่ค่าความสว่าง),.
- ถ้าภาพเป็นสีมาตรฐาน จะมี 3 Channels (ใน OpenCV คือ B-G-R เรียงตามลำดับ),.
- ถ้าภาพโปร่งใส (มี Alpha channel) จะมี 4 Channels (BGRA).
- เปรียบเทียบ: สำหรับภาพสี ให้จินตนาการว่าเราเอาตาราง Excel 3 แผ่น (แผ่นสีน้ำเงิน สีเขียว สีแดง) มาวางซ้อนทับกันครับ
3. ความลึกของข้อมูล (Depth / Data Type:
depth()) นี่คือจุดที่คนตกม้าตายเยอะที่สุด!depth()คือตัวบอกว่า “ใน 1 ช่องพิกเซล เราใช้พื้นที่หน่วยความจำเท่าไหร่ในการเก็บตัวเลข” ซึ่งแบ่งเป็น 3 กลุ่มหลักๆ ที่เจอบ่อย:- 8-bit Unsigned (
CV_8U): เก็บตัวเลขจำนวนเต็มบวกได้ตั้งแต่ 0 ถึง 255,. นี่คือรูปแบบมาตรฐานของภาพทั่วไป (JPG, PNG) ที่เราโหลดเข้ามาครับ - 16-bit Unsigned (
CV_16U/CV_16S): เก็บตัวเลขได้ตั้งแต่ 0 ถึง 65,535. มักใช้กับกล้องอุตสาหกรรม (Machine Vision Cameras), กล้องการแพทย์ หรือกล้อง Depth Sensor ที่ต้องการความละเอียดของแสง (Dynamic Range) สูงๆ - 32-bit Float (
CV_32F): เก็บตัวเลขทศนิยม นิยมปรับสเกลให้อยู่ในช่วง 0.0 ถึง 1.0,. ทำไมเราต้องใช้ทศนิยม? เพราะเวลาเราเอาภาพไปคูณ หาร หรือหาค่าเฉลี่ยทางคณิตศาสตร์ (เช่น การทำ Filter, การป้อนเข้า Neural Networks) ถ้าใช้แบบ 8-bit ค่ามันจะล้น 255 หรือปัดเศษทิ้งจนข้อมูลเพี้ยนครับ! เราเลยต้องแปลงเป็น Float ก่อนคำนวณเสมอ,
- 8-bit Unsigned (
รหัสลับของ OpenCV (Type Macro): เวลาเราประกาศสร้าง
cv::Matเราจะใช้ Macro ที่รวม Depth และ Channels เข้าด้วยกัน เช่นCV_8UC3,8U= 8-bit Unsigned IntegerC3= 3 Channels (สี BGR)- ถ้าเป็น
CV_32FC1ก็จะหมายถึงภาพทศนิยม 32-bit แบบ 1 Channel (Grayscale) ครับ

4. 💻 ร่ายมนต์คำสั่ง (Show me the Code)
เรามาดูตัวอย่างโค้ด C++ ในการตรวจสอบคุณสมบัติของภาพ และการแปลงชนิดข้อมูลจาก 8-bit ไปเป็น 32-bit Float เพื่อเตรียมความพร้อมสำหรับการทำ Machine Learning กันครับ
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 1. โหลดภาพสีต้นฉบับเข้ามา (ค่าเริ่มต้นเป็น CV_8UC3)
Mat img = imread("factory_defect.jpg", IMREAD_COLOR);
if(img.empty()) return -1;
// 2. ดึงคุณสมบัติ (Properties) พื้นฐานออกมาดู
cout << "--- Image Properties ---" << endl;
cout << "Width (Cols) : " << img.cols << " pixels" << endl;
cout << "Height (Rows) : " << img.rows << " pixels" << endl;
cout << "Channels : " << img.channels() << " (BGR)" << endl;
// คืนค่าเป็นรหัสตัวเลข (เช่น 0 คือ CV_8U, 5 คือ CV_32F)
cout << "Depth Code : " << img.depth() << endl;
// 3. ปัญหา: ถ้าเราเอา img ไปคูณ 2.5 ค่าพิกเซลที่เกิน 255 จะถูกตัดทิ้ง (Overflow)
// ทางแก้: แปลงภาพเป็น 32-bit Float (CV_32F)
Mat imgFloat;
// แปลงประเภทข้อมูล พร้อมกับหาร 255.0 เพื่อบีบสเกลให้อยู่ในช่วง 0.0 - 1.0 (Normalization)
img.convertTo(imgFloat, CV_32FC3, 1.0 / 255.0);
// 4. ลองเช็กค่า Properties ของภาพใหม่ดู
cout << "\n--- Converted Image ---" << endl;
cout << "New Channels : " << imgFloat.channels() << endl;
cout << "New Depth Code: " << imgFloat.depth() << " (CV_32F)" << endl;
// 5. แสดงผลภาพ
// ฟังก์ชัน imshow ฉลาดพอที่จะรู้ว่าถ้าเป็น Float (0.0-1.0) มันจะคูณ 255 กลับให้ตอนโชว์จอ
imshow("Original 8-bit", img);
imshow("Normalized 32-bit Float", imgFloat);
waitKey(0);
return 0;
}5. 🛡️ เคล็ดลับจากคัมภีร์ลับ (Under the Hood / Pro-Tips)
- อันตรายจากการสุ่มสี่สุ่มห้าคูณเลข (Data Type Mismatch): ปัญหาที่ทำให้แอปพลิเคชันพังบ่อยที่สุดคือการป้อน Mat ผิด Type เข้าไปในฟังก์ชันครับ ตัวอย่างเช่น ฟังก์ชันบางตัวใน OpenCV หรือโมเดล Deep Learning ต้องการภาพแบบ
CV_32Fเท่านั้น ถ้าน้องๆ โยนCV_8Uเข้าไป โปรแกรมจะพ่นcv::Exceptionแล้วดับทันที ดังนั้นการเช็กimg.type()หรือimg.depth()ก่อนประมวลผลจึงเป็นเรื่องสำคัญมาก - กับดักของฟังก์ชัน
imshowกับภาพทศนิยม: จำไว้เสมอครับว่า ถ้าเราโชว์ภาพ 8-bit (CV_8U) ตัวimshowจะวาดภาพตามปกติ (0-255) แต่ถ้าเราโยนภาพทศนิยม (CV_32F) ให้มันวาด มันจะทึกทักเอาเองว่าค่าในนั้นต้องอยู่ระหว่าง 0.0 ถึง 1.0 แล้วมันจะเอาไปคูณ 255 ให้เองตอนวาดออกจอ, ถ้าน้องๆ แปลงภาพเป็นCV_32Fแต่ลืมหารสเกล (คือค่ายังเป็น 0-255 เท่าเดิม) ภาพที่โชว์ออกมาจะกลายเป็นสีขาวโพลนแสบตาไปทั้งหน้าต่างเลยครับ!
6. 🏁 บทสรุป (To be continued…)
การเข้าใจเรื่อง Size, Channels และที่สำคัญที่สุดคือ Depth (Data Types) เป็นรากฐานที่สำคัญมากในการเขียน Computer Vision Pipeline ระดับอุตสาหกรรม เพราะมันช่วยให้เรารู้ว่าหน่วยความจำเราเก็บข้อมูลแบบไหน และต้องระวังเรื่องการคำนวณทางคณิตศาสตร์อย่างไรไม่ให้ข้อมูลพิกเซลสูญหาย
เมื่อเราเข้าใจโครงสร้างของภาพอย่างทะลุปรุโปร่งแล้ว ในตอนต่อไป พี่จะพาน้องๆ ถลกแขนเสื้อ เจาะลึกลงไปหาวิธีการ “เข้าถึงและแก้ไขพิกเซลทีละจุด” (Pixel Access) เพื่อสร้างเวทมนตร์ Image Filter หรือการลบ Noise เฉพาะจุดด้วยมือของเราเองกันครับ รอติดตามได้เลย!
ต้องการที่ปรึกษาด้านการพัฒนาระบบ AI Camera หรือ Machine Vision ให้กับโรงงานของคุณ? ทีมงาน WP Solution พร้อมให้บริการออกแบบและติดตั้งระบบแบบครบวงจร ดูรายละเอียดบริการของเราได้ที่: www.wpsolution2017.com หรือพูดคุยปรึกษาเบื้องต้นได้ที่ Line: wisit.p