def save(self): # flush cached game if len(self.cur_game.hits) > 0: self.games.append(self.cur_game) self.process_game_data() self.save_highlights() output_path = '%s/%s.json' % (scv.config['path']['output'], self.video_id) with open(output_path, 'w') as fp: json.dump([game.__dict__ for game in self.games], fp) scv.log('Saved JSON data to %s' % output_path)
def save_highlights(self): i = 0 fps = int(self.video.get(5)) fourcc = cv2.VideoWriter_fourcc(*'XVID') size = (int(self.video.get(3)), int(self.video.get(4))) videos = [] for game in self.games: # save kills for death in game.deaths: patient = death[1] agent = int( not patient) # this won't support more than two players i += 1 video_path = '%s/ko-%s-%s-%s-%d.avi' % ( scv.config['path']['output'], game.fighters[agent], game.fighters[patient], self.video_id, i) videos.append((video_path, death[0] - 5000, death[0] + 1000)) # save combos for combo in game.combos: patient = combo[2] agent = int( not patient) # this won't support more than two players i += 1 video_path = '%s/combo-%s-%s-%s-%d.avi' % ( scv.config['path']['output'], game.fighters[agent], game.fighters[patient], self.video_id, i) videos.append((video_path, combo[0] - 1000, combo[1] + 250)) # save videos for i, video in enumerate(videos): scv.log(('Saving video', i, video[0])) writer = cv2.VideoWriter(video[0], fourcc, fps, size) self.video.set(0, video[1]) while self.video.get(0) < video[2]: ret, frame = self.video.read() writer.write(frame) writer.release()
def detect_state_oog(self, src_im): im_gray = cv2.cvtColor(src_im, cv2.COLOR_BGR2GRAY) matches, conf, scale = self.match_template(im_gray, tpl_ssel, threshold=0.7) if matches: im_h, im_w = im_gray.shape im_cropped = im_gray[0:im_h, 0:int(im_w / 2)] stage, _res, conf = knn_stages.identify(im_cropped) self.cur_game.stage = stage scv.log((self.cur_frame, 'Entering stage', stage), scv.DEBUG_EVENTS) else: # TODO: Possibly do something with character selection page? # Can potentially extract player tags and costumes # matches, conf, scale = self.match_template(src_im, tpl_csel) # if matches: # scv.log(('Found charselect template', matches, conf, scale), scv.DEBUG_DETECT) pass return scv.State.unknown
def process_video(self): if not self.video.isOpened(): raise ValueError('Video is not loaded') while self.video.isOpened(): ret, frame = self.video.read() if not ret: break # Update position counters self.cur_time = int(self.video.get(0)) self.cur_frame = int(self.video.get(1)) progress = int((self.cur_frame / self.total_frames) * 100) if progress != self.cur_progress: self.cur_progress = progress scv.log('Progress: %d%%' % progress) new_state = self.detect_state(frame) if self.state != new_state: scv.log(('State changed from %s to %s' % (self.state, new_state), self.cur_time), scv.DEBUG_EVENTS) self.state = new_state # Show video if debug flag set. # Navigate video with arrows, space pauses, Q quits if scv.debug_level & scv.DEBUG_VIDEO: if self.paused: scv.log(('Frame %d: %0.2fs' % (self.cur_frame, float(self.cur_time) / 1000), )) cv2.imshow('Smash4CV', frame) key = cv2.waitKey(0 if self.paused else 1) if key == 113: # q(uit) break elif key == 32: # space (pause/unpause) self.paused = not self.paused elif key == 65361: # left arrow self.paused = True self.video.set(1, self.cur_frame - 2) elif key == 65363: # right arrow self.paused = True
def detect_state(self, src_im): state = scv.State.unknown unknown_regions = 0 if self.state == scv.State.ingame: # Continued in-game state for i, (x, y) in enumerate(self.regions): # Extract ROI x1 = int(x - (self.template_scale * 73)) x2 = int(x + (self.template_scale * 75)) y1 = int(y - (self.template_scale * 10)) y2 = int(y + (self.template_scale * 61)) region_im = src_im[y1:y2, x1:x2] region_im = cv2.resize(region_im, (100, 50)) # Detect name if no good match yet if i not in self.cur_game.fighters: name_im = region_im[40:50, 0:67] name_im = cv2.cvtColor(name_im, cv2.COLOR_BGR2GRAY) cv2.imshow('name', name_im) name, _res, name_conf = knn_names.identify(name_im) if name_conf <= 2000: scv.log(('Detected fighter', i, name), scv.DEBUG_DETECT) self.cur_game.fighters[i] = name # Detect digits and compare digits_im = region_im[10:40, 30:100] digits = self.read_digits(digits_im) # Death must be detected continously for 15 frames to trigger if digits == scv.DETECT_DEAD: self.death_cache[i] -= 1 if self.death_cache[i] > -15: digits = scv.DETECT_UNKNOWN # Add event when changed if digits > scv.DETECT_UNKNOWN and digits != self.digit_cache[ i]: if digits != scv.DETECT_DEAD: self.death_cache[i] = 0 self.cur_game.hits.append((self.cur_time, i, digits)) scv.log( (self.cur_frame, self.cur_game.fighters[i] if i in self.cur_game.fighters else 'Fighter %d/%d' % (i, len(self.regions)), '%d%%' % digits), scv.DEBUG_EVENTS) else: self.cur_game.deaths.append((self.cur_time, i)) scv.log( (self.cur_frame, self.cur_game.fighters[i] if i in self.cur_game.fighters else 'Fighter %d/%d' % (i, len(self.regions)), 'died'), scv.DEBUG_EVENTS) self.digit_cache[i] = digits if digits == scv.DETECT_UNKNOWN: unknown_regions += 1 # Show ROIs while debugging if scv.debug_level & scv.DEBUG_VIDEO: cv2.circle(src_im, (x, y), 4, (0, 255, 0), 2) cv2.rectangle(src_im, (x1, y1), (x2, y2), (0, 0, 255), 1) state = scv.State.ingame if state != scv.State.ingame or unknown_regions == len(self.regions): im_gray = cv2.cvtColor(src_im, cv2.COLOR_BGR2GRAY) im_h, im_w = im_gray.shape is_loading = self.detect_state_loading(im_gray) if is_loading: if self.state != scv.State.loading: # Entering loading state if self.state == scv.State.ingame: # Save any previously detected game if len(self.cur_game.hits) > 0: self.games.append(self.cur_game) # Reset game state self.cur_game = SmashGame(start=self.cur_time) # Attempt to detect out-of-game state data from recent frame self.video.set(1, self.cur_frame - 21) ret, state_im = self.video.read() detected_state = self.detect_state_oog(state_im) # Reset video to original position self.video.set(1, self.cur_frame + 1) else: # In the middle of loading state pass state = scv.State.loading elif self.state == scv.State.loading or self.state == scv.State.unknown: # Exiting loading state # Attempt to detect an in-game state in coming frame self.video.set(1, self.cur_frame + 29) ret, state_im = self.video.read() regions, scale = self.detect_state_ig(state_im) if regions: # Initialize in-game state self.regions = regions self.template_scale = scale self.digit_cache = [0] * len(regions) self.death_cache = [0] * len(regions) state = scv.State.ingame # Reset video to original position self.video.set(1, self.cur_frame + 1) return state