"ไม่ต้องมีเวทมนตร์ ไม่ต้องไปหาแม่มด แค่คุณทำสิ่งที่โลกระลึกถึงตลอดกาล แค่นั้นคุณก็เป็นอมตะแล้ว"
บันทึกเบื้องหลังการพัฒนาระบบแปลงรูปเป็นภาพ 360° บนเว็บ 360runaround.com
7 Jul 2016 16:09   [10437 views]

ปล่อยของเล่นใหม่ไปเมื่อวานนี้อย่างเว็บ 360runaround.com ที่เอาไว้แปลงรูปภาพให้กลายเป็นภาพแบบ 360 องศาสำหรับโพสต์ลงเฟสบุ๊คด้วยวิธีง่ายๆ สำหรับรายละเอียดลองอ่านจากบล็อกที่แล้วได้ >> แปลงรูปเป็นภาพแบบ 360° ลงเฟสบุ๊คในคลิกเดียวด้วยเว็บ 360runaround.com

แต่เบื้องหลังความง่ายนั้นจริงๆแล้วการพัฒนานั้นซับซ้อนและวุ่นวายมาก บล็อกนี้เลยมาขอเขียนบันทึกเบื้องหลังว่าเทคนิคที่ใช้ทำยังไงเผื่อใครอยากจะลองเล่นอะไรแปลกๆกับเทรนด์เก๋ๆเทรนด์นี้ครับ

เข้าใจภาพแบบ Equirectangular

สำหรับเรื่องที่เป็น​ Fundamental ของภาพ 360 องศาที่ทุกคนต้องทำความเข้าใจก่อนจะไปทำอย่างอื่นต่อคือ "รูปแบบของภาพ 360 องศา" ซึ่งถ้าใครเคยใช้กล้อง 360 Camera อย่างพวก Ricoh Theta ก็จะรู้ว่าภาพที่ได้ออกมาจะหน้าตาแบบนี้

ก็จะเห็นความบิดเบี้ยว แต่จริงๆแล้วความบิดเบี้ยวนี้เป็นไปอย่างมีแบบแผน และเราเรียกภาพในรูปแบบนี้ว่า Equirectangular ครับ และนี่แหละคือ Fundamental ที่เราต้องเข้าใจมันก่อน

ก่อนอื่นเลย ถามว่าการสามารถมองภาพไปรอบๆ 360 องศาได้จริงๆแล้วมันคืออะไร? จริงๆมันก็คือการเอาตัวเองไปอยู่ในทรงกลมนั่นเอง ก็เลยสามารถหมุนไปดูรอบๆได้อย่างเนียนกิ๊ก อันนี้เป็นภาพทรงกลมที่ว่าเมื่อมองจากภายนอก

แต่ตอนที่เราเอามาดูจริงๆเราจะมองจากภายใน เหมือนเราไปยืนอยู่ตรงกลางทรงกลมนี้ ซึ่งภาพที่ได้มาจะครอปออกมาเพียงส่วนเดียวตามมุมมองของสายตา ก็เลยจะได้ออกมาเป็นแบบนี้

สรุปแล้วภาพ 360 องศาคือ Texture ของทรงกลมในโลกสามมิตินั่นเอง เพียงแต่เราต้องทำการแปลงภาพนั้นให้กลายเป็น 2 มิติเพื่อให้อยู่ในรูปแบบไฟล์ภาพ JPG ทั่วไป คำถามคือเราจะแปลงกันยังไง?

ก่อนอื่นต้องดูโครงสร้างทางเรขาคณิตของทรงกลมก่อน โครงสร้างของมันหน้าตาประมาณนี้ครับ มันคือการเอาสี่เหลี่ยมโค้งๆมาประกอบกัน แต่ละอันเล็กใหญ่ไม่เท่ากันตามตำแหน่งของชิ้นนั้นๆ

หรือถ้าเล่นกับทรงกลมที่คลาสสิคและทุกคนคุ้นตาหน่อยก็คือ ลูกโลก

โจทย์คือเราต้องแปลงภาพพื้นผิวของทรงกลมนี้ให้กลายเป็นภาพ 2 มิติ วิธีคือเราต้อง "กาง" กริดสี่เหลี่ยมแต่ละชิ้นบนลูกโลกนั้นออกมาให้กลายเป็นสี่เหลี่ยมจัตุรัส ผลที่ได้ออกมาจึงเป็นภาพอัตราส่วน 2:1 แบบนี้

ก็จะเห็นว่าตรงกลางๆจะค่อนข้างคล้ายเดิม แต่บริเวณด้านบนและด้านล่างจะถูกยืดออกเยอะมาก และนี่แหละครับภาพ Equirectangular หละ

ซึ่ง Pattern ภาพแบบนี้เป็นรูปแบบมาตรฐานที่ใช้ในทุกรูป Equirectangular ครับ อันนี้ลองเอาภาพด้านบนมาใส่ Grid ให้ดู

ก็จะเห็นว่ายืดตรง Grid บนๆกับล่างๆจะยืดออกและบิดเบี้ยว ส่วนตรงกลางค่อนข้างตรงอัตราส่วน แบบเดียวกับลูกโลกทุกประการ ถ้าเอาภาพด้านบนนี้มาแปะลงทรงกลมก็จะได้เป็นทรงกลมแบบนี้

และนี่แหละคือทรงกลมที่มองได้รอบและเป็นหลักการ Projection Mapping ที่เอาไปใช้แสดงผลบนเว็บต่างๆเช่น Facebook นั่นเอง ซึ่งเว็บเหล่านั้นจะใช้การมองจากข้างใน ได้ออกมาเป็นแบบนี้

สวัสดีภาพ 360 องศา ! แค่นี้แหละ ไม่ได้ยากอะไรเลยใช่ม้า แต่เนื่องจากมันเป็นอะไรที่ไม่ Linear ทำให้เราไม่สามารถสร้างภาพ Equirectangular ด้วย Photoshop ได้ แต่ต้องอาศัยการแปลงตามหลักคณิตศาสตร์

คณิตศาสตร์เบื้องหลังภาพ Equirectangular

ภาพ Equirectangular มันคือการแปลง Texture Map ของทรงกลมให้กลายเป็นภาพ 2 มิติ แต่ในชีวิตจริงวัตถุต่างๆไม่ได้วางเป็นพื้นผิวของทรงกลมเหมือนลูกโลก แล้วเราจะสร้างภาพ Equirectangular ขึ้นมาได้ยังไง?

คำตอบคือให้ลองเปลี่ยนมุมมองว่าเราไม่สนใจเลยว่าวัตถุจะวางยังไง ให้สนใจแค่ว่า "เรามองเห็นยังไง" และเพื่อให้มองได้รอบ มุมมองที่เราสนใจจึงมี 6 ด้านด้วยกันได้แก่ บน-ล่าง-ซ้าย-ขวา-หน้า-หลัง

โดยภาพแบบนี้เราเรียกมันว่า Cubemap

และให้คิดซะว่าเราไปยืนอยู่ตรงกลางทรงกลมและภาพที่เห็นรอบๆนั้นเป็นภาพของพื้นผิวทรงกลมรอบตัวเรา อันนี้เป็นภาพโครงสร้าง

และนี่เป็นภาพมุมมองทั้ง 6 ที่ Map ลงพื้นผิวทรงกลมแล้ว

ดังนั้นถ้ามองจากด้านในก็จะเห็นว่าภาพ 6 มุมมองที่เห็นด้านบนจริงๆแล้วเป็นโครงสร้างตามนี้

และเมื่อมันเป็นทรงกลมแล้ว เราก็จะสามารถแปลงเป็นภาพแบบ Equirectangular ได้ทันที และด้วยวิธีคิดแบบนี้ทำให้เราสามารถแปลงภาพใดๆที่มีมุมมองครบ 6 ด้านให้กลายเป็นภาพ 360 องศาได้เสมอ

แต่โจทย์ที่ได้จะเปลี่ยนไปเล็กน้อยเนื่องจากภาพแต่ละด้านมันไม่ใช่ Linear แต่เป็นภาพทรงโค้ง เราเลยต้องแก้ Distortion โดยใช้สมการการแปลงความบิดเบี้ยวตามรูปแบบด้านล่างนี้

ซึ่งถ้าเอา Mapping ด้านบนไป Project ลงบนทรงกลมก็จะได้ด้านทั้ง 6 ในรูปแบบทรงกลมอย่างพอดิบพอดี

สุดท้ายภาพด้านบนที่เป็น Cubemap จึงถูกแปลงเป็น Equirectangular ได้ดังนี้

เป็นอันเสร็จพิธีครับ นี่คือวิธีที่กล้องต่างๆสร้างภาพแบบนี้ขึ้นมา หากอยากรู้พวกสมการต่างๆ (ซึ่งส่วนใหญ่เป็น sin/cos) สามารถไปอ่าน Paper เรื่อง Spherical Projections (Stereographic and Cylindrical) ในส่วนของ Converting to and from 6 cubic environment maps and a spherical map ได้ครับ

ภาพ Equirectangular ที่โพสต์ลงเฟสบุ๊คได้

ความรู้อีกอย่างนึงที่ต้องรู้คือ แล้ว Facebook รู้ได้ไงว่าภาพของเราเ็น Equirectangular?

จริงๆไม่มีอะไรมาก Facebook ดูจาก Exif ครับ โดยมีเงื่อนไขสองอย่าง

1) ภาพต้องเป็นอัตราส่วน 2:1

2) ภาพต้องเป็น JPEG (เพื่อใส่ Exif ได้)

2) ข้อมูล Exif ต้องระบุว่าถ่ายมาจากกล้อง 360 องศาที่ Facebook สนับสนุน ซึ่งได้แก่ Ricoh Theta S, Giroptic 360 Cam, Samsung Gear 360, LG 360 Cam, C Realtech ALLie, 360Fly หรือ Panono

หากอยากลองเล่นอะไรแปลกๆ ให้ลองสร้างภาพ JPEG อัตราส่วน 2:1 ขึ้นมาสักภาพนึงแล้วแก้ Exif เข้าไปให้เป็น Maker = RICOH และ Model = RICOH THETA S ดูครับ

ภาพที่เห็นจะสามารถอัปขึ้น Facebook และกลายเป็นภาพ 360 องศาทันที แต่ถ้าภาพนั้นเป็นภาพถ่าย Linear ธรรมดา มันก็จะเหมือนกับเอาภาพนั้นไปใส่เป็น Texture ทรงกลม ผลคือด้านบนและด้านล่างจะยู่ๆ ส่วนตรงกลางจะป่องๆหน่อยดูไม่เป็นอัตราส่วนนั่นเอง ใช้งานจริงไม่ค่อยดีเท่าไหร่แต่หากอยากลองเล่นก็ทำได้ครับ

เทคนิคฝั่ง Server

ก็รู้พื้นฐานของภาพ 360 องศาแล้ว ก็แค่หาวิธีสร้างภาพ Equirectangular ขึ้นมาแล้วใส่ Exif หลอกว่าถ่ายจากกล้อง 360 ก็เป็นอันเรียบร้อย แต่นั่นแหละก็จะเห็นว่ามันมีความยากและไม่ได้ตรงไปตรงมาเท่าไหร่ ต้องทำภาพขึ้นมาจากมุมมองต่างๆและแก้สมการออกมาเป็นภาพๆเดียวให้ได้

สำหรับเว็บ 360runaround.com ใช้เทคนิคเหล่านี้ในการสร้างภาพขึ้นมาจากฝั่ง Server ครับ

1) ระบบแปลงภาพบน Server เขียนขึ้นมาด้วย Node.js 100% ทุกอย่างรันบน Node.js ไม่มี Dependency อื่นใดที่ต้องลงเพิ่ม ซึ่งอันนี้เป็นการเลือกเทคโนโลยีตามความเหมาะสมเพราะ Node.js สามารถทำงานที่ต้องการอย่างงานด้านการเรนเดอร์ภาพ 3 มิติได้และทำงานได้เร็วมาก นอกจากนั้นยังมีไลบรารี่ที่เกี่ยวข้องอย่างครบถ้วน สรุปคือดีทั้งเรื่องความเป็นไปได้และประสิทธิภาพนั่นเอง

2) การสร้างภาพ Equirectangular มีขั้นตอนดังนี้

- เรนเดอร์ภาพ 3 มิติจริงๆด้วย OpenGL โดยใช้ headless-gl ซึ่งไว้สร้าง GL Context โดยที่ไม่ต้องสร้าง Window ขึ้นมา ทำให้เราสามารถทำงานกับ OpenGL บน Node.js ได้โดย Syntax ที่ใช้จะเป็นของ WebGL เพราะ headless-gl เป็นการพอร์ต WebGL ไปใช้บน Node.js นั่นเอง

- เรนเดอร์ทั้งหมด 6 มุมมอง ได้แก่ บน-ล่าง-ซ้าย-ขวา-หน้า-หลัง ด้วยมุมมอง Field of View Angle 90 องศา ซึ่งสุดท้ายจะได้ภาพ Cubemap ออกมานั่นเอง

- แปลงภาพ Cubemap ให้เป็น Equirectangular ด้วย GLSL

- บันทึกภาพที่ได้เป็นไฟล์ JPEG และยัด Exif ไปว่าถ่ายจาก RICOH THETA S

ก็เป็นอันจบโปรเซส ได้ผลลัพธ์ออกมาพร้อมโพสต์ลงเฟสบุ๊คแล้ว สรุปรวมแล้วการสร้างภาพขึ้นมา 1 ภาพ จะต้องผ่านการเรนเดอร์ทั้งหมด 7 ครั้ง 1280x1280 พิกเซล 6 ภาพ และ 4096x2048 อีก 1 ภาพ จึงไม่ต้องแปลกใจที่การแปลงภาพจะกิน CPU 100% ยาวไปเลย 10-20 วินาที ฝั่ง Server ทำงานหนักมากจริงๆ แต่ก็เพื่อให้ได้ภาพที่สมบูรณ์ออกมาและสามารถปรับเปลี่ยนได้อย่างอิสระ ถ้าอยากได้รูปทรงใหม่ก็แค่เพิ่มโมเดล 3 มิติขึ้นมาแล้วใช้อัลกอริทึมเดิมในการแปลงภาพ

3) เพื่อความง่ายในการ Deploy ก็เลยยัดใส่ยัดระบบแปลงภาพใส่ไว้ใน Docker ตอนนี้สามารถ Deploy ระบบได้ด้วยบรรทัดเดียว

4) แต่เนื่องจาก headless-gl จำเป็นต้องใช้หน้าจอในการสร้าง Context แต่ Environment ใน Docker Container ไม่มีหน้าจออยู่ ก็เลยจำลองหน้าจอด้วย Xvfb (Virtual Framebuffer for X) ขึ้นมาเพื่อให้ใช้งานได้ใน Container โดยที่ไม่จำเป็นต้องมีหน้าจอ

5) ระบบวางอยู่บน Server 4 Cores และใช้ Node.js Cluster Fork โปรเซสมาทั้งหมด 4 โปรเซสเพื่อ Utilize CPU ทั้ง 4 Cores

6) เนื่องจาก Node.js ทำงานหนักพอสมควร Response Time เลยต่ำ ก็เลยทำเว็บไว้เป็น Static HTML และติดต่อกับระบบแปลงภาพผ่าน AJAX ส่วนไฟล์เว็บและ Static Files อื่นๆอย่างไฟล์ภาพก็ Deliver ด้วย nginx ทำให้เข้าเว็บได้เร็วถึงแม้ Server จะประมวลผลหนักตลอดเวลา

และหลังจากปรับจูน Server ตลอดเวลาเมื่อวานนี้ ตอนนี้เว็บเสถียรและไม่ล่มแล้ว สามารถเข้าใช้งานได้สมบูรณ์ครับ =)

7) ใช้ three.js เป็น Engine การแสดงพรีวิวภาพ 360 บนหน้าเว็บ โดยปรับจูนค่าต่างๆให้ตรงกับเฟสบุ๊ค เช่น อัตราส่วน 1:1 และมี Field of View Angle ที่ 65 องศา

ทั้งหมดก็ใช้เวลา 2 วันในการพัฒนา และอีก 1 วันในการตบตีกับ Designer จนทำเป็นหน้าเว็บออกมาเสร็จ ... แหะๆ


คร่าวๆก็ประมาณนี้ครับ ก็หวังว่าข้อมูลในบล็อกนี้จะมีประโยชน์ เผื่ออยากจะพลิกแพลงทำอะไรแผลงๆครับ ^_^

บทความที่เกี่ยวข้อง

Oct 29, 2016, 22:07
24324 views
แก้ปัญหา USB WiFi Dongle ชิปเซต Ralink ใช้งานไม่ได้บน Hackintosh
Oct 20, 2016, 16:20
15909 views
มีอะไรใหม่ใน Android 7.1 Developer Preview ฉบับนักพัฒนา
0 Comment(s)
Loading