人がいる方向をむく(アクションカメラ APEMAN A79)OpenCV-Python

touch-sp.hatenablog.com
前回はOpenCVではどうしてもうまくいかなかったと書いた。
数日間調べ続けてようやく解決策を発見した。
stackoverflow.com

import mxnet as mx
import gluoncv

import serial, time
import cv2, queue, threading

class VideoCapture:

  def __init__(self, name):
    self.cap = cv2.VideoCapture(name)
    self.q = queue.Queue()
    t = threading.Thread(target=self._reader)
    t.daemon = True
    t.start()

  # read frames as soon as they are available, keeping only most recent one
  def _reader(self):
    while True:
      ret, frame = self.cap.read()
      if not ret:
        break
      if not self.q.empty():
        try:
          self.q.get_nowait()   # discard previous (unprocessed) frame
        except queue.Empty:
          pass
      self.q.put(frame)

  def read(self):
    return self.q.get()

ser =serial.Serial("COM4", 9600)
time.sleep(1.5)

#初期設定
servo_angle = 90

ctx = mx.gpu()
# Load the model
classes = ['face']
net = gluoncv.model_zoo.get_model('ssd_512_mobilenet1.0_custom', classes=classes, pretrained=False, root='./model')
net.load_parameters('ssd_512_mobilenet1.0_face.params')
net.collect_params().reset_ctx(ctx)
# Compile the model for faster speed
net.hybridize()

cap = VideoCapture('rtsp://192.72.1.1:554/liveRTSP/av4/track0')
time.sleep(1)

while True:

    frame = cap.read()

    frame = mx.nd.array(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)).astype('uint8')    
    rgb_nd, frame = gluoncv.data.transforms.presets.ssd.transform_test(frame, short=320)
  
    # Run frame through network
    class_IDs, scores, bounding_boxes = net(rgb_nd.as_in_context(ctx))

    if scores[0][0] > 0.6:
        x_min = bounding_boxes[0][0][0]
        y_min = bounding_boxes[0][0][1]
        x_max = bounding_boxes[0][0][2]
        y_max = bounding_boxes[0][0][3]
    
        if x_min > 360:
            if x_min > 480:
                servo_angle += 15
            else:
                servo_angle += 3
        if x_max < 280:
            if x_max <160:
                servo_angle -= 15
            else:
                servo_angle -= 3

        servo_angle = 160 if servo_angle > 160 else servo_angle
        servo_angle = 20 if servo_angle < 20 else servo_angle

        #bufferがゼロになるまで待つ
        finished = False
        while not finished:
            finished = (ser.out_waiting == 0)
        
        send_data = servo_angle.to_bytes(1, 'big')

        ser.write(send_data)
        ser.write((255).to_bytes(1, 'big'))

    # Display the result
    img = gluoncv.utils.viz.cv_plot_bbox(frame, bounding_boxes[0], scores[0], class_IDs[0], class_names=net.classes, thresh=0.6)
    gluoncv.utils.viz.cv_plot_image(img)
    
    # escを押したら終了
    if cv2.waitKey(1) == 27:
        break

    time.sleep(0.2)

cv2.destroyAllWindows()

VLC」を使うより「OpenCV」を使った方が映像転送の際のタイムラグが少ない。
「APEMAN A79」でも「iPhone」の時と同等の速度が出ている。
当初「APEMAN A79」と「iPhone」の違いが原因と思っていたが、どうやら「VLC」と「OpenCV」の違いが原因であった。