{"id":15636552,"url":"https://github.com/yxlao/camtools","last_synced_at":"2025-05-16T18:06:53.029Z","repository":{"id":50330112,"uuid":"440100236","full_name":"yxlao/camtools","owner":"yxlao","description":"CamTools: Camera Tools for Computer Vision","archived":false,"fork":false,"pushed_at":"2025-02-18T08:30:02.000Z","size":774,"stargazers_count":193,"open_issues_count":7,"forks_count":12,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-15T14:54:54.785Z","etag":null,"topics":["3d-reconstruction","calibration","camera","computer-graphics","computer-vision","extrinsic","extrinsic-parameters","extrinsics","intrinsic","intrinsic-parameters","intrinsics","lidar","nerf","opencv","opengl","ray","raycasting"],"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/yxlao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/contributing.rst","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":"2021-12-20T08:45:53.000Z","updated_at":"2025-05-04T05:01:27.000Z","dependencies_parsed_at":"2023-10-04T10:15:19.551Z","dependency_job_id":"1b688f02-e66c-40fb-845b-2780705d0638","html_url":"https://github.com/yxlao/camtools","commit_stats":{"total_commits":148,"total_committers":1,"mean_commits":148.0,"dds":0.0,"last_synced_commit":"4a64aacaec578523189710685820a229ecf1e5f3"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yxlao%2Fcamtools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yxlao%2Fcamtools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yxlao%2Fcamtools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yxlao%2Fcamtools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yxlao","download_url":"https://codeload.github.com/yxlao/camtools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582905,"owners_count":22095518,"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":["3d-reconstruction","calibration","camera","computer-graphics","computer-vision","extrinsic","extrinsic-parameters","extrinsics","intrinsic","intrinsic-parameters","intrinsics","lidar","nerf","opencv","opengl","ray","raycasting"],"created_at":"2024-10-03T11:04:58.480Z","updated_at":"2025-05-16T18:06:53.002Z","avatar_url":"https://github.com/yxlao.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camtools_logo_dark.png\"\u003e\n    \u003cimg alt=\"CamTools Logo\" src=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camtools_logo_light.png\" width=\"360\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eCamTools: Camera Tools for Computer Vision\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://camtools.readthedocs.io/\"\u003e\u003cstrong\u003eDocs\u003c/strong\u003e\u003c/a\u003e |\n  \u003ca href=\"https://github.com/yxlao/camtools\"\u003e\u003cstrong\u003eRepo\u003c/strong\u003e\u003c/a\u003e |\n  \u003ca href=\"#installation\"\u003e\u003cstrong\u003eInstallation\u003c/strong\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n[![Formatter](https://github.com/yxlao/camtools/actions/workflows/formatter.yml/badge.svg)](https://github.com/yxlao/camtools/actions/workflows/formatter.yml)\n[![Unit Test](https://github.com/yxlao/camtools/actions/workflows/unit_test.yml/badge.svg)](https://github.com/yxlao/camtools/actions/workflows/unit_test.yml)\n[![PyPI](https://github.com/yxlao/camtools/actions/workflows/pypi.yml/badge.svg)](https://github.com/yxlao/camtools/actions/workflows/pypi.yml)\n[![PyPI](https://img.shields.io/pypi/v/camtools?style=flat\u0026label=PyPI\u0026logo=PyPI\u0026logoColor=959DA5\u0026labelColor=323940\u0026color=808080)](https://pypi.org/project/camtools)\n[![Docs](https://readthedocs.org/projects/camtools/badge/?version=latest)](https://camtools.readthedocs.io/en/latest/?badge=latest)\n\nCamTools is a collection of tools for handling cameras in computer vision. It\ncan be used for plotting, converting, projecting, ray casting, and doing more\nwith camera parameters. It follows the standard camera coordinate system with\nclear and easy-to-use APIs.\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource\n      media=\"(prefers-color-scheme: dark)\"\n      srcset=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_dark.png\"\u003e\n    \u003cimg\n      alt=\"CamTools Logo\"\n      src=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_coordinates_light.png\"\n      width=\"520\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n## What can you do with CamTools?\n\n1. Plot cameras. Useful for debugging 3D reconstruction and NeRFs!\n\n   ```python\n   import camtools as ct\n   import open3d as o3d\n   cameras = ct.camera.create_camera_frustums(Ks, Ts)\n   o3d.visualization.draw_geometries([cameras])\n   ```\n\n   \u003cp align=\"center\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/camera_frames.png\" width=\"360\" /\u003e\n   \u003c/p\u003e\n\n2. Convert camera parameters.\n\n   ```python\n   pose = ct.convert.T_to_pose(T)     # Convert T to pose\n   T    = ct.convert.pose_to_T(pose)  # Convert pose to T\n   R, t = ct.convert.T_to_R_t(T)      # Convert T to R and t\n   C    = ct.convert.pose_to_C(pose)  # Convert pose to camera center\n   K, T = ct.convert.P_to_K_T(P)      # Decompose projection matrix P to K and T\n                                      # And more...\n   ```\n\n3. Projection and ray casting.\n\n   ```python\n   # Project 3D points to pixels.\n   pixels = ct.project.points_to_pixel(points, K, T)\n\n   # Back-project depth image to 3D points.\n   points = ct.project.im_depth_to_points(im_depth, K, T)\n\n   # Ray cast a triangle mesh to depth image given the camera parameters.\n   im_depth = ct.raycast.mesh_to_im_depth(mesh, K, T, height, width)\n\n   # And more...\n   ```\n\n4. Image and depth I/O with no surprises.\n\n   Strict type checks and range checks are enforced. The image and depth I/O\n   APIs are specifically designed to solve the following pain points:\n\n   - Is my image of type `float32` or `uint8`?\n   - Does it have range `[0, 1]` or `[0, 255]`?\n   - Is it RGB or BGR?\n   - Does my image have an alpha channel?\n   - When saving depth image as integer-based `.png`, is it correctly scaled?\n\n   ```python\n   ct.io.imread()\n   ct.io.imwrite()\n   ct.io.imread_detph()\n   ct.io.imwrite_depth()\n   ```\n\n5. Command-line tools `ct` (runs in terminal).\n\n   ```bash\n   # Crop image boarders.\n   ct crop-boarders *.png --pad_pixel 10 --skip_cropped --same_crop\n\n   # Draw synchronized bounding boxes interactively.\n   ct draw-bboxes path/to/a.png path/to/b.png\n\n   # For more command-line tools.\n   ct --help\n   ```\n\n   \u003cp align=\"center\"\u003e\n      \u003cimg src=\"https://user-images.githubusercontent.com/1501945/241416210-e11ff3bf-22e6-46c0-8ba0-d177a0015323.png\" width=\"400\" /\u003e\n   \u003c/p\u003e\n\n6. And more.\n   - Solve line intersections.\n   - COLMAP tools.\n   - Points normalization.\n   - ...\n\n## Installation\n\nTo install CamTools, simply do:\n\n```bash\npip install camtools\n```\n\nAlternatively, you can install CamTools from source with one of the following\nmethods:\n\n```bash\ngit clone https://github.com/yxlao/camtools.git\ncd camtools\n\n# Installation mode, if you want to use camtools only.\npip install .\n\n# Editable mode, if you want to modify camtools on the fly.\npip install -e .\n\n# Editable mode and dev dependencies.\npip install -e .[dev]\n\n# Help VSCode resolve imports when installed with editable mode.\n# https://stackoverflow.com/a/76897706/1255535\npip install -e .[dev] --config-settings editable_mode=strict\n\n# Enable torch-related features (e.g. computing image metrics)\npip install camtools[torch]\n\n# Enable torch-related features in editable mode\npip install -e .[torch]\n```\n\n## Camera coordinate system\n\nA homogeneous point `[X, Y, Z, 1]` in the world coordinate can be projected to a\nhomogeneous point `[x, y, 1]` in the image (pixel) coordinate using the\nfollowing equation:\n\n$$\n\\lambda\n\\left[\\begin{array}{l}\nx \\\\\ny \\\\\n1\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nf_{x} \u0026 0 \u0026 c_{x} \\\\\n0 \u0026 f_{y} \u0026 c_{y} \\\\\n0 \u0026 0 \u0026 1\n\\end{array}\\right]\\left[\\begin{array}{llll}\nR_{00} \u0026 R_{01} \u0026 R_{02} \u0026 t_{0} \\\\\nR_{10} \u0026 R_{11} \u0026 R_{12} \u0026 t_{1} \\\\\nR_{20} \u0026 R_{21} \u0026 R_{22} \u0026 t_{2}\n\\end{array}\\right]\\left[\\begin{array}{c}\nX \\\\\nY \\\\\nZ \\\\\n1\n\\end{array}\\right].\n$$\n\nWe follow the standard OpenCV-style camera coordinate system as illustrated at\nthe beginning of the README.\n\n- **Camera Coordinates:** right-handed, with $Z$ pointing away from the camera\n  towards the view direction and $Y$ axis pointing down. Note that the OpenCV\n  convention (camtools' default) is different from the OpenGL/Blender\n  convention, where $Z$ points towards the opposite view direction, $Y$ points\n  up and $X$ points right. To convert between the OpenCV camera coordinates and\n  the OpenGL-style coordinates, use the conversion functions:\n  - `ct.convert.T_opencv_to_opengl()`\n  - `ct.convert.T_opengl_to_opencv()`\n  - `ct.convert.pose_opencv_to_opengl()`\n  - `ct.convert.pose_opengl_to_opencv()`\n- **Image Coordinates:** starts from the top-left corner of the image, with $x$\n  pointing right (corresponding to the image width) and $y$ pointing down\n  (corresponding to the image height). This is consistent with OpenCV. Pay\n  attention that the 0th dimension in the image array is the height (i.e., $y$)\n  and the 1st dimension is the width (i.e., $x$). That is:\n  - $x$ \u003c=\u003e $u$ \u003c=\u003e width \u003c=\u003e column \u003c=\u003e the 1st dimension\n  - $y$ \u003c=\u003e $v$ \u003c=\u003e height \u003c=\u003e row \u003c=\u003e the 0th dimension\n- `K`: `(3, 3)` camera intrinsic.\n  ```python\n  K = [[fx,  s, cx],\n       [ 0, fy, cy],\n       [ 0,  0,  1]]\n  ```\n- `T` or `W2C`: `(4, 4)` camera extrinsic.\n  ```python\n  T = [[R  | t   = [[R00, R01, R02, t0],\n        0  | 1]]    [R10, R11, R12, t1],\n                    [R20, R21, R22, t2],\n                    [  0,   0,   0,  1]]\n  ```\n  - `T` is also known as the world-to-camera `W2C` matrix, which transforms a\n    point in the world coordinate to the camera coordinate.\n  - `T`'s shape is `(4, 4)`, not `(3, 4)`.\n  - `T` is the inverse of `pose`, i.e., `np.linalg.inv(T) == pose`.\n  - The camera center `C` in world coordinate is projected to `[0, 0, 0, 1]` in\n    camera coordinate.\n- `R`: `(3, 3)` rotation matrix.\n  ```python\n  R = T[:3, :3]\n  ```\n  - `R` is a rotation matrix. It is an orthogonal matrix with determinant 1, as\n    rotations preserve volume and orientation.\n    - `R.T == np.linalg.inv(R)`\n    - `np.linalg.norm(R @ x) == np.linalg.norm(x)`, where `x` is a `(3,)`\n      vector.\n- `t`: `(3,)` translation vector.\n  ```python\n  t = T[:3, 3]\n  ```\n  - `t`'s shape is `(3,)`, not `(3, 1)`.\n- `pose` or `C2W`: `(4, 4)` camera pose. It is the inverse of `T`.\n  - `pose` is also known as the camera-to-world `C2W` matrix, which transforms a\n    point in the camera coordinate to the world coordinate.\n  - `pose` is the inverse of `T`, i.e., `pose == np.linalg.inv(T)`.\n- `C`: camera center.\n  ```python\n  C = pose[:3, 3]\n  ```\n  - `C`'s shape is `(3,)`, not `(3, 1)`.\n  - `C` is the camera center in world coordinate. It is also the translation\n    vector of `pose`.\n- `P`: `(3, 4)` the camera projection matrix.\n  - `P` is the world-to-pixel projection matrix, which projects a point in the\n    homogeneous world coordinate to the homogeneous pixel coordinate.\n  - `P` is the product of the intrinsic and extrinsic parameters.\n    ```python\n    # P = K @ [R | t]\n    P = K @ np.hstack([R, t[:, None]])\n    ```\n  - `P`'s shape is `(3, 4)`, not `(4, 4)`.\n  - It is possible to decompose `P` into intrinsic and extrinsic matrices by QR\n    decomposition.\n  - Don't confuse `P` with `pose`. Don't confuse `P` with `T`.\n- For more details, please refer to the following blog posts:\n  [part 1](https://ksimek.github.io/2012/08/14/decompose/),\n  [part 2](https://ksimek.github.io/2012/08/22/extrinsic/),\n  and [part 3](https://ksimek.github.io/2013/08/13/intrinsic/).\n\n## Building Documentation\n\nTo build and view the documentation locally:\n\n```bash\n# Install documentation dependencies\npip install -e .[docs]\n\n# Build the documentation\nmake -C docs clean \u0026\u0026 make -C docs html\n\n# (Optional) Build the documentation with warnings as errors\nmake -C docs clean \u0026\u0026 make -C docs html SPHINXOPTS=\"-W --keep-going\"\n\n# Start a local server to view the documentation\npython -m http.server 8000 --directory docs/_build/html\n```\n\nThen open your browser and navigate to `http://localhost:8000` to view the documentation.\n\nThe documentation is also automatically built by GitHub Actions on pull requests and pushes to main. After merging to main, you can view:\n\n- Public documentation at https://camtools.readthedocs.io/en/latest/\n- Admin panel at https://app.readthedocs.org/projects/camtools/\n\n## Contributing\n\n- Follow [Angular's commit message convention](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format) for PRs.\n  - This applies to PR's title and ultimately the commit messages in `main`.\n  - The prefix shall be one of `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `test`.\n  - Use lowercase.\n- Format your code with [black](https://github.com/psf/black). This will be enforced by the CI.\n\n## Build with CamTools\n\nIf you use CamTools in your project, consider adding one of the following\nbadges to your project.\n\n\u003cp\u003e\n\u003ca href=\"https://github.com/yxlao/camtools\"\u003e\u003cimg alt=\"Built with CamTools\" src=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/built_with_camtools_dark.svg\" width=240\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/yxlao/camtools\"\u003e\u003cimg alt=\"Built with CamTools\" src=\"https://raw.githubusercontent.com/yxlao/camtools/main/camtools/assets/built_with_camtools_light.svg\" width=240\u003e\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyxlao%2Fcamtools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyxlao%2Fcamtools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyxlao%2Fcamtools/lists"}