Face LandmarksをiPadからudpで送り、Pythonで中継、受信、表示したら驚いた！



###Face LandmarksをiPadからudpで送り、Pythonで中継、受信、表示したら驚いた！

　何に驚いたのかというと、短いコードで、求める結果が得られることです。
下図は、dlibの68点の顔のランドマークデータをiPadのアプリで生成し、インターネット経由でサーバーで中継し、macで表示した画面（リアルタイム動画）ですが、この表示用のPythonコードは【コード１】示すようにわずか、30行くらいです。Pythonistの方々には当たり前なんでしょうが、初心者にとっては驚異的です。アイデアのプロトタイピングには最強と感じました。

![2020-08-22 11.45のイメージ.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430586/4edfd596-a2f3-5b47-8548-25389e0fc6a3.jpeg)


##Face LandmarksをiPadからudpで送る

face landmarksのデータをカメラの顔の画像から作成し、udpで送る側ですが、
下記のgitのXcodeのプロジェクトを使いました。（同じ作者のgitに、Android版、
Javascript版もあります。）


https://github.com/Tastenkunst/brfv4_ios_examples

その中のtrack_single_face.hppを以下のように変更します。

addr.sin_port = htons(5000);
addr.sin_addr.s_addr = inet_addr("XXX.XX.XXX.XXX");

のポート番号5000と
"XXX.XX.XXX.XXX"のIPは、ご自分の設定に合わせます。
Xcodeのプロジェクトをビルドして起動するし、自分の顔を撮影すると、68点の顔のランドマークの位置データを68x2バイトのデータとしてサーバーに送信します。

```track_single_face.hpp

#ifndef __brf__cpp__BRFCppExample_hpp
#define __brf__cpp__BRFCppExample_hpp

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

namespace brf {



class BRFCppExample: public BRFBasicCppExample {
    
    int sock;
    struct sockaddr_in addr;
    uint16_t shorts;
    uint8_t  bytes[sizeof(uint8_t)*136*2];

public: BRFCppExample() : BRFBasicCppExample()
{
  
    addr.sin_family = AF_INET;
    addr.sin_port = htons(5000);
    addr.sin_addr.s_addr = inet_addr("XXX.XX.XXX.XXX");
    //sock = socket(AF_INET, SOCK_DGRAM, 0);
}

public: void initCurrentExample(brf::BRFManager& brfManager, brf::Rectangle& resolution) {

	brf::trace("BRFv4 - basic - face tracking - track single face" + brf::to_string("\n")+
		"Detect and track one face and draw the 68 facial landmarks.");
}

public: void updateCurrentExample(brf::BRFManager& brfManager, brf::DrawingUtils& draw) {

	// In a webcam example imageData is the mirrored webcam video feed.
	// In an image example imageData is the (not mirrored) image content.

	brfManager.update();

	// Drawing the results:

	draw.clear();

	// Face detection results: a rough rectangle used to start the face tracking.

	//draw.drawRects(brfManager.getAllDetectedFaces(),    false, 1.0, 0x00a1ff, 0.5);
	//draw.drawRects(brfManager.getMergedDetectedFaces(), false, 2.0, 0xffd200, 1.0);

	// Get all faces. The default setup only tracks one face.

	std::vector< std::shared_ptr<brf::BRFFace> >& faces = brfManager.getFaces();

	for(size_t i = 0; i < faces.size(); i++) {

		brf::BRFFace& face = *faces[i];

		if(		face.state == brf::BRFState::FACE_TRACKING_START ||
				face.state == brf::BRFState::FACE_TRACKING) {

            sock = socket(AF_INET, SOCK_DGRAM, 0);
            for(int i = 0; i < 136; i++){
                shorts = (int)face.vertices[i];
                bytes[i * 2] = (uint8_t) (shorts & 0xFF);
                bytes[(i * 2) + 1] = (uint8_t) (shorts >> 8);
            }
        
            sendto(sock, bytes, sizeof(bytes), 0, (struct sockaddr *)&addr, sizeof(addr));
            close(sock);
			//draw.drawTriangles(	face.vertices, face.triangles, false, 1.0, 0x00a0ff, 0.4);
			draw.drawVertices(	face.vertices, 2.0, false, 0x00a0ff, 0.4);
           
		}
	}
}

};

}
#endif // __brf__cpp__BRFCppExample_hpp

```
###サーバーサイド
　続いてサーバーサイドです。ランドマークデータをLAN側に送り出すだけのコードです。
ポート番号、IPはご自分の環境に合わせてます。
Pythonで書くとサーバー側もシンプルに書けます。


```sendBak.py
import socket
import time

print (time.ctime(),flush=True)

host = '192.168.10.101'
port =  5001
bufsize = 512
portFace = 5000

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#sock.settimeout(0.01)
sock.bind((host,port))
#
sock2 = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock2.settimeout(30.0)
sock2.bind((host,portFace))

while True:
    global data, addr
    string,addr = sock.recvfrom(bufsize)
    print("from:",addr,time.ctime())
    while True:
        try:     
            data, (host, port) = sock2.recvfrom(bufsize)
            result = sock.sendto(data,addr)
            #if result  != 272:
            #     print(result) 
        except socket.error as e:
            #print ('Error: %s' % e)
            break



```


###OpenCV版　【コード１】
　最後に受信側です。これはPythonの動く環境ならMac、Windows、Linuxを問いません。
iOSのPythoinistaでもOKです。

```FaceLineOpenCV.py

import cv2
import socket
import numpy as np
from struct import unpack
from datetime import datetime

host = 'XXXX.com'
port =  5001
message = b'hello'

if __name__ == "__main__" :
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    #server.bind(('192.168.2.100', 5000))
    width = 600
    height = 800
    img = np.zeros((height, width, 3), np.uint8)
    server.sendto(message, (host, port))
    
    while True:
       
        data, addr = server.recvfrom(1024)
        cv2.rectangle(img, (0, 0), (600, 700), color=(255, 0, 0), thickness=-1)
        val = unpack('<'+'H'*(len(data)//2), data)
        j = 0
        for i in range(64):
            cv2.circle(img, (int(val[j]), int(val[j+1])), 4, (255, 255, 256), -1)
            j = j + 2

        cv2.imshow('camera capture', img)
        k = cv2.waitKey(1) #
        if k == 27:
            break

    cv2.destroyAllWindows()

```
###上はOpenCV版ですが、下記のようにPyQtでも動作します。


```FaceLineQt.py
import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QTimer
import socket
import numpy as np
from struct import unpack


host = 'XXXX.com'
port =  5001
bufsize = 512
message = b'hello'


class Widget(QWidget):

    def __init__(self, parent=None):

        super(Widget, self).__init__(parent)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.start(20)#更新
        #
        self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        #self.server.settimeout(0.5)
        #self.server.setblocking(False)
        #self.img = np.zeros((height, width, 3), np.uint8)
        self.server.sendto(message, (host, port))

    def paintEvent(self, event):

        
  
        data, addr = self.server.recvfrom(bufsize)
        val = unpack('<'+'H'*(len(data)//2), data)

        painter = QPainter(self)
        painter.setPen(Qt.red)
        painter.setBrush(Qt.red)
        j = 0
        for i in range(68):
                painter.drawRect(int(val[j]), int(val[j+1]), 5, 5)
                j = j + 2
        
      
def main():

    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    w.raise_()
    app.exec_()


if __name__ == '__main__':

    main()
```
コードの説明は追って追加したいと思います。

Pythonista版を追加しました。
Pythonistaの場合は高速のsceneを使います。

```FalceLinePythonista.py

from scene import *
import socket
import numpy as np
from struct import unpack

host = 'XXXX.com'
port =  5001
bufsize = 512

class FaceLine (Scene):
	def setup(self):
		
		self.landmarks = []
		for i in range(68):
				shape = ShapeNode(ui.Path.oval(0,0,5,5), 'white')
				self.landmarks.append(shape)
				self.add_child(shape)
				
		self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		#
		message = b'hello'
		self.server.sendto(message, (host, port))
		
	def update(self):	
		#
		data, addr = self.server.recvfrom(bufsize)
		val = unpack('<'+'H'*(len(data)//2), data)
		dtx = np.array(val[::2])
		dty = np.array(val[1::2])
		
		for i in range(68):
				self.landmarks[i].position = (dtx[i]+200,800-dty[i])
			

run(FaceLine())


```