{"id":14977712,"url":"https://github.com/34j/vr180-convert","last_synced_at":"2025-10-05T02:23:12.530Z","repository":{"id":233853116,"uuid":"787920471","full_name":"34j/vr180-convert","owner":"34j","description":"Simple VR180 image (format) converter (e.g. equidistant → equirectangular, rotation calibration)","archived":false,"fork":false,"pushed_at":"2025-09-19T10:38:49.000Z","size":6107,"stargazers_count":12,"open_issues_count":16,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-19T12:37:41.244Z","etag":null,"topics":["opencv","python","stereo-calibration","stereo-camera","stereo-images","stereo-matching","stereo-vision","virtual-reality","vr180"],"latest_commit_sha":null,"homepage":"","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/34j.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["34j"]}},"created_at":"2024-04-17T12:41:25.000Z","updated_at":"2025-09-15T12:13:50.000Z","dependencies_parsed_at":"2025-05-12T17:47:29.677Z","dependency_job_id":"16ebad92-690d-4e86-b312-78c78f8384e6","html_url":"https://github.com/34j/vr180-convert","commit_stats":{"total_commits":75,"total_committers":4,"mean_commits":18.75,"dds":0.4,"last_synced_commit":"681c083a7c0e696da8dcd79cc694774ec263cfff"},"previous_names":["34j/vr180-convert"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/34j/vr180-convert","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/34j%2Fvr180-convert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/34j%2Fvr180-convert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/34j%2Fvr180-convert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/34j%2Fvr180-convert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/34j","download_url":"https://codeload.github.com/34j/vr180-convert/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/34j%2Fvr180-convert/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278399691,"owners_count":25980334,"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-10-05T02:00:06.059Z","response_time":54,"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":["opencv","python","stereo-calibration","stereo-camera","stereo-images","stereo-matching","stereo-vision","virtual-reality","vr180"],"created_at":"2024-09-24T13:56:11.285Z","updated_at":"2025-10-05T02:23:12.513Z","avatar_url":"https://github.com/34j.png","language":"Python","funding_links":["https://github.com/sponsors/34j"],"categories":[],"sub_categories":[],"readme":"# VR180 image converter\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/34j/vr180-convert/actions/workflows/ci.yml?query=branch%3Amain\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/34j/vr180-convert/ci.yml?branch=main\u0026label=CI\u0026logo=github\u0026style=flat-square\" alt=\"CI Status\" \u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://vr180-convert.readthedocs.io\"\u003e\n    \u003cimg src=\"https://img.shields.io/readthedocs/vr180-convert.svg?logo=read-the-docs\u0026logoColor=fff\u0026style=flat-square\" alt=\"Documentation Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/34j/vr180-convert\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/github/34j/vr180-convert.svg?logo=codecov\u0026logoColor=fff\u0026style=flat-square\" alt=\"Test coverage percentage\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://python-poetry.org/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=\" alt=\"Poetry\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/ambv/black\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square\" alt=\"black\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/pre-commit/pre-commit\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white\u0026style=flat-square\" alt=\"pre-commit\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.org/project/vr180-convert/\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/vr180-convert.svg?logo=python\u0026logoColor=fff\u0026style=flat-square\" alt=\"PyPI Version\"\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/pypi/pyversions/vr180-convert.svg?style=flat-square\u0026logo=python\u0026amp;logoColor=fff\" alt=\"Supported Python versions\"\u003e\n  \u003cimg src=\"https://img.shields.io/pypi/l/vr180-convert.svg?style=flat-square\" alt=\"License\"\u003e\n\u003c/p\u003e\n\n---\n\n**Documentation**: \u003ca href=\"https://vr180-convert.readthedocs.io\" target=\"_blank\"\u003ehttps://vr180-convert.readthedocs.io \u003c/a\u003e\n\n**Source Code**: \u003ca href=\"https://github.com/34j/vr180-convert\" target=\"_blank\"\u003ehttps://github.com/34j/vr180-convert \u003c/a\u003e\n\n---\n\nSimple VR180 image converter on top of OpenCV and NumPy.\n\n## Installation\n\nInstall this via pip (or your favourite package manager):\n\n```shell\npipx install vr180-convert\n```\n\n## Usage\n\nSimply run the following command to convert 2 fisheye images to a SBS equirectangular VR180 image:\n\n```shell\nv1c lr left.jpg right.jpg\n```\n\n| left.jpg                       | right.jpg                       | Output                                               |\n| ------------------------------ | ------------------------------- | ---------------------------------------------------- |\n| ![left](docs/_static/test.jpg) | ![right](docs/_static/test.jpg) | ![output](docs/_static/test.lr.PolynomialScaler.jpg) |\n\nIf left and right image paths are the same, the image is divided into two halves (left and right, SBS) and processed as if they were separate images.\n\n## Advanced usage\n\n### Automatic image search\n\nIf one of left or right image path is a directory, the program will search for the closest image (in terms of creation time) in the other directory.\n\n```shell\nv1c lr left.jpg right_dir\nv1c lr left_dir right.jpg\n```\n\nSince clocks on cameras may not be very accurate in some cases, it is recommended to check how quickly the clocks of the two cameras shift, and synchronize the clocks before shooting.\nHowever, it can be adjusted by specifying `-ac` option.\n\n```shell\nv1c lr left.jpg right_dir -ac 1 # the clock of the right camera is 1 second faster / ahead\nv1c lr left_dir right.jpg -ac 1 # the clock of the right camera is 1 second faster / ahead\n```\n\n### Radius estimation\n\nThe radius of the non-black area of the input image is assumed by counting black pixels by default, but it would be better to specify it manually to get stable results:\n\n```shell\nv1c lr left.jpg right.jpg --radius 1000\nv1c lr left.jpg right.jpg --radius max # min(width, height) / 2\n```\n\n### Calibration\n\n[Rotation matching using the least-squares method](https://lisyarus.github.io/blog/posts/3d-shape-matching-with-quaternions.html) can be performed by clicking corresponding points that can be regarded as infinitely far away from the camera.\n\n- Using AKAZE as a feature matcher:\n\n```shell\nv1c lr left.jpg right.jpg --automatch fm\n```\n\nSince matching by AKAZE involves false detections, matching points with high loss are considered outliers, and the least-squares method is repeated multiple times to remove them.\nChecking if the image should be swapped using feature matching might be theoretically possible but not implemented.\n\n#### See Also\n\n- [1.1.16. Robustness regression: outliers and modeling errors](https://scikit-learn.org/stable/modules/linear_model.html#robustness-regression-outliers-and-modeling-errors)\n\n- Manually specifying the corresponding points using the GUI:\n\n```shell\nv1c lr left.jpg right.jpg --automatch gui\n```\n\n- Manually specifying the corresponding points using the CLI:\n\n```shell\nv1c lr left.jpg right.jpg --automatch \"0,0;0,0;1,1;1,1\" # left_x1,left_y1;right_x1,right_y1;...\n```\n\n$$\na_k, b_k \\in \\mathbb{R}^3,\n\\min_{R \\in SO(3)} \\sum_k \\|R a_k - b_k\\|^2\n$$\n\nPlease also refer to the [Documentation](https://vr180-convert.readthedocs.io/en/latest/math.html) for mathematical details.\n\n### Anaglyph\n\n`--merge` option (which exports as [anaglyph](https://en.wikipedia.org/wiki/Anaglyph_3D) image) can be used to check if the calibration is successful by checking if the infinitely far points are overlapped.\n\n```shell\nv1c lr left.jpg right.jpg --automatch gui --merge\n```\n\n### Swap\n\nIf the camera is mounted upside down, you can simply use the `--swap` option without changing the transformer or other parameters:\n\n```shell\nv1c lr left.jpg right.jpg --swap\n```\n\nOr the image can be simply swapped using the `swap` command:\n\n```shell\nv1c swap rl.jpg\n```\n\nin case one notices that the left and right images are swapped after the conversion.\n\n### Convert to Google's format (Photo Sphere XMP Metadata)\n\nThis format is special in that it base64-encodes the right-eye image into the metadata of the left-eye image.\nRequired for Google Photos, etc.\n\nYou can convert the image to this format by:\n\n```shell\nv1c xmp lr.jpg\n```\n\nThe [python-xmp-toolkit](https://github.com/python-xmp-toolkit/python-xmp-toolkit) used in this command requires [exempi](https://libopenraw.freedesktop.org/exempi/) to be installed. Note that if this command is called on Windows, it will attempt to install this library and its dependencies and then run the command on WSL using `subprocess`.\n\n#### References\n\n- [imrivera/google\\-photos\\-vr180\\-test: Test for XMP metadata parsing for VR180 pictures in Google Photos](https://github.com/imrivera/google-photos-vr180-test)\n- [temoki/make_vr180photo_py: 左眼カメラ画像と右眼カメラ画像を結合して VR180 3D フォトを作成する Python スクリプト](https://github.com/temoki/make_vr180photo_py)\n\n### Custom conversion model\n\nYou can also specify the conversion model by adding Python code directly to the `--transformer` option:\n\n```shell\nv1c lr left.jpg right.jpg --transformer 'EquirectangularEncoder() * Euclidean3DRotator(from_rotation_vector([0, np.pi / 4, 0])) * FisheyeDecoder(\"equidistant\")'\n```\n\nIf tuple, the first transformer is applied to the left image and the second transformer is applied to the right image. If a single transformer is given, it is applied to both images.\n\nPlease refer to the [API documentation](https://vr180-convert.readthedocs.io/) for the available transformers and their parameters.\nFor `from_rotation_vector`, please refer to the [numpy-quaternion documentation](https://quaternion.readthedocs.io/en/latest/Package%20API%3A/quaternion/#from_rotation_vector).\n\n### Single image conversion\n\nTo convert a single image, use `v1c s` instead.\n\n### Running commands for all images in a directory\n\n```shell\nfind left_dir -type f -name '*.jpg' -exec v1c lr {} right_dir --automatch fm --radius max -ac 0 --out-path out \\;\n```\n\n### Help\n\nFor more information, please refer to the help or API documentation:\n\n```shell\nv1c --help\n```\n\n## Usage as a library\n\nFor more complex transformations, it is recommended to create your own `Transformer`.\n\nNote that the transformation is applied in inverse order (new[(x, y)] = old[transform(x, y)], e.g. to decode [orthographic](https://en.wikipedia.org/wiki/Fisheye_lens#Mapping_function) fisheye images, `transform_polar` should be `arcsin(theta)`, not `sin(theta)`.)\n\n```python\nfrom vr180_convert import PolarRollTransformer, apply_lr\n\nclass MyTransformer(PolarRollTransformer):\n    def transform_polar(\n        self, theta: NDArray, roll: NDArray, **kwargs: Any\n    ) -\u003e tuple[NDArray, NDArray]:\n        return theta**0.98 + theta**1.01, roll\n\ntransformer = EquirectangularEncoder() * MyTransformer() * FisheyeDecoder(\"equidistant\")\napply_lr(transformer, left_path=\"left.jpg\", right_path=\"right.jpg\", out_path=\"output.jpg\")\n```\n\n## Tips\n\n### How to determine which image is left or right\n\n\u003c!--\n- In the left image, the subject faces more to the right.\n- In the right image, the subject faces more to the left.\n- In other words, in a SBS image, the subject is oriented toward the center.\n\nIn anaglyph images,\n\n- The left eye is covered with a red film, so the portion for the left eye is shown in blue.\n- The right eye is covered with a blue film, so the portion for the right eye is shown in red.\n| Film Color          | \u003cspan style=\"color:red\"\u003eRed\u003c/span\u003e   | \u003cspan style=\"color:blue\"\u003eBlue\u003c/span\u003e |\n| Anaglyph Color      | \u003cspan style=\"color:blue\"\u003eBlue\u003c/span\u003e | \u003cspan style=\"color:red\"\u003eRed\u003c/span\u003e   |\n--\u003e\n\n|                     | Left                        | Right                       |\n| ------------------- | --------------------------- | --------------------------- |\n| Subject Orientation | Right                       | Left                        |\n| Film Color          | ${\\color{red}\\text{Red}}$   | ${\\color{blue}\\text{Blue}}$ |\n| Anaglyph Color      | ${\\color{blue}\\text{Blue}}$ | ${\\color{red}\\text{Red}}$   |\n\n- In a SBS image, the subject is oriented toward the center.\n\n### How to edit images\n\nThis program cannot read RAW files. To deal with white-outs, etc., it is required to process each image with a program such as Photoshop, Lightroom, [RawTherapee](https://rawtherapee.com/downloads/), [Darktable](https://www.darktable.org/install/), etc.\n\nHowever, this is so exhaustive, so it is recommended to take the images with JPEG format with care to **avoid overexposure** and to **match the settings** of the two cameras, then convert them with this program and edit the converted images.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample of editing in RawTherapee (Light editing)\u003c/summary\u003e\n\n1. Rank the left images in RawTherapee.\n2. Use this program to convert the images.\n3. Edit the converted images in RawTherapee.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample of editing in Photoshop (Exquisite editing)\u003c/summary\u003e\n\n1. Rank the left images in RawTherapee or Lightroom.\n2. Open left image as a Smart Object `LRaw`.\n3. Add right image as a Smart Object `RRaw`.\n4. Make **minimal** corrections just to match the exposure using `Camera Raw Filter`.\n5. Make each Smart Object into Smart Objects (`L`, `R`) again and do any image-dependent processing, such as removing the background.\n6. Make both images into a single Smart Object (`P`) and process them as a whole.\n7. Export as a PNG file.\n8. Hide the other Smart Object (`L` or `R`) (created in step 3) in the Smart Object `P` (created in step 4) and save the Smart Object `P`, then export as a PNG file.\n9. Use this program to convert the images.\n\n\u003c/details\u003e\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## Credits\n\nThis package was created with\n[Copier](https://copier.readthedocs.io/) and the\n[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)\nproject template.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F34j%2Fvr180-convert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F34j%2Fvr180-convert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F34j%2Fvr180-convert/lists"}