getFaceBox

freedragon發表於2024-06-07
'''
xgo圖形化python庫
'''
import cv2
import cv2 as cv
import copy
import argparse
import numpy as np
import mediapipe as mp
import shutil,requests
import urllib.request
import math
import os,sys,time,logging
import spidev as SPI
import LCD_2inch
import onnxruntime 
import RPi.GPIO as GPIO
from PIL import Image,ImageDraw,ImageFont
from ctypes import c_void_p
from tensorflow.keras.utils import img_to_array
from keras.preprocessing import image
from pyexpat import model
from keras.models import load_model
import json
from xgolib import XGO


__versinon__ = '1.1.0'
__last_modified__ = '2023/3/30'

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

display = LCD_2inch.LCD_2inch()
display.Init()
display.clear()
splash = Image.new("RGB",(320,240),"black")
display.ShowImage(splash)

#字型載入
font1 = ImageFont.truetype("/home/pi/xgoEdu/Font/msyh.ttc",15)
#情緒識別
face_classifier=cv2.CascadeClassifier('/home/pi/xgoEdu/model/haarcascade_frontalface_default.xml')
classifier = load_model('/home/pi/xgoEdu/model/EmotionDetectionModel.h5')
class_labels=['Angry','Happy','Neutral','Sad','Surprise']

#年紀及性別識別
# 網路模型  和  預訓練模型
faceProto = "/home/pi/xgoEdu/model/opencv_face_detector.pbtxt"
faceModel = "/home/pi/xgoEdu/model/opencv_face_detector_uint8.pb"

ageProto = "/home/pi/xgoEdu/model/age_deploy.prototxt"
ageModel = "/home/pi/xgoEdu/model/age_net.caffemodel"

genderProto = "/home/pi/xgoEdu/model/gender_deploy.prototxt"
genderModel = "/home/pi/xgoEdu/model/gender_net.caffemodel"

# 模型均值
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
ageList = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
genderList = ['Male', 'Female']

# 載入網路
ageNet = cv.dnn.readNet(ageModel, ageProto)
genderNet = cv.dnn.readNet(genderModel, genderProto)
# 人臉檢測的網路和模型
faceNet = cv.dnn.readNet(faceModel, faceProto)
padding = 20

cap =cv2.VideoCapture(0)
cap.set(3,320)
cap.set(4,240)

'''
人臉檢測
'''
def getFaceBox(net, frame, conf_threshold=0.7):
    frameOpencvDnn = frame.copy()
    frameHeight = frameOpencvDnn.shape[0]
    frameWidth = frameOpencvDnn.shape[1]
    blob = cv.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], True, False)
    net.setInput(blob)
    detections = net.forward()
    bboxes = []
    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > conf_threshold:
            x1 = int(detections[0, 0, i, 3] * frameWidth)
            y1 = int(detections[0, 0, i, 4] * frameHeight)
            x2 = int(detections[0, 0, i, 5] * frameWidth)
            y2 = int(detections[0, 0, i, 6] * frameHeight)
            bboxes.append([x1, y1, x2, y2])
            cv.rectangle(frameOpencvDnn, (x1, y1), (x2, y2), (0, 255, 0), int(round(frameHeight / 150)),8)  
    return frameOpencvDnn, bboxes

'''
手勢識別函式
'''
def hand_pos(angle):
    pos = None
    # 大拇指角度
    f1 = angle[0]
    # 食指角度
    f2 = angle[1]
    # 中指角度
    f3 = angle[2]
    # 無名指角度
    f4 = angle[3]
    # 小拇指角度
    f5 = angle[4]
    if f1 < 50 and (f2 >= 50 and (f3 >= 50 and (f4 >= 50 and f5 >= 50))):
        pos = 'Good!'
    elif f1 < 50 and (f2 >= 50 and (f3 < 50 and (f4 < 50 and f5 < 50))):
        pos = 'Ok!'
    elif f1 < 50 and (f2 < 50 and (f3 >= 50 and (f4 >= 50 and f5 < 50))):
        pos = 'Rock!'
    elif f1 >= 50 and (f2 >= 50 and (f3 >= 50 and (f4 >= 50 and f5 >= 50))):
        pos = 'stone'
    elif f1 >= 50 and (f2 < 50 and (f3 >= 50 and (f4 >= 50 and f5 >= 50))):
        pos = '1'
    elif f1 >= 50 and (f2 < 50 and (f3 < 50 and (f4 < 50 and f5 >= 50))):
        pos = '3'
    elif f1 >= 50 and (f2 < 50 and (f3 < 50 and (f4 < 50 and f5 < 50))):
        pos = '4'
    elif f1 < 50 and (f2 < 50 and (f3 < 50 and (f4 < 50 and f5 < 50))):
        pos = '5'
    elif f1 >= 50 and (f2 < 50 and (f3 < 50 and (f4 >= 50 and f5 >= 50))):
        pos = '2'
    return pos
def color(value):
  digit = list(map(str, range(10))) + list("ABCDEF")
  value = value.upper()
  if isinstance(value, tuple):
    string = '#'
    for i in value:
      a1 = i // 16
      a2 = i % 16
      string += digit[a1] + digit[a2]
    return string
  elif isinstance(value, str):
    a1 = digit.index(value[1]) * 16 + digit.index(value[2])
    a2 = digit.index(value[3]) * 16 + digit.index(value[4])
    a3 = digit.index(value[5]) * 16 + digit.index(value[6])
    return (a3, a2, a1)

'''
競賽用
'''
class XgoExtend(XGO):
    #def __init__(self, port, screen, button, cap, baud=115200, version='xgomini'):
    def __init__(self, port, baud=115200, version='xgomini'):
        super().__init__(port, baud, version)
        self.reset()
        time.sleep(0.5)
        self.init_yaw = self.read_yaw()
        self.calibration = {}
        with open("/home/pi/xgoEdu/model/calibration.json", 'r', encoding='utf-8') as f:
            self.k = json.load(f)
        self.block_k1 = self.k["BLOCK_k1"]
        self.block_k2 = self.k["BLOCK_k2"]
        self.block_b = self.k["BLOCK_b"]
        self.block_ky = self.k["BLOCK_ky"]
        self.block_by = self.k["BLOCK_by"]
        self.cup_k1 = self.k["CUP_k1"]
        self.cup_k2 = self.k["CUP_k2"]
        self.cup_b = self.k["CUP_b"]

    def show_img(self, img):
        imgok = Image.fromarray(img, mode='RGB')
        #self.screen.ShowImage(imgok)
        display.ShowImage(imgok)
    def check_quit(self):
        button = XGOEDU()
        if button.xgoButton("b"):
            #關閉攝像頭並釋放物件
            #self.cap.release()
            cap.release()
            #關閉視窗
            cv2.destroyAllWindows()
            #退出系統
            sys.exit()

    def move_by(self, distance, vx, vy, k, mintime):
        runtime = k * abs(distance) + mintime
        self.move_x(math.copysign(vx, distance))
        self.move_y(math.copysign(vy, distance))
        time.sleep(runtime)
        self.move_x(0)
        self.move_y(0)
        time.sleep(0.2)

    def move_x_by(self, distance, vx=18, k=0.035, mintime=0.55):
        self.move_by(distance, vx, 0, k, mintime)
        pass

    def move_y_by(self, distance, vy=18, k=0.0373, mintime=0.5):
        self.move_by(distance, 0, vy, k, mintime)
        pass

    def adjust_x(self, distance, vx=18, k=0.045, mintime=0.6):
        self.move_by(distance, vx, 0, k, mintime)
        pass

    def adjust_y(self, distance, vy=18, k=0.0373, mintime=0.5):
        self.move_by(distance, 0, vy, k, mintime)
        pass

    def adjust_yaw(self, theta, vyaw=16, k=0.08, mintime=0.5):
        runtime = abs(theta) * k + mintime
        self.turn(math.copysign(vyaw, theta))
        time.sleep(runtime)
        self.turn(0)
        pass

    def turn_to(self, theta, vyaw=60, emax=10):
        cur_yaw = self.read_yaw()
        des_yaw = self.init_yaw + theta
        while abs(des_yaw - cur_yaw) >= emax:
            self.turn(math.copysign(vyaw, des_yaw - cur_yaw))
            cur_yaw = self.read_yaw()
            print(cur_yaw)
        self.turn(0)
        time.sleep(0.2)
        pass

    def prepare_for_block(self, x, y, angle, des_x=14, emax_x=1.8, emax_y=1.9, emax_yaw=3.5):
        e_x = x - des_x
        if angle > emax_yaw:
            self.adjust_yaw(-angle)
            # if y < 4 and x > 16.5:
            #     time.sleep(0.3)
            #     self.adjust_y(2)
        elif angle < -emax_yaw:
            self.adjust_yaw(-angle)
            # if y > -4 and x > 16.5:
            #     time.sleep(0.3)
            #     self.adjust_y(-2)
        else:
            if abs(y) > emax_y:
                self.adjust_y(-y)
            else:
                if abs(e_x) > emax_x:
                    self.adjust_x(e_x)
                else:
                    print("DONE BLOCK")
                    self.action(0x83)
                    time.sleep(7)
                    self.reset()
                    time.sleep(0.5)
                    return True
        return False

    def prepare_for_cup(self, x1, x2, y1, y2, vx_k, des_x=16, emax_y=1.8):
        if abs(y1 + y2) > emax_y:
            self.adjust_y(-(y1 + y2) / 2)
            time.sleep(0.3)
        else:
            if 23 < (x1 + x2) / 2 < 60:  # 過濾掉誤識別資料
                self.move_x_by((x1 + x2) / 2 - des_x, k=vx_k, mintime=0.65)
                print("DONE CUP")
                self.action(0x84)
                time.sleep(7)
                return True
        return False

    def cal_block(self, s_x, s_y):
        # k1 = 0.00323
        # k2 = -1.272
        # b = 139.5
        # # k1 = 0.002875
        # # k2 = -1.061
        ky = 0.00574
        # b = 108.1
        # x = k1 * s_x * s_x + k2 * s_x + b
        # y = (ky * x + 0.01) * (s_y - 160)
        x = self.block_k1 * s_x * s_x + self.block_k2 * s_x + self.block_b
        # y = self.block_ky * (s_y - 160) * x + self.block_by
        y = (ky * x + 0.01) * (s_y - 160)
        return x, y

    def cal_cup(self, width1, width2, cup_y1, cup_y2):
        kw1 = 1.453e-05
        kw2 = - 1.461e-05
        kc1 = 0.0146
        kc2 = -1.81
        ky = 0.006418
        by = -0.007943
        bc = 77.71
        # 橫向畸變
        # kwidth1 = kw1 * (cup_y1 - 160) * (cup_y1 - 160) - kw2 * abs(cup_y1 - 160) + 1
        # kwidth2 = kw1 * (cup_y2 - 160) * (cup_y2 - 160) - kw2 * abs(cup_y2 - 160) + 1
        # width1 = width1 / kwidth1
        # width2 = width2 / kwidth2
        x1 = self.cup_k1 * width1 * width1 + self.cup_k2 * width1 + self.cup_b
        x2 = self.cup_k1 * width2 * width2 + self.cup_k2 * width2 + self.cup_b
        y1 = (ky * x1 - by) * (cup_y1 - 160)
        y2 = (ky * x2 - by) * (cup_y2 - 160)
        return x1, x2, y1, y2

    def get_color_mask(self, color):
        # if color == 'red':
        #     color_lower = np.array([173, 90, 46])
        #     color_upper = np.array([183, 255, 255])
        # elif color == 'green':
        #     color_lower = np.array([73, 150, 70])
        #     color_upper = np.array([88, 255, 255])
        # elif color == 'blue':
        #     color_lower = np.array([100, 100, 50])
        #     color_upper = np.array([110, 255, 255])
        if color == 'red':
            color_lower = (0, 145, 132)
            color_upper = (255, 255, 255)
        elif color == 'green':
            color_lower = (40, 0, 130)
            color_upper = (220, 110, 230)
        elif color == 'blue':
            color_lower = (0, 0, 0)
            color_upper = (255, 136, 120)
        return color_upper, color_lower

    def filter_img(self, frame, color):
        # if color == 'green':
        #     frame_gb = cv2.GaussianBlur(frame, (3, 3), 1)
        #     hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        #     color_upper, color_lower = self.get_color_mask(color)
        #     mask = cv2.inRange(hsv, color_lower, color_upper)
        # else:
        frame = cv2.GaussianBlur(frame, (3, 3), 1)
        color_upper, color_lower = self.get_color_mask(color)
        frame_lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
        mask = cv2.inRange(frame_lab, color_lower, color_upper)

        img_mask = cv2.bitwise_and(frame, frame, mask=mask)
        return img_mask

    def detect_contours(self, frame, color):
        img_mask = self.filter_img(frame, color)
        CANNY_THRESH_1 = 15
        CANNY_THRESH_2 = 120
        edges = cv2.Canny(img_mask, CANNY_THRESH_1, CANNY_THRESH_2)
        edges = cv2.dilate(edges, None, iterations=1)
        edges = cv2.erode(edges, None, iterations=1)
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        return contours, img_mask

    def detect_block(self, contours):
        flag = False
        length, width, angle, s_x, s_y = 0, 0, 0, 0, 0
        for i in range(0, len(contours)):
            if cv2.contourArea(contours[i]) < 20 ** 2:
                continue
            rect = cv2.minAreaRect(contours[i])
            if 0.5 < rect[1][0] / rect[1][1] < 2:
                continue
            if not flag:
                if rect[2] > 45:
                    length = rect[1][0]
                    width = rect[1][1]
                    angle = rect[2] - 90
                else:
                    length = rect[1][1]
                    width = rect[1][0]
                    angle = rect[2]
                s_x = rect[0][1]  # s_代表螢幕座標系
                s_y = rect[0][0]
                flag = True
            else:  # 識別出兩個及以上的矩形退出
                flag = False
                break
        return flag, length, width, angle, s_x, s_y

    def detect_cup(self, contours):
        num = 0
        width1, width2, s_y1, s_y2 = 0, 0, 0, 0
        index = [0, 0]
        flag = True
        for i in range(0, len(contours)):
            if cv2.contourArea(contours[i]) < 15 ** 2:
                continue
            rect = cv2.minAreaRect(contours[i])
            if 0.5 < rect[1][0] / rect[1][1] < 2:
                if num == 2:
                    flag = False
                    break
                index[num] = i
                num += 1
        if flag and num == 2:
            c1 = contours[index[0]]
            c2 = contours[index[1]]
            rect1 = cv2.minAreaRect(c1)
            rect2 = cv2.minAreaRect(c2)
            if rect1[2] > 45:
                width1 = rect1[1][1]
            else:
                width1 = rect1[1][0]

            if rect2[2] > 45:
                width2 = rect2[1][1]
            else:
                width2 = rect2[1][0]
            s_y1 = rect1[0][0]
            s_y2 = rect2[0][0]
        else:
            flag = False
        return flag, width1, width2, s_y1, s_y2

    '''
    def detect_single_cup(self,contours):
    '''
    def detect_single_cup(self, contours):
        flag = False
        length, width, angle, s_x, s_y = 0, 0, 0, 0, 0
        for i in range(0, len(contours)):
            if cv2.contourArea(contours[i]) < 15 ** 2:
                continue
            rect = cv2.minAreaRect(contours[i])
            if not (0.5 < rect[1][0] / rect[1][1] < 2):
                continue
            if not flag:
                if rect[2] > 45:
                    length = rect[1][0]
                    width = rect[1][1]
                    angle = rect[2] - 90
                else:
                    length = rect[1][1]
                    width = rect[1][0]
                    angle = rect[2]
                s_x = rect[0][1]
                s_y = rect[0][0]
                flag = True
            else:
                flag = False
                break
        return flag, width, s_y
    
    def search_for_block(self, color, COUNT_MAX=25):
        count = 0
        length, width, angle, s_x, s_y = 0, 0, 0, 0, 0
        x, y = 0, 0
        self.attitude('p', 10)
        while True:
            #ret, frame = self.cap.read()
            ret, frame = cap.read()
            self.check_quit()
            contours, img = self.detect_contours(frame, color)
            flag, temp_length, temp_width, temp_angle, temp_s_x, temp_s_y = self.detect_block(contours)
            if not flag:
                self.show_img(img)
                continue

            cv2.putText(img, '%4.1f' % temp_s_x, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % temp_s_y, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % temp_angle, (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % x, (20, 130), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y, (20, 160), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            self.show_img(img)
            count += 1
            length = (count - 1) * length / count + temp_length / count
            width = (count - 1) * width / count + temp_width / count
            angle = (count - 1) * angle / count + temp_angle / count
            s_x = (count - 1) * s_x / count + temp_s_x / count
            s_y = (count - 1) * s_y / count + temp_s_y / count
            if count == COUNT_MAX:
                count = 0
                x, y = self.cal_block(s_x, s_y)
                print("block position x: %4.1f, y: %4.1f" % (x, y))
                done = self.prepare_for_block(x, y, angle)
                if done:
                    break

    '''
    def search_for_block_two_color(self,color1,color2,COUNT_MAX=25):
    '''
    def search_for_block_two_color(self, color1, color2, COUNT_MAX=25):
        count = 0
        length, width, angle, s_x, s_y = 0, 0, 0, 0, 0
        x, y = 0, 0
        self.attitude('p', 10)
        while True:
            ret, frame = cap.read()
            self.check_quit()
            contours, img = self.detect_contours(frame, color1)
            flag, temp_length, temp_width, temp_angle, temp_s_x, temp_s_y = self.detect_block(contours)
            if not flag:
                contours, img = self.detect_contours(frame, color2)
                flag, temp_length, temp_width, temp_angle, temp_s_x, temp_s_y = self.detect_block(contours)
                if not flag:
                    self.show_img(img)
                    continue
    
    def search_for_cup(self, color, COUNT_MAX=25, direction=0, k=0.035):
        count = 0
        width1, width2, s_y1, s_y2 = 0, 0, 0, 0
        x1, x2, y1, y2 = 0, 0, 0, 0
        while True:
            self.check_quit()
            #ret, frame = self.cap.read()
            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color)
            flag, temp_width1, temp_width2, temp_s_y1, temp_s_y2 = self.detect_cup(contours)
            if not flag:
                self.show_img(img)
                continue

            # cv2.putText(img, '%4.1f' % temp_width1, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            # cv2.putText(img, '%4.1f' % temp_width2, (90, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            # cv2.putText(img, '%4.1f' % temp_s_y1, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            # cv2.putText(img, '%4.1f' % temp_s_y2, (90, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % x1, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % x2, (90, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y1, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y2, (90, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            self.show_img(img)

            count += 1
            width1 = (count - 1) * width1 / count + temp_width1 / count
            width2 = (count - 1) * width2 / count + temp_width2 / count
            s_y1 = (count - 1) * s_y1 / count + temp_s_y1 / count
            s_y2 = (count - 1) * s_y2 / count + temp_s_y2 / count
            if count == COUNT_MAX:
                count = 0
                x1, x2, y1, y2 = self.cal_cup(width1, width2, s_y1, s_y2)
                print("x1: %4.2f, x2: %4.2f, y1: %4.2f, y2: %4.2f" % (x1, x2, y1, y2))
                done = False
                done = self.prepare_for_cup(x1, x2, y1, y2, vx_k=k)
                if direction != 0:
                    self.turn_to(direction, vyaw=30, emax=2)
                if done:
                    break
    '''
    def search_for_cup_two_color(self,color1,color2,COUNT_MAX=25):
    '''
    def search_for_cup_two_color(self, color1, color2, COUNT_MAX=25, direction=0, k=0.035):
        count = 0
        width1, width2, s_y1, s_y2 = 0, 0, 0, 0
        x1, x2, y1, y2 = 0, 0, 0, 0
        while True:
            self.check_quit()
            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color1)
            flag, temp_width1, temp_width2, temp_s_y1, temp_s_y2 = self.detect_cup(contours)
            if not flag:
                contours, img = self.detect_contours(frame, color2)
                flag, temp_width1, temp_width2, temp_s_y1, temp_s_y2 = self.detect_cup(contours)
                if not flag:
                    self.show_img(img)
                    continue
            cv2.putText(img, '%4.1f' % x1, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % x2, (90, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y1, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y2, (90, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            self.show_img(img)

            count += 1
            width1 = (count - 1) * width1 / count + temp_width1 / count
            width2 = (count - 1) * width2 / count + temp_width2 / count
            s_y1 = (count - 1) * s_y1 / count + temp_s_y1 / count
            s_y2 = (count - 1) * s_y2 / count + temp_s_y2 / count
            if count == COUNT_MAX:
                count = 0
                x1, x2, y1, y2 = self.cal_cup(width1, width2, s_y1, s_y2)
                print("x1: %4.2f, x2: %4.2f, y1: %4.2f, y2: %4.2f" % (x1, x2, y1, y2))
                done = False
                done = self.prepare_for_cup(x1, x2, y1, y2, vx_k=k)
                if direction != 0:
                    self.turn_to(direction, vyaw=30, emax=2)
                if done:
                    break
    '''
    def search_for_cip_CQ(self,color1,color2,COUNT_MAX=25,direction=0,k=0.035)
    '''
    def search_for_cup_CQ(self, color1, color2, COUNT_MAX=25, direction=0, k=0.035):
        count = 0
        width1, width2, s_y1, s_y2 = 0, 0, 0, 0
        x1, x2, y1, y2 = 0, 0, 0, 0
        while True:
            self.check_quit()
            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color1)
            flag, temp_width1, temp_s_y1 = self.detect_single_cup(contours)
            if not flag:
                self.show_img(img)
                continue

            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color2)
            flag, temp_width2, temp_s_y2 = self.detect_single_cup(contours)
            if not flag:
                self.show_img(img)
                continue
            cv2.putText(img, '%4.1f' % x1, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % x2, (90, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y1, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            cv2.putText(img, '%4.1f' % y2, (90, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
            self.show_img(img)

            count += 1
            width1 = (count - 1) * width1 / count + temp_width1 / count
            width2 = (count - 1) * width2 / count + temp_width2 / count
            s_y1 = (count - 1) * s_y1 / count + temp_s_y1 / count
            s_y2 = (count - 1) * s_y2 / count + temp_s_y2 / count
            if count == COUNT_MAX:
                count = 0
                x1, x2, y1, y2 = self.cal_cup(width1, width2, s_y1, s_y2)
                print("x1: %4.2f, x2: %4.2f, y1: %4.2f, y2: %4.2f" % (x1, x2, y1, y2))
                done = False
                done = self.prepare_for_cup(x1, x2, y1, y2, vx_k=k)
                if direction != 0:
                    self.turn_to(direction, vyaw=30, emax=2)
                if done:
                    break
    def calibration_block(self, color, COUNT_MAX=25):
        count = 0
        block_num = 0
        state = 0
        path = 'calibration.json'
        s_x = 0
        s_y = 0
        s_x_list = [186, 174.5, 163.5, 150.5, 143.5, 138.5, 131.5, 125.6]
        x_list = [13, 15, 17, 20, 22, 24, 27, 30]

        ky_list = []
        y_list = [2.25, 0.25, -1.75, 0.25, 2.25, 0.25, -1.75, 0.25]
        kx_list = []
        #機器狗俯身
        self.attitude('p', 10)
        while True:
            self.check_quit()
            #ret為是否補貨成功,frame為捕獲的每一幀影像
            #ret, frame = self.cap.read()
            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color)
            if state == 0:
                cv2.putText(img, 'Put BLOCK in', (50, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (200, 0, 200), 2)
                cv2.putText(img, '- ' + str(block_num + 1) + ' -', (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 2,
                            (200, 0, 200), 2)
                cv2.putText(img, 'Then press D(up right)', (30, 170), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
                self.show_img(img)
                button = XGOEDU()
                if button.xgoButton("d"):
                    state = 1
                    time.sleep(0.5)
            elif state == 1:
                flag, temp_length, temp_width, temp_angle, temp_s_x, temp_s_y = self.detect_block(contours)
                if not flag:
                    self.show_img(img)
                    continue
                cv2.putText(img, 'Detecting......', (30, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (200, 0, 200), 2)
                cv2.putText(img, '%4.1f' % temp_s_x, (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
                cv2.putText(img, '%4.1f' % temp_s_y, (30, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
                self.show_img(img)
                count += 1
                s_x = s_x * (count - 1) / count + temp_s_x / count
                s_y = s_y * (count - 1) / count + temp_s_y / count
                if count == COUNT_MAX:
                    count = 0
                    s_x_list.append(s_x)
                    s_x_list.append(s_x)
                    ky_list.append(y_list[block_num])
                    kx_list.append((s_y - 160) * (14 + block_num * 3))
                    x_list.append(14 + block_num * 3)
                    x_list.append(14 + block_num * 3)
                    block_num += 1
                    state = 0
                    print("Finish" + str(block_num))
                    if block_num == 8:
                        z = np.polyfit(s_x_list, x_list, 2)
                        self.calibration["BLOCK_k1"] = z[0]
                        self.calibration["BLOCK_k2"] = z[1]
                        self.calibration["BLOCK_b"] = z[2]
                        z = np.polyfit(kx_list, ky_list, 1)
                        self.calibration["BLOCK_ky"] = z[0]
                        self.calibration["BLOCK_by"] = z[1]
                        break

    def calibration_cup(self, color, COUNT_MAX=25):
        count = 0
        cap_num = 0
        state = 0
        path = 'calibration.json'
        width1 = 0
        width2 = 0
        width_list = []
        x_list = []

        while True:
            self.check_quit()
            #ret, frame = self.cap.read()
            ret, frame = cap.read()
            contours, img = self.detect_contours(frame, color)
            if state == 0:
                cv2.putText(img, 'Put Two Cups In', (50, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (200, 0, 200), 2)
                cv2.putText(img, '- ' + str(cap_num + 1) + ' -', (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 2,
                            (200, 0, 200), 2)
                cv2.putText(img, 'Then press D(up right)', (30, 170), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 0, 200), 2)
                self.show_img(img)
                button = XGOEDU()
                if button.xgoButton("d"):
                    state = 1
                    time.sleep(0.5)
            elif state == 1:
                flag, temp_width1, temp_width2, temp_s_y1, temp_s_y2 = self.detect_cup(contours)
                if not flag:
                    self.show_img(img)
                    continue
                cv2.putText(img, 'Detecting......', (30, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (200, 0, 200), 2)
                self.show_img(img)
                count += 1
                width1 = width1 * (count - 1) / count + temp_width1 / count
                width2 = width2 * (count - 1) / count + temp_width2 / count
                if count == COUNT_MAX:
                    count = 0
                    width_list.append(width1)
                    width_list.append(width2)
                    x_list.append(24.7 + cap_num * 2)
                    x_list.append(24.7 + cap_num * 2)
                    cap_num += 1
                    state = 0
                    print("Finish" + str(cap_num))
                    if cap_num == 8:
                        z = np.polyfit(width_list, x_list, 2)
                        self.calibration["CUP_k1"] = z[0]
                        self.calibration["CUP_k2"] = z[1]
                        self.calibration["CUP_b"] = z[2]
                        with open(path, 'w', encoding='utf-8') as f:
                            json.dump(self.calibration, f)
                        break

    def calibration_contest(self, color='red'):
        self.calibration_block(color)
        self.reset()
        time.sleep(1)
        self.calibration_cup(color)

    def show_filter_img(self, color):
        while True:
            #ret, frame = self.cap.read()
            ret, frame = cap.read()
            img = self.filter_img(frame, color)
            self.show_img(img)
            self.check_quit()

class XGOEDU():
    def __init__(self):
        self.key1=17
        self.key2=22
        self.key3=23
        self.key4=24
        GPIO.setup(self.key1,GPIO.IN,GPIO.PUD_UP)
        GPIO.setup(self.key2,GPIO.IN,GPIO.PUD_UP)
        GPIO.setup(self.key3,GPIO.IN,GPIO.PUD_UP)
        GPIO.setup(self.key4,GPIO.IN,GPIO.PUD_UP)
    #畫布初始化
    def lcd_init(self,color):
        if color == "black":
            splash = Image.new("RGB",(320,240),"black")
        elif color == "white":
            splash = Image.new("RGB",(320,240),"white")
        elif color == "red":
            splash = Image.new("RGB",(320,240),"red") 
        elif color == "green":
            splash = Image.new("RGB",(320,240),"green")
        elif color == "blue":
            splash = Image.new("RGB",(320,240),"blue")
        display.ShowImage(splash)
    #繪畫直線
    '''
    x1,y1為初始點座標,x2,y2為終止點座標
    '''
    def lcd_line(self,x1,y1,x2,y2):
        draw = ImageDraw.Draw(splash)
        draw.line([(x1,y1),(x2,y2)],fill = "WHITE",width = 2)
        display.ShowImage(splash)
    #繪畫圓
    '''
    x1,y1,x2,y2為定義給定邊框的兩個點,angle0為初始角度,angle1為終止角度
    '''
    def lcd_circle(self,x1,y1,x2,y2,angle0,angle1):
        draw = ImageDraw.Draw(splash)
        draw.arc((x1,y1,x2,y2),angle0,angle1,fill=(255,255,255),width = 2)
        display.ShowImage(splash)
    #繪畫矩形
    '''
    x1,y1為初始點座標,x2,y2為對角線終止點座標
    '''
    def lcd_rectangle(self,x1,y1,x2,y2):
        draw = ImageDraw.Draw(splash)
        draw.rectangle((x1,y1,x2,y2),fill = None,outline = "WHITE",width = 2)
        display.ShowImage(splash)
    #清除螢幕
    def lcd_clear(self):
        splash = Image.new("RGB",(320,240),"black")
        display.ShowImage(splash)
    #顯示圖片
    '''
    圖片的大小為320*240,jpg格式
    '''
    def lcd_picture(self,filename):
        image = Image.open(filename)
        display.ShowImage(image)
    #顯示文字
    '''
    font1為載入字型,微軟雅黑
    目前支援英文和數字,暫不支援中文
    '''
    def lcd_text(self,x1,y1,content):
        draw = ImageDraw.Draw(splash)
        draw.text((x1,y1),content,fill = "WHITE",font=font1)
        display.ShowImage(splash)
    #key_value
    '''
    a左上按鍵
    b右上按鍵
    c左下按鍵
    d右下按鍵
    返回值 0未按下,1按下
    '''
    def xgoButton(self,Button):
        if Button == "a":
            last_state_a =GPIO.input(self.key1)
            time.sleep(0.02)
            return(not last_state_a)
        elif Button == "b":
            last_state_b=GPIO.input(self.key2)
            time.sleep(0.02)
            return(not last_state_b)
        elif Button == "c":
            last_state_c=GPIO.input(self.key3)
            time.sleep(0.02)
            return(not last_state_c)
        elif Button == "d":
            last_state_d=GPIO.input(self.key4)
            time.sleep(0.02)
            return(not last_state_d)
    #speaker
    '''
    filename 檔名 字串
    '''
    def xgoSpeaker(self,filename):
        os.system("mplayer"+" "+filename)
    #audio_record
    '''
    filename 檔名 字串
    seconds 錄製時間S 字串
    '''
    def xgoAudioRecord(self,filename,seconds):
        command1 = "sudo arecord -D hw:1,0 -d"
        command2 = "-f S32_LE -r 16000 -c 2"
        os.system(command1+" "+seconds+" "+command2+" "+filename)
    '''
    開啟攝像頭
    '''
    def cameraOn(self):
        while True:
            success,image = cap.read()
            if not success:
                print("Ignoring empty camera frame")
                continue
            #cv2.imshow('frame',image)
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image,1)
            imgok = Image.fromarray(image)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    '''
    開啟攝像頭並拍照
    '''
    def takePhoto(self):
        while True:
            success,image = cap.read()
            if not success:
                print("Ignoring empty camera frame")
                continue
            cv2.imshow('frame',image)
            cv2.imwrite('/home/pi/xgoEdu/camera/file.jpg',image)
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image,1)
            imgok = Image.fromarray(image)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:  # esc 鍵 推出
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"): # 裝置C返回鍵
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    '''
    手勢識別
    '''
    def gestureRecognition(self):
        hand = hands(0,2,0.6,0.5)
        while True:
            success,image = cap.read()
            datas = hand.run(image)
            cv2.imshow('OpenCV',image)
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image,1)
            if not success:
                print("Ignoring empty camera frame")
                continue
            for data in datas:
                pos_left = ''
                pos_right = ''
                rect = data['rect']
                right_left = data['right_left']
                center = data['center']
                dlandmark = data['dlandmark']
                hand_angle = data['hand_angle']
                XGOEDU.rectangle(self,image,rect,"#33cc00",2)
                #XGOEDU.text(self,image,right_left,center,2,"#cc0000",5)
                if right_left == 'L':
                    XGOEDU.text(self,image,hand_pos(hand_angle),(180,80),1.5,"#33cc00",2)
                    pos_left = hand_pos(hand_angle)
                elif right_left == 'R':
                    XGOEDU.text(self,image,hand_pos(hand_angle),(50,80),1.5,"#ff0000",2)
                    pos_right = hand_pos(hand_angle)
                for i in dlandmark:
                    XGOEDU.circle(self,image,i,3,"#ff9900",-1)
            imgok = Image.fromarray(image)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            #return(pos_left,pos_right)
    '''
    yolo
    '''
    def yoloFast(self):
        yolo = yoloXgo('/home/pi/xgoEdu/model/Model.onnx',
        ['person','bicycle','car','motorbike','aeroplane','bus','train','truck','boat','traffic light','fire hydrant','stop sign','parking meter','bench','bird','cat','dog','horse','sheep','cow','elephant','bear','zebra','giraffe','backpack','umbrella','handbag','tie','suitcase','frisbee','skis','snowboard','sports ball','kite','baseball bat','baseball glove','skateboard','surfboard','tennis racket','bottle','wine glass','cup','fork','knife','spoon','bowl','banana','apple','sandwich','orange','broccoli','carrot','hot dog','pizza','donut','cake','chair','sofa','pottedplant','bed','diningtable','toilet','tvmonitor','laptop','mouse','remote','keyboard','cell phone','microwave','oven','toaster','sink','refrigerator','book','clock','vase','scissors','teddy bear','hair drier','toothbrush'],
        [352,352],0.6)
        while True:
            success,image = cap.read()
            datas = yolo.run(image)
            cv2.imshow('OpenCV',image)
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image,1)
            if not success:
                print("Ignoring empty camera frame")
                continue
            if datas:
                for data in datas:
                    XGOEDU.rectangle(self,image,data['xywh'],"#33cc00",2)
                    xy= (data['xywh'][0], data['xywh'][1])
                    XGOEDU.text(self,image,data['classes'],xy,1,"#ff0000",2)
                    value_yolo = data['classes']
            imgok = Image.fromarray(image)4
            display.ShowImage(imgok)
            #return(value_yolo)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    '''
    人臉座標點檢測
    '''
    def face_detect(self):
        face = face_detection(0.7)
        while True:
            success,image = cap.read()
            datas = face.run(image)
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image,1)
            if not success:
                print("Ignoring empty camera frame")
                continue
            for data in datas:
                print(data)
                lefteye = str(data['left_eye'])
                righteye = str(data['right_eye'])
                nose = str(data['nose'])
                mouth = str(data['mouth'])
                leftear = str(data['left_ear'])
                rightear = str(data['right_ear'])
                cv2.putText(image,'lefteye',(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,0,0),2)
                cv2.putText(image,lefteye,(100,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,0,0),2)
                cv2.putText(image,'righteye',(10,50),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,255,0),2)
                cv2.putText(image,righteye,(100,50),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,255,0),2)
                cv2.putText(image,'nose',(10,70),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
                cv2.putText(image,nose,(100,70),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
                cv2.putText(image,'leftear',(10,90),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,255,0),2)
                cv2.putText(image,leftear,(100,90),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,255,0),2)
                cv2.putText(image,'rightear',(10,110),cv2.FONT_HERSHEY_SIMPLEX,0.7,(200,0,200),2)
                cv2.putText(image,rightear,(100,110),cv2.FONT_HERSHEY_SIMPLEX,0.7,(200,0,200),2)
                XGOEDU.rectangle(self,image,data['rect'],"#33cc00",2)
            #cv2.imshow('OpenCV',image)
            imgok = Image.fromarray(image)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    '''
    情緒識別
    '''
    def emotion(self):
        while True:
            success,image=cap.read()
            labels=[]
            gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
            faces=face_classifier.detectMultiScale(gray,1.3,5)
            label=''
            for (x,y,w,h) in faces:
                cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)
                roi_gray=gray[y:y+h,x:x+w]
                roi_gray=cv2.resize(roi_gray,(48,48),interpolation=cv2.INTER_AREA)
                if np.sum([roi_gray])!=0:
                    roi=roi_gray.astype('float')/255.0
                    roi=img_to_array(roi)
                    roi=np.expand_dims(roi,axis=0)

                    preds=classifier.predict(roi)[0]
                    label=class_labels[preds.argmax()]
                    print(label)
                    label_position=(x,y)
                else:
                    pass
            b,g,r = cv2.split(image)
            image = cv2.merge((r,g,b))
            image = cv2.flip(image, 1)
            try:
                cv2.putText(image,label,label_position,cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),3)
            except:
                pass
            imgok = Image.fromarray(image)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    '''
    年紀及性別檢測
    '''
    def agesex(self):
        while True:
            t = time.time()
            hasFrame,image = cap.read()
            image = cv.flip(image, 1)
            frameFace, bboxes = getFaceBox(faceNet, image)
            if not bboxes:
                print("No face Detected, Checking next frame")
            gender=''
            age=''
            for bbox in bboxes:
                face = image[max(0, bbox[1] - padding):min(bbox[3] + padding, image.shape[0] - 1),
                       max(0, bbox[0] - padding):min(bbox[2] + padding, image.shape[1] - 1)]
                blob = cv.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
                genderNet.setInput(blob)   
                genderPreds = genderNet.forward()   
                gender = genderList[genderPreds[0].argmax()]  
                ageNet.setInput(blob)
                agePreds = ageNet.forward()
                age = ageList[agePreds[0].argmax()]
                label = "{},{}".format(gender, age)
                cv.putText(frameFace, label, (bbox[0], bbox[1] - 10), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2,cv.LINE_AA)  
            b,g,r = cv2.split(frameFace)
            frameFace = cv2.merge((r,g,b))
            imgok = Image.fromarray(frameFace)
            display.ShowImage(imgok)
            if cv2.waitKey(5) & 0xFF == 27:
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
            if XGOEDU.xgoButton(self,"c"):
                XGOEDU.lcd_clear(self)
                time.sleep(0.5)
                break
    
    def rectangle(self,frame,z,colors,size):
        frame=cv2.rectangle(frame,(int(z[0]),int(z[1])),(int(z[0]+z[2]),int(z[1]+z[3])),color(colors),size)
        return frame
        
    def circle(self,frame,xy,rad,colors,tk):
        frame=cv2.circle(frame,xy,rad,color(colors),tk)
        return frame
    
    def text(self,frame,text,xy,font_size,colors,size):
        frame=cv2.putText(frame,text,xy,cv2.FONT_HERSHEY_SIMPLEX,font_size,color(colors),size)
        return frame       

class hands():
    def __init__(self,model_complexity,max_num_hands,min_detection_confidence,min_tracking_confidence):
        self.model_complexity = model_complexity
        self.max_num_hands = max_num_hands
        self.min_detection_confidence = min_detection_confidence
        self.min_tracking_confidence = min_tracking_confidence
        self.mp_hands = mp.solutions.hands
        self.hands = self.mp_hands.Hands(
            max_num_hands=self.max_num_hands,
            min_detection_confidence=self.min_detection_confidence,
            min_tracking_confidence=self.min_tracking_confidence,
        )
    
    def run(self,cv_img):
        image = cv_img
        debug_image = copy.deepcopy(image)
        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
        results = self.hands.process(image)
        hf=[]
        if results.multi_hand_landmarks is not None:
            for hand_landmarks, handedness in zip(results.multi_hand_landmarks,
                                                  results.multi_handedness):
                # 手的重心計算
                cx, cy = self.calc_palm_moment(debug_image, hand_landmarks)
                # 手的外接矩形計算
                rect = self.calc_bounding_rect(debug_image, hand_landmarks)
                # 手的個關鍵點
                dlandmark = self.dlandmarks(debug_image,hand_landmarks,handedness)

                hf.append({'center':(cx,cy),'rect':rect,'dlandmark':dlandmark[0],'hand_angle':self.hand_angle(dlandmark[0]),'right_left':dlandmark[1]})
        return hf

    def calc_palm_moment(self, image, landmarks):
        image_width, image_height = image.shape[1], image.shape[0]
        palm_array = np.empty((0, 2), int)
        for index, landmark in enumerate(landmarks.landmark):
            landmark_x = min(int(landmark.x * image_width), image_width - 1)
            landmark_y = min(int(landmark.y * image_height), image_height - 1)
            landmark_point = [np.array((landmark_x, landmark_y))]
            if index == 0:  # 手首1
                palm_array = np.append(palm_array, landmark_point, axis=0)
            if index == 1:  # 手首2
                palm_array = np.append(palm_array, landmark_point, axis=0)
            if index == 5:  # 人差指:付け根
                palm_array = np.append(palm_array, landmark_point, axis=0)
            if index == 9:  # 中指:付け根
                palm_array = np.append(palm_array, landmark_point, axis=0)
            if index == 13:  # 薬指:付け根
                palm_array = np.append(palm_array, landmark_point, axis=0)
            if index == 17:  # 小指:付け根
                palm_array = np.append(palm_array, landmark_point, axis=0)
        M = cv.moments(palm_array)
        cx, cy = 0, 0
        if M['m00'] != 0:
            cx = int(M['m10'] / M['m00'])
            cy = int(M['m01'] / M['m00'])
        return cx, cy

    def calc_bounding_rect(self, image, landmarks):
        image_width, image_height = image.shape[1], image.shape[0]
        landmark_array = np.empty((0, 2), int)
        for _, landmark in enumerate(landmarks.landmark):
            landmark_x = min(int(landmark.x * image_width), image_width - 1)
            landmark_y = min(int(landmark.y * image_height), image_height - 1)
            landmark_point = [np.array((landmark_x, landmark_y))]
            landmark_array = np.append(landmark_array, landmark_point, axis=0)
        x, y, w, h = cv.boundingRect(landmark_array)
        return [x, y, w, h]

    def dlandmarks(self,image, landmarks, handedness):
        image_width, image_height = image.shape[1], image.shape[0]
        landmark_point = []
        for index, landmark in enumerate(landmarks.landmark):
            if landmark.visibility < 0 or landmark.presence < 0:
                continue
            landmark_x = min(int(landmark.x * image_width), image_width - 1)
            landmark_y = min(int(landmark.y * image_height), image_height - 1)
            landmark_point.append((landmark_x, landmark_y))
        return landmark_point,handedness.classification[0].label[0]

    def vector_2d_angle(self, v1, v2):
        v1_x = v1[0]
        v1_y = v1[1]
        v2_x = v2[0]
        v2_y = v2[1]
        try:
            angle_= math.degrees(math.acos((v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5))))
        except:
            angle_ = 180
        return angle_

    def hand_angle(self,hand_):
        angle_list = []
        # thumb 大拇指角度
        angle_ = self.vector_2d_angle(
            ((int(hand_[0][0])- int(hand_[2][0])),(int(hand_[0][1])-int(hand_[2][1]))),
            ((int(hand_[3][0])- int(hand_[4][0])),(int(hand_[3][1])- int(hand_[4][1])))
            )
        angle_list.append(angle_)
        # index 食指角度
        angle_ = self.vector_2d_angle(
            ((int(hand_[0][0])-int(hand_[6][0])),(int(hand_[0][1])- int(hand_[6][1]))),
            ((int(hand_[7][0])- int(hand_[8][0])),(int(hand_[7][1])- int(hand_[8][1])))
            )
        angle_list.append(angle_)
        # middle 中指角度
        angle_ = self.vector_2d_angle(
            ((int(hand_[0][0])- int(hand_[10][0])),(int(hand_[0][1])- int(hand_[10][1]))),
            ((int(hand_[11][0])- int(hand_[12][0])),(int(hand_[11][1])- int(hand_[12][1])))
            )
        angle_list.append(angle_)
        # ring 無名指角度
        angle_ = self.vector_2d_angle(
            ((int(hand_[0][0])- int(hand_[14][0])),(int(hand_[0][1])- int(hand_[14][1]))),
            ((int(hand_[15][0])- int(hand_[16][0])),(int(hand_[15][1])- int(hand_[16][1])))
            )
        angle_list.append(angle_)
        # pink 小拇指角度
        angle_ = self.vector_2d_angle(
            ((int(hand_[0][0])- int(hand_[18][0])),(int(hand_[0][1])- int(hand_[18][1]))),
            ((int(hand_[19][0])- int(hand_[20][0])),(int(hand_[19][1])- int(hand_[20][1])))
            )
        angle_list.append(angle_)
        return angle_list
    
class yoloXgo():
    def __init__(self,model,classes,inputwh,thresh):
        self.session = onnxruntime.InferenceSession(model)
        self.input_width=inputwh[0]
        self.input_height=inputwh[1]
        self.thresh=thresh
        self.classes=classes
        
    def sigmoid(self,x):
        return 1. / (1 + np.exp(-x))

    # tanh函式
    def tanh(self,x):
        return 2. / (1 + np.exp(-2 * x)) - 1

    # 資料預處理
    def preprocess(self,src_img, size):
        output = cv2.resize(src_img,(size[0], size[1]),interpolation=cv2.INTER_AREA)
        output = output.transpose(2,0,1)
        output = output.reshape((1, 3, size[1], size[0])) / 255
        return output.astype('float32') 

    # nms演算法
    def nms(self,dets,thresh=0.45):
        # dets:N*M,N是bbox的個數,M的前4位是對應的(x1,y1,x2,y2),第5位是對應的分數
        # #thresh:0.3,0.5....
        x1 = dets[:, 0]
        y1 = dets[:, 1]
        x2 = dets[:, 2]
        y2 = dets[:, 3]
        scores = dets[:, 4]
        areas = (x2 - x1 + 1) * (y2 - y1 + 1)  # 求每個bbox的面積
        order = scores.argsort()[::-1]  # 對分數進行倒排序
        keep = []  # 用來儲存最後留下來的bboxx下標

        while order.size > 0:
            i = order[0]  # 無條件保留每次迭代中置信度最高的bbox
            keep.append(i)

            # 計算置信度最高的bbox和其他剩下bbox之間的交叉區域
            xx1 = np.maximum(x1[i], x1[order[1:]])
            yy1 = np.maximum(y1[i], y1[order[1:]])
            xx2 = np.minimum(x2[i], x2[order[1:]])
            yy2 = np.minimum(y2[i], y2[order[1:]])

            # 計算置信度高的bbox和其他剩下bbox之間交叉區域的面積
            w = np.maximum(0.0, xx2 - xx1 + 1)
            h = np.maximum(0.0, yy2 - yy1 + 1)
            inter = w * h

            # 求交叉區域的面積佔兩者(置信度高的bbox和其他bbox)面積和的必烈
            ovr = inter / (areas[i] + areas[order[1:]] - inter)

            # 保留ovr小於thresh的bbox,進入下一次迭代。
            inds = np.where(ovr <= thresh)[0]

            # 因為ovr中的索引不包括order[0]所以要向後移動一位
            order = order[inds + 1]
        
        output = []
        for i in keep:
            output.append(dets[i].tolist())

        return output

    def run(self, img,):
        pred = []

        # 輸入影像的原始寬高
        H, W, _ = img.shape

        # 資料預處理: resize, 1/255
        data = self.preprocess(img, [self.input_width, self.input_height])

        # 模型推理
        input_name = self.session.get_inputs()[0].name
        feature_map = self.session.run([], {input_name: data})[0][0]

        # 輸出特徵圖轉置: CHW, HWC
        feature_map = feature_map.transpose(1, 2, 0)
        # 輸出特徵圖的寬高
        feature_map_height = feature_map.shape[0]
        feature_map_width = feature_map.shape[1]

        # 特徵圖後處理
        for h in range(feature_map_height):
            for w in range(feature_map_width):
                data = feature_map[h][w]

                # 解析檢測框置信度
                obj_score, cls_score = data[0], data[5:].max()
                score = (obj_score ** 0.6) * (cls_score ** 0.4)

                # 閾值篩選
                if score > self.thresh:
                    # 檢測框類別
                    cls_index = np.argmax(data[5:])
                    # 檢測框中心點偏移
                    x_offset, y_offset = self.tanh(data[1]), self.tanh(data[2])
                    # 檢測框歸一化後的寬高
                    box_width, box_height = self.sigmoid(data[3]), self.sigmoid(data[4])
                    # 檢測框歸一化後中心點
                    box_cx = (w + x_offset) / feature_map_width
                    box_cy = (h + y_offset) / feature_map_height
                    
                    # cx,cy,w,h => x1, y1, x2, y2
                    x1, y1 = box_cx - 0.5 * box_width, box_cy - 0.5 * box_height
                    x2, y2 = box_cx + 0.5 * box_width, box_cy + 0.5 * box_height
                    x1, y1, x2, y2 = int(x1 * W), int(y1 * H), int(x2 * W), int(y2 * H)

                    pred.append([x1, y1, x2, y2, score, cls_index])
        datas=np.array(pred)
        data=[]
        if len(datas)>0:
            boxes=self.nms(datas)
            for b in boxes:
                obj_score, cls_index = b[4], int(b[5])
                x1, y1, x2, y2 = int(b[0]), int(b[1]), int(b[2]), int(b[3])
                s={'classes':self.classes[cls_index],'score':'%.2f' % obj_score,'xywh':[x1,y1,x2-x1,y2-y1],}
                data.append(s)
            return data
        else:
            return False

class face_detection():
    def __init__(self,min_detection_confidence):
        self.model_selection = 0
        self.min_detection_confidence =min_detection_confidence
        self.mp_face_detection = mp.solutions.face_detection
        self.face_detection = self.mp_face_detection.FaceDetection(
            min_detection_confidence=self.min_detection_confidence,
        )

    def run(self,cv_img):
        image = cv_img
        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
        results = self.face_detection.process(cv_img)
        face=[]
        if results.detections is not None:
            for detection in results.detections:
                data =self.draw_detection(image,detection) 
                face.append(data)
        return face
    def draw_detection(self, image, detection):
        image_width, image_height = image.shape[1], image.shape[0]
        bbox = detection.location_data.relative_bounding_box
        bbox.xmin = int(bbox.xmin * image_width)
        bbox.ymin = int(bbox.ymin * image_height)
        bbox.width = int(bbox.width * image_width)
        bbox.height = int(bbox.height * image_height)


        # 位置:右目
        keypoint0 = detection.location_data.relative_keypoints[0]
        keypoint0.x = int(keypoint0.x * image_width)
        keypoint0.y = int(keypoint0.y * image_height)


        # 位置:左目
        keypoint1 = detection.location_data.relative_keypoints[1]
        keypoint1.x = int(keypoint1.x * image_width)
        keypoint1.y = int(keypoint1.y * image_height)


        # 位置:鼻
        keypoint2 = detection.location_data.relative_keypoints[2]
        keypoint2.x = int(keypoint2.x * image_width)
        keypoint2.y = int(keypoint2.y * image_height)


        # 位置:口
        keypoint3 = detection.location_data.relative_keypoints[3]
        keypoint3.x = int(keypoint3.x * image_width)
        keypoint3.y = int(keypoint3.y * image_height)

        # 位置:右耳
        keypoint4 = detection.location_data.relative_keypoints[4]
        keypoint4.x = int(keypoint4.x * image_width)
        keypoint4.y = int(keypoint4.y * image_height)

        # 位置:左耳
        keypoint5 = detection.location_data.relative_keypoints[5]
        keypoint5.x = int(keypoint5.x * image_width)
        keypoint5.y = int(keypoint5.y * image_height)

        data={'id':detection.label_id[0],
            'score':round(detection.score[0], 3),
            'rect':[int(bbox.xmin),int(bbox.ymin),int(bbox.width),int(bbox.height)],
            'right_eye':(int(keypoint0.x),int(keypoint0.y)),
            'left_eye':(int(keypoint1.x),int(keypoint1.y)),
            'nose':(int(keypoint2.x),int(keypoint2.y)),
            'mouth':(int(keypoint3.x),int(keypoint3.y)),
            'right_ear':(int(keypoint4.x),int(keypoint4.y)),
            'left_ear':(int(keypoint5.x),int(keypoint5.y)),
            }
        return data