{"id":19585407,"url":"https://github.com/rsnitsch/vhs-deshaker","last_synced_at":"2025-04-27T11:33:32.123Z","repository":{"id":146482344,"uuid":"615001791","full_name":"rsnitsch/vhs-deshaker","owner":"rsnitsch","description":"Fix horizontal shaking in digitized VHS videos.","archived":false,"fork":false,"pushed_at":"2024-02-16T12:49:04.000Z","size":529,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2024-02-16T13:45:22.633Z","etag":null,"topics":["deshaker","software-tbc","tbc","time-base-correction","time-base-corrector","vhs","video","video-processing"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rsnitsch.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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}},"created_at":"2023-03-16T18:46:41.000Z","updated_at":"2024-02-01T21:50:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"b6466a2e-5a2c-4798-b69c-2e61111f8cec","html_url":"https://github.com/rsnitsch/vhs-deshaker","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsnitsch%2Fvhs-deshaker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsnitsch%2Fvhs-deshaker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsnitsch%2Fvhs-deshaker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsnitsch%2Fvhs-deshaker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsnitsch","download_url":"https://codeload.github.com/rsnitsch/vhs-deshaker/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224069542,"owners_count":17250455,"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":["deshaker","software-tbc","tbc","time-base-correction","time-base-corrector","vhs","video","video-processing"],"created_at":"2024-11-11T07:54:04.588Z","updated_at":"2024-11-11T07:54:05.733Z","avatar_url":"https://github.com/rsnitsch.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vhs-deshaker \u003c!-- omit from toc --\u003e\r\n\r\nThis tool is for fixing a very specific horizontal shaking issue that affects some digitized VHS videos.\r\n\r\n## Table of Contents \u003c!-- omit from toc --\u003e\r\n\r\n- [Example](#example)\r\n- [How does vhs-deshaker work?](#how-does-vhs-deshaker-work)\r\n- [Install](#install)\r\n- [Usage](#usage)\r\n  - [Handling of audio streams](#handling-of-audio-streams)\r\n  - [Pipe video data to ffmpeg directly](#pipe-video-data-to-ffmpeg-directly)\r\n- [Build instructions](#build-instructions)\r\n- [Docker image](#docker-image)\r\n  - [Docker troubleshooting: Input file cannot be opened](#docker-troubleshooting-input-file-cannot-be-opened)\r\n- [See also](#see-also)\r\n\r\n## Example\r\n\r\nInput example:\r\n\r\nhttps://user-images.githubusercontent.com/1746428/225921679-25f9ba88-bee2-4557-9181-5fd0df6a07b4.mp4\r\n\r\nvhs-deshaker output:\r\n\r\nhttps://user-images.githubusercontent.com/1746428/225921734-024c3526-5193-4755-940d-00e3f5f86097.mp4\r\n\r\nI do not know what causes this type of horizontal shaking. I first encountered it when digitizing some of our old VHS cassettes with family/childhood videos. Some cassettes were more affected than others, suggesting a mechanical/physical problem.\r\n\r\n## How does vhs-deshaker work?\r\n\r\nUnfortunately I was unable to fix our videos with traditional video deshaking software. So I decided to try and create my own algorithm for fixing this nasty issue.\r\n\r\nMy algorithm analyzes the pure black at the left and right borders of VHS video frames. Ideally the pure black area should always have the same width. In my VHS videos,\r\nit should have been 8px on both left and right side of the frames. But due to horizontal shaking and distortions the actual width differed greatly between and within\r\nframes. vhs-deshaker attempts to reverse these deviations by realigning the rows of the video frames such that all frames start at the same column.\r\n\r\nTo achieve that, vhs-deshaker first applies edge detection to the pure black areas at the left and right borders of VHS videos to determine the horizontal shift for each line:\r\n\r\n![alt text](docs/hiw_1_line_starts_raw.jpg)\r\n\r\nThese shifts, actually described as line_starts (from left side of image) and line_ends (from right side of image), are then denoised. Denoising means that very small consecutive segments of line_starts are removed:\r\n\r\n![alt text](docs/hiw_2_line_starts_after_denoising.jpg)\r\n\r\nThe next step is to merge the information from the line_starts and the line_ends. For rows with both line_start and line_end information, the larger consecutive segment\r\nwins because it is considered more reliable:\r\n\r\n![alt text](docs/hiw_3_line_starts_merged.jpg)\r\n\r\nThere are still many rows for which no line_start data is available. These gaps are filled by linear interpolation (for inner gaps) and extrapolated via nearest-known-value\r\nreplication (for outer gaps):\r\n\r\n![alt text](docs/hiw_4_line_starts_interpolated.jpg)\r\n\r\nFinally, the line_starts are smoothed. This smoothes any remaining discontinuities in the line_starts and thus ensures that neighboring lines are always shifted\r\nby a very similar amount:\r\n\r\n![alt text](docs/hiw_5_line_starts_smoothed_final.jpg)\r\n\r\nThis data is then used to shift/realign all rows of the frame.\r\n\r\n## Install\r\n\r\nYou can download the latest binary from the releases page. The accompanying opencv DLL files must be kept in the same folder as the executable.\r\n\r\nIt is recommended to also install the ffmpeg binaries on your system and edit your PATH environment variable such that they can be executed from the commandline. (ffmpeg is needed to add back the audio stream to the output files, see below.)\r\n\r\n## Usage\r\n\r\nIn general the command can be executed like this:\r\n\r\n    vhs-deshaker -i \u003cinput-file\u003e -o \u003coutput-file\u003e [OPTION...]\r\n\r\nThe following options are supported:\r\n  \r\n    -i, --input arg               Input video\r\n    -o, --output arg              Output video\r\n    -f, --framerate arg           Enforce this framerate for the output video\r\n    -c, --colrange arg            Column range, -1 = use double the value\r\n                                  given by -w (default: -1)\r\n    -t, --target-line-start arg   Target line start, -1 = use same value as\r\n                                  given by -w (default: -1)\r\n    -w, --pure-black-width arg    Pure black area width (default: 8)\r\n    -p, --pure-black-threshold arg\r\n                                  Pure black threshold (default: 20)\r\n    -m, --min-line-start-segment-length arg\r\n                                  Min line start segment length (default: 15)\r\n    -k, --line-start-smoothing-kernel-size arg\r\n                                  Line start smoothing kernel size (default:\r\n                                  51)\r\n    -h, --help                    Print usage\r\n\r\nThe most important parameter of all is `-w` / `--pure-black-width`. A wrong `-w` value can _increase_ shaking. To get good results you have to measure the width of\r\nthe pure black borders on the left and right side of your video. If your video is very shaky you will likely have to estimate/guess this width\r\nand/or just try multiple values until you converge to the best setting.\r\n\r\nThe parameter `-p` / `--pure-black-threshold` is also fairly important because it is used to determine if a border pixel belongs to the pure black area or to the actual\r\ncontent of your video. Measure the brightness/intensity (grayscale value) of your video's border pixels and then add a small \"margin of safety\" to this number. This\r\nwill be the ideal value for `-p`. The \"margin of safety\" should be picked a little larger if your video is very noisy.\r\n\r\n### Handling of audio streams\r\n\r\nUnfortunately vhs-deshaker can only process video streams. The audio will not be included in the output file. Therefore you have to add back the audio stream manually to the output file. Furthermore, you should know that vhs-shaker uses the lossless HuffYUV video codec to generate the output file. Therefore the output files will be huge and you should make sure your disk has enough free space. Also, the output files must have .avi format / extension because mp4 does not support the HuffYUV codec.\r\n\r\nI recommend to use ffmpeg to add back the audio stream to the deshaked video file. For example:\r\n\r\n    # Remove horizontal shaking\r\n    vhs-deshaker.exe -i input.avi -o deshaked.avi\r\n\r\n    # Add back the audio stream and recode the video stream from HuffYUV to H.264\r\n    ffmpeg -vn -i input.avi -an -i deshaked.avi -c:a copy -pix_fmt yuv420p final.mp4\r\n\r\nThe videos generated by vhs-deshaker are using the HuffYUV codec with full colorspace (YUV 4:4:4). By default ffmpeg would thus generate a H.264 encoded video with\r\nthe same YUV 4:4:4 colorspace. I recommend to overwrite this with the ``-pix_fmt yuv420p`` option for better filesize and - most importantly - device compatibility,\r\nsince many devices do not support H.264 with YUV 4:4:4.\r\n\r\nFor details on how to use ffmpeg to mux audio/video streams you can read this StackOverflow post: https://stackoverflow.com/a/12943003/623685\r\n\r\n### Pipe video data to ffmpeg directly\r\n\r\nRaw video output to stdout can be enabled by specifying `stdout` as output file. You can use this to pipe the raw video data directly to ffmpeg.\r\n\r\nExample:\r\n\r\n    vhs-deshaker -i input.avi -o stdout | ffmpeg -f rawvideo -c:v rawvideo -s 720x564 -pix_fmt bgr24 -r 50 -i pipe: -pix_fmt yuv420p deshaked.mp4\r\n\r\nPlease note you have to:\r\n\r\n- specify the correct video resolution in the call to ffmpeg (e.g. `-s 720x564`).\r\n- specify the framerate in the call to ffmpeg (e.g. `-r 50`).\r\n\r\nAdvantages of piping to ffmpeg:\r\n\r\n- You do not have to keep around an intermediate video file that is extremely large due to the lossless HuffYUV codec.\r\n- You can deshake and merge the audio stream from the original input file in a single step (not shown in the above example).\r\n\r\n## Build instructions\r\n\r\nSee BUILD.md.\r\n\r\n## Docker image\r\n\r\nYou can also run vhs-deshaker via docker. I provide a docker image at [`rsnitsch/vhs-deshaker`](https://hub.docker.com/r/rsnitsch/vhs-deshaker).\r\n\r\nExample command:\r\n\r\n    docker run -it --rm -v \"$(pwd):/videos\" --user $(id -u):$(id -g) rsnitsch/vhs-deshaker:latest -i \u003cinput-file\u003e -o \u003coutput-file\u003e [OPTION...]\r\n\r\n### Docker troubleshooting: Input file cannot be opened\r\n\r\nIf the input file cannot be opened, double-check that your input file is actually visible within docker. The option `-v \"$(pwd):/videos\"`\r\n*should* map the current working directory (on your host system) inside the docker container at `/videos` (which is the working directory\r\nof vhs-deshaker inside the docker container).\r\n\r\nYou can use the following command to list the files visible inside the vhs-deshaker docker container at `/videos`:\r\n\r\n    docker run --entrypoint ls -it --rm -v \"$(pwd):/videos\" --user $(id -u):$(id -g) rsnitsch/vhs-deshaker:latest -Al\r\n\r\nThis executes `ls -Al` inside the docker container which lists the files in `/videos`. You should see the same contents as in your\r\ncurrent working directory on your host system. If not, then you have to find out why and fix this first.\r\n\r\n## See also\r\n\r\nOnline threads discussing the issue:\r\n\r\n- https://forum.videohelp.com/threads/394670-VHS-Horizontal-Stabilisation\r\n- https://forum.videohelp.com/threads/392186-Way-too-shakey-captured-VHS-video\r\n- https://forum.doom9.org/showthread.php?t=174756\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsnitsch%2Fvhs-deshaker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsnitsch%2Fvhs-deshaker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsnitsch%2Fvhs-deshaker/lists"}