{"id":16866972,"url":"https://github.com/webern/kslice","last_synced_at":"2026-04-16T13:36:54.255Z","repository":{"id":88144219,"uuid":"172548718","full_name":"webern/kslice","owner":"webern","description":"Key Frame Analyzer in C++","archived":false,"fork":false,"pushed_at":"2019-03-02T17:13:46.000Z","size":848,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-24T21:25:00.996Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/webern.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","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":"2019-02-25T17:06:51.000Z","updated_at":"2019-03-02T17:13:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"c6e5f8e0-e7f2-4edc-ba4e-2724302753c6","html_url":"https://github.com/webern/kslice","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webern%2Fkslice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webern%2Fkslice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webern%2Fkslice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webern%2Fkslice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webern","download_url":"https://codeload.github.com/webern/kslice/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244273923,"owners_count":20427014,"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":[],"created_at":"2024-10-13T14:52:17.283Z","updated_at":"2026-04-16T13:36:49.227Z","avatar_url":"https://github.com/webern.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kslice\n\n#### Continuous Integration Status\n\nContinuous integration testing is running on CircleCI. Click [here](https://circleci.com/gh/webern/kslice) to go to the project.\n\n  * `master` [![CircleCI](https://circleci.com/gh/webern/kslice/tree/master.svg?style=svg)](https://circleci.com/gh/webern/kslice/tree/master)\n  * `develop` [![CircleCI](https://circleci.com/gh/webern/kslice/tree/develop.svg?style=svg)](https://circleci.com/gh/webern/kslice/tree/develop)\n\n## Description\n\n`kslice` is a command line utility for extracting i-frames (also known as key frames) from video and normalizing them (by grayscale pixel averaging) to a grid (e.g. 32x32, 64x64, etc.) This is the format often used as input into neural nets and other classification algorithms.\n\nGiven a video file and dimensions (e.g. 32x32, 64x64, 128x128), `kslice` extracts i-frames from the video, converts the frames to grayscale, splits each frame into a grid of said dimensions, calculates the median value of all the pixels of each cell of the grid and writes the values to a CSV file.\n\nEach i-frame is output as a csv row where the first position is the timestamp (in seconds) and the rest of the row represents the pixels of the grid (0 - 255, 8-bit grayscale).\n\nFor example: if a frame timestamp (in seconds) is 3.14 and the dimensions are 3x3, an example line might look like:\n\n`3.14,42,255,9,13,67,0,27,33,123  // timestamp + 9 values (3x3)`\n\nEach line of the csv will represent one frame.\n\n#### Usage\n\nThe `kslice` command takes 5 parameters:\n\n  * `--help` a flag to display the help info.\n  * `--input` the video file to process.\n  * `--optput` the csv file to write. Optional: when omitted the csv data will be written to stdout.\n  * `--x-size` the size of the output grid's x dimension. Optional: defaults to 32.\n  * `--y-size` the size of the output grid's y dimension. Optional: defaults to 32.\n\nSo an example call looks like this:\n\n`kslice --input=\"./path/my.mp4\" --output=\"data.csv\" --x-size=20 --y-size=20`\n\n#### Dependencies\n\n  * `kslice` makes an external system call to `ffprobe`, so you must have this installed and visible in your `$PATH`.\n  * `kslice` links to OpenCV at runtime.\n  * `boost` is required at compile time, but is statically linked.\n\n## Build\n\n#### Setup\n\nYou must install `boost`, `ffmpeg` and `opencv`. The easiest way to see what is needed to setup the a machine is to look at the CircleCI Dockerfile in `.circleci/Dockerfile` which takes a clean Ubuntu 18.04 machine and gets it ready to build `kslice`.\n\n###### Prerequisites:\n\n```\nsudo apt-get update \u0026\u0026 sudo apt-get install -y \\\n    build-essential \\\n    cmake \\\n    ffmpeg \\\n    libopencv-dev \\\n    libboost-all-dev\n```\n\n#### Build\n\nLet's say you have cloned the repo to `/repos/kslice` and that you want to do an out-of-source build. The following commands will compile `kslice` and tests.\n\n```\nmkdir -p /repos/kslice-build\ncd /repos/kslice-build\ncmake ../kslice\nmake -j12\n```\n\nThere is no `make install` step, just use the binary from `/repos/kslice-build/kslice`, or move it where you want it.\n\n#### Test\n\nHaving compiled per the instructions above, we can run the tests with:\n\n`/repos/kslice-build/lib/kstest`\n\n## Approach\n\nInitially I wanted to create a self-contained program, so I began by writing a program that links to, and uses the ffmpeg api. This proved to be quite difficult because the ffmpeg api is unstable and complicated. Also, I might have been doing something wrong because ffmpeg wanted to link to all of my `macOS`'s frameworks, such as CoreFoundation, CoreVideo, etc. This was feeling like a bad path, so I took a step back.\n\nI then started working with OpenCV which is much easier to use. But I discovered that, although it has ffmpeg as an underlying dependency, it doesn't expose the functionality to detect I-Frames.\n\nWith these challenges I decided that it would be acceptable for the `kslice` program to make an external call to `ffprobe` to get the list of i-frames from the file.\n\n#### Program Flow\n\n  * The program calls `ffprobe` to get a list of i-frames. It requests this in `xml` format and writes this data to a temporary file.\n  * The program then reads in the temporary `xml` file and extracts the list of i-frame indices.\n  * The program then uses OpenCV to get these frames with timestamps, convert them to grayscale, and downsample the frame to the size of the desired output grid using linear interpolation.\n  * The frames are then output to either a csv file or stdout.\n\n#### Exceptions\n\nAny of the above steps may throw an exception. All exceptions are caught in main where an error message is printed to the user before exiting with a non-zero code. A different non-zero code is used for each exit path to aide in error troubleshooting.\n\n#### Architecture\n\nThe bulk of the program logic is separated from `kslice` main in a library with the namespace `kscore`.\n\nIn such a small program, we don't need much in the way of object-oriented patterns. However, I did see an opportunity to inject a strategy object which represents the call to `ffprobe`. `IFrameStrategy` is implemented by `FFProbeStrategy`.  In theory, we could implement different strategies for this and simply inject them into the program logic. We could also mock this for unit testing.\n\nIf we view the `kscore` library from the standpoint of a client, the headers for boost and OpenCV are 'hidden' in the cpp files. Thus clients of the library need not add these include paths.\n\n#### Testing\n\nThe `kscore` library is tested using Catch2. Not all functions are tested due to lack of time, but there are two end-to-end integration tests which prove that `kslice`, as a whole, is working properly. A few additional unit-level tests are written as well, for example the parsing of program arguments is tested.\n\nThe library, tests and `slice` executable are being compiled and tested with each commit via CircleCI (see top for link).\n\n#### Conclusion\n\nThis has been a fun and unique coding challenge. Thank you for considering me for the C++ developer opportunity. Based on this challenge and what I have learned about the company, I believe working on your project would be quite exciting!\n\n## Research Links\n\nThese are some links that I wanted to keep track of while working on this:\n\n  * [ffmpeg api example](https://www.ffmpeg.org/doxygen/0.6/api-example_8c-source.html)\n  * [cmake ffmpeg find script](https://gist.github.com/royshil/6318407)\n  * [ffmpeg decode example](https://unick-soft.ru/article.php?id=14)\n  * [An ffmpeg and SDL Tutorial](http://dranger.com/ffmpeg/tutorial01.html)\n  * [Unanswered OpenCV Question on Keyframe Extraction](http://answers.opencv.org/question/60390/how-to-get-keyframes-from-videofile/)\n  * [Image Hashing Blogpost](https://www.pyimagesearch.com/2017/11/27/image-hashing-opencv-python/)\n  * [SO ffmpeg linking question](https://stackoverflow.com/questions/25742626/ffmpeg-link-error)\n  * [A Question I Posted on OpenCV](http://answers.opencv.org/question/209548/extracting-keyframes-i-frames-from-a-video)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebern%2Fkslice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebern%2Fkslice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebern%2Fkslice/lists"}