def _apply_compute_error(cls, instance, poll_interval=8): from vfs.gop import Gop from vfs.videoio import compute_mse current_interval, maximum_interval = poll_interval, 256 while instance.running: leaf_gop_id, root_gop_id = instance.database.execute( 'WITH RECURSIVE' ' parent(leaf_id, id, parent_id, mse) AS ' ' (SELECT id, id, parent_id, mse FROM gops ' ' WHERE id = (SELECT id FROM gops WHERE mse IS NULL LIMIT 1)' ' UNION ALL ' ' SELECT parent.leaf_id, gops.id, gops.parent_id, gops.mse FROM parent ' ' INNER JOIN gops ON parent.parent_id = gops.id)' 'SELECT leaf_id, id FROM parent ' 'WHERE parent_id IS NULL AND mse IS NOT NULL ' '').fetchone() or [None, None] if leaf_gop_id is not None and root_gop_id is not None: mse = compute_mse(Gop.get(leaf_gop_id), Gop.get(root_gop_id)) instance.database.execute( 'UPDATE gops SET mse = ?, estimated_mse = ? WHERE id = ?', (mse, mse, leaf_gop_id)) logging.info( "ErrorWorker: Computed precise error for GOP %d", leaf_gop_id) current_interval = poll_interval else: current_interval = min(current_interval * 2, maximum_interval) cls._sleep(instance, current_interval)
def evict_gop(database, gop_id, cut): from vfs.gop import Gop from vfs.physicalvideo import PhysicalVideo evict_gop = Gop.get(gop_id) gop_count = database.execute( "SELECT COUNT(*) FROM gops WHERE physical_id = ?", evict_gop.physical_id).fetchone()[0] if gop_count == 1: PhysicalVideo.delete(evict_gop.video()) #logging.info('EvictWorker: Evicting GOP %d (covered by %d; last GOP, also deleting physical video)', gop_id, cover_gop_id) logging.info( 'EvictWorker: Evicting GOP %d (last GOP, also deleting physical video)', gop_id) elif cut: new_physical_video = PhysicalVideo.add(evict_gop.video().logical(), evict_gop.video().height, evict_gop.video().width, evict_gop.video().codec) database.executebatch([ 'UPDATE gops SET physical_id = {} WHERE physical_id = {} AND start_time < {}' .format(new_physical_video.id, evict_gop.physical_id, evict_gop.start_time), 'DELETE FROM gops WHERE id = {}'.format(gop_id) ]) Gop.delete(evict_gop) #logging.info('EvictWorker: Evicting GOP %d (covered by %d; middle GOP, cutting physical video)', gop_id, cover_gop_id) logging.info( 'EvictWorker: Evicting GOP %d (middle GOP, cutting physical video)', gop_id) else: #logging.info('EvictWorker: Evicting GOP %d (covered by %d; endpoint GOP)', gop_id, cover_gop_id) logging.info('EvictWorker: Evicting GOP %d (endpoint GOP)', gop_id) Gop.delete(evict_gop)
def load(cls, logical_video, filename, resolution=None, codec=None, fps=None): from vfs.gop import Gop if resolution is None and codec is None: resolution, codec, fps = get_shape_and_codec(filename) elif codec is None: resolution = get_shape(filename) assert(resolution is not None) assert(codec is not None) assert(fps is not None) physical = cls.add(logical_video, *resolution, codec=codec) output_filename_template = os.path.join( VFS.instance().path, cls._gop_filename_template(logical_video, physical, '%04d')) #'{}-{}-%04d.{}'.format(logical_video.name, physical.id, extensions[physical.codec])) gop_filenames = split_video(filename, output_filename_template, resolution, codec, fps) Gop.addmany(physical, [(filename, start_time, end_time, os.path.getsize(filename), fps, 0, 0, None) for (filename, start_time, end_time) in gop_filenames]) logging.info('Ingested physical video %s-%d', logical_video.name, physical.id) return physical
def task(candidate_gop): from vfs.gop import Gop closest_gops = Descriptor.closest_match(epoch, candidate_gop) if candidate_gop else (None, None) overlap_gop = None with cls.lock: for gop_id, matches in closest_gops: if VFS.instance().database.execute( 'SELECT examined FROM gops WHERE id = ? AND joint = 0', gop_id).fetchone()[0] <= epoch: overlap_gop = Gop.get(gop_id) VFS.instance().database.execute( 'UPDATE gops SET examined = ? WHERE id = ?', (epoch + 1, overlap_gop.id)).close() break if not overlap_gop is None: #VFS.instance().database.execute( # 'UPDATE gops SET examined = ? WHERE id in (?, ?)', (epoch + 10, candidate_gop.id, overlap_gop.id)).close() #threading.Thread(target=cls.co_compress, args=(candidate_gop, overlap_gop, matches)).start() return cls.co_compress(candidate_gop, overlap_gop, matches) #return True, candidate_gop, pool.submit(cls.co_compress, candidate_gop, overlap_gop, matches) #return True, candidate_gop, future #bytes_saved elif not candidate_gop is None: logging.info("Deferring joint compression for gop %d-%d", candidate_gop.physical_id, candidate_gop.id) VFS.instance().database.execute( 'UPDATE gops SET examined = ? WHERE id = ?', (epoch + 1, candidate_gop.id)).close() return 0 #False, candidate_gop, None else: return 0 #False, None, None
def solve_constraint(logical, resolution, roi, t, fps, codec): physical, fragments = _prepare(logical, resolution, t) logging.info( f'Solving for {len(physical)} physical videos with {len(fragments)} fragments' ) if len(physical) == 1: # Only one physical video, so not really much to solve distinct_fragment_ids = ( f['id'] for f in sorted(fragments, key=lambda f: f['start']) if f['end'] >= t[0] and f['start'] < t[1]) elif len(physical) > 1: video_objects, goal_ints = build_from_video_info( physical, fragments, t) fragment_ids = find_best_intervals(video_objects, goal_ints, codec, resolution) distinct_fragment_ids = list(dict.fromkeys(fragment_ids)) else: logging.error("No physical videos found for solver to examine") assert False gops = [Gop.get(id) for id in distinct_fragment_ids] return gops
def delete(cls, video): from vfs.gop import Gop logging.info('Deleting Physical Video %d', video.id) gops = video.gops() references = {filename: count for (filename, count) in VFS.instance().database.execute( 'SELECT filename, COUNT(*) ' 'FROM gops ' 'WHERE filename IN (SELECT filename FROM gops WHERE physical_id = ?) ' 'GROUP BY filename', video.id).fetchall()} VFS.instance().database.execute('DELETE FROM gops WHERE physical_id = ?', video.id) VFS.instance().database.execute('DELETE FROM physical_videos WHERE id = ?', video.id) video.id = None for gop in gops: Gop.delete(gop, references=references.get(gop.filename, 0))
def gops(self): from vfs.gop import Gop if self._gops is None: self._gops = list(map(lambda args: Gop(*args), VFS.instance().database.execute( 'SELECT id, physical_id, filename, start_time, end_time, cluster_id, joint, examined, ' ' histogram, descriptors, keypoints, size, zstandard, fps, mse, estimated_mse, parent_id, original_size ' 'FROM gops WHERE physical_id = ? ORDER BY id', self.id).fetchall())) return self._gops
def get_random_gop(engine, name, r, t, index, avoid_ids=None): gops = engine.database.execute( "SELECT gops.id FROM gops, physical_videos, logical_videos WHERE logical_videos.id == physical_videos.logical_id AND gops.physical_id = physical_videos.id AND height = ? AND width = ? AND name = ? ORDER BY gops.id LIMIT ?", (r[0], r[1], name, index + 1)).fetchall() #gops = engine.database.execute("SELECT gops.id FROM gops, physical_videos, logical_videos WHERE logical_videos.id == physical_videos.logical_id AND gops.physical_id = physical_videos.id AND height = ? AND width = ? AND end_time - start_time != ? AND name = ?", (r[0], r[1], t, name)).fetchall() gop = Gop.get(gops[-1]) #random.choice(gops)) if not os.path.exists(gop.filename): return get_random_gop(engine, name, r, t, index, avoid_ids) elif gop.id in (avoid_ids or []): return get_random_gop(engine, name, r, t, index, avoid_ids) else: return gop
def cache_reconstructions(logical, resolution, codec, times, fps, cache_sequences): for sequence in (sequence for sequence in cache_sequences if sequence): physical = PhysicalVideo.add(logical, *resolution, codec) new_gop_data = [] transitive_estimated_mses = VFS.instance().database.execute( 'WITH RECURSIVE' ' error(id, estimated_mse) AS ' ' (SELECT id, estimated_mse FROM gops child WHERE id IN ({}) '. format(','.join(str(gop.id) for gop, _, _ in sequence)) + ' UNION ALL ' ' SELECT id, estimated_mse FROM gops parent WHERE parent.id = id)' 'SELECT id, SUM(estimated_mse) FROM error GROUP BY id').fetchall() for (_, current_estimated_mse), (index, (gop, filename, new_mse)) in zip( transitive_estimated_mses, enumerate(sequence)): new_filename = path.join( VFS.instance().path, PhysicalVideo._gop_filename_template(logical, physical, index)) new_gop_data.append( (new_filename, max(gop.start_time, times[0]), min(gop.end_time, times[1]), os.path.getsize(filename), fps, None, 2 * (current_estimated_mse + (new_mse or 0)), gop.id)) #TODO remove guard #duplicates = VFS.instance().database.execute("SELECT * FROM gops, physical_videos WHERE start_time = ? AND end_time = ? and height = ? and width = ? and codec = ? and gops.physical_id = physical_videos.id", # (new_gop_data[-1][0], new_gop_data[-1][1], resolution[0], resolution[1], codec)).fetchall() #if len(duplicates) > 0: # print("Duplicate detected???") os.rename(filename, new_filename) Gop.addmany(physical, new_gop_data) logging.info('Cached physical video %s-%d', logical.name, physical.id) return new_gop_data
def solve_exact(logical, resolution, roi, t, fps, codec): #if roi is not None: # return None gop_id = VFS.instance().database.execute( """ SELECT gops.id FROM gops, physical_videos WHERE height = ? AND width = ? AND start_time <= ? AND end_time >= ? AND codec = ? AND gops.physical_id = physical_videos.id AND logical_id = ?""", (resolution[0], resolution[1], t[0], t[1], codec, logical.id)).fetchone() return [Gop.get(gop_id[0])] if gop_id else None
def get_candidate(cls, epoch): from vfs.gop import Gop smallest_cluster = (VFS.instance().database.execute( 'SELECT cluster_id, COUNT(*) FROM gops candidate ' 'WHERE examined <= ? AND joint = 0 AND ' ' cluster_id > 0 ' # What did this do? #' physical_id < (SELECT MAX(physical_id) FROM gops WHERE examined <= ? AND joint = 0 AND cluster_id = candidate.cluster_id) ' 'GROUP BY cluster_id ' 'ORDER BY COUNT(*) ASC ' 'LIMIT 1', (epoch)).fetchone() or [None])[0] #(epoch, epoch) if smallest_cluster is not None: return Gop.get(*VFS.instance().database.execute( 'SELECT MIN(id) FROM gops ' 'WHERE cluster_id = ? AND examined <= ? AND joint = 0', (smallest_cluster, epoch)).fetchone()) else: return None
def execute(): candidate_gop = JointCompression.get_candidate(epoch) if candidate_gop is None: return 0 #VFS.instance().database.execute( # 'UPDATE gops SET examined = ? WHERE id = ?', (epoch + 1, candidate_gop.id)).close() closest_gops = Descriptor.closest_match( epoch, candidate_gop) if candidate_gop else (None, None) for gop_id, matches in closest_gops: overlap_gop = Gop.get(gop_id) if not overlap_gop is None: return JointCompression.co_compress(candidate_gop, overlap_gop, matches) else: logging.info("No GOP found") return None
def closest_match(cls, epoch, gop, matches_required=DEFAULT_MATCHES_REQUIRED, radius=400): #400): from vfs.gop import Gop with log_runtime("Joint compression candidate selection"): cluster_gops = VFS.instance().database.execute( "SELECT id, filename, descriptors FROM gops WHERE cluster_id = ? AND physical_id != ? AND descriptors IS NOT NULL AND joint = 0 AND examined <= ? AND NOT EXISTS (SELECT id FROM gop_joint_aborted WHERE gop1 = ? AND gop2 = id)", (gop.cluster_id, gop.physical_id, epoch, gop.id)).fetchall() candidate_gops = [] for gop2_id, gop2_filename, gop2_descriptors in cluster_gops: success, matches = cls.adhoc_match(gop.descriptors, gop2_descriptors) if success and len(matches) > matches_required: candidate_gops.append( (gop.filename.split('-')[-1] == gop2_filename.split( '-')[-1], len(matches), gop2_id, matches)) if candidate_gops[-1][1] > 400 or candidate_gops[-1][ 0]: # Break on "good enough" match to try out break candidate_gop = sorted(candidate_gops, reverse=True)[0] if candidate_gops else None return [(candidate_gop[2], candidate_gop[3])] matcher = cls._get_matcher(epoch, gop) physical_map = cls._get_physical_map(gop.cluster_id).get( gop.physical_id, None) index_map = cls._get_index_map(gop.cluster_id) all_matches = matcher.radiusMatch(queryDescriptors=gop.descriptors, maxDistance=radius) good_matches = defaultdict(lambda: []) first_matches = {} complete = set() # For each frame/descriptor pair, find matches that pass the Lowe threshold test #all_matches = all_matches[:5000] filtered_matches = ( m for d in all_matches for m in d if index_map[m.imgIdx] > gop.id and m.imgIdx not in physical_map and not (m.imgIdx, m.queryIdx) in complete) #for descriptor_matches in all_matches: # for match in descriptor_matches: with log_runtime("Lowes test"): for match in filtered_matches: #if match.imgIdx not in physical_map and \ # index_map[match.imgIdx] > gop.id and \ #if not (match.imgIdx, match.queryIdx) in complete: # First match if (match.imgIdx, match.queryIdx) not in first_matches: first_matches[match.imgIdx, match.queryIdx] = match # Second match else: if first_matches[ match.imgIdx, match. queryIdx].distance < cls.LOWE_THRESHOLD * match.distance: good_matches[match.imgIdx].append( first_matches[match.imgIdx, match.queryIdx]) del first_matches[match.imgIdx, match.queryIdx] complete.add((match.imgIdx, match.queryIdx)) # Some matches may not have a second match to apply Lowe's threshold on. # Check to see if we should have seen it and count it if so. for first_match in first_matches.values(): if first_match.distance / cls.LOWE_THRESHOLD < radius: good_matches[first_match.imgIdx].append(first_match) ignore_ids = set(VFS.instance().database.execute( 'SELECT gop2 FROM gop_joint_aborted WHERE gop1 = ?', gop.id).fetchall()) best_indexes = [ index for index, matches in good_matches.items() if len(matches) >= matches_required and index_map[index] not in ignore_ids ] #best_ids = [index_map[index] for index in best_indexes if index_map[index] not in ignore_ids] #best_id = VFS.instance().database.execute( # 'SELECT MIN(id) FROM gops WHERE joint=0 AND id in ({})'.format(','.join(map(str, best_ids)))).fetchone()[0] #best_index = max((index for index, matches in good_matches.items() if len(matches) >= matches_required), # default=None) #best = sorted([(index_map[index], good_matches[index]) for index in best_indexes], key=lambda pair: len(pair[1]), reverse=True) gops = Gop.get_all(index_map[index] for index in best_indexes) best = [(gop, good_matches[index]) for gop, index in zip(gops, best_indexes)] best = sorted(best, key=lambda pair: (pair[0].filename.split('-')[-1] == gop. filename.split('-')[-1], len(pair[1])), reverse=True) best = best[:len(best) // 20 + 1] # Keep top 5% best = [(mgop.id, matches) for mgop, matches in best] #best = sorted([(mgop.id, cv2.compareHist(gop.histogram, mgop.histogram, cv2.HISTCMP_CHISQR), gop.filename, mgop.filename, matches) for (mgop, matches) in best], key=lambda pair: (len(pair[2]), cv2.compareHist(gop.histogram, pair[0].histogram, cv2.HISTCMP_CHISQR), -pair[0].id), reverse=True) #best = sorted([(id, cv2.compareHist(gop.histogram, Gop.get(id).histogram, cv2.HISTCMP_CHISQR), gop.filename, Gop.get(id).filename, matches) for (id, matches) in best], key=lambda pair: (len(pair[2]), cv2.compareHist(gop.histogram, Gop.get(pair[0]).histogram, cv2.HISTCMP_CHISQR), -pair[0]), reverse=True) return best if best_id is not None: return Gop.get(best_id), good_matches[best_indexes[best_ids.index( best_id)]] #best_index] #if best_index is not None: # return Gop.get(index_map[best_index]), good_matches[best_index] #return Gop.get(VFS.instance().database.execute( # '''SELECT id FROM gops # WHERE cluster_id = ? AND joint = 0 # LIMIT 1 # OFFSET ?''', (gop.cluster_id, best_index)).fetchone()[0]), good_matches[best_index] else: return None, None