def main(): db = Database() # Frames on disk can either be stored uncompressed (raw bits) or compressed # (encoded using some form of image or video compression). When Scanner # reads frames from a table, it automatically decodes the data if necessary. # The Op DAG only sees the raw frames. For example, this table is stored # as compressed video. def make_blurred_frame(): frame = db.sources.FrameColumn() blurred_frame = db.ops.Blur(frame=frame, kernel_size=3, sigma=0.5) sampled_frame = db.streams.Range(blurred_frame, 0, 30) return frame, sampled_frame # By default, if an Op outputs a frame with 3 channels with type uint8, # those frames will be compressed using video encoding. No other frame # type is currently compressed. frame, blurred_frame = make_blurred_frame() output_op = db.sinks.Column(columns={'frame': blurred_frame}) job = Job( op_args={ frame: db.table('example').column('frame'), output_op: 'output_table_name', }) db.run(output_op, [job], force=True) frame, blurred_frame = make_blurred_frame() # The compression parameters can be controlled by annotating the column low_quality_frame = blurred_frame.compress_video(quality=35) output_op = db.sinks.Column(columns={'frame': low_quality_frame}) job = Job( op_args={ frame: db.table('example').column('frame'), output_op: 'low_quality_table', }) db.run(output_op, [job], force=True) # If no compression is desired, this can be specified by indicating that # the column should be lossless. frame, blurred_frame = make_blurred_frame() # The compression parameters can be controlled by annotating the column lossless_frame = blurred_frame.lossless() output_op = db.sinks.Column(columns={'frame': lossless_frame}) job = Job(op_args={ frame: db.table('example').column('frame'), output_op: 'pristine_frame', }) db.run(output_op, [job], force=True) # Any column which is saved as compressed video can be exported as an mp4 # file by calling save_mp4 on the column. This will output a file called # 'low_quality_video.mp4' in the current directory. db.table('low_quality_table').column('frame').save_mp4('low_quality_video')
def main(): # Look at resize_op/resize_op.cpp to start this tutorial. db = Database() cwd = os.path.dirname(os.path.abspath(__file__)) if not os.path.isfile(os.path.join(cwd, 'resize_op/build/libresize_op.so')): print( 'You need to build the custom op first: \n' '$ pushd {}/resize_op; mkdir build && cd build; cmake ..; make; popd' .format(cwd)) exit() # To load a custom op into the Scanner runtime, we use db.load_op to open the # shared library we compiled. If the op takes arguments, it also optionally # takes a path to the generated python file for the arg protobuf. db.load_op(os.path.join(cwd, 'resize_op/build/libresize_op.so'), os.path.join(cwd, 'resize_op/build/resize_pb2.py')) frame = db.sources.FrameColumn() # Then we use our op just like in the other examples. resize = db.ops.MyResize(frame=frame, width=200, height=300) output_op = db.sinks.Column(columns={'resized_frame': resize}) job = Job(op_args={ frame: db.table('example').column('frame'), output_op: 'example_resized', }) db.run(output_op, [job], force=True)
def main(): db = Database() frame = db.sources.FrameColumn() # You can tell Scanner which frames of the video (or which rows of a video # table) you want to sample. Here, we indicate that we want to stride # the frame column by 4 (select every 4th frame) strided_frame = db.streams.Stride(frame, 4) # We process the sampled frame same as before. hist = db.ops.Histogram(frame=strided_frame) output_op = db.sinks.Column(columns={'hist': hist}) # For each job, you can specify how sampling should be performed for # a specific stream. In the same way we used the op_args argument to bind # a table to an input column, we can bind sampling arguments to strided_frame # to override the default striding of 4 we specified above job = Job( op_args={ frame: db.table('example').column('frame'), # The "strided" sampling mode will run over every 8th frame, # i.e. frames [0, 8, 16, ...] strided_frame: 8, output_op: 'example_hist_strided' }) output_tables = db.run(output_op, [job], force=True) # Loop over the column's rows. Each row is a tuple of the frame number and # value for that row. video_hists = output_tables[0].column('hist').load(readers.histograms) num_rows = 0 for frame_hists in video_hists: assert len(frame_hists) == 3 assert frame_hists[0].shape[0] == 16 num_rows += 1 assert num_rows == round(db.table('example').num_rows() / 8) # Here's some examples of other sampling modes. # Range takes a specific subset of a video. Here, it runs over all frames # from 0 to 100 db.streams.Range(frame, 0, 100) # Gather takes an arbitrary list of frames from a video. db.streams.Gather(frame, [10, 17, 32])
def main(): db = Database() frame = db.sources.FrameColumn() histogram = db.ops.Histogram(frame=frame) output_op = db.sinks.Column(columns={'hist': histogram}) job = Job( op_args={ frame: db.table('example').column('frame'), output_op: 'example_hist_profile' }) [output_table] = db.run(output_op, [job], force=True) # The profiler contains information about how long different parts of your # computation take to run. We use Google Chrome's trace format, which you # can view by going to chrome://tracing in Chrome and clicking "load" in # the top left. output_table.profiler().write_trace('hist.trace')
}, output_op: opt.table_name, }) [_out_table] = db.run(output_op, [job], force=True, tasks_in_queue_per_pu=1) print(db.summarize()) encoded_image2 = db.sources.Column() # encoded_image2 = db.sources.FrameColumn() frame2 = db.ops.ImageDecoder(img=encoded_image2) resized_frame = db.ops.device_resize(frame=frame2, width=128, height=128) output = db.sinks.FrameColumn(columns={'frame': resized_frame}) job = Job( op_args={ encoded_image2: db.table(opt.table_name).column('image'), output: 'instance_segmentation' }) [table ] = db.run(output=output, jobs=[job], force=True, work_packet_size=opt.work_packet_size, io_packet_size=opt.io_packet_size, pipeline_instances_per_node=opt.pipeline_instances_per_node, tasks_in_queue_per_pu=opt.tasks_in_queue_per_pu)
data = data[:10] print(len(data)) db.new_table('test', ['img'], data, force=True) img = db.sources.FrameColumn() cwd = os.path.dirname(os.path.abspath(__file__)) if not os.path.isfile(os.path.join(cwd, 'resize_op/build/libresize_op.so')): print( 'You need to build the custom op first: \n' '$ pushd {}/resize_op; mkdir build && cd build; cmake ..; make; popd'. format(cwd)) exit() # To load a custom op into the Scanner runtime, we use db.load_op to open the # shared library we compiled. If the op takes arguments, it also optionally # takes a path to the generated python file for the arg protobuf. db.load_op(os.path.join(cwd, 'resize_op/build/libresize_op.so'), os.path.join(cwd, 'resize_op/build/resize_pb2.py')) # Then we use our op just like in the other examples. resize = db.ops.MyResize(frame=img, width=200, height=300) output_op = db.sinks.Column(columns={'resized_frame': resize}) job = Job( op_args={ img: db.table('test').column('img'), # encoded_image: {'paths': image_files, **params}, output_op: 'example_resized', }) db.run(output_op, [job], force=True)
from scannerpy import Database, Job # Ingest a video into the database db = Database() db.ingest_videos([('example_table', 'sample-clip.mp4')]) # Define a Computation Graph frame = db.sources.FrameColumn() # Read from the database sampled_frame = db.ops.Stride(frame, 3) # Select every third frame resized = db.ops.Resize(frame=sampled_frame, width=640, height=480) # Resize input frame output_frame = db.sinks.Column(columns={'frame': resized}) # Save resized frame job = Job( op_args={ frame: db.table('example_table').column( 'frame'), # Column to read input frames from output_frame: 'resized_example' # Name of output table }) # Execute the computation graph and return a handle to the newly produced tables output_tables = db.run(output=output_frame, jobs=[job], force=True) # Save the resized video as an mp4 file output_tables[0].column('frame').save_mp4('resized-video')
from scannerpy import Database, DeviceType, Job from scannerpy.stdlib import parsers import os.path as osp import numpy as np db = Database() if not db.has_table('example'): db.ingest_videos([('example', 'example.mp4')]) input_table = db.table('example') frame, frame_info = input_table.as_op().all(warmup_size = 1) flow = db.ops.OpticalFlow( frame = frame, frame_info = frame_info, device=DeviceType.GPU) job = Job(columns = [flow, frame_info], name = 'example_flows') output_table = db.run(job) vid_flows = [flow for _, flow in output_table.load(['flow', 'frame_info'], parsers.flow)] np.save('flows.npy', vid_flows)
args = arg_parser.parse_args() db = Database(config_path=args.scanner_config) cluster_id_src = db.sources.Column() cameras_src = db.sources.Column() images_src = db.sources.Column() points3d_src = db.sources.Column() def remove_empty_rows(*input_cols): return [db.streams.Stride(col, args.stride) for col in input_cols] cluster_id, cameras, images, points3d = remove_empty_rows( cluster_id_src, cameras_src, images_src, points3d_src) output = db.sinks.Column( columns={'cluster_id': cluster_id, 'cameras': cameras, 'images': images, 'points3d': points3d}) job = Job(op_args={ cluster_id_src: db.table(args.input_table).column('cluster_id'), cameras_src: db.table(args.input_table).column('cameras'), images_src: db.table(args.input_table).column('images'), points3d_src: db.table(args.input_table).column('points3d'), output: args.output_table }) output_tables = db.run(output, [job], force=True) print(db.summarize())
frame = db.sources.FrameColumn() strided_frame = db.streams.Range(frame, 0, 60) # Call the newly created object detect op cls_boxes, cls_segms, cls_keyps = db.ops.Detectron( frame=strided_frame, config_path=config_path, weights_path=weights_path, device=DeviceType.GPU) objdet_frame = db.ops.DetectronVizualize( frame=strided_frame, cls_boxes=cls_boxes, cls_segms=cls_segms, cls_keyps=cls_keyps) output_op = db.sinks.Column(columns={'frame': objdet_frame}) job = Job( op_args={ frame: db.table('example').column('frame'), output_op: 'example_obj_detect', }) [out_table] = db.run( output=output_op, jobs=[job], force=True, pipeline_instances_per_node=1) out_table.column('frame').save_mp4('{:s}_detected'.format(movie_name)) print('Successfully generated {:s}_detected.mp4'.format(movie_name))
# The Op DAG only sees the raw frames. For example, this table is stored # as compressed video. def make_blurred_frame(): frame = db.sources.FrameColumn() blurred_frame = db.ops.Blur(frame=frame, kernel_size=3, sigma=0.5) sampled_frame = db.streams.Range(blurred_frame, 0, 30) return frame, sampled_frame # By default, if an Op outputs a frame with 3 channels with type uint8, # those frames will be compressed using video encoding. No other frame # type is currently compressed. frame, blurred_frame = make_blurred_frame() output_op = db.sinks.Column(columns={'frame': blurred_frame}) job = Job(op_args={ frame: db.table('example').column('frame'), output_op: 'output_table_name', }) db.run(output_op, [job], force=True) frame, blurred_frame = make_blurred_frame() # The compression parameters can be controlled by annotating the column low_quality_frame = blurred_frame.compress_video(quality=35) output_op = db.sinks.Column(columns={'frame': low_quality_frame}) job = Job(op_args={ frame: db.table('example').column('frame'), output_op: 'low_quality_table', }) db.run(output_op, [job], force=True) # If no compression is desired, this can be specified by indicating that
# Ingest the videos into the database db.ingest_videos(videos_to_process) # Define the same Computation Graph frame = db.sources.FrameColumn() # Read from the database sampled_frame = db.streams.Stride(frame, 3) # Select every third frame resized = db.ops.Resize(frame=sampled_frame, width=640, height=480) # Resize input frame output_frame = db.sinks.Column(columns={'frame': resized}) # Save resized frame # Create multiple jobs now, one for each video we want to process jobs = [] for table_name, _ in videos_to_process: job = Job( op_args={ frame: db.table(table_name).column( 'frame'), # Column to read input frames from output_frame: '{:s}-resized'.format( table_name) # Name of output table }) jobs.append(job) # Execute the computation graph and return a handle to the newly produced tables output_tables = db.run(output=output_frame, jobs=jobs, force=True) # Save the resized video as an mp4 file for output_table in output_tables: output_table.column('frame').save_mp4(output_table.name())
def main(): # Initialize a connection to the Scanner database. Loads configuration from the # ~/.scanner.toml configuration file. db = Database() # Create a Scanner table from our video in the format (table name, # video path). If any videos fail to ingest, they'll show up in the failed # list. If force is true, it will overwrite existing tables of the same # name. example_video_path = util.download_video() _, failed = db.ingest_videos([('example', example_video_path), ('example2', example_video_path), ('thisshouldfail', 'thisshouldfail.mp4')], force=True) print(db.summarize()) print('Failures:', failed) # Scanner processes videos by forming a graph of operations that operate # on input frames from a table and produce outputs to a new table. # FrameColumn declares that we want to read from a table column that # represents a video frame. frame = db.sources.FrameColumn() # These frames are input into a Histogram op that computes a color histogram # for each frame. hist = db.ops.Histogram(frame=frame) # Finally, any columns provided to Output will be saved to the output # table at the end of the computation. Here, 'hist' is the name of the # column for the output table. output_op = db.sinks.Column(columns={'hist': hist}) # A job defines a table you want to create. In op_args, we bind the # FrameColumn from above to the table we want to read from and name # the output table 'example_hist' by binding a string to output_op. job = Job(op_args={ frame: db.table('example').column('frame'), output_op: 'example_hist' }) job2 = Job(op_args={ frame: db.table('example2').column('frame'), output_op: 'example_hist2' }) # This executes the job and produces the output table. You'll see a progress # bar while Scanner is computing the outputs. output_tables = db.run(output=output_op, jobs=[job, job2], force=True) # Load the histograms from a column of the output table. The # readers.histograms function converts the raw bytes output by Scanner # into a numpy array for each channel. video_hists = output_tables[0].column('hist').load(readers.histograms) # Loop over the column's values, a set of 3 histograms (for each color channel) per element. num_rows = 0 for frame_hists in video_hists: assert len(frame_hists) == 3 assert frame_hists[0].shape[0] == 16 num_rows += 1 assert num_rows == db.table('example').num_rows()
# You can tell Scanner which frames of the video (or which rows of a video # table) you want to sample. Here, we indicate that we want to stride # the frame column by 4 (select every 4th frame) strided_frame = db.streams.Stride(frame, 4) # We process the sampled frame same as before. hist = db.ops.Histogram(frame=strided_frame) output_op = db.sinks.Column(columns={'hist': hist}) # For each job, you can specify how sampling should be performed for # a specific stream. In the same way we used the op_args argument to bind # a table to an input column, we can bind sampling arguments to strided_frame # to override the default striding of 4 we specified above job = Job( op_args={ frame: db.table('example').column('frame'), # The "strided" sampling mode will run over every 8th frame, # i.e. frames [0, 8, 16, ...] strided_frame: 8, output_op: 'example_hist_strided' }) output_tables = db.run(output_op, [job], force=True) # Loop over the column's rows. Each row is a tuple of the frame number and # value for that row. video_hists = output_tables[0].column('hist').load(readers.histograms) num_rows = 0 for frame_hists in video_hists: assert len(frame_hists) == 3 assert frame_hists[0].shape[0] == 16 num_rows += 1
default='submodels', help='the input table where submodels are stored') arg_parser.add_argument('--output_table', dest='output_table', help='the name of the output table', default='models') args = arg_parser.parse_args() db = Database(config_path=args.scanner_config) cwd = os.path.dirname(os.path.abspath(__file__)) db.load_op(os.path.join(cwd, 'op_cpp/build/libmerge_mapping.so'), os.path.join(cwd, 'op_cpp/build/merge_mapping_pb2.py')) num_submodels = db.table(args.input_table).num_rows() print("num submodels: %d" % num_submodels) cluster_id_src = cluster_id = db.sources.Column() cameras_src = cameras = db.sources.Column() images_src = images = db.sources.Column() points3d_src = points3d = db.sources.Column() cluster_id, cameras, images, points3d = db.ops.MergeMappingCPU( cluster_id=cluster_id, cameras=cameras, images=images, points3d=points3d, num_models=num_submodels) output = db.sinks.Column(
db.load_op(os.path.join(cwd, 'op_cpp/build/libextraction_op.so'), os.path.join(cwd, 'op_cpp/build/siftExtraction_pb2.py')) image_ids = db.sources.Column() frames = db.sources.FrameColumn() # run SIFT extractions keypoints, descriptors, cameras = db.ops.SiftExtraction(image_ids=image_ids, frames=frames) output = db.sinks.Column( columns={ 'image_id': image_ids, 'keypoints': keypoints, 'descriptors': descriptors, 'camera': cameras }) job = Job( op_args={ image_ids: db.table(args.input_table).column('image_id'), frames: db.table(args.input_table).column('frame'), output: args.output_table }) output_tables = db.run(output, [job], force=True, io_packet_size=args.packet_size, work_packet_size=args.packet_size) print(db.summarize())
type=int, help='the number of key images that adjacent cluster share') args = arg_parser.parse_args() db = Database(config_path=args.scanner_config) cwd = os.path.dirname(os.path.abspath(__file__)) db.load_op( os.path.join(cwd, 'op_cpp/build/libincremental_mapping.so'), os.path.join(cwd, 'op_cpp/build/incremental_mapping_pb2.py')) step_size = args.cluster_size - args.cluster_overlap matching_stencil = range(0, args.matching_overlap + args.cluster_size) num_images = db.table(args.extraction_table).num_rows() image_ids = db.sources.Column() pair_image_ids = db.sources.Column() two_view_geometries = db.sources.Column() keypoints = db.sources.Column() camera = db.sources.Column() cluster_id, cameras, images, points3d = db.ops.IncrementalMappingCPU( image_id=image_ids, pair_image_ids=pair_image_ids, two_view_geometries=two_view_geometries, keypoints=keypoints, camera=camera, stencil=matching_stencil, step_size=step_size,
import sys import os.path sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../..') import util import numpy as np util.download_video() db = Database() video_path = util.download_video() if True or not db.has_table('example'): print('Ingesting video into Scanner ...') db.ingest_videos([('example', video_path)], force=True) input_table = db.table('example') descriptor = NetDescriptor.from_file(db, 'nets/faster_rcnn_coco.toml') caffe_args = db.protobufs.CaffeArgs() caffe_args.net_descriptor.CopyFrom(descriptor.as_proto()) caffe_args.batch_size = 1 frame = db.ops.FrameInput() caffe_frame = db.ops.CaffeInput(frame=frame, args=caffe_args, device=DeviceType.CPU) cls_prob, rois, fc7 = db.ops.FasterRCNN(caffe_input=caffe_frame, args=caffe_args, device=DeviceType.GPU) bboxes, feature = db.ops.FasterRCNNOutput(cls_prob=cls_prob, rois=rois,
poseimg_frame = db.ops.ImageDecoder(img=encoded_poseimg) encoded_edges = db.sources.Column() edge_frame = db.ops.ImageDecoder(img=encoded_edges) my_segment_imageset_class = db.ops.InstanceSegment(frame=frame, poseimg=poseimg_frame, edges=edge_frame, sigma1=1.0, sigma2=0.01) output_op = db.sinks.FrameColumn( columns={'frame': my_segment_imageset_class}) job = Job( op_args={ encoded_image: db.table(opt.table_name).column('image'), encoded_poseimg: db.table(opt.table_name).column('poseimg'), encoded_edges: db.table(opt.table_name).column('edges'), output_op: 'instance_segmentation', }) start = time.time() [out_table ] = db.run(output_op, [job], force=True, work_packet_size=opt.work_packet_size, io_packet_size=opt.io_packet_size, pipeline_instances_per_node=opt.pipeline_instances_per_node, tasks_in_queue_per_pu=opt.tasks_in_queue_per_pu) end = time.time() print('Total time for instance segm in scanner: {0:.3f} sec'.format(end -
end=args.num_reg_images) R, T, K, width, height, scan_width, bitmap, depth_min, depth_max, id = db.ops.PreparePatchMatch( sparse_reconstruction_path=args.input_path, batch=args.num_reg_images, image_id=image_id_sampled) output = db.sinks.Column( columns={ 'R': R, 'T': T, 'K': K, 'width': width, 'height': height, 'scan_width': scan_width, 'bitmap': bitmap, 'depth_min': depth_min, 'depth_max': depth_max, 'image_id': id, }) job = Job(op_args={ image_id: db.table('frames').column('image_id'), output: args.output_table, }) output_tables = db.run(output, [job], force=True, work_packet_size=args.num_reg_images, io_packet_size=args.num_reg_images) print(db.summarize())
[_out_table] = db.run(output_op, [job], force=True, tasks_in_queue_per_pu=1) print(db.summarize()) encoded_image = db.sources.FrameColumn() frame = db.ops.ImageDecoder(img=encoded_image) my_edge_detection_class = db.ops.EdgeDetection(frame=frame, model_path='model.yml.gz') output_op = db.sinks.FrameColumn( columns={'frame': my_edge_detection_class}) job = Job( op_args={ encoded_image: db.table('edge_detect').column('image'), output_op: 'edge_output', }) start = time.time() [out_table ] = db.run(output_op, [job], force=True, work_packet_size=opt.work_packet_size, io_packet_size=opt.io_packet_size, pipeline_instances_per_node=opt.pipeline_instances_per_node, tasks_in_queue_per_pu=opt.tasks_in_queue_per_pu) end = time.time() print( 'Total time for edge detection in scanner: {0:.3f} sec for {1} images'. format(end - start, len(image_files)))
keypoints = db.sources.Column() descriptors = db.sources.Column() camera = db.sources.Column() pair_image_ids, two_view_geometries = db.ops.SequentialMatchingCPU( image_ids=image_ids, keypoints=keypoints, descriptors=descriptors, stencil=matching_stencil) output = db.sinks.Column(columns={ 'pair_image_ids': pair_image_ids, 'two_view_geometries': two_view_geometries }) job = Job( op_args={ image_ids: db.table(args.input_table).column('image_id'), keypoints: db.table(args.input_table).column('keypoints'), descriptors: db.table(args.input_table).column('descriptors'), camera: db.table(args.input_table).column('camera'), output: args.output_table }) output_tables = db.run( output, [job], force=True, io_packet_size=args.packet_size, work_packet_size=args.packet_size) print(db.summarize())
if len(sys.argv) <= 1: print('Usage: main.py <video_file>') exit(1) movie_path = sys.argv[1] print('Detecting poses in video {}'.format(movie_path)) movie_name = os.path.splitext(os.path.basename(movie_path))[0] db = Database() video_path = movie_path if not db.has_table(movie_name): print('Ingesting video into Scanner ...') db.ingest_videos([(movie_name, video_path)], force=True) input_table = db.table(movie_name) sampler = db.streams.Range sampler_args = {'start': 120, 'end': 480} [poses_table] = pipelines.detect_poses(db, [input_table.column('frame')], sampler, sampler_args, '{:s}_poses'.format(movie_name)) print('Drawing on frames...') frame = db.sources.FrameColumn() sampled_frame = sampler(frame) poses = db.sources.Column() drawn_frame = db.ops.PoseDraw(frame=sampled_frame, poses=poses) output = db.sinks.Column(columns={'frame': drawn_frame}) job = Job(
help="the number of overlapping images to use for each reference image") arg_parser.add_argument( '--output_table', dest='output_table', help='the name of the output table', default='patch_match') args = arg_parser.parse_args() db = Database(config_path=args.scanner_config) cwd = os.path.dirname(os.path.abspath(__file__)) db.load_op( os.path.join(cwd, 'op_cpp/build/libpatch_match.so')) num_images = db.table(args.input_table).num_rows() R = db.sources.Column() T = db.sources.Column() K = db.sources.Column() width = db.sources.Column() height = db.sources.Column() scan_width = db.sources.Column() bitmap = db.sources.Column() depth_min = db.sources.Column() depth_max = db.sources.Column() image_id = db.sources.Column() def get_partition_ranges(num, step): ranges = [] r = list(range(0, num_images, step))
import numpy as np import time import sys if len(sys.argv) <= 1: print('Usage: main.py <video_file>') exit(1) video_path = sys.argv[1] print('Performing optical flow on {}...'.format(video_path)) video_name = os.path.splitext(os.path.basename(video_path))[0] db = Database() if not db.has_table(video_name): db.ingest_videos([(video_name, video_path)]) input_table = db.table(video_name) frame = db.sources.FrameColumn() flow = db.ops.OpticalFlow( frame = frame, device=DeviceType.CPU) sampled_flow = db.streams.Range(flow, 0, 60) output = db.sinks.Column(columns={'flow': sampled_flow}) job = Job(op_args={ frame: input_table.column('frame'), output: input_table.name() + '_flow' }) [output_table] = db.run(output=output, jobs=[job], pipeline_instances_per_node=1, force=True)
from scannerpy import Database, Job db = Database() # Ingest a video into the database # The input is formatted as a list of (table_name, video_path). db.ingest_videos([('sample-clip', 'sample-clip.mp4')], force=True) # Define a Computation Graph frame = db.sources.FrameColumn() # Read from the database sampled_frame = db.streams.Stride(frame, 3) # Select every third frame resized = db.ops.Resize(frame=sampled_frame, width=640, height=480) # Resize input frame output_frame = db.sinks.Column(columns={'frame': resized}) # Save resized frame # Bind arguments to the source and sink nodes job = Job( op_args={ frame: db.table('sample-clip').column( 'frame'), # Column to read input frames from output_frame: 'sample-clip-resized' # Name of output table }) # Execute the computation graph and return a handle to the newly produced tables output_tables = db.run(output=output_frame, jobs=[job], force=True) # Save the resized video as an mp4 file output_tables[0].column('frame').save_mp4(output_tables[0].name())