{"id":16576273,"url":"https://github.com/es-alexander/pythonic-cv","last_synced_at":"2025-08-18T20:45:21.534Z","repository":{"id":45928569,"uuid":"269639382","full_name":"ES-Alexander/pythonic-cv","owner":"ES-Alexander","description":"Performant pythonic wrapper of unnecessarily painful opencv functionality","archived":false,"fork":false,"pushed_at":"2022-04-02T02:40:46.000Z","size":1446,"stargazers_count":44,"open_issues_count":11,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-02T07:18:02.171Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/ES-Alexander.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-05T13:09:32.000Z","updated_at":"2025-04-09T17:03:06.000Z","dependencies_parsed_at":"2022-08-31T13:00:23.258Z","dependency_job_id":null,"html_url":"https://github.com/ES-Alexander/pythonic-cv","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/ES-Alexander/pythonic-cv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ES-Alexander%2Fpythonic-cv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ES-Alexander%2Fpythonic-cv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ES-Alexander%2Fpythonic-cv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ES-Alexander%2Fpythonic-cv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ES-Alexander","download_url":"https://codeload.github.com/ES-Alexander/pythonic-cv/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ES-Alexander%2Fpythonic-cv/sbom","scorecard":{"id":43606,"data":{"date":"2025-08-11","repo":{"name":"github.com/ES-Alexander/pythonic-cv","commit":"fca76c582df549e102266f38e85ba1e3010edc1b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: dist/pythonic_cv-1.2.2-py3-none-any.whl:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T22:10:29.795Z","repository_id":45928569,"created_at":"2025-08-14T22:10:29.795Z","updated_at":"2025-08-14T22:10:29.795Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271059731,"owners_count":24692425,"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","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-11T22:07:45.756Z","updated_at":"2025-08-18T20:45:20.941Z","avatar_url":"https://github.com/ES-Alexander.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"_________________________________\n Version: 1.2.2                  \n Author: ES Alexander            \n Release Date: 26/May/2021       \n_________________________________\n\n# About\nOpenCV is a fantastic tool for computer vision, with significant Python support\nthrough automatically generated bindings. Unfortunately some basic functionality\nis frustrating to use, and documentation is sparse and fragmented as to how best to\napproach even simple tasks such as efficiently processing a webcam feed.\n\nThis library aims to address frustrations in the OpenCV Python api that can be\nfixed using pythonic constructs and methodologies. Solutions are not guaranteed to\nbe optimal, but every effort has been made to make the code as performant as\npossible while ensuring ease of use and helpful errors/documentation.\n\n# Requirements\nThis library requires an existing version of `OpenCV` with Python bindings to be \ninstalled (e.g. `python3 -m pip install opencv-python`). Some features (mainly\nproperty access helpers) may not work for versions of OpenCV earlier than 4.2.0. \nThe library was tested using Python 3.8.5, and is expected to work down to at least\nPython 3.4 (although the integrated advanced features example uses matmul (@) for\nsome processing, which was introduced in Python 3.5).\n\n`Numpy` is also used throughout, so a recent version is suggested (tested with 1.19.0).\n`mss` is used for cross-platform screen-capturing functionality (requires \u003e= 3.0.0)\n\n# Installation\nThe library can be installed from pip, with `python3 -m pip install pythonic-cv`.\n\n# Usage\nNew functionality is provided in the `pcv` module, as described below. All other \nopencv functionality should be accessed through the standard `cv2` import.\n\n## Main Functionality\nThe main implemented functionality is handling video through a context manager, \nwhile also enabling iteration over the input stream. While iterating, key-bindings\nhave been set up for play/pause (`SPACE`) and stopping playback (`q`). A dictionary\nof pause_effects can be passed in to add additional key-bindings while paused without\nneeding to create a subclass. In addition, video playback can be sped up with `w`,\nslowed down with `s`, and if enabled allows rewinding with `a` and returning to\nforwards playback with `d`. Forwards playback at 1x speed can be restored with `r`.\nWhile paused, video can be stepped backwards and forwards using `a` and `d`. All\ndefault key-bindings can be overwritten using the play_commands and pause_effects\ndictionaries and the quit and play_pause variables on initialisation.\n\n## Video I/O Details\nFor reading and writing video files, the `VideoReader` and `VideoWriter` classes should \nbe used. For streaming, the classes `Camera`, `SlowCamera`, and `LockedCamera` are \nprovided. The simplest of these is `SlowCamera`, which has slow iteration because image\ngrabbing is performed synchronously, with a blocking call while reading each frame. \n`Camera` extends `SlowCamera` with additional logic to perform repeated grabbing in a\nseparate thread, so processing and image grabbing can occur concurrently. `LockedCamera`\nsits between the two, providing thread based I/O but with more control over when each \nimage is taken. Analogues for all three camera classes are provided in the `pcv.screen`\nmodule - `SlowScreen`, `Screen`, and `LockedScreen`. The interface is the same as that\nfor the corresponding camera class, except that a monitor index (-1 for all) or screen \nregion is passed in instead of a camera id.\n\n`Camera` is most useful for applications with processing speeds that require the most\nup to date information possible and don't want to waste time decoding frames that are\ngrabbed too early to be processed (frame grabbing occurs in a separate thread, and only\nthe latest frame is retrieved (decoded) on read). `SlowCamera` should only be used where\npower consumption or overall CPU usage are more important than fast processing, or in\nhardware that is only capable of single-thread execution, in which case the\nseparate image-grabbing thread will only serve to slow things down.\n\n`LockedCamera` is intended to work asynchronously like `Camera`, but with more control.\nIt allows the user to specify when the next image should be taken, which leads to less\nwasted CPU and power usage on grabbing frames that aren't used, but with time for the \nimage to be grabbed and decoded before the next iteration needs to start. The locking \nprotocol adds a small amount of additional syntax, and starting the image\ngrabbing process too late in an iteration can result in waits similar to those in\n`SlowCamera`, while starting the process too early can result in images being somewhat\nout of date. Tuning can be done using the 'preprocess' and 'process' keyword arguments,\nwith an in-depth usage example provided in `something_fishy.py`. When used correctly\n`LockedCamera` has the fastest iteration times, or if delays are used to slow down the\nprocess it can have CPU and power usage similar to that of `SlowCamera`.\n\nIf using a video file to simulate a live camera stream, use `SlowCamera` or \n`LockedCamera` - `Camera` will skip frames.\n\nThere is also a `GuaranteedVideoWriter` class which guarantees the output framerate by\nrepeating frames when given input too slowly, and skipping frames when input is too\nfast.\n\n## Overview\n![Overview of classes diagram](https://github.com/ES-Alexander/pythonic-cv/blob/master/Overview.png)\n\n## Examples\n### Basic Camera Stream\n```python\nfrom pcv.vidIO import Camera\nfrom pcv.process import channel_options, downsize\n\n# start streaming camera 0 (generally laptop webcam/primary camera), and destroy 'frame'\n#   window (default streaming window) when finished.\n# Auto-initialised to have 1ms waitKey between iterations, breaking on 'q' key-press,\n#   and play/pause using the spacebar.\nwith Camera(0) as cam:\n    cam.stream()\n\n# stream camera 0 on window 'channels', downsized and showing all available channels.\nwith LockedCamera(0, display='channels', \n                  process=lambda img: channel_options(downsize(img, 4))) as cam:\n    cam.stream()\n```\n\n### Stream and Record\n```python\nfrom pcv.vidIO import Camera\n\nwith Camera(0) as cam:\n    print(\"press 'q' to quit and stop recording.\")\n    cam.record_stream('me.mp4')\n```\n\n### Screen Functionality\n#### Main Monitor\n```python\nimport cv2\nfrom pcv.screen import LockedScreen\n\n# stream monitor 0, and record to 'screen-record.mp4' file.\nwith LockedScreen(0, process=lambda img: \\\n                  cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) as screen:\n    # video-recording requires 3-channel (BGR) or single-channel\n    #  (greyscale, isColor=False) to work\n    screen.record_stream('screen-record.mp4')\n```\n#### Screen Region\n```python\nfrom pcv.Screen import Screen\n\nwith Screen({'left': -10, 'top': 50, 'width': 100, 'height': 200}) as screen:\n    screen.stream()\n```\n\n### VideoReader\n```python\nfrom pcv.vidIO import VideoReader\nfrom pcv.process import downsize\n\n# just play (simple)\n# Press 'b' to jump playback back to the beginning (only works if pressed\n#   before playback is finished)\nwith VideoReader('my_vid.mp4') as vid:\n    vid.stream()\n    \n# start 15 seconds in, end at 1:32, downsize the video by a factor of 4\nwith VideoReader('my_vid.mp4', start='15', end='1:32', \n                 preprocess=lambda img: downsize(img, 4)) as vid:\n    vid.stream()\n    \n# enable rewinding and super fast playback\n# Press 'a' to rewind, 'd' to go forwards, 'w' to speed up, 's' to slow down\n#    and 'r' to reset to forwards at 1x speed.\nwith VideoReader('my_vid.mp4', skip_frames=0) as vid:\n    vid.stream()\n    \n# headless mode (no display), operating on every 10th frame\nwith VideoReader('my_vid.mp4', auto_delay=False, skip_frames=10,\n                 process=my_processing_func) as vid:\n    vid.headless_stream()\n```\n\n### Mouse Events\n```python\n# Example courtesy of @MatusGasparik in Issue#15\nimport cv2\nfrom pcv.vidIO import VideoReader\nfrom pcv.interact import MouseCallback\n\ndef on_mouse_click(event, x, y, flags, params=None):\n    if event == cv2.EVENT_LBUTTONDOWN:\n        print('Click', x, y)\n\n# create a window beforehand so a mouse callback can be assigned [REQUIRED]\nwindow = 'foo'\ncv2.namedWindow(window)\nwith VideoReader('my_vid.mp4', display=window) as vid:\n    vid.stream(mouse_handler=MouseCallback(vid.display, on_mouse_click))\n```\n\n### Advanced Examples\nCheck the `pcv/examples` folder for some examples of full programs using this library.\n\n#### Something Fishy\nThis example is a relatively basic augmented reality example, which creates a tank of\nfish that swim around on top of a video/webbcam feed. You can catch the fish (click and\ndrag a 'net' over them with your mouse), or tickle them (move around in your webcam feed).\nThere are several generally useful processing techniques included, so take a look \nthrough the code and find the functionality that's most interesting to you to explore\nand modify.\n\nTo run the example use `python3 -m pcv.examples.something_fishy`, or optionally specify\nthe path to a newline separated text file of names (of your friends and family for \nexample), and run with `python3 -m pcv.examples.something_fishy path/to/names.txt`.\n\n#### Video Switcher\nThis was made in response to a question from `u/guillerubio` on reddit, to show how to\nefficiently read multiple videos simultaneously while only displaying one, and switching\nbetween videos based on what's happening in a webcam feed. \n\nThe current video is switched out when the camera is covered/uncovered. Switching is\nperformed intelligently by only actually reading frames from a single 'active' video\nat a time. The `VideoSwitcher` class allows tracking along all the videos simultaneously\nbased on how many frames have occurred since they were last active, as well as just\nresuming from where each one left off when it was last active. When a video ends it gets\nstarted again (in an infinite loop).\n\nTo run the example use `python3 -m pcv.examples.cam_video_switch` while in a directory\nwith the `.mp4` files you want to switch between.\n\n#### Hand Writer\nThis uses Google's `mediapipe` library for hand detection, to show a relatively simple\nAR workflow, where a pointed right hand index finger can be used to write/draw on a\ncamera feed. Once you've installed the library (`python3 -m pip install mediapipe`),\nrun with `python3 -m pcv.examples.hand_write` and start drawing.\n\nThe pointing detection algorithm is quite rudimentary, so may require some angling of\nyour hand before it detects that the pointer finger is straight and the other fingers\nare closed. It currently intentionally only detects right hands, so your left hand can\nalso be in the frame without causing issues. The side detection assumes a front-facing\n(selfie) camera, as is commonly the case with webcams and similar.\n\n# Contributors 💜\n\n- MatusGasparik\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fes-alexander%2Fpythonic-cv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fes-alexander%2Fpythonic-cv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fes-alexander%2Fpythonic-cv/lists"}