By Arnon Puitrakul - 16 กันยายน 2024
เคยสงสัยกันมั้ยว่า Filter ที่เราใช้เบลอภาพ ไม่ว่าจะเพื่อความสวยงาม หรืออะไรก็ตาม แท้จริงแล้ว มันทำงานอย่างไร วันนี้เราจะพาไปดูคณิตศาสตร์และเทคนิคเบื้องหลังกันว่า กว่าที่รูปภาพจะถูกเบลอได้ มันเกิดจากอะไร
ก่อนเราจะไปเข้าใจการทำงานของสิ่งที่จะเล่าในบทความนี้ สำหรับคนที่ยังไม่เคยเรียนในเรื่องกลุ่ม Image Processing หรือ Digital Image ขอติวความรู้พื้นฐานสักเล็กน้อยเพื่อความเข้าใจในหัวเรื่องต่อไป แต่ถ้าใครรู้อยู่แล้ว ก็ข้ามไปเลยมันพื้นมาก ๆ
ภาพที่เราเห็นเป็นภาพที่สวยงาม เมื่อเราเซฟมาเป็นลักษณะของ Digital File ภาพทั้งหมดจะถูกซอยออกเป็นช่องสี่เหลี่ยมเล็ก ๆ เยอะมาก ๆ แต่ละสี่เหลี่ยมเล็ก ๆ เราเรียกว่า Pixel เหมือนที่เวลาเราบอกว่า กล้องตัวนี้มีความละเอียด 32 MP นั่นแปลว่า เมื่อเราถ่ายภาพออกมาแล้วเซฟเป็นไฟล์ภาพ ภาพนั้นจะประกอบด้วยช่องสี่เหลี่ยมจำนวน 32 ล้านช่องอยู่ในไฟล์
โดยในแต่ละช่องหากเราแหกมันออกมา เราจะพบว่าจริง ๆ แล้วมันมี 3 ชั้น (Channel) ด้วยกันคือ Red,Green และ Blue หรือก็คือชั้นที่บอกค่าของสีแต่ละสี (แต่ถ้าเป็นภาพขาวดำ เราก็จะเหลือแค่ Channel เดียว) เมื่อเราเอาทั้ง 3 ชั้นนี้มารวมกัน ก็จะได้เป็นสีที่เราเห็นนั่นเอง ซึ่งแต่ละชั้นจะสามารถมีค่าได้ตั้งแต่ 0-255 (สำหรับรูปภาพที่ใช้ Bit Depth 8-bit) คล้าย ๆ กับเวลาเราเลือกสีในเครื่องคอมพิวเตอร์ในลักษณะ RGB เป๊ะ ๆ เลย
ดังนั้นรูปภาพเดียวกัน สิ่งที่เราเห็นคือภาพที่ประกอบเป็นสี ๆ รวมกันแล้ว แต่สำหรับเครื่องคอมพิวเตอร์มันคือ Matrix 3 มิติขนาดใหญ่ที่ประกอบด้วยตัวเลขจำนวนตามความละเอียด คูณ 3 จากสีแต่ละ Channel
เชื่อหรือไม่ว่า การที่เราเบลอภาพ, ทำให้ภาพคมขึ้น และอีกหลาย ๆ งานล้วนใช้วิธีการเดียวกันคือการทำ Kernel Convolution รวมไปถึงหลักการที่ CNN (Convolutional Neural Network) ใช้ก็ถูก Apply มาจากหลักการนี้แหละ
การทำ Kernel Convolution มีองค์ประกอบทั้งหมด 2 อย่างคือ รูปภาพ และ Kernel โดย Kernel มันคือ Matrix ชุดนึงที่มีขนาดเท่าไหร่ก็ได้ เช่นเราอาจจะใช้ขนาด 3x3 ก็ได้ แล้วแต่เราออกแบบเลย ซึ่งมันเป็นส่วนสำคัญมาก ๆ ว่าเราจะทำอะไร เช่น Kernal สำหรับการเบลอภาพ มันก็จะแตกต่างจาก Kernel ที่ใช้ทำให้ภาพคมมากขึ้น
เราขอยกตัวอย่าง Kernel สำหรับการเบลอที่ง่ายที่สุด เราเรียกว่า Mean Blur ง่ายจริง ๆ คือ Kernel ประกอบด้วย 1 หมดเลย เช่น เรากำหนดให้ Kernel ขนาด 3x3 เราก็ะจะได้แบบด้านบนเลย
เวลาเรา Apply Kernel ลงไปง่ายมากคือ เราเอา Kernel ไปทาบกับภาพ แล้วจับทำ Dot Product กันเลยแล้วหารด้วยขนาดของ Kernel ในที่นี้คือ 3x3 = 9 เราก็หาร 9 ลงไปเป็นการเฉลี่ยให้เท่ากัน ทำให้ภาพไม่สว่างวาบหรือมืดตึบ
def apply_convolution (img: np.array, kernel: np.array) -> np.array :
img_height, img_width = img.shape[0], img.shape[1]
kernel_height, kernel_width = kernel.shape[0], kernel.shape[1]
final_image = np.zeros((img_height-kernel_height+1, img_width-kernel_width+1,3))
for i in range(kernel_height//2, img_height-kernel_height//2-1) :
for j in range(kernel_width//2, img_width-kernel_width//2-1) :
sliding_window = img[i-kernel_height//2: i+kernel_height//2+1, j-kernel_width//2: j+kernel_width//2+1]
final_image[i,j] = (sliding_window * kernel).sum()
return np.clip(final_image, 0, 255)
เราลองเขียน Function ง่าย ๆ เพื่อ Apply Kernel ลงไปในภาพ วิธีการก็คือเหมือนกับที่เราเล่าไปก่อนหน้านี้เลย ลองอ่านดูได้
sharpen_kernel = np.array([
[0,-1,0],
[-1,5,-1],
[0,-1,0]
])
ด้วยวิธีการเดียวกัน เพียงแค่เราเปลี่ยน Kernel งานที่ทำก็เปลี่ยนได้เช่นกัน เช่นด้านบน เราแค่เปลี่ยน Kernel ก็จะกลายเป็นการทำให้ภาพคมขึ้น หรือกระทั่งงานอื่น ๆ ในทาง Image Processing ก็ใช้วิธีนี้เช่นเดียวกัน นี่แหละคือเบื้องหลังของสิ่งที่เกิดขึ้นใน Photoshop มันก็คือคณิตศาสตร์ไม่ใช่เวทย์มนต์แต่อย่างใด
final_image = cv2.filter2D(img, -1, kernel)
เมื่อกี้เขียนเพื่อแสดงให้เห็นขั้นตอนการทำงานของมันจริง ๆ แต่แน่นอนว่าคำสั่งที่สำคัญขนาดนี้ เราไม่จำเป็นต้องเขียนเองหรอก ใน Library อย่าง OpenCV เขามีมาให้เราเรียบร้อยแล้ว เราสามารถใช้คำสั่งด้านบนเพื่อ Apply Kernel ตามที่เราต้องการได้เลย แล้วพวกนี้มันเขียนใน C ด้วย ดังนั้นเร็วกว่าการเขียนใน Python ที่ต้องรันผ่าน Interpreter แน่นอน
ในช่วงหลายปีที่ผ่านมานี้ เรามักจะเห็นความสามารถอย่าง Portrait Mode บนกล้องโทรศัพท์ไม่ว่าจะเป็น iPhone, Google Pixel และ Samsung เองก็ตาม เราอาจจะรู้สึกว่ามันมหัศจรรย์มาก ๆ สามารถทำให้ภาพเบลอหลังได้เหมือนกับสิ่งที่กล้องใหญ่ทำได้เลย
เหตุที่กล้องใหญ่เขาเบลอหลังได้มากกว่า ส่วนใหญ่เป็นที่ขนาดของ Sensor เพราะหากเราเอากล้องโทรศัพท์ และกล้อง Full-Frame ที่ตั้งค่า Shutter Speed, Aperture และ ISO เท่ากันหมด ภาพที่ได้จากกล้องโทรศัพท์จะมีความหน้าชัดหลังเบลอน้อยกว่า ภาษาภาพถ่ายเราเรียกว่า DoF มันลดลงตามขนาดของ Sensor
ปัญหาคือ กล้องโทรศัพท์มันเล็กนิดเดียว การจะใส่ Sensor ใหญ่ระดับ Full-Frame เบลอหลังสวย ๆ มันเป็นเรื่องยาก ดังนั้นมันจะต้องมีขั้นตอนอะไรบางอย่างเพื่อทำให้ภาพมันมีความหน้าชัดหลังเบลอสวยได้เหมือนกับกล้องใหญ่
ในเมื่อเราเบลอภาพได้แล้ว งั้นเราก็เบลอมันซะเลยสิ ไม่ยาก แต่เอ๊ะ ถ้าเราเบลอทั้งหมดแล้วตัวแบบมันจะเหลืออะไรมันไม่ต่างจากการเบลอปกติ ดังนั้นเราจำเป็นต้องใช้เทคนิคบางอย่างในการแยกส่วนที่เป็น Foreground ออกจาก Background ให้ได้ เทคนิคนั้นเราเรียกว่า Image Segmentation
ซึ่งการทำ Image Segmentation เราสามารถทำได้หลากหลายวิธีมาก ๆ ตั้งแต่การใช้งาน Edge Detection จนไปถึงการใช้ Machine Learning สมัยใหม่ในการจัดการเลยทีเดียว จากนั้นมันจะเริ่ม Apply Blur Kernel ลงไปใน Background
กล้องบางตัวค่อนข้างฉลาดมากกว่านั้นคือ อาศัยกล้องหลาย ๆ ระยะทำงานพร้อม ๆ กัน เช่น iPhone ที่มันมีกล้อง Telephoto ด้วย หลักการคือเมื่อเราใช้เลนส์ที่ระยะยาวมากขึ้น วัตถุอยู่เท่าเดิม DoF มันจะแคบลง ทำให้หากเราเอาภาพของกล้องซูมมาเทียบกับกล้องหลักที่ใช้ถ่าย มันก็พอจะทำ Depth Map บอกระยะได้คร่าว ๆ ว่าอันไหนน่าจะเป็น Foreground และ Background หรือกระทั่งการใช้ LiDAR ในการจับระยะทำ Depth Map ออกมา เมื่อรวมกับ Image Segmentation วิธีการอื่น ๆ ก็จะทำให้ประสิทธิภาพดีขึ้นกว่าเดิมอีก
แต่ปัญหาของการทำแค่นี้คือ เราจะเห็นว่าภาพมันเบลอแบบแข็ง ๆ เห็นขอบของการเบลอชัดเจน โดยเฉพาะในภาพที่มีความซับซ้อนยิ่งเห็นได้ชัดเจนมากขึ้นไปอีก วิธีการแก้ของพวกนี้คือการใช้ Edge Smoothing เช่นการเบลอรอบ ๆ ขอบ ทำให้ขอบมันเบลอเนียน ๆ ไปกับ Background ที่เบลอก็สามารถช่วยได้เช่นกัน
หรือถ้าเรามี Depth Map เราสามารถเขียนให้เครื่อง Apply Blur Intensity ที่ไม่เท่ากันโดยการอ้างอิงจาก Depth Map ก็ได้เช่นกัน ด้วยวิธีนี้ก็จะทำให้ภาพที่ได้ออกมาดูเป็นธรรมชาติ "คล้าย"ภาพจากกล้องใหญ่ที่เบลอด้วยฟิสิกส์จริง ๆ ได้
แต่แน่นอนว่า วิธีนี้ก็ยังไม่ได้เป็นวิธีที่ Ultimate สามารถสร้างภาพให้มีความหน้าชัดหลังเบลอได้เท่ากับการใช้กล้องจริงแน่นอน ลองดูจากภาพด้านบน เราใช้ Portrait Mode บน iPhone 14 Pro หากเราซูมเข้าไปที่ไรผม เราจะเห็นว่าภาพที่ได้จากโทรศัพท์มันเบลอหายไปหมดเลย เรียกว่าผมยุ่งมีปลอยผมอะไรยังไงคือหายหมด นั่นเป็นเพราะ Image Segmentation มันยังไม่สามารถจัดการกับจุดที่ละเอียดมาก ๆ ของภาพอย่างไรผมได้นั่นเอง
นี่ก็คือหนึ่งในตัวอย่างของการนำเทคนิคการเบลอภาพมาใช้กับเทคนิค Image Segmentation เพื่อให้เราได้ภาพที่ใกล้เคียงกับกล้องใหญ่มากขึ้นนั่นเอง
การเบลอเป็นเทคนิคนึงที่ทำให้เราสามารถลดรายละเอียดของภาพลงไปได้ โดยอาจจะใช้เพื่อสร้างความสวยงาม, ลด Noise หรือกระทั่งเป็นหนึ่งในขั้นตอนของการปรับแปลงรูปเพื่อนำไปวิเคราะห์หาข้อมูลต่อไป สิ่งที่เอามาเล่าในบทความนี้อย่างการทำ Kernel Convolution เป็นเทคนิคพื้นฐานของ Image Filter หลาย ๆ ตัวเลยก็ว่าได้ พวก Filter ที่เราใช้งานกันในปัจจบันส่วนใหญ่ก็ใช้พื้นฐานมาจากวิธีนี้เช่นเดียวกัน สำหรับคนทั่วไปก็อ่านเอาพอรู้ เอาสนุกละกัน แต่สำหรับเด็กคอมน่าจะเป็นเรื่องที่ทุกคนต้องเรียนแหละ พื้นฐานมาก ๆ
เคยสงสัยกันมั้ยว่า Filter ที่เราใช้เบลอภาพ ไม่ว่าจะเพื่อความสวยงาม หรืออะไรก็ตาม แท้จริงแล้ว มันทำงานอย่างไร วันนี้เราจะพาไปดูคณิตศาสตร์และเทคนิคเบื้องหลังกันว่า กว่าที่รูปภาพจะถูกเบลอได้ มันเกิดจากอะไร...
หลังจากดูงาน Google I/O 2024 ที่ผ่านมา เรามาสะดุดเรื่องของการใส่ Watermark ลงไปใน Content ที่ Generate จาก AI วันนี้เราจะมาเล่าให้อ่านกันว่า วิธีการทำ Watermark ใน Content ทำอย่างไร...
ก่อนหน้านี้เราทำ Content เล่าความแตกต่างระหว่าง CPU, GPU และ NPU ทำให้เราเกิดคำถามขึ้นมาว่า เอาเข้าจริง เราจำเป็นต้องมี NPU อยู่ในตลาดจริง ๆ รึเปล่า หรือมันอาจจะเป็นแค่ Hardware ตัวนึงที่เข้ามาแล้วก็จากไปเท่านั้น วันนี้เราจะมาเล่าให้อ่านกัน...
บทความนี้ เราเขียนสำหรับมือใหม่ หรือคนที่ไม่ได้เรียนด้านนี้แต่อยากรู้ละกัน สำหรับวันนี้เรามาพูดถึงคำที่ถ้าเราทำงานกับพวก Developer เขาคุยกันบ่อย ๆ ใช้งานกันเยอะ ๆ อย่าง Database กันว่า มันคืออะไร ทำไมเราต้องใช้ และ เราจะมีตัวเลือกอะไรในการใช้งานบ้าง...