├── .gitignore ├── gif1.gif ├── gif2.gif ├── gif3.gif ├── gif4.gif ├── gif5.gif ├── gif6.gif ├── conf.py ├── README.md ├── read_skeleton.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/** 2 | *.skeleton 3 | *.avi 4 | -------------------------------------------------------------------------------- /gif1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif1.gif -------------------------------------------------------------------------------- /gif2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif2.gif -------------------------------------------------------------------------------- /gif3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif3.gif -------------------------------------------------------------------------------- /gif4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif4.gif -------------------------------------------------------------------------------- /gif5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif5.gif -------------------------------------------------------------------------------- /gif6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ugenteraan/NTU-RGB-Skeleton-Python/HEAD/gif6.gif -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | parser = argparse.ArgumentParser() 4 | #args 5 | parser.add_argument('--skel', required=True, help='Input path to the skeleton file.', type=str) 6 | parser.add_argument('--video', help='Input path to the corresponding RGB video file.', type=str) 7 | parser.add_argument('--frame_height', default=720, help='The height of the frame displayed.', type=int) 8 | parser.add_argument('--frame_width', default=1280, help='The width of the frame displayed.', type=int) 9 | parser.add_argument('--subject_ID', action='store_true', help='Use this arg to include the ID of the subject.') 10 | parser.add_argument('--save', action='store_true', help='Use this arg to save the video on disk as output.avi.') 11 | args = parser.parse_args() 12 | 13 | #skeleton line between points based on the paper 14 | skel_line_conf = [ 15 | [1,13], 16 | [1,17], 17 | [1,2], 18 | [2,21], 19 | [21,5], 20 | [21,9], 21 | [21,3], 22 | [3,4], 23 | [5,6], 24 | [6,7], 25 | [7,8], 26 | [9,10], 27 | [10,11], 28 | [11,12], 29 | [13,14], 30 | [14,15], 31 | [15,16], 32 | [17,18], 33 | [18,19], 34 | [19,20] 35 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NTU-RGB-Skeleton-Python 2 | 3 | Functionalities: 4 | 5 | 1. Read the .skeleton file provided in NTU RGB skeleton dataset and get the x and y coordinates of the skeleton points in each frame for each subjects. Usage ```python read_skeleton.py --skel=PATH TO .SKELETON FILE``` 6 | 2. Read both the .skeleton file and the corresponding RGB video file provided in NTU RGB skeleton dataset to draw the skeleton joints as defined in their research paper here : [http://openaccess.thecvf.com/content_cvpr_2016/papers/Shahroudy_NTU_RGBD_A_CVPR_2016_paper.pdf](http://openaccess.thecvf.com/content_cvpr_2016/papers/Shahroudy_NTU_RGBD_A_CVPR_2016_paper.pdf) Usage : ```python read_skeleton.py --skel=PATH TO .SKELETON FILE --video=PATH TO .AVI FILE``` 7 | 3. Optional arguments : 8 | a)```python read_skeleton.py --skel=PATH TO .SKELETON FILE --video=PATH TO .AVI FILE --frame_height=FRAME HEIGHT --frame_width=FRAME WIDTH``` to control the height and width of the displayed frame. 9 | b) ```python read_skeleton.py --skel=PATH TO .SKELETON FILE --subject_ID``` to get each subject's ID from each frame. 10 | c) ```python read_skeleton.py --skel=PATH TO .SKELETON FILE --video=PATH TO .AVI FILE --save``` to save the video on the disk. 11 | 12 | 13 | **Sample Outputs** 14 | 15 | ![Output 1](gif1.gif) 16 | ![Output 2](gif2.gif) 17 | ![Output 3](gif3.gif) 18 | ![Output 4](gif4.gif) 19 | ![Output 5](gif5.gif) 20 | ![Output 6](gif6.gif) 21 | 22 | 23 | **License** 24 | ------- 25 | **MIT** 26 | 27 | -------------------------------------------------------------------------------- /read_skeleton.py: -------------------------------------------------------------------------------- 1 | from conf import args as args 2 | import utils 3 | import cv2 4 | import numpy as np 5 | 6 | 7 | def read_skeleton(): 8 | 9 | #open the skeleton file 10 | file = open(args.skel, 'r') 11 | 12 | read_lines = [] 13 | 14 | for line in file: 15 | #strip away the '\n' characters and split the values by the spaces. 16 | #append each line to "read_lines" list. 17 | read_lines.append(line.strip('\n').split(' ')) 18 | 19 | 20 | num_of_frames = int(read_lines[0][0]) #get the number of frames 21 | 22 | 23 | skeleton_data = None 24 | 25 | if args.subject_ID : #to include the information about the subject. 26 | 27 | skeleton_data = utils.care_subject(num_of_frames, read_lines) #read the information from the list 28 | 29 | else: 30 | 31 | skeleton_data = utils.no_subject(num_of_frames, read_lines) #read the information from the list 32 | 33 | 34 | 35 | #if the video argument is present 36 | if args.video is not None: 37 | 38 | cap = cv2.VideoCapture(args.video) 39 | 40 | out = None 41 | 42 | #to save video 43 | if args.save: 44 | out = cv2.VideoWriter('output.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 30, (args.frame_width,args.frame_height)) 45 | 46 | frame_idx = 1 #frame number in dictionary starts from 1 47 | 48 | while cap.isOpened(): 49 | 50 | ret, frame = cap.read() 51 | 52 | if ret is True: 53 | 54 | skel_info = skeleton_data[str(frame_idx)] 55 | num_subjects = len(skel_info) 56 | 57 | for sub in range(num_subjects): 58 | 59 | utils.draw_skeleton(skel_info[sub], frame) #draws the skeleton on the frame 60 | 61 | frame = cv2.resize(frame, (args.frame_width, args.frame_height)) 62 | 63 | #to save video 64 | if args.save: 65 | out.write(frame) 66 | 67 | cv2.imshow("Frame", frame) 68 | 69 | if cv2.waitKey(25) & 0xff == ord('q'): 70 | break 71 | 72 | frame_idx += 1 73 | else: 74 | 75 | cap.release() 76 | if args.save: 77 | out.release() 78 | cv2.destroyAllWindows() 79 | 80 | 81 | return skeleton_data 82 | 83 | 84 | if __name__=='__main__': 85 | 86 | _ = read_skeleton() 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import conf 4 | 5 | args = conf.args 6 | 7 | def care_subject(num_of_frames, read_lines): 8 | ''' 9 | Reads the skeleton file line by line and returns a dictionary with the following format : 10 | { 11 | 'frame 1' : {'subject_id_1' : np.array of the 1-st subject's skeletal position on frame 1 (25,2), 12 | . , 13 | . , 14 | . , 15 | 'subject_id_m : np.array of the m-th subject's skeletal position on frame 1 (25,2)}, 16 | . , 17 | . , 18 | . , 19 | 'frame n' : {'subject_id 1' : np.array of the 1-st subject's skeletal position on frame n (25,2), 20 | . , 21 | . , 22 | . , 23 | 'subject_id m': np array of the m-th subject's skeletal position on frame n (25,2)} 24 | 25 | } 26 | 27 | Parameter 28 | --------- 29 | num_of_frames : number of frames in the video that the skeletal data appears. | integer 30 | read_lines : list consisting of values from the .skeleton file line by line. | list 31 | 32 | ''' 33 | 34 | frame_skel = {} 35 | 36 | idx = 0 37 | for frame in range(1, num_of_frames+1): # +1 since the loop iterates only till num_of_frames - 1 38 | idx += 1 #next line 39 | num_subjects = int(read_lines[idx][0]) 40 | 41 | temp_sub = {} 42 | 43 | for sub in range(num_subjects): 44 | idx += 1 #next line 45 | sub_id = read_lines[idx][0] 46 | idx += 1 #next line 47 | 48 | temp_skel = [] 49 | for skeleton in range(int(read_lines[idx][0])): 50 | 51 | idx += 1 #next line 52 | jointx = float(read_lines[idx][5]) #5th index is where the x-coordinate of the skeleton is stored. 53 | jointy = float(read_lines[idx][6]) #6th index is where the y-coordinate of the skeleton is stored. 54 | jointz = float(read_lines[idx][7]) #7th index is where the z-coordinate of the skeleton is stored. 55 | 56 | temp_skel.append((jointx, jointy, jointz)) 57 | 58 | #use the subject id as the key to the dictionary. 59 | temp_sub[sub_id] = np.asarray(temp_skel) #store the list as np array. 60 | 61 | frame_skel[str(frame)] = temp_sub 62 | 63 | return frame_skel 64 | 65 | 66 | 67 | 68 | def no_subject(num_of_frames, read_lines): 69 | ''' 70 | Reads the skeleton file line by line and returns a dictionary with the following format : 71 | { 72 | 'frame 1' : [[np.array of the 1-st subject's skeletal position on frame 1 (25,2)], 73 | . , 74 | . , 75 | . , 76 | [np.array of the m-th subject's skeletal position on frame 1 (25,2)], (m, 25, 2)], 77 | . , 78 | . , 79 | . , 80 | 'frame n' : [[np.array of the 1-st subject's skeletal position on frame n (25,2)], 81 | . , 82 | . , 83 | . , 84 | [np array of the m-th subject's skeletal position on frame n (25,2)], (m, 25, 2)] 85 | 86 | } 87 | In this format, each subject's skeletal position information can be extracted using index value instead of specifying the 88 | subject's ID like in previous format. 89 | 90 | Parameter 91 | --------- 92 | num_of_frames : number of frames in the video that the skeletal data appears. | integer 93 | read_lines : list consisting of values from the .skeleton file line by line. | list 94 | 95 | ''' 96 | 97 | frame_skel = {} 98 | 99 | idx = 0 100 | for frame in range(1, num_of_frames+1): 101 | idx += 1 #next line 102 | num_subjects = int(read_lines[idx][0]) 103 | 104 | temp_sub = [] 105 | 106 | for sub in range(num_subjects): 107 | idx += 1 #next line 108 | sub_id = read_lines[idx][0] 109 | idx += 1 #next line 110 | 111 | temp_skel = [] #a list instead of a dictionary 112 | for skeleton in range(int(read_lines[idx][0])): 113 | 114 | idx += 1 #next line 115 | jointx = float(read_lines[idx][5]) #5th index is where the x-coordinate of the skeleton is stored. 116 | jointy = float(read_lines[idx][6]) #6th index is where the y-coordinate of the skeleton is stored. 117 | jointz = float(read_lines[idx][7]) #7th index is where the z-coordinate of the skeleton is stored. 118 | 119 | temp_skel.append((jointx, jointy, jointz)) 120 | 121 | #must convert to float32 since cv2 circle does not accept float 64 data type. 122 | temp_sub.append(np.asarray(temp_skel).astype('float32')) #append each subject's skeletal information into the list. 123 | 124 | frame_skel[str(frame)] = temp_sub 125 | 126 | return frame_skel 127 | 128 | 129 | 130 | def draw_skeleton(skeleton_data, frame): 131 | 132 | #Even though our points are 3 Dimensional, we are only plotting the x and y coordinate of the skeletons 133 | 134 | for idx in range(len(skeleton_data)): 135 | 136 | x = skeleton_data[idx][0] 137 | y = skeleton_data[idx][1] 138 | 139 | 140 | #draw cirlces on the points 141 | cv2.circle(frame, (x, y), 5, (0,255,0), -1) 142 | 143 | #to draw the lines joining the points 144 | for idx in range(len(conf.skel_line_conf)): 145 | 146 | #get the index of the points from the skeleton configuration list. -1 since index position starts from 0. 147 | first_xy = conf.skel_line_conf[idx][0] - 1 148 | second_xy = conf.skel_line_conf[idx][1] - 1 149 | 150 | x1 = skeleton_data[first_xy][0] 151 | y1 = skeleton_data[first_xy][1] 152 | 153 | x2 = skeleton_data[second_xy][0] 154 | y2 = skeleton_data[second_xy][1] 155 | 156 | #use the index to get the x-y coordinate of the first point and the second point 157 | cv2.line(frame, (x1, y1), (x2, y2), (0,255,0), 2) 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | --------------------------------------------------------------------------------