Using OpenPose for social distancing detection on Colab
ที่ผ่านมาผมได้รับโจทย์ให้ประมาณระยะห่างระหว่างบุคคลจากภาพวีดีโอ ซึ่งเครื่องคอมพิวเตอร์ของผมเองก็ไม่ได้มี GPU ที่แรงมาก ผมจึงใช้ Colab ในการทดลองเขียนโจทย์นี้
ในโจทย์นี้ผมเลือกใช้ OpenPose + Norfair เพื่อตรวจจับและติดตามคนที่อยู่ในภาพ และหลังจากนั้นจึงใช้ Perspective Transform ของ OpenCV เพื่อปรับเปลี่ยนภาพจากมุมกล้องมาเป็นภาพ Top View เพื่อคำนวณระยะห่างของคนแต่ละคน ทั้งนี้สิ่งที่จำเป็นของการใช้ Perspective Transform ของ OpenCV คือการ map จุดอ้างอิงในภาพกับพื้นที่จริงรวมถึงระยะระหว่างจุดอ้างอิงเหล่านั้น
เพื่อความสะดวกผมจึงระบุจุดอ้างอิงเป็นรูปสี่เหลี่ยมผืนผ้าในพื้นที่จริงเพื่อให้ง่ายต่อการสร้าง numpy array ของจุดอ้างอิง และการแปลงจุดอ้างอิงเหล่านั้นมาเป็นภาพ Top View
จากภาพข้างบน สมมุติว่าพื้นที่ๆเราสนใจคือพื้นที่ในขอบสีแดงที่เห็นในรูป โดยผมประมาณพื้นที่ดังกล่าวเป็นรูปสี่เหลี่ยมขนาด ?? x 21 m. และขนาดที่ประมาณนี้เมื่อเอาภาพในกรอบสีแดงมาคำนวณหา Transformation Matrix เพื่อแปลงจุดในภาพให้กลายเป็นสี่เหลี่ยม เราก็จะสามารถใช้ Transformation Matrix นั้นประมาณตำแหน่งของคนในกรอบสี่เหลี่ยมเพื่อประเมินระยะห่างได้ด้วย
ในบทความนี้ผมประมาณด้วยการเดาล้วนๆ แต่ถ้าจะใช้จริงควรไปวัดจริง หรือไม่ก็มีคนที่ประมาณขนาดจากรูปได้แม่นๆหน่อย
เอาล่ะเริ่มเลยแล้วกัน
Colab OpenPose installation
ก่อนอื่นก็ติดตั้ง Norfair + OpenPose ()บน Colab ก่อนด้วย Script ตามข้างล่าง
pip install norfair[video]
โดยบรรทัดด้านบนเป็นการติดตั้ง Norfair ลงบน colab หลังจากนั้นจะทำการ Clone OpenPose Github ดังนี้
import osfrom os.path import exists, join, basename, splitextgit_repo_url = 'https://github.com/CMU-Perceptual-Computing-Lab/openpose.git'project_name = splitext(basename(git_repo_url))[0]if not exists(project_name):# see: https://github.com/CMU-Perceptual-Computing-Lab/openpose/issues/949# install new CMake becaue of CUDA10!wget -q https://cmake.org/files/v3.13/cmake-3.13.0-Linux-x86_64.tar.gz!tar xfz cmake-3.13.0-Linux-x86_64.tar.gz --strip-components=1 -C /usr/local# clone openpose!git clone -q --depth 1 $git_repo_url!sed -i 's/execute_process(COMMAND git checkout master WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\/3rdparty\/caffe)/execute_process(COMMAND git checkout f019d0dfe86f49d1140961f8c7dec22130c83154 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\/3rdparty\/caffe)/g' openpose/CMakeLists.txt# install system dependencies!apt-get -qq install -y libatlas-base-dev libprotobuf-dev libleveldb-dev libsnappy-dev libhdf5-serial-dev protobuf-compiler libgflags-dev libgoogle-glog-dev liblmdb-dev opencl-headers ocl-icd-opencl-dev libviennacl-dev# install python dependencies!pip install -q youtube-dl
เมื่อเราได้ code ของ OpenPose แล้วก็ download 3rd Party ตัวอื่นๆ ลงใน OpenPose directory
%cd /content/openpose!git submodule update --init --recursive --remote%cd /content/
หลังจากนั้นติดตั้ง library ที่จำเป็นอื่นๆ
!apt-get install python3-dev!pip install numpy opencv-python
เมื่อมาถึงจุดนี้เราก็จะมี source code พร้อมที่จะ compile OpenPose แล้ว เราจำเป็นต้องตั้งค่าให้มีการ compile Python wrapper ด้วย โดยเปลี่ยนค่า BUILD_python ให้เป็น ON ในไฟล์ /content/openpose/CMakeLists.txt ก่อนหลังจากนั้นจึงเริ่ม compile และ install ในขั้นตอนถัดไป
!cd openpose && rm -rf build || true && mkdir build && cd build && cmake .. && make -j`nproc`%cd /content/openpose/build!make install%cd /content/openpose/build/python/openpose!make install
ในขั้นตอน compile ใช้เวลาประมาณ 20–30 นาทีบน Colab และต้องทำทุกครั้งที่ เลิกเขียนทำให้เสียเวลามากในการเริ่มต้นเขียนในแต่ละครั้ง ซึ่งผมพยายามจะ zip directory เพื่อเก็บไว้ใน GDrive แล้วแต่ยังไม่สำเร็จ
import syssys.path.append('/content/openpose/python/openpose')
เมื่อติดตั้ง OpenPose เรียบร้อยแล้ว ให้ run คำสั่งข้างบนเพื่อทำให้ Colab รู้ที่อยู่ของ OpenPose
และในตอนนี้ OpenPose ก็จะพร้อมใช้งาน
Notebook ที่ผมใช้งานสามารถคัดลอกได้จาก https://colab.research.google.com/drive/1Pv3hGcl1mbZBxnWqwYKu-5lXyGFe8D0i?usp=sharing โดยใน Notebook นี้ผมเรียกใช้ OpenPose จาก https://github.com/surapoom/openpose.git ซึ่งผมได้แก้ไขไฟล์ /content/openpose/CMakeLists.txt เพื่อให้ติดตั้ง Python Wrapper แล้ว
Social Distancing
ในกรณีผู้ที่ต้องการดูตัวอย่างการใช้ OpenPose ด้วย Python สามารถดูได้จาก directory /content/openpose/examples/tutorial_api_python
ไฟล์ที่ใช้ในการประเมิน Social Distancing (openpose_social_distance_medium.py)สามารถ download ได้จาก https://drive.google.com/file/d/1uH--sHpDfi5zVWh5qdhc-IS8dJ7AEnUU/view?usp=sharing โดยไฟล์นี้จะ export
- ภาพตีกรอบสี่เหลี่ยมรอบๆหน้าของคนที่เดินผ่าน
- ไฟล์ภาพหน้าคนแต่ละคนที่อยู่ในพื้นที่ที่กำหนดแยกออกมา ตาม frame ของวีดีโอ และ id ที่ทำการ track ได้
- ไฟล์ csv แสดงค่า distance ของคนที่มีระยะใกล้เกินกว่าค่า Threshold ในแต่ละ frame
- ไฟล์ csv แสดงค่าความหนาแน่นของคนในพื้นที่กรอบสี่เหลี่ยมในแต่ละ frame
เพื่อความกระชับของบทความผมจะไม่อธิบายรายละเอียดของโปรแกรม แต่จะกล่าวถึงแนวคิดและวิธีการใช้งานอย่างคร่าวๆ
การตั้งค่าที่จำเป็นจะมีอยู่ 2 ช่วงสำคัญๆดังนี้
# Insert the path to your openpose instalation folder hereopenpose_install_path = "/content/openpose/python"detection_per_seconds = 0frame_skip_period = 1detection_threshold = 0.7distance_threshold = 0.4min_p_dist = 2.0 # meter in distance between persons# for detecting feet and faceminimum_detection_scores = 0.7 # check maximum of all partsminimum_part_detection_scores = 0.5image_out_path = '/content/'video_path = '/content'video_out_path = '/content/'
ในช่วงที่ 1 บรรทัดที่ 444–458 โดยตั้งค่าดังนี้
detection_per_seconds = 0frame_skip_period = 1
ค่า detection_per_seconds หากมีค่ามากกว่า 0 โปรแกรมคำนวณค่า frame_skip_period (ค่าการ skip เฟรมของวีดีโอ) เพื่อให้ทำการตรวจจับภาพจากวีดโอตามจำนวนที่ตั้งค่าต่อวินาที
detection_threshold = 0.7distance_threshold = 0.4
ค่า detection_threshold ใช้เพื่อกำหนดค่าให้กับ Norfair ใช้ในการพิจารณาว่าจุดที่ OpenPose ตรวจจับได้ต้องมีค่าคะแนนความน่าจะเป็นขั้นต่ำเท่าไหร่ ส่วนค่า distance_threshold เป็นค่าความแตกต่างที่ยอมรับได้ว่าเป็นคนๆเดียวกันระหว่างภาพในเฟรมปัจจุบันกับภาพในเฟรมที่ผ่านมา
min_p_dist = 2.0
ค่า min_p_dist ใช้เพื่อตั้งระยะห่างระหว่างคนสองคน หากระยะห่างมีค่าน้อยกว่าค่าดังกล่าวโปรแกรมจะทำการรายงานออกมายัง human_distance.csv
minimum_detection_scores = 0.7 # check maximum of all partsminimum_part_detection_scores = 0.5
ค่า minimum_detection_scores ใช้สำหรับตรวจจับค่าความน่าจะเป็นสูงสุดของกลุ่มตำแหน่งต่ำแหน่งของมนุษย์ที่ตรวจจับได้จาก OpenPose ว่าเป็นการตรวจพบคน ส่วนค่า minimum_part_detection_scores ใช้เป็นค่าความน่าจะเป็นขั้นต่ำในการยอมรับการตรวจจับตำแหน่งต่างๆของมนุษย์
image_out_path = '/content/'video_path = '/content'video_out_path = '/content/'
ในส่วนของ path ต่างๆข้างบนใช้เพื่อระบุ path ปลายทางสำหรับการเขียนรูปออก
ในช่วงที่ 2 จะอยู่ในช่วงบรรทัดที่ 528–533
# Perspective Transform settingorg_width, org_length = 9.0, 9.0 #m.transform_scale = 100 #pixel / m.boundary_pts = [[1002, 215], [1690, 309], [1296, 899], [254, 625]]pts_org = np.array(boundary_pts, dtype=np.float32)boundary_area = org_width * org_length
โดยค่า org_width, org_length ใช้เพื่อระบุความกว้าง ความยาวของสี่เหลี่ยมในโลกจริง transform_scale ใช้เพื่อกำหนดสเกลสำหรับภาพที่ปรับแล้ว boundary_pts คือจุด 4 จุดที่เป็นมุมของสี่เหลี่ยมในภาพ
โดยโปรแกรมจะคำนวณหา transformation matrix ด้วยฟังก์ชั่น cv2.getPerspectiveTransform(pts_in_frame, pts_in_top_view) และโปรแกรมจะใช้ transformation matrix นี้ในการประเมินตำแหน่งของคนแต่ละคนในภายหลัง
ทั้งนี้โปรแกรมจะทำส่งเฟรมของวีดีโอไปยัง OpenPose เพื่อตรวจจับหาจุดต่างๆของคน โดยค่าที่ OpenPose ส่งกลับมาเป็น Array ขนาด 25 rows ซึ่งเป็นตัวแทนของจุดต่างของคนโดยที่ค่าที่ส่งกลับมาประกอบด้วย ตำแหน่ง x, y และ score สำหรับจุดต่างๆแต่ละจุดได้แก่
{0, "Nose"},{1, "Neck"},{2, "RShoulder"},{3, "RElbow"},{4, "RWrist"},{5, "LShoulder"},{6, "LElbow"},{7, "LWrist"},{8, "MidHip"},{9, "RHip"},{10, "RKnee"},{11, "RAnkle"},{12, "LHip"},{13, "LKnee"},{14, "LAnkle"},{15, "REye"},{16, "LEye"},{17, "REar"},{18, "LEar"},{19, "LBigToe"},{20, "LSmallToe"},{21, "LHeel"},{22, "RBigToe"},{23, "RSmallToe"},{24, "RHeel"},{25, "Background"}
หลังจากนั้นโปรแกรมจะส่งค่า Array ไปยัง Norfair เพื่อทำการ track คนแต่ละคนในแต่ละเฟรม หลังจากนั้นจะส่งค่าของคนแต่ละคนไปประเมินตำแหน่งของเท้า โดยเบื้องต้นโปรแกรมจะหาตำแหน่งของเท้าโดยตรงก่อน หากไม่พบจะทำการประมาณด้วยการใช้สัดส่วนของคนตามภาพด้านล่าง
เมื่อได้ตำแหน่งของเท้ามาแล้วจะใช้ transformation matrix เพื่อหาตำแหน่งของเท้าในภาพ Top View เพื่อคำนวณระยะห่างต่อไป
ในการใช้งานพบว่าโปรแกรมทำงานช้ากว่า Yolo ค่อนข้างมาก อย่างไรก็ตามการได้ตำแหน่งของคนน่าจะสามารถใช้งานต่างๆได้อีกมาก นอกจากนี้การ tracking ด้วย Norfair พบว่าหากคนอยู่ใกล้กับกล้องมาก จะทำให้การเคลื่อนที่เชิง pixel มีค่ามาก ซึ่งจะทำให้ไม่สามารถ skip frame ได้มากนัก รวมถึง Norfair ใช้ KalmanFilter ในการประมาณตำแหน่งของเฟรมถัดไป ซึ่งหากความเร็วในภาพมีการเปลี่ยนแปลงฉับพลันจะทำให้ระบบ tracking มีปัญหาค่อนข้างมาก