{"id":18751834,"url":"https://github.com/lemurpwned/video-sampler","last_synced_at":"2025-04-13T00:13:59.482Z","repository":{"id":195855740,"uuid":"693818002","full_name":"LemurPwned/video-sampler","owner":"LemurPwned","description":"Effective frame sampling for ML applications.","archived":false,"fork":false,"pushed_at":"2024-12-21T14:02:33.000Z","size":70510,"stargazers_count":18,"open_issues_count":4,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T18:11:20.485Z","etag":null,"topics":["data-mining","frame-selection","machine-learning","sampling","video-processing","video-sampling"],"latest_commit_sha":null,"homepage":"https://lemurpwned.github.io/video-sampler/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LemurPwned.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-19T19:18:47.000Z","updated_at":"2025-02-03T08:09:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"48505235-1527-43a2-b08b-15d938f6dfc6","html_url":"https://github.com/LemurPwned/video-sampler","commit_stats":null,"previous_names":["lemurpwned/video-sampler"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LemurPwned%2Fvideo-sampler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LemurPwned%2Fvideo-sampler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LemurPwned%2Fvideo-sampler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LemurPwned%2Fvideo-sampler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LemurPwned","download_url":"https://codeload.github.com/LemurPwned/video-sampler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248517368,"owners_count":21117441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-mining","frame-selection","machine-learning","sampling","video-processing","video-sampling"],"created_at":"2024-11-07T17:17:29.993Z","updated_at":"2025-04-13T00:13:59.464Z","avatar_url":"https://github.com/LemurPwned.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# video-sampler\n\n\u003cdiv align=\"center\"\u003e\n\n[![Python Version](https://img.shields.io/pypi/pyversions/video-sampler.svg)](https://pypi.org/project/video-sampler/)\n[![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/LemurPwned/video-sampler/pulls?utf8=%E2%9C%93\u0026q=is%3Apr%20author%3Aapp%2Fdependabot)\n\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white)](https://github.com/LemurPwned/video-sampler/blob/main/.pre-commit-config.yaml)\n\n[![License](https://img.shields.io/github/license/LemurPwned/video-sampler)](https://github.com/LemurPwned/video-sampler/blob/main/LICENSE)\n[![Downloads](https://img.shields.io/pypi/dm/video-sampler.svg)](https://img.shields.io/pypi/dm/video-sampler.svg)\n\nVideo sampler allows you to efficiently sample video frames and summarize the videos.\nCurrently, it uses keyframe decoding, frame interval gating and perceptual hashing to reduce duplicated samples.\n\n**Use case:** video data collection for machine learning, video summarisation, video frame analysis.\n\n\u003c/div\u003e\n\n## Table of Contents\n\n- [video-sampler](#video-sampler)\n  - [Table of Contents](#table-of-contents)\n  - [Documentation](#documentation)\n  - [Features](#features)\n  - [Installation and Usage](#installation-and-usage)\n    - [Basic usage](#basic-usage)\n      - [GPU support](#gpu-support)\n      - [Streaming and RTSP support](#streaming-and-rtsp-support)\n      - [Image sampling](#image-sampling)\n      - [YT-DLP integration plugin](#yt-dlp-integration-plugin)\n        - [Extra YT-DLP options](#extra-yt-dlp-options)\n      - [OpenAI summary](#openai-summary)\n      - [API examples](#api-examples)\n    - [Advanced usage](#advanced-usage)\n      - [Gating](#gating)\n      - [CLIP-based gating comparison](#clip-based-gating-comparison)\n      - [Blur gating](#blur-gating)\n  - [Benchmarks](#benchmarks)\n  - [Benchmark videos](#benchmark-videos)\n  - [Flit commands](#flit-commands)\n    - [Build](#build)\n    - [Install](#install)\n    - [Publish](#publish)\n  - [🛡 License](#-license)\n  - [📃 Citation](#-citation)\n\n## Documentation\n\nDocumentation is available at [https://lemurpwned.github.io/video-sampler/](https://lemurpwned.github.io/video-sampler/).\n\n## Features\n\n- [x] Direct sampling methods:\n  - [x] `hash` - uses perceptual hashing to reduce duplicated samples\n  - [x] `entropy` - uses entropy to reduce duplicated samples (work in progress)\n  - [x] `gzip` - uses gzip compressed size to reduce duplicated samples (work in progress)\n  - [x] `buffer` - uses sliding buffer to reduce duplicated samples\n  - [x] `grid` - uses grid sampling to reduce duplicated samples\n- [x] Gating methods (modifications on top of direct sampling methods):\n  - [x] `clip` - uses CLIP to filter out frames that do not contain the specified objects\n  - [x] `blur` - uses blur detection to filter out frames that are too blurry\n- [x] Language capture:\n  - [x] Keyword capture from subtitles\n- [x] Integrations\n  - [x] YTDLP integration -- streams directly from [yt-dlp](http://github.com//yt-dlp/yt-dlp) queries,\n        playlists or single videos\n  - [x] OpenAI multimodal models integration for video summaries\n\n## Installation and Usage\n\nIf you intend to use all the integrations, you need all the dependencies:\n\n```bash\npython3 -m pip install -U video_sampler[all]\n```\n\nfor minimalist no-cli usage install:\n\n```bash\npython3 -m pip install -U video_sampler\n```\n\nAvailable extras are:\n\n- `yt-dlp` - for YT-DLP integration\n- `clip` - for CLIP models integration\n- `language` - for language capture\n- `all` - for all dependencies\n- `dev` - for development dependencies\n\nTo see all available options, run:\n\n```bash\npython3 -m video_sampler --help\n```\n\n### Basic usage\n\nPlain:\n\n```bash\npython3 -m video_sampler hash FatCat.mp4 ./dataset-frames/ --hash-size 3 --buffer-size 20\n```\n\nFrom the config file (this is the recommended way if you plan to re-use the same config for different videos):\n\n```bash\npython3 -m video_sampler config ./configs/hash_base.yaml /my-video-folder/ ./my-output-folder\n```\n\nYou can set the number of workers to use with the `n_workers` parameter. The default is 1.\n\n#### GPU support\n\nGPU support is experimental and may not work for all GPUs. Definitely, only NVIDIA GPUs are supported for now.\n\nTo use the GPU sampler, you need to install the `gpu` extra:\n\n```bash\npython3 -m pip install -U video_sampler[gpu]\n```\n\nIf you have some installation issues with `pycuda`, make sure your PATH is set correctly.\nFor example, on Linux, you may need to add the following to your `.bashrc` or `.zshrc`:\n\n```bash\nexport PATH=/usr/local/cuda/bin:$PATH\n```\n\nYou can use the GPU sampler by running the following command:\n\n```bash\npython3 -m video_sampler hash ./videos/FatCat.mp4 ./output-frames/ --use-gpu-decoder\n```\n\nThe `--use-gpu-decoder` flag can be used with all main commands:\n\n- `hash` - Generate frame hashes\n- `buffer` - Extract frames from video\n- `clip` - Extract frames from video\n\nFor a complete list of commands and their options, run `python3 -m video_sampler --help`.\n\nFor configuration, you simply add `use_gpu_decoder: true` to the config file. See [./configs/hash_gpu.yaml](./configs/hash_gpu.yaml) for an example.\n\nKnown limitations due to PyNvVideoCodec library:\n\n- Keyframes only mode is not supported with GPU decoder.\n- Timestamps are estimated from the FPS, so they may not be 100% accurate.\n- Only NVIDIA GPUs are supported.\n\n#### Streaming and RTSP support\n\nRTSP support is experimental and may not work for all RTSP servers, but it should work for most of them.\nYou can test out the RTSP support by running the following command:\n\n```bash\npython3 -m video_sampler config ./configs/hash_base.yaml rtsp://localhost:8554/some-stream ./sampled-stream/\n```\n\n[RTSP simple server](https://github.com/bhaney/rtsp-simple-server) is a good way to test RTSP streams.\n\nOther streams (MJPEG) also work, e.g.\n\n```bash\npython3 -m video_sampler config ./configs/hash_base.yaml \"http://honjin1.miemasu.net/nphMotionJpeg?Resolution=640x480\u0026Quality=Standard\" ./sampled-stream/\n```\n\nFor proper streaming, you may want to adjust `min_frame_interval_sec` and buffer sizes to have a shorter flush time. Keep in mind that streaming will be sampled until interrupted, so you may want to specify the end time of the stream with [`end_time_s` parameter](./video_sampler/config.py#L81). If the stream is a looped video, this is especially important -- otherwise, you'll end up overwriting the same frames over and over again.\n\n#### Image sampling\n\nIf your frames are ordered, then you can use the `image_sampler` module to sample them. The images should have some concept of ordering, e.g. they should be named in a way that allows for sorting, e.g. `image_001.png`, `image_002.png`, etc, because the sampler will deduplicate based on the circular buffer of hashes.\nAn example of a config for `image_sampler` is given in [./configs/image_base.yaml](./configs/image_base.yaml).\nKey changes respective to `video_sampler` are:\n\n- `frame_time_regex` - regex to extract frame time from the filename. If not provided, the frames will be lexiographically ordered.\n- any video sampling params such as `min_frame_interval_sec`, `keyframes_only` will be disregarded.\n\nYou can run the image sampler with -- you need to specify the `images` flag.\n\n```bash\npython3 -m video_sampler config ./configs/image_base.yaml \"./folder-frames/worlds-smallest-cat-bbc\" ./sampled-output/ --images\n```\n\n#### YT-DLP integration plugin\n\nBefore using please consult the ToS of the website you are scraping from -- use responsibly and for research purposes.\nTo use the YT-DLP integration, you need to install `yt-dlp` first (see [yt-dlp](http://github.com//yt-dlp/yt-dlp)).\nThen, you simply add `--yt-dlp` to the command, and it changes the meaning of the `video_path` argument.\n\n- to search\n\n```bash\nvideo_sampler hash \"ytsearch:cute cats\" ./folder-frames/ \\\n  --hash-size 3 --buffer-size 20 --ytdlp\n```\n\n- to sample a single video\n\n```bash\nvideo_sampler hash \"https://www.youtube.com/watch?v=W86cTIoMv2U\" ./folder-frames/ \\\n    --hash-size 3 --buffer-size 20 --ytdlp\n```\n\n- to sample a playlist\n\n```bash\nvideo_sampler hash \"https://www.youtube.com/watch?v=GbpP3Sxp-1U\u0026list=PLFezMcAw96RGvTTTbdKrqew9seO2ZGRmk\" ./folder-frames/ \\\n  --hash-size 3 --buffer-size 20 --ytdlp\n```\n\n- segment based on the keyword extraction\n\n```bash\nvideo_sampler hash \"https://www.youtube.com/watch?v=GbpP3Sxp-1U\u0026list=PLFezMcAw96RGvTTTbdKrqew9seO2ZGRmk\" ./folder-frames/ \\\n  --hash-size 3 --buffer-size 20 --ytdlp --keywords \"cat,dog,another keyword,test keyword\"\n```\n\nThe videos are never directly downloaded, only streamed, so you can use it to sample videos from the internet without downloading them first.\n\n##### Extra YT-DLP options\n\nYou can pass extra options to yt-dlp by using the `-yt-extra-args` flag. For example:\n\nthis will only sample videos uploaded before 2019-01-01:\n\n```bash\n... --ytdlp --yt-extra-args '--datebefore 20190101'\n```\n\nor this will only sample videos uploaded after 2019-01-01:\n\n```bash\n... --ytdlp --yt-extra-args '--dateafter 20190101'\n```\n\nor this will skip all shorts:\n\n```bash\n... --ytdlp --yt-extra-args '--match-filter \"original_url!*=/shorts/ \u0026 url!*=/shorts/\"\n```\n\n#### OpenAI summary\n\nTo use the OpenAI multimodal models integration, you need to install `openai` first `pip install openai`.\nThen, you simply add `--summary-interval` to the command and the url.\n\nIn the example, I'm using [llamafile](https://github.com/Mozilla-Ocho/llamafile) LLAVA model to summarize the video every 50 frames. If you want to use the OpenAI multimodal models, you need to export `OPENAI_API_KEY=your_api_key` first. The format should also work with default OpenAI stuff.\n\nTo replicate, run LLAVA model locally and set the `summary-url` to the address of the model. Specify the `summary-interval` to the minimal interval in seconds between frames that are to be summarised/described.\n\n```bash\nvideo_sampler hash ./videos/FatCat.mp4 ./output-frames/ --hash-size 3 --buffer-size 20 --summary-url \"http://localhost:8080/completion\" --summary-interval 50\n```\n\nSupported env in case you need those:\n\n- `OPENAI_API_KEY` - OpenAI API key\n- `OPENAI_MODEL` - OpenAI model name\n\nConfirmed that you can make it work with e.g. LM Studio, but you need to adjust the `summary-url` to the correct address, e.g. it might be `\"http://localhost:8080/completions\"`. Similar if you want to use the OpenAI API.\n\nSome frames, based on the interval specified, will be summarised by the model and the result will saved in the `./output-frames/summaries.json` folder. The frames that are summarised come after the sampling and gating process happens, and only those frames that pass both stages are viable for summarisation.\n\n```jsonl\nsummaries.jsonl\n---\n{\"time\": 56.087, \"summary\": \"A cat is walking through a field of tall grass, with its head down and ears back. The cat appears to be looking for something in the grass, possibly a mouse or another small creature. The field is covered in snow, adding a wintry atmosphere to the scene.\"}\n{\"time\": 110.087, \"summary\": \"A dog is walking in the snow, with its head down, possibly sniffing the ground. The dog is the main focus of the image, and it appears to be a small animal. The snowy landscape is visible in the background, creating a serene and cold atmosphere.\"}\n{\"time\": 171.127, \"summary\": \"The image features a group of animals, including a dog and a cat, standing on a beach near the ocean. The dog is positioned closer to the left side of the image, while the cat is located more towards the center. The scene is set against a beautiful backdrop of a blue sky and a vibrant green ocean. The animals appear to be enjoying their time on the beach, possibly taking a break from their daily activities.\"}\n```\n\n#### API examples\n\nSee examples in [./scripts](./scripts/run_benchmarks.py).\n\n### Advanced usage\n\nThere are 3 sampling methods available:\n\n- `hash` - uses perceptual hashing to reduce duplicated samples\n- `entropy` - uses entropy to reduce duplicated samples (work in progress)\n- `gzip` - uses gzip compressed size to reduce duplicated samples (work in progress)\n\nTo launch any of them you can run and substitute `method-name` with one of the above:\n\n```bash\nvideo_sampler buffer `method-name` ...other options\n```\n\ne.g.\n\n```bash\nvideo_sampler buffer entropy --buffer-size 20 ...\n```\n\nwhere `buffer-size` for `entropy` and `gzip` mean the top-k sliding buffer size. Sliding buffer also uses hashing to reduce duplicated samples.\n\n#### Gating\n\nAside from basic sampling rules, you can also apply gating rules to the sampled frames, further reducing the number of frames.\nThere are 3 gating methods available:\n\n- `pass` - pass all frames\n- `clip` - use CLIP to filter out frames that do not contain the specified objects\n- `blur` - use blur detection to filter out frames that are too blurry\n\nHere's a quick example of how to use clip:\n\n```bash\npython3 -m video_sampler clip ./videos ./scratch/clip --pos-samples \"a cat\" --neg-samples \"empty background, a lemur\"  --hash-size 4\n```\n\n#### CLIP-based gating comparison\n\nHere's a brief comparison of the frames sampled with and without CLIP-based gating with the following config:\n\n```python\n  gate_def = dict(\n      type=\"clip\",\n      pos_samples=[\"a cat\"],\n      neg_samples=[\n          \"an empty background\",\n          \"text on screen\",\n          \"a forest with no animals\",\n      ],\n      model_name=\"ViT-B-32\",\n      batch_size=32,\n      pos_margin=0.2,\n      neg_margin=0.3,\n  )\n```\n\nEvidently, CLIP-based gating is able to filter out frames that do not contain a cat and in consequence, reduce the number of frames with plain background. It also thinks that a lemur is a cat, which is not entirely wrong as fluffy creatures go.\n\n|                      Pass gate (no gating)                      |                            CLIP gate                            |                              Grid                               |\n| :-------------------------------------------------------------: | :-------------------------------------------------------------: | :-------------------------------------------------------------: |\n|   \u003cimg width=\"256\" src=\"./assets/FatCat.mp4_hash_4_pass.gif\"\u003e   |   \u003cimg width=\"256\" src=\"./assets/FatCat.mp4_hash_4_clip.gif\"\u003e   |   \u003cimg width=\"256\" src=\"./assets/FatCat.mp4_grid_4_pass.gif\"\u003e   |\n|  \u003cimg width=\"256\" src=\"./assets/SmolCat.mp4_hash_4_pass.gif\"\u003e   |  \u003cimg width=\"256\" src=\"./assets/SmolCat.mp4_hash_4_clip.gif\"\u003e   |  \u003cimg width=\"256\" src=\"./assets/SmolCat.mp4_grid_4_pass.gif\"\u003e   |\n| \u003cimg width=\"256\" src=\"./assets/HighLemurs.mp4_hash_4_pass.gif\"\u003e | \u003cimg width=\"256\" src=\"./assets/HighLemurs.mp4_hash_4_clip.gif\"\u003e | \u003cimg width=\"256\" src=\"./assets/HighLemurs.mp4_grid_4_pass.gif\"\u003e |\n\nThe effects of gating in numbers, for this particular set of examples (see `produced` vs `gated` columns). `produced` represents the number of frames sampled without gating, here after the perceptual hashing, while `gated` represents the number of frames sampled after gating.\n\n| video          | buffer | gate | decoded | produced | gated |\n| -------------- | ------ | ---- | ------- | -------- | ----- |\n| FatCat.mp4     | grid   | pass | 179     | 31       | 31    |\n| SmolCat.mp4    | grid   | pass | 118     | 24       | 24    |\n| HighLemurs.mp4 | grid   | pass | 161     | 35       | 35    |\n| FatCat.mp4     | hash   | pass | 179     | 101      | 101   |\n| SmolCat.mp4    | hash   | pass | 118     | 61       | 61    |\n| HighLemurs.mp4 | hash   | pass | 161     | 126      | 126   |\n| FatCat.mp4     | hash   | clip | 179     | 101      | 73    |\n| SmolCat.mp4    | hash   | clip | 118     | 61       | 31    |\n| HighLemurs.mp4 | hash   | clip | 161     | 126      | 66    |\n\n#### Blur gating\n\nHelps a little with blurry videos. Adjust threshold and method (`laplacian` or `fft`) for best results.\nSome results from `fft` at `threshold=20`:\n\n| video      | buffer | gate | decoded | produced | gated |\n| ---------- | ------ | ---- | ------- | -------- | ----- |\n| MadLad.mp4 | grid   | pass | 120     | 31       | 31    |\n| MadLad.mp4 | hash   | pass | 120     | 110      | 110   |\n| MadLad.mp4 | hash   | blur | 120     | 110      | 85    |\n\n## Benchmarks\n\nConfiguration for this benchmark:\n\n```bash\nSamplerConfig(min_frame_interval_sec=1.0, keyframes_only=True, buffer_size=30, hash_size=X, queue_wait=0.1, debug=True)\n```\n\n|                                 Video                                 | Total frames | Hash size | Decoded | Saved |\n| :-------------------------------------------------------------------: | :----------: | :-------: | :-----: | :---: |\n|        [SmolCat](https://www.youtube.com/watch?v=W86cTIoMv2U)         |     2936     |     8     |   118   |  106  |\n|        [SmolCat](https://www.youtube.com/watch?v=W86cTIoMv2U)         |      -       |     4     |    -    |  61   |\n| [Fat Cat](https://www.youtube.com/watch?v=kgrV3_g9rYY\u0026ab_channel=BBC) |     4462     |     8     |   179   |  163  |\n| [Fat Cat](https://www.youtube.com/watch?v=kgrV3_g9rYY\u0026ab_channel=BBC) |      -       |     4     |    -    |  101  |\n|       [HighLemurs](https://www.youtube.com/watch?v=yYXoCHLqr4o)       |     4020     |     8     |   161   |  154  |\n|       [HighLemurs](https://www.youtube.com/watch?v=yYXoCHLqr4o)       |      -       |     4     |    -    |  126  |\n\n---\n\n```bash\nSamplerConfig(\n    min_frame_interval_sec=1.0,\n    keyframes_only=True,\n    queue_wait=0.1,\n    debug=False,\n    print_stats=True,\n    buffer_config={'type': 'entropy'/'gzip', 'size': 30, 'debug': False, 'hash_size': 8, 'expiry': 50}\n)\n```\n\n|                                 Video                                 | Total frames |  Type   | Decoded | Saved |\n| :-------------------------------------------------------------------: | :----------: | :-----: | :-----: | :---: |\n|        [SmolCat](https://www.youtube.com/watch?v=W86cTIoMv2U)         |     2936     | entropy |   118   |  39   |\n|        [SmolCat](https://www.youtube.com/watch?v=W86cTIoMv2U)         |      -       |  gzip   |    -    |  39   |\n| [Fat Cat](https://www.youtube.com/watch?v=kgrV3_g9rYY\u0026ab_channel=BBC) |     4462     | entropy |   179   |  64   |\n| [Fat Cat](https://www.youtube.com/watch?v=kgrV3_g9rYY\u0026ab_channel=BBC) |      -       |  gzip   |    -    |  73   |\n|       [HighLemurs](https://www.youtube.com/watch?v=yYXoCHLqr4o)       |     4020     | entropy |   161   |  59   |\n|       [HighLemurs](https://www.youtube.com/watch?v=yYXoCHLqr4o)       |      -       |  gzip   |    -    |  63   |\n\n## Benchmark videos\n\n- [SmolCat](https://www.youtube.com/watch?v=W86cTIoMv2U)\n- [Fat Cat](https://www.youtube.com/watch?v=kgrV3_g9rYY\u0026ab_channel=BBC)\n- [HighLemurs](https://www.youtube.com/watch?v=yYXoCHLqr4o)\n- [MadLad](https://www.youtube.com/watch?v=MWyBgudQqsI)\n\n## Flit commands\n\n#### Build\n\n```\nflit build\n```\n\n#### Install\n\n```\nflit install\n```\n\n#### Publish\n\nRemember to bump the version in `pyproject.toml` before publishing.\n\n```\nflit publish\n```\n\n## 🛡 License\n\n[![License](https://img.shields.io/github/license/LemurPwned/video-sampler)](https://github.com/LemurPwned/video-sampler/blob/main/LICENSE)\n\nThis project is licensed under the terms of the `MIT` license. See [LICENSE](https://github.com/LemurPwned/video-sampler/blob/main/LICENSE) for more details.\n\n## 📃 Citation\n\n```bibtex\n@misc{video-sampler,\n  author = {video-sampler},\n  title = {Video sampler allows you to efficiently sample video frames},\n  year = {2023},\n  publisher = {GitHub},\n  journal = {GitHub repository},\n  howpublished = {\\url{https://github.com/LemurPwned/video-sampler}}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flemurpwned%2Fvideo-sampler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flemurpwned%2Fvideo-sampler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flemurpwned%2Fvideo-sampler/lists"}