วันจันทร์ที่ 29 ตุลาคม พ.ศ. 2561

ข้อมูลอุปกรณ์ : Two way mirror

Mirror

    
mirror size

Two way mirror

    เป็นกระจกชนิดพิเศษ ที่จะเห็นใช้กันในห้องชี้ตัวผู้ต้องหา คือ เมื่อมองจากด้านหน้ามันจะเหมือนกระจกเงาธรรมดา แต่ถ้ามองจากด้านหลังจะสามารถมองทะลุไปเห็นอีกด้านได้

วิธีตรวจสอบ

Professional Plastics : เว็บที่ขาย Two way Mirror

   

ราคาของ two way mirror ขนาด 32 Inch x 18 Inch


ถ้าแปลงเป็นเงินบาทจะได้
98 $ x 33 = 3234 ฿

วันอังคารที่ 23 ตุลาคม พ.ศ. 2561

ทดสอบความเร็วการทำงานจากการอ่านค่า FPS

ทำการทดสอบว่าในการรับภาพจากกล้องนั้น มีความเร็วในการตรวจจับสูงหรือต่ำต่างกันแค่ไหน โดยทดสอบจากการจับเวลาก่อนดึงข้อมูลจากกล้องจนถึงช่วงที่ทำการคืนค่าเพื่อนำมาใช้แสดงผล และคำนวณโดยใช้สูตร 1/(เวลาล่าสุด - เวลาเริ่มต้น) โดยมีผลดังนี้



ค่า FPS ที่ได้จาก Raspberry Pi จะเห็นได้ว่า มีช่วงที่ FPS ตกไปบ้างเป็นบางช่วง และค่า FPS ที่ได้จะอยู่ในช่วงประมาณ 10-30 FPS




ค่า FPS ที่ได้จากการทำงานบน Window จะพบว่ามีค่า FPS จะอยู่ในช่วงที่ค่อนข้างสูง และค่า FPS ที่ได้จะอยู่ในช่วงประมาณ 60-90 FPS




สุดท้ายจะเป็นค่า FPS ที่ได้จากการทดลองการทำงานบน Ubuntu จะพบว่ามีค่า FPS ที่ค่อนข้างไล่เลี่ยกัน คือในช่วง 40 FPS
ซึ่งผลของการทำงานที่ได้มานั้นหลักๆจะขึ้นอยู่กับความเร็วในการทำงานของ Hardware ด้วยซึ่งเครื่องที่ลง Ubuntu ใช้ Intel Core i7-3632QM (2.20 - 3.20 GHz) RAM 8 GB และเครื่องที่เป็น Window ใช้ Intel Core i7-7700HQ (2.80 – 3.80 GHz) RAM 8 GB

วันเสาร์ที่ 13 ตุลาคม พ.ศ. 2561

หลักการทำงานของโค้ด Smart Mirror & Hand Detection ( Part 3 การติดตามมือ)


blobs_buffer[n] == [] case
1.จะเช็ค array ที่ถูกเก็บอยู่ใน blobs_buffer ทุกตัว คือตั้งแต่ blobs_buffer[1], blobs_buffer[2], blobs_buffer[3]
2.เมื่อเช็คจนครบทุกตัวแล้ว n + 1 > buffer_size จากนั้น จะ set id ให้กับ blob และ นำ [blob.centroid, blob.centroid] ไปเก็บใส่ไว้ใน blobs_movement ที่ตำแหน่ง i ( ตำแหน่งของตัวใน array จะเท่ากับ id ของ blob)


(น่าจะเป็นช่วงที่ มีเฟรม 2 ผ่านเข้ามา และต้องเอาไปใช้เทียบกับเฟรม 1)
blobs_buffer[n] != [] case ( blobs_buffer[n] มีค่า)
1.วนลูปเช็คหาว่า blob ที่ได้มากับ blobs_buffer[n][j] เป็น blob อันเดียวกันมั้ย //เช็คว่า blob ของ frame ก่อนหน้า กับ frame ปัจจุบัน ว่าเป็นอันเดียวกันหรือไม่
2.ถ้าเป็น blob เดียวกัน ให้เอา id จาก blobs_buffer[n][j] มาเก็บใน new_id และตั้งตัวแปร exist = false
3.วนลูปเช็คว่า id ของ blob ใน blobs มีค่าซ้ำกับ new_id หรือไม่ ถ้ามันซ้ำกัน จะเปลี่ยนค่า exist เป็น True
4.ถ้า exist = true
4.1 ถ้า n + 1 < buffer_size จะให้ทำ blobs_track(blob, i, n+1)
4.2 ถ้า n + 1 > buffer_size จะให้
  • new_id = 1
  • วนลูปหา id ที่มากที่สุดจาก old_id + 1 ที่มากกว่า new_id และเปลี่ยนค่า new_id เป็น max(old_id) + 1
  • set id ให้ blob เป็น new_id
  • ใส่ new_id ไปใน old_id[0]
  • นำ [blob.centroid, blob.centroid] ไปเก็บใส่ไว้ใน blobs_movement ที่ตำแหน่ง new_id
5.ถ้า exist = False
5.1 set id ให้กับ blob เป็น new_id
5.2 เช็คว่า blobs_buffer[n][j] มีลักษณะเป็นมือหรือไม่ ถ้าเป็น  ให้ set blob เป็นมือ
5.3 ใส่ blob.centroid ลงใน blobs_movement ที่ตำแหน่ง new_id
5.4 ให้ blobs_movement ที่ตำแหน่ง new_id เป็น blobs_movement 20 ตำแหน่งท้ายสุด
6. ถ้าเจอ blob ที่ไม่ใช่ id เป็น -1 ให้ออกจากลูป

7. ถ้า id ของ blob เป็น -1
7.1 ถ้า n + 1  < buffer_size ให้ทำ blobs_track(blob, i, n + 1) // หา id ของ blob ที่ไม่เท่ากับ -1
7.2 ถ้า n + 1  >= buffer_size ให้ทำ

  • new_id = 1
  • วนลูปหา id ที่มากที่สุดจาก old_id + 1 ที่มากกว่า new_id และเปลี่ยนค่า new_id เป็น max(old_id) + 1
  • set id ให้ blob เป็น new_id
  • ใส่ new_id ไปใน old_id[0]
  • นำ [blob.centroid, blob.centroid] ไปเก็บใส่ไว้ใน blobs_movement ที่ตำแหน่ง new_id

วันพฤหัสบดีที่ 11 ตุลาคม พ.ศ. 2561

หลักการทำงานของโค้ด Smart Mirror & Hand Detection ( Part 2 ส่วนเค่าและตรวจสอบว่าเป็นมือหรือไม่ )

ต่อจาก Part ที่แล้วในส่วนถัดไปจะเป็นการทำงานของ BlobAnalysis โดยมีการทำงานดังนี้

  • ทำการวน Loop ตามความยาวของ array cs
  • ส่งค่า array cs ชุดที่ i ไปที่ class BlobAnalysis เพื่อเตรียมการคำนวณ


  • ในที่นี้ cs[i] ที่ส่งเข้ามาจะถูกเก็บในตัวแปรชื่อ contour ซึ่ง self.contour ก็คือการเก็บค่า contour ไว้ใช้ใน class 
  • self.contour_s คือตัวแปรที่เก็บค่า contour ซึ่งถูกปรับการเรียงเป็นแนวตั้ง และ squeeze() คือการลบ array นอกสุดออก 1 มิติ เช่น หากข้อมูลมีลักษณะเป็น [ [1,2] ] array ตัวนอกจะถูกลบออก
    1 มิติ กลายเป็น [ 1,2 ] แทน
  • self.contour_point จะไปเรียกฟังค์ชันซึ่งทำการนำ array self.contour_s มาแปลงค่าเป็น numpy array เป็น python list เพื่อให้สามารถทำการคำนวณใน python ได้ง่ายขึ้น
  • self.centroid จะไปเรียกฟังค์ชัน get_centroid() เพื่อนำค่า self.contour ไปคำนวณ Moment เพื่อนำมาหาจุดศูนย์กลางของข้อมูล ผลที่ได้จะเป็นลักษณะคู่อันดับ (x,y)
  • self.convex_hull จะเรียกฟังค์ชัน get_convex_hull โดยจะนำ self.contour มาทำการหา hull หรือส่วนโค้งเว้าใน contour นำมาคำนวณเพื่อเก็บใส่ตัวแปร epsilon และนำ hull กับ epsilon ไปเพื่อปรับค่า contour ในคำสั่ง cv2.approxPolyDP(convexHull,epsilon,True) และปรับการเรียงเป็นแนวตั้งพร้อมลบ array นอกสุดออก 1 มิติ และแปลงค่าจาก numpy array เป็น python list ก่อนทำการเก็บค่า
  • self.approx_hull_count จะทำการเก็บค่าความยาว self.convex_hull ไว้
  • self.id กำหนดให้มีค่าเป็น -1
  • self.area ทำการเก็บค่าพื้นที่ของ contour ด้วยคำสั่ง cv2.contourArea(self.contour)
  • self.deflect_count_90 จะเรียกฟังค์ชัน get_deflect_count(90) โดยค่าที่ส่งไปคือมุมที่เราต้องการทำไปคิด และนำ self.contour มาหา convex_hull จากนั้นนำ contour กับ hull มาทำการหา deflect ของภาพ จากนั้นจะนำ deflect มาทำการคำนวณหามุม และเข้าเงื่อนไขเพื่อเช็คว่าค่ามุมที่หามาได้น้อยกว่าหรือเท่ากับ 90 หรือไม่ ซึ่งก็คือการหามุมระหว่างร่องมือ หากไม่ถึง 90 จะเพิ่มค่า count 1 ค่า คนครบทุกตัว และเก็บค่า count ไป
  • self.isHand จะเรียกคำสั่ง self.check_isHand() ซึ่งจะนำค่า self.deflect_count_90 มาเข้าเงื่อนไขว่ามีค่า == 4 หรือไม่ ซึ่งเป็นการเปรียบเทียบว่า มือเรานั้นจะมีร่องมือเพียง 4 ร่องเท่านั้น หากมีค่าเท่ากับ 4 พอดีแสดงว่า blob ที่ได้มานั้นเป็นรูปมือจริงๆ จะทำการเก็บค่าเป็น True หากมีค่ามากกว่า 4 หรือน้อยกว่า 4 แสดงว่าไม่ใช่มือ เป็นอันเสร็จสิ้นการทำงานส่วนเก็บค่าตัวแปร
แหล่งอ้างอิง

squeeze :
https://www.tutorialspoint.com/numpy/numpy_squeeze.htm

np.vstack :
https://www.tutorialspoint.com/numpy/numpy_vstack.htm

np.array().tolist()
https://stackoverflow.com/questions/1966207/converting-numpy-array-into-python-list-structure

moment, epsilon, convexHull :
https://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html
https://www.learnopencv.com/find-center-of-blob-centroid-using-opencv-cpp-python/

โค้ดส่วนที่ทำงาน

class BlobAnalysis:
    def __init__(self,contour):
        self.contour = contour
        self.contour_s = np.vstack(contour).squeeze()
        self.contour_point = self.get_contour_point()
        self.centroid = self.get_centroid()
        self.convex_hull = self.get_convex_hull()
        self.approx_hull_count = self.get_approx_hull_count()
        self.id = -1
        self.area = cv2.contourArea(self.contour)
        self.deflect_count_90 = self.get_deflect_count(90)
        self.isHand = self.check_isHand()

    def set_id(self,i):
        self.id = i

    def get_contour_point(self):
        return np.array(self.contour_s).tolist()

    def get_centroid(self):
        m = cv2.moments(self.contour)
        cX = int(m['m10'] / m['m00'])
        cY = int(m['m01'] / m['m00'])
        return (cX, cY)

    def get_convex_hull(self):
        convexHull = cv2.convexHull(self.contour)
        epsilon = 0.015*cv2.arcLength(convexHull,True)
        approx = cv2.approxPolyDP(convexHull,epsilon,True)
        approx = np.vstack(approx).squeeze()
        return np.array(approx).tolist()

    def get_approx_hull_count(self):
        approx = self.convex_hull
        return len(approx)

    def get_deflect_count(self,max_angle):
        count = 0
        hull = cv2.convexHull(self.contour,returnPoints = False)
        defects = cv2.convexityDefects(self.contour, hull)
        for i in range(defects.shape[0]):
            s,e,f,d = defects[i,0]
            start = self.contour_s[s]
            end = self.contour_s[e]
            far = self.contour_s[f]
            a = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2)
            b = math.sqrt((far[0] - start[0])**2 + (far[1] - start[1])**2)
            c = math.sqrt((end[0] - far[0])**2 + (end[1] - far[1])**2)
            angle = math.acos((b**2 + c**2 - a**2)/(2*b*c)) * 57
            if angle <= max_angle:
                count += 1
        return count

    def check_isHand(self):
        if self.deflect_count_90 == 4 :
            return True
        else:
            return False

หลักการทำงานของโค้ด Smart Mirror & Hand Detection ( Part 1 ส่วนการเปิดหน้าเว็บและการรับค่าจากกล้อง Kinect )

ไฟล์การทำงานจะถูกแบ่งออกเป็น 3 ส่วนคือ
1. keymap.py ที่จะทำหน้าที่เปิดหน้าเว็บ และรับข้อมูล input จาก kinect มา map กับปุ่มของ keyboard เพื่อสั่งการ
2. kinect_hand.py ทำหน้าที่รับข้อมูลจากกล้อง kinect มาทำการประมวลผลและตรวจสอบว่าแต่ละ blob เป็นมือหรือไม่ และมีการเคลื่อนไหวอย่างไร เพื่อส่งผลที่ได้ไปยัง keymap
3. index.html เป็นไฟล์หน้าเว็บที่เอาไว้แสดงบน smart mirror

โดยการทำงานเริ่มแรกนั้นจะเริ่มจาก keymap.py

  • ขั้นแรกจะทำการ import library สำคัญๆ ทั้ง os, pynput.keyboard, time, threading รวมไปถึง kinect_hand.py เพื่อใช้ฟังค์ชันภายในไฟล์
  • เป็นการทำงานโดยแบ่ง Thread ออกเป็น 2 ตัว คือ Thread ของเว็บ และ Thread ของการทำ keyboard mapping
  • Thread ของเว็บจะเริ่มทำงานก่อนโดยทำการเปิดหน้าเว็บจากไฟล์ index.html โดยใช้ฟังค์ชัน webbrowser.open
  • Thread ของการทำ keyboard mapping จะเริ่มทำงานต่อจากการเปิดเว็บ
  • สร้างตัวแปรชื่อ keyboard ทำหน้าที่เป็น controller เพื่อสั่งการเหมือนการกดปุ่มบน keyboard
  • ใช้ os.system("sleep 5") เพื่อให้รอ 5 วินาทีก่อนทำงานขั้นถัดไป
  • keyboard.press(Key.f11) และ keyboard.release(Key.f11) ใช้แทนการกดและปล่อยปุ่ม f11 เพื่อให้หน้าจอเข้าสู่โหมด fullscreen
  • กำหนดค่า fps เริ่มต้นเป็น 0
  • ให้ last gesture หรือท่าการเคลื่อนไหวสุดท้ายเป็น "undefined action" หรือการเคลื่อนไหวที่ระบุไม่ได้
  • เก็บค่า t0 เป็นเวลาเริ่มต้นจาก time.clock() จากนั้นจึงเข้าสู่ Loop
  • ตัวแปร input จะทำหน้าที่เสมือนกับไฟล์ kinect_hand โดยในที่นี้จะเรียกฟังค์ชัน get_input() เพื่อทำการเก็บข้อมูลจากกล้อง Kinect
ขั้นต่อมาจะอยู่ในส่วนการทำงานของ kinect_hand.py
  • ก่อนจะทำงานในส่วนของฟังค์ชัน get_input() ในไฟล์จะทำการ import library สำหรับทำการคำนวณและจัดการเกี่ยวกับ image processing โดยเฉพาะเช่น numpy, openCV2, math, pygame, time และ freenect สำหรับรับข้อมูลจากกล้อง Kinect
  • ทำการประกาศตัวแปรหลักๆคือ xsize, ysize, blobs สำหรับเก็บข้อมูล, buffer_size = 3, blobs_movement ที่เก็บค่าในรูปของ dict หรือ {} ส่วน blob_buffer และ old_id จะมีลักษณะเป็น
    [[]] * buffer_size ซึ่งจะได้ array 1 ตัว ที่ภายในบรรจุ array ไว้ค่าของ buffer_size ซึ่งในที่นี้คือ 3 โดยมีหน้าตาดังนี้

  • ในฟังค์ชัน get_input() จะทำการเรียกใช้ xsize และ ysize แบบ pass by reference และส่งค่าที่เรียกมาไปฟังค์ชัน pygame_refresh(xsize ,ysize)
  • ในฟังค์ชัน pygame_refresh() จะทำการเรียกใช้ตัวแปรอื่นๆแบบ pass by reference คือ blobs, blob_buffer, old_id และ blobs_movement โดยจะทำการล้างค่าใน blobs ทุกครั้งเพื่อเตรียมรับค่าใหม่ ( blobs = [] )
  • ต่อมาจะทำการเรียกฟังค์ชัน update_old_id() โดยเรียกใช้ old_id มาและนำเข้า Loop แรกซึ่งวนตามจำนวน buffer_size คือ 3 และทำการล้างค่าใน array old_id ที่ i ก่อนเข้า Loop ที่ 2 ที่วนตามความกว้างของ blobs_buffer ตัวที่ i จากนั้นทำการนำ id ของ blobs_buffer ชุดที่ i ตัวที่ j
    ( blobs_buffer[i][j].id ) ไปใส่ใน old_id ตัวที่ i โดยการทำงานคร่าวๆจะมีลักษณะและผลคล้ายรูปดังนี้

  • ต่อไปจะเรียกใช้ฟังค์ชัน get_contour เพื่อเก็บค่าไว้ในตัวแปร cs โดยส่ง xsize และ ysize ไปด้วย
  • ใน get_contour จะทำงานเริ่มจากไปนำข้อมูลจากกล้อง Kinect จากคำสั่ง sync_get_depth() มาเก็บไว้ในตัวแปร depth
  • แปลงข้อมูลให้เป็น float32 จากคำสั่ง astype(np.float32)
  • กลับภาพตามแนวแกน y โดยใช้คำสั่ง cv2.flip(depth, 1) ถ้าเปลี่ยนจาก 1 เป็น 0 จะเป็นหมุนตามแนวแกน x ถ้าเป็น -1 จะหมุนทั้ง 2 แกน
  • ทำการปรับขนาดของภาพตามค่า xsize และ ysize จาก cv2.resize(depth,(xsize,ysize))
  • ทำการเบอลภาพและลบ gaussian noise ในภาพโดยใช้คำสั่ง cv2.GaussianBlur(depth, (5,5), 0) โดย (5,5) คือความกว้างและความสูงของ Kernel size ตามลำดับ และ 0 คือค่าเบี่ยงเบนมาตรฐานของ gaussian kernel ซึ่งจะมีทั้งของแกน X และ Y หากให้ Y เป็น 0 ค่าของ X จะเป็น 0 เช่นกัน และคำนวณโดยใช้ Kernel size เป็นเกณฑ์แทน โดยภาพซ้ายและขวาจะเป็นภาพก่อนและหลังทำ gaussian blur ตามลำดับ

  • ทำการตัดขอบของวัตถุในภาพเพื่อลบ Noise โดยใช้คำสั่ง cv2.erode(depth, None, iterations=1) โดย None คือไม่ใส่ kernel ในการคำนวณและทำซ้ำเพียง 1 รอบ ( iterations=1 )
  • ทำการเพิ่มขนาดของวัตถุที่ถูกตัดขอบไปและเพิ่มจุดที่เสียหายจาก Noise โดยใช้คำสั่ง cv2.dilate(depth, None, iterations=1) ซึ่งภาพที่ได้จะมีลักษณะดังนี้ ตั้งแต่ก่อนทำ erode หลังทำ erode และทำ dilate ตามลำดับ
  • หาค่าความลึกของมือที่ต่ำที่สุด ( min_hand_depth ) โดยใช้คำสั่ง np.amin โดยจะดึงค่าที่น้อยที่สุดใน array ออกมาลบด้วย 10
  • หาค่า max_hand_depth โดยเอา min_hand_depth มาบวกกับค่า hand_depth ซึ่งกำหนดไว้ที่ 80
  • จากนั้นจะทำการเช็คเงื่อนไข หาก max_hand_depth มีค่ามากกว่า 700 จะกำหนดให้ค่า max_hand_depth = 700
  • จากนั้นจะทำการแปลงข้อมูลโดยใช้คำสั่ง cv2.threshold(depth, max_hand_depth, min_hand_depth, cv2.THRESH_BINARY_INV) เพื่อเปลี่ยนให้ค่าที่น้อยกว่า max_hand_depth หรือ Threshold value ให้กลายเป็นค่าสีที่เรากำหนดหรือค่า min_hand_depth ซึ่งใช้ THRESH_BINARY_INV

    การทำ THRESH_BINARY_INV คือการ invert ข้อมูลโดยเปลี่ยนให้ค่าที่มากกว่า Threshold value ให้กลายเป็น 0 หรือสีดำทั้งหมด ส่วนค่าที่น้อยกว่า Threshold value จะเปลี่ยนเป็นค่าสีที่เรากำหนดซึ่งอยู่ในช่วง [ 0 - 255 ] สามารถดูเปรียนเทียบกับแบบปกติได้ดังนี้ ( ในรูปให้ค่า Threshold อยู่กึ่งกลางพอดี )

  • ทำการแปลงค่าที่ได้หลังจากทำ Threshold ให้อยู่ในช่วงข้อมูล 8-bit [ 0 - 255] โดยใช้คำสั่ง
    cv2.convertScaleAbs(BW) 
  • นำค่า Threshold ไปทำการหาเส้นขอบของภาพหรือ Contour ของภาพจากคำสั่ง
    cv2.findContours(BW,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) โดย RETR_TREE เป็น mode ที่ทำการเก็บข้อมูลทั้งหมดมาทำเป็น contour และ CHAIN_APPROX_SIMPLE คือ method ที่จะเอาเฉพาะจุด End Point จากข้อมูล contour ทั้งหมดเช่น มุมของภาพดังรูป

  • ข้อมูลที่ทำ contour จะเก็บไว้ในตัวแปร cs และสร้าง array cs_f เพื่อเตรียมขั้นตอนสุดท้าย
  • นำข้อมูล cs เข้า Loop โดยวนตามจำนวนข้อมูลใน cs และเช็คเงื่อนไขว่า ข้อมูล cs ตัวที่ i ตัวใดมีค่า contour area มากกว่า 500 ให้บรรจุเข้าไปใน array cs_f จากโค้ดด้านล่าง

  • ลบข้อมูลใน array depth และ BW ทั้งหมดจากคำสั่ง depth,BW และ return array cs_f  ไปยังตัวแปร cs ที่เรียกในฟังค์ชัน pygame_refresh

>>> Continue Part 2
แหล่งอ้างอิง

contour : https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html
https://docs.opencv.org/3.3.1/d4/d73/tutorial_py_contours_begin.html

convertScaleAbs : https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html

Threshold :
https://medium.com/@a5730051/threshold-opencv-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-768214f155eb
https://skconann.blogspot.com/2018/02/image-simple-thresholding.html

Erosion & Dilation :
https://wiki.morange.co.th/Morphological_Transformations

GaussianBlur :
https://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html

Flip :
https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#gaca7be533e3dac7feb70fc60635adf441

โค้ดส่วนที่ใช้งาน

kinect_hand.py


def get_contours(xsize,ysize):
    (depth,_) = get_depth()
    depth = depth.astype(np.float32)
    depth = cv2.flip(depth, 1)
    depth = cv2.resize(depth,(xsize,ysize))
    depth = cv2.GaussianBlur(depth, (5,5), 0)
    depth = cv2.erode(depth, None, iterations=1)
    depth = cv2.dilate(depth, None, iterations=1)
    min_hand_depth = np.amin(depth)-10
    hand_depth = 80
    max_hand_depth = min_hand_depth + hand_depth
    if max_hand_depth > 700 :
        max_hand_depth = 700
    (_,BW) = cv2.threshold(depth, max_hand_depth, min_hand_depth, cv2.THRESH_BINARY_INV)
    BW = cv2.convertScaleAbs(BW)
    #BW = cv2.resize(BW,(xsize,ysize))
    cs,_ = cv2.findContours(BW,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    cs_f = []
    for i in range(len(cs)):
        if cv2.contourArea(cs[i]) > 500:
            cs_f.append(cs[i])
    del depth,BW
    return cs_f

blobs = []
buffer_size = 3
blobs_buffer = [[]] * buffer_size
old_id = [[]] * buffer_size
blobs_movement = {}

def update_old_id():
    global old_id
    for i in range(buffer_size):
        old_id[i] = []
        for j in range(len(blobs_buffer[i])):
            old_id[i].append(blobs_buffer[i][j].id)

def pygame_refresh(xsize,ysize):
    global blobs
    blobs = []
    global blobs_buffer
    global old_id
    global blobs_movement
    update_old_id()
    cs = get_contours(xsize,ysize)
    for i in range(len(cs)):
        blob = BlobAnalysis(cs[i])
        blob = blobs_track(blob,i,0)
        blobs.append(blob)
    for i in range(buffer_size):
        if i == 0:
            blobs_buffer[i] = blobs
        else:
            blobs_buffer[i] = blobs_buffer[i-1]
    return 1

xsize,ysize = 280,210

def get_input():
    global xsize,ysize
    pygame_refresh(xsize,ysize)

วันจันทร์ที่ 8 ตุลาคม พ.ศ. 2561

สิ่งที่ได้ทดลอง ( 30 กันยายน - 6 ตุลาคม 2561)



ทดสอบทุก case ที่คาดว่าทำให้เกิด Error

1.การเดินผ่านในระยะใกล้ตัวกล้อง

        ทดสอบโดยการเดินผ่านหน้ากล้องโดยไม่แบมือหรือเก็บมือไว้ด้านหลังในระยะตั้งแต่ 10-20 cm โดยประมาณ
        ผลลัพธ์ : ในระยะที่ใกล้กล้องมากๆ การรับผลจะเริ่มเพี้ยนและส่งผลให้ไปสั่งการได้ในบางรอบ

2.การเดินผ่านในระยะใกล้ตัวกล้องโดยที่กางมือไปด้วย

        ทดสอบโดยการเดินผ่านหน้ากล้องโดยแบมือและแกว่งมือไปมาในระยะตั้งแต่ 10-20 cm โดยประมาณ
        ผลลัพธ์ : หากกล้องตรวจจับมือได้ควบคู่กับการเดิน อาจส่งผลให้การเดินผ่านในครั้งต่อไปจะไปสั่งการ Kinect ได้

3.การโบกมือในระยะที่ใกล้ตัวกล้อง

        การโบกมือในระยะที่ใกล้ตัวกล้อง : ทำการกางมือและโบกซ้ายขวาในระยะตั้งแต่ 10-20 cm โดยประมาณ
        ผลลัพธ์ : อาจส่งผลให้การการตรวจจับรวนและไปสั่งการ Kinect ได้ในบางครั้ง

4.การกางมือบริเวณด้านหน้าตัวโดยให้ห่างจากตัวพอประมาณ

        การกางมือบริเวณด้านหน้าตัวโดยให้ห่างจากตัวพอประมาณ : ทำการกางมือให้ห่างจากลำตัวเล็กน้อย และโยกตัวไปมาพร้อมกับมือ
        ผลลัพธ์ : จะมีลักษณะคล้ายกับการเดินแบบกางมือ คือ เมื่อกล้องตรวจจับไปมือได้แล้ว หากเราเก็บมือและโยกตัวหรือเอวไปทางซ้ายหรือขวาก็มีโอกาสที่จะไปสั่งการ kinect ได้

5.การเดินผ่านในระยะใกล้ตัวกล้องโดยที่กางมือไปด้วย

        การเดินผ่านในระยะใกล้ตัวกล้องโดยที่กางมือไปด้วย : ทดสอบโดยการเดินผ่านหน้ากล้องโดยแบมือและแกว่งมือไปมาในระยะตั้งแต่ 10-20 cm โดยประมาณ        ผลลัพธ์คือ หากกล้องตรวจจับมือได้ควบคู่กับการเดิน อาจส่งผลให้การเดินผ่านในครั้งต่อไปจะไปสั่งการ Kinect ได้

6.การกางมือบริเวณด้านหน้าตัวโดยให้มือแนบกับตัว

        ทำการกางมือนำไปแนบกับลำตัวและโยกตัวไปมาพร้อมกับมือ
        ผลลัพธ์ : มีโอกาสน้อยมากที่จะไปสั่งการ Kinect ให้เลื่อนซ้ายหรือขวาได้

คาดถึงสาเหตุของ Error ที่เกิดขึ้น

จากการทดสอบใน case ที่กล่าวมา พอจะจำกัดได้ว่า Error เกิดขึ้นได้ ดังนี้

  • ระยะที่ใกล้มากเกินไป
  • Frame หรือ Blob ที่ตรวจจับว่าเป็นมือยังค้างอยู่
  • ความสูงในการติดตั้ง เนื่องจากที่ทดสอบตั้งที่ความสูงจากพื้นประมาณ 75-85 cm

ศึกษาการทำงานทั้งหมดของโค้ดเพื่อหาทางแก้ไข

        ได้ทำการศึกษาการทำงานของโค้ดโดยลึกเพื่อตรวจสอบว่าการทำงานจริงๆนั้นเป็นอย่างไร รวมไปถึงตรวจดูลำดับการทำงานว่าลำดับการทำงานเป็นอย่างไร เพื่อหาวิธีจัดการเรื่อง Frame หรือ Blob ที่ยังติดสถานะว่าเป็นมือค้างอยู่ทำให้การตรวจจับผิดพลาด
        จากการที่ Uncomment Code บางส่วนที่รุ่นพี่ปิดไว้เพื่อลดภาระการทำงานของ Raspberry Pi นั้นช่วยให้การตรวจจับเสถียรขึ้นแต่ยังพบกับปัญหาตามที่กล่าวไว้ข้างต้น
        หากสามารถนำข้อมูลที่เป็นภาพตรวจจับจากมือมาแสดงเพิ่มเติมได้จะนำมาเสนอในครั้งถัดไปเพื่อเช็คความถูกต้อง


ปัญหาที่พบของ Kinect เกี่ยวกับ Hotplug



        จากการที่ทดลองหาปัญหาหลายๆอย่างขณะทดลองก็ได้พบว่า สาเหตุที่ทดลอง Run Code แล้วไม่สามารถตรวจจับตัว Kinect ได้นั้น นอกจากจะเป็นปัญหาเรื่องพลังงานแล้ว ยังมาจากอีกสาเหตุคือ ในตอนที่ปิดการทำงานของโค้ดนั้น ระบบตรวจจับของ Kinect ยังไม่ถูกปิดไปด้วย เมื่อทดลองปิดโค้ดและ Run Code ใหม่อีกครั้งจึงตรวจหาอุปกรณ์ไม่พบ เพราะ Kinect ยังทำงานอยู่ จึงต้องหาวิธีสั่งการเพื่อปิดการทำงานทั้งโค้ดและกล้องในเวลาเดียวกัน