Custom Model Tutorial#

Run on Google Colab View source on GitHub Download notebook

Start EVA server#

We are reusing the start server notebook for launching the EVA server.

!wget -nc "https://raw.githubusercontent.com/georgia-tech-db/eva/master/tutorials/00-start-eva-server.ipynb"
%run 00-start-eva-server.ipynb
cursor = connect_to_server()
File '00-start-eva-server.ipynb' already there; not retrieving.

[  -z "$(lsof -ti:5432)" ] || kill -9 $(lsof -ti:5432)
nohup eva_server > eva.log 2>&1 &

WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Note: you may need to restart the kernel to use updated packages.

Download custom user-defined function (UDF), model, and video#

# Download UDF
!wget -nc https://www.dropbox.com/s/lharq14izp08bfz/gender.py

# Download built-in Face Detector
!wget -nc https://raw.githubusercontent.com/georgia-tech-db/eva/master/eva/udfs/face_detector.py

# Download models
!wget -nc https://www.dropbox.com/s/0y291evpqdfmv2z/gender.pth

# Download videos
!wget -nc https://www.dropbox.com/s/f5447euuuis1vdy/short.mp4
File 'gender.py' already there; not retrieving.

File 'face_detector.py' already there; not retrieving.

File 'gender.pth' already there; not retrieving.

File 'short.mp4' already there; not retrieving.

Load video for analysis#

cursor.execute("DROP TABLE TIKTOK;")
response = cursor.fetch_all()
print(response)
cursor.execute("LOAD VIDEO 'short.mp4' INTO TIKTOK;")
response = cursor.fetch_all()
print(response)
cursor.execute("""SELECT id FROM TIKTOK WHERE id < 5""")
response = cursor.fetch_all()
print(response)
@status: ResponseStatus.FAIL
@batch: 
 None
@error: Table: TIKTOK does not exist
@status: ResponseStatus.SUCCESS
@batch: 
                            0
0  Number of loaded VIDEO: 1
@query_time: 0.09735049899973092
@status: ResponseStatus.SUCCESS
@batch: 
    tiktok.id
0          0
1          1
2          2
3          3
4          4
@query_time: 0.08554376400024921

Visualize Video#

from IPython.display import Video
Video("short.mp4", embed=True)

Create GenderCNN and FaceDetector UDFs#

cursor.execute("""DROP UDF GenderCNN;""")
response = cursor.fetch_all()
print(response)

cursor.execute("""CREATE UDF IF NOT EXISTS 
                  GenderCNN
                  INPUT (data NDARRAY UINT8(3, 224, 224)) 
                  OUTPUT (label TEXT(10)) 
                  TYPE  Classification 
                  IMPL 'gender.py';
        """)
response = cursor.fetch_all()
print(response)

cursor.execute("""CREATE UDF IF NOT EXISTS
                  FaceDetector
                  INPUT  (frame NDARRAY UINT8(3, ANYDIM, ANYDIM))
                  OUTPUT (bboxes NDARRAY FLOAT32(ANYDIM, 4),
                          scores NDARRAY FLOAT32(ANYDIM))
                  TYPE  FaceDetection
                  IMPL  'face_detector.py';
        """)
response = cursor.fetch_all()
print(response)
@status: ResponseStatus.FAIL
@batch: 
 None
@error: UDF GenderCNN does not exist, therefore cannot be dropped.
@status: ResponseStatus.SUCCESS
@batch: 
                                                    0
0  UDF GenderCNN successfully added to the database.
@query_time: 11.116637402999913
@status: ResponseStatus.SUCCESS
@batch: 
                                                  0
0  UDF FaceDetector already exists, nothing added.
@query_time: 0.011710384999787493

Run Face Detector on video#

cursor.execute("""SELECT id, FaceDetector(data).bboxes 
                  FROM TIKTOK WHERE id < 10""")
response = cursor.fetch_all()
print(response)
@status: ResponseStatus.SUCCESS
@batch: 
    tiktok.id  facedetector.bboxes
0          0  [[ 90 208 281 457]]
1          1  [[ 91 208 281 457]]
2          2  [[ 90 207 283 457]]
3          3  [[ 90 207 284 458]]
4          4  [[ 90 208 282 460]]
5          5  [[ 89 209 283 460]]
6          6  [[ 88 208 283 461]]
7          7  [[ 89 206 282 464]]
8          8  [[ 90 224 281 469]]
9          9  [[ 94 234 279 468]]
@query_time: 0.3409588349995829

Composing UDFs in a query#

Detect gender of the faces detected in the video by composing a set of UDFs (GenderCNN, FaceDetector, and Crop)

cursor.execute("""SELECT id, bbox, GenderCNN(Crop(data, bbox)) 
                  FROM TIKTOK JOIN LATERAL  UNNEST(FaceDetector(data)) AS Face(bbox, conf)  
                  WHERE id < 50;""")
response = cursor.fetch_all()
print(response)
@status: ResponseStatus.SUCCESS
@batch: 
     tiktok.id             Face.bbox gendercnn.label
0           0   [90, 208, 281, 457]          female
1           1   [91, 208, 281, 457]          female
2           2   [90, 207, 283, 457]          female
3           3   [90, 207, 284, 458]          female
4           4   [90, 208, 282, 460]          female
5           5   [89, 209, 283, 460]          female
6           6   [88, 208, 283, 461]          female
7           7   [89, 206, 282, 464]          female
8           8   [90, 224, 281, 469]          female
9           9   [94, 234, 279, 468]          female
10         10   [94, 231, 280, 469]          female
11         11   [93, 232, 278, 470]          female
12         12   [94, 232, 280, 469]          female
13         13   [94, 226, 281, 468]          female
14         14   [93, 225, 281, 469]          female
15         15   [92, 222, 281, 471]          female
16         16   [91, 220, 282, 472]          female
17         17   [91, 219, 282, 472]          female
18         18   [91, 224, 280, 471]          female
19         19   [91, 229, 278, 470]          female
20         20   [90, 228, 277, 469]          female
21         21   [86, 220, 278, 474]          female
22         22   [86, 220, 277, 473]          female
23         23   [87, 223, 276, 472]          female
24         24   [85, 221, 276, 473]          female
25         25   [86, 223, 276, 473]          female
26         26   [87, 222, 277, 472]          female
27         27   [87, 222, 277, 472]          female
28         28   [89, 222, 278, 471]          female
29         29   [92, 221, 279, 468]          female
30         30   [95, 222, 280, 468]          female
31         31   [97, 222, 282, 470]          female
32         32   [98, 219, 286, 472]          female
33         33   [99, 223, 286, 472]          female
34         34   [98, 224, 287, 471]          female
35         35   [99, 223, 288, 471]          female
36         36  [102, 223, 288, 471]          female
37         37  [101, 223, 290, 472]          female
38         38  [100, 213, 292, 472]          female
39         39   [98, 210, 293, 473]          female
40         40   [98, 211, 292, 472]          female
41         41   [97, 211, 291, 472]          female
42         42   [96, 211, 291, 472]          female
43         43   [96, 212, 290, 471]          female
44         44   [96, 212, 289, 470]          female
45         45   [94, 213, 289, 470]          female
46         46   [94, 213, 289, 470]          female
47         47   [94, 214, 288, 470]          female
48         48   [95, 217, 285, 470]          female
49         49   [95, 221, 285, 471]          female
@query_time: 2.6860659730000407

Visualize Output#

import cv2
from matplotlib import pyplot as plt

def annotate_video(detections, input_video_path, output_video_path):
    color=(207, 248, 64)
    thickness=4

    vcap = cv2.VideoCapture(input_video_path)
    width = int(vcap.get(3))
    height = int(vcap.get(4))
    fps = vcap.get(5)
    fourcc = cv2.VideoWriter_fourcc(*'MP4V') #codec
    video=cv2.VideoWriter(output_video_path, fourcc, fps, (width,height))

    frame_id = 0
    # Capture frame-by-frame
    ret, frame = vcap.read()  # ret = 1 if the video is captured; frame is the image

    while ret:
        df = detections
        df = df[['Face.bbox', 'gendercnn.label']][df['tiktok.id'] == frame_id]
        
        if df.size:
            for bbox, label in df.values:
                x1, y1, x2, y2 = bbox
                x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
                frame=cv2.rectangle(frame, (x1, y1), (x2, y2), color, thickness) # object bbox
                cv2.putText(frame, str(label), (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, thickness-1) # object label
            
            video.write(frame)
            # Show every fifth frame
            if frame_id % 5 == 0:
                plt.imshow(frame)
                plt.show()

        if frame_id == 50:
            return

        frame_id+=1
        ret, frame = vcap.read()

    video.release()
    vcap.release()
#!pip install ipywidgets
from ipywidgets import Video
input_path = 'short.mp4'
output_path = 'annotated_short.mp4'

dataframe = response.batch.frames
annotate_video(dataframe, input_path, output_path)
OpenCV: FFMPEG: tag 0x5634504d/'MP4V' is not supported with codec id 12 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x7634706d/'mp4v'
../../_images/e78f4c48bdbb6fd686d6bbfa32520519a2df3bd7a5d4c63c35b53318c29b90d3.png ../../_images/586c7b103200559fa76f3718e0d83bc785be00e76048c39357d15b2e20a57d13.png ../../_images/9d9dafe7e354bdd2186bb2b0cc7995164a7a9f3ed0031d33e0165729d5fe2ac8.png ../../_images/1ec83780299af661b836e7f67f6bab530696d1d3b1148416ec2aa1b287d2e345.png ../../_images/cc6dfff5f0e79363bf23f88e718819411ae00543f129219cd6294113f1d07062.png ../../_images/ffdf1cb67a492284fe712059fe56b6696c3d4c378688f750526695b578533cdc.png ../../_images/b9cc94ffc7d110d2a0acbf7e0da6999c5c6a47cbdc8e6f387c4aecfd40556070.png ../../_images/ab60550a621337216d907a0561902ebae0d788cab79fee3e7eefe04d90b76830.png ../../_images/b396ee2dfdb7d6664a4262aad8089ce58bc77f7bdaffd579ed3f977d308212c0.png ../../_images/bc9a268728399be4b80c4313c184026eb6fd918a62d2459b2ce3db90d5b44213.png