Skip to content

Commit 5be60bc

Browse files
authored
Merge pull request #49 from gngdb/master
Add script to process video files
2 parents 4c2a0d5 + c2b3df5 commit 5be60bc

6 files changed

+160
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ hiddenlayer/
55
.ipynb_checkpoints/
66
__pycache__
77
*.prototxt
8+
videos/

README.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ to run a demo with a feed from your webcam or run
5050

5151
python demo.py
5252

53-
to use a image from the images folder.
53+
to use a image from the images folder or run
54+
55+
python demo_video.py <video-file>
56+
57+
to process a video file (requires [ffmpeg-python][ffmpeg]).
58+
59+
[ffmpeg]: https://pypi.org/project/ffmpeg-python/
5460

5561
### Todo list
5662
- [x] convert caffemodel to pytorch.
@@ -73,6 +79,18 @@ to use a image from the images folder.
7379
#### Body + Hand
7480
![](images/demo_preview.png)
7581

82+
#### Video Body
83+
84+
![](images/kc-e129SBb4-sample.processed.gif)
85+
86+
Attribution: [this video](https://www.youtube.com/watch?v=kc-e129SBb4).
87+
88+
#### Video Hand
89+
90+
![](images/yOAmYSW3WyU-sample.small.processed.gif)
91+
92+
Attribution: [this video](https://www.youtube.com/watch?v=yOAmYSW3WyU).
93+
7694
### Citation
7795
Please cite these papers in your publications if it helps your research (the face keypoint detector was trained using the procedure described in [Simon et al. 2017] for hands):
7896

demo_video.py

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import copy
2+
import numpy as np
3+
import cv2
4+
from glob import glob
5+
import os
6+
import argparse
7+
import json
8+
9+
# video file processing setup
10+
# from: https://stackoverflow.com/a/61927951
11+
import argparse
12+
import subprocess
13+
import sys
14+
from pathlib import Path
15+
from typing import NamedTuple
16+
17+
18+
class FFProbeResult(NamedTuple):
19+
return_code: int
20+
json: str
21+
error: str
22+
23+
24+
def ffprobe(file_path) -> FFProbeResult:
25+
command_array = ["ffprobe",
26+
"-v", "quiet",
27+
"-print_format", "json",
28+
"-show_format",
29+
"-show_streams",
30+
file_path]
31+
result = subprocess.run(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
32+
return FFProbeResult(return_code=result.returncode,
33+
json=result.stdout,
34+
error=result.stderr)
35+
36+
37+
# openpose setup
38+
from src import model
39+
from src import util
40+
from src.body import Body
41+
from src.hand import Hand
42+
43+
body_estimation = Body('model/body_pose_model.pth')
44+
hand_estimation = Hand('model/hand_pose_model.pth')
45+
46+
def process_frame(frame, body=True, hands=True):
47+
canvas = copy.deepcopy(frame)
48+
if body:
49+
candidate, subset = body_estimation(frame)
50+
canvas = util.draw_bodypose(canvas, candidate, subset)
51+
if hands:
52+
hands_list = util.handDetect(candidate, subset, frame)
53+
all_hand_peaks = []
54+
for x, y, w, is_left in hands_list:
55+
peaks = hand_estimation(frame[y:y+w, x:x+w, :])
56+
peaks[:, 0] = np.where(peaks[:, 0]==0, peaks[:, 0], peaks[:, 0]+x)
57+
peaks[:, 1] = np.where(peaks[:, 1]==0, peaks[:, 1], peaks[:, 1]+y)
58+
all_hand_peaks.append(peaks)
59+
canvas = util.draw_handpose(canvas, all_hand_peaks)
60+
return canvas
61+
62+
# writing video with ffmpeg because cv2 writer failed
63+
# https://stackoverflow.com/questions/61036822/opencv-videowriter-produces-cant-find-starting-number-error
64+
import ffmpeg
65+
66+
# open specified video
67+
parser = argparse.ArgumentParser(
68+
description="Process a video annotating poses detected.")
69+
parser.add_argument('file', type=str, help='Video file location to process.')
70+
parser.add_argument('--no_hands', action='store_true', help='No hand pose')
71+
parser.add_argument('--no_body', action='store_true', help='No body pose')
72+
args = parser.parse_args()
73+
video_file = args.file
74+
cap = cv2.VideoCapture(video_file)
75+
76+
# get video file info
77+
ffprobe_result = ffprobe(args.file)
78+
info = json.loads(ffprobe_result.json)
79+
videoinfo = [i for i in info["streams"] if i["codec_type"] == "video"][0]
80+
input_fps = videoinfo["avg_frame_rate"]
81+
# input_fps = float(input_fps[0])/float(input_fps[1])
82+
input_pix_fmt = videoinfo["pix_fmt"]
83+
input_vcodec = videoinfo["codec_name"]
84+
85+
# define a writer object to write to a movidified file
86+
postfix = info["format"]["format_name"].split(",")[0]
87+
output_file = ".".join(video_file.split(".")[:-1])+".processed." + postfix
88+
89+
90+
class Writer():
91+
def __init__(self, output_file, input_fps, input_framesize, input_pix_fmt,
92+
input_vcodec):
93+
if os.path.exists(output_file):
94+
os.remove(output_file)
95+
self.ff_proc = (
96+
ffmpeg
97+
.input('pipe:',
98+
format='rawvideo',
99+
pix_fmt="bgr24",
100+
s='%sx%s'%(input_framesize[1],input_framesize[0]),
101+
r=input_fps)
102+
.output(output_file, pix_fmt=input_pix_fmt, vcodec=input_vcodec)
103+
.overwrite_output()
104+
.run_async(pipe_stdin=True)
105+
)
106+
107+
def __call__(self, frame):
108+
self.ff_proc.stdin.write(frame.tobytes())
109+
110+
def close(self):
111+
self.ff_proc.stdin.close()
112+
self.ff_proc.wait()
113+
114+
115+
writer = None
116+
while(cap.isOpened()):
117+
ret, frame = cap.read()
118+
if frame is None:
119+
break
120+
121+
posed_frame = process_frame(frame, body=not args.no_body,
122+
hands=not args.no_hands)
123+
124+
if writer is None:
125+
input_framesize = posed_frame.shape[:2]
126+
writer = Writer(output_file, input_fps, input_framesize, input_pix_fmt,
127+
input_vcodec)
128+
129+
cv2.imshow('frame', posed_frame)
130+
131+
# write the frame
132+
writer(posed_frame)
133+
134+
if cv2.waitKey(1) & 0xFF == ord('q'):
135+
break
136+
137+
cap.release()
138+
writer.close()
139+
cv2.destroyAllWindows()
10.7 MB
Loading
5.7 MB
Loading

src/body.py

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def __call__(self, oriImg):
116116
for j in range(nB):
117117
vec = np.subtract(candB[j][:2], candA[i][:2])
118118
norm = math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
119+
norm = max(0.001, norm)
119120
vec = np.divide(vec, norm)
120121

121122
startend = list(zip(np.linspace(candA[i][0], candB[j][0], num=mid_num), \

0 commit comments

Comments
 (0)