├── camorph ├── lib │ ├── __init__.py │ ├── model │ │ ├── __init__.py │ │ ├── FileHandler.py │ │ └── Camera.py │ ├── utils │ │ ├── __init__.py │ │ ├── bin_writer_utils.py │ │ ├── file_utils.py │ │ └── bin_reader_utils.py │ ├── vis.rst │ ├── crucial_property.rst │ ├── model.rst │ ├── utils.rst │ ├── visualizer.py │ └── crucial_property.py ├── ext │ ├── FBX │ │ ├── util │ │ │ ├── __init__.py │ │ │ └── ReaderSemanticUtil.py │ │ ├── model │ │ │ ├── FBXNode.py │ │ │ ├── FBXPrimitiveValues.py │ │ │ ├── FBXConnection.py │ │ │ ├── FBXHierachyNode.py │ │ │ ├── FBXProperty.py │ │ │ ├── FBXNodeObject.py │ │ │ └── __init__.py │ │ └── __init__.py │ ├── COLMAP │ │ ├── util │ │ │ ├── __init__.py │ │ │ └── ReaderSyntacticUtil.py │ │ ├── __init__.py │ │ └── model │ │ │ ├── __init__.py │ │ │ ├── ColmapCamera.py │ │ │ ├── ColmapImage.py │ │ │ └── CameraModel.py │ ├── Meshroom │ │ ├── model │ │ │ ├── __init__.py │ │ │ └── sfm.py │ │ ├── util │ │ │ ├── __init__.py │ │ │ ├── WriterSyntacticUtil.py │ │ │ └── ReaderSyntacticUtil.py │ │ ├── __init__.py │ │ └── Mehsroom.py │ ├── Unity │ │ ├── model │ │ │ ├── __init__.py │ │ │ ├── UnityGameObject.py │ │ │ ├── UnityTransform.py │ │ │ └── UnityCamera.py │ │ ├── util │ │ │ ├── __init__.py │ │ │ ├── ReaderSyntacticUtil.py │ │ │ ├── ReaderSemanticUtil.py │ │ │ └── WriterSyntacticUtil.py │ │ ├── __init__.py │ │ └── Unity.py │ ├── LLFF │ │ ├── __init__.py │ │ └── LLFF.py │ ├── NeRF │ │ └── __init__.py │ ├── MPEG_OMAF │ │ ├── __init__.py │ │ └── MPEG_OMAF.py │ ├── RealityCapture │ │ └── __init__.py │ └── __init__.py ├── sphinx │ ├── license.rst │ ├── camorph.rst │ ├── formats │ │ ├── NeRF │ │ │ ├── NeRF_coordinatesystem.png │ │ │ └── NeRF.rst │ │ ├── FBX │ │ │ ├── fbx_coordinatesystem_cam-1.png │ │ │ └── FBX.rst │ │ ├── COLMAP │ │ │ └── colmap_coordinatesystem_cam.png │ │ ├── Unity │ │ │ ├── Unity_coordinatesystem_cam-1.png │ │ │ └── Unity.rst │ │ ├── Meshroom │ │ │ ├── Meshrrom_coordinatesystem_cam-1.png │ │ │ └── Meshroom.rst │ │ ├── MPEG_OMAF │ │ │ ├── MPEG_OMAF_coordinatesystem_cam-1.png │ │ │ └── MPEG_OMAF.rst │ │ └── RealityCapture │ │ │ ├── RealityCapture_coordinatesystem_cam-1.png │ │ │ └── RealityCapture.rst │ ├── formats.rst │ ├── code.rst │ ├── autocomputation.rst │ ├── about.rst │ ├── crucial_properties.rst │ └── plugin.rst ├── __main__.py ├── __init__.py ├── index.rst ├── conf.py ├── cli.py └── camorph.py ├── stats ├── .gitignore ├── Header.png ├── docs ├── sphinx │ ├── formats │ │ ├── NeRF │ │ │ ├── NeRF_coordinatesystem.png │ │ │ └── NeRF.md │ │ ├── FBX │ │ │ ├── fbx_coordinatesystem_cam-1.png │ │ │ └── FBX.md │ │ ├── COLMAP │ │ │ ├── colmap_coordinatesystem_cam.png │ │ │ └── COLMAP.md │ │ ├── Unity │ │ │ ├── Unity_coordinatesystem_cam-1.png │ │ │ └── Unity.md │ │ ├── Meshroom │ │ │ ├── Meshrrom_coordinatesystem_cam-1.png │ │ │ └── Meshroom.md │ │ ├── MPEG_OMAF │ │ │ ├── MPEG_OMAF_coordinatesystem_cam-1.png │ │ │ └── MPEG_OMAF.md │ │ └── RealityCapture │ │ │ ├── RealityCapture_coordinatesystem_cam-1.png │ │ │ └── RealityCapture.md │ ├── code.md │ ├── autocomputation.md │ ├── about.md │ ├── formats.md │ ├── crucial_properties.md │ ├── plugin.md │ └── camorph.md ├── lib │ ├── vis.md │ ├── crucial_property.md │ ├── model.md │ └── utils.md └── index.md ├── camorph_cli.bat ├── main.py ├── Makefile ├── pyproject.toml ├── make.bat ├── thirdPartyLegalNotices ├── pyparsing.txt ├── setuptools-scm.txt ├── six.txt ├── tomli.txt ├── fontools.txt ├── pyYaml.txt ├── PyQuaternion.txt ├── packaging.txt ├── Pillow.txt ├── cycler.txt ├── rdflib.txt ├── Numpy.txt ├── isodate.txt ├── dateutil.txt ├── kiwi.txt └── matplotlib.txt ├── requirements.txt ├── LICENSE.txt ├── camorph_win.yml ├── camorph_linux.yml └── README.md /camorph/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/FBX/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/lib/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/lib/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/Meshroom/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/Meshroom/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/Unity/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/Unity/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXNode.py: -------------------------------------------------------------------------------- 1 | class FBXNode(dict): 2 | pass -------------------------------------------------------------------------------- /stats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/stats -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | /build/ 3 | testdata/ 4 | .vscode/ 5 | *egg-info/ -------------------------------------------------------------------------------- /Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/Header.png -------------------------------------------------------------------------------- /camorph/ext/FBX/__init__.py: -------------------------------------------------------------------------------- 1 | from .FBX import FBX 2 | 3 | camorph_extension = FBX 4 | -------------------------------------------------------------------------------- /camorph/ext/LLFF/__init__.py: -------------------------------------------------------------------------------- 1 | from .LLFF import LLFF 2 | 3 | camorph_extension = LLFF -------------------------------------------------------------------------------- /camorph/ext/NeRF/__init__.py: -------------------------------------------------------------------------------- 1 | from .NeRF import NeRF 2 | 3 | camorph_extension = NeRF -------------------------------------------------------------------------------- /camorph/ext/Unity/__init__.py: -------------------------------------------------------------------------------- 1 | from .Unity import Unity 2 | 3 | camorph_extension = Unity -------------------------------------------------------------------------------- /camorph/sphinx/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | Lorem ipsum dolor sit amet 5 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/__init__.py: -------------------------------------------------------------------------------- 1 | from .COLMAP import COLMAP 2 | 3 | camorph_extension = COLMAP -------------------------------------------------------------------------------- /camorph/ext/Meshroom/__init__.py: -------------------------------------------------------------------------------- 1 | from .Mehsroom import Meshroom 2 | 3 | camorph_extension = Meshroom -------------------------------------------------------------------------------- /camorph/__main__.py: -------------------------------------------------------------------------------- 1 | import cli 2 | 3 | if __name__ == '__main__': 4 | cli.run(run_as_module=True) 5 | -------------------------------------------------------------------------------- /camorph/ext/MPEG_OMAF/__init__.py: -------------------------------------------------------------------------------- 1 | from .MPEG_OMAF import MPEG_OMAF 2 | 3 | camorph_extension = MPEG_OMAF -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXPrimitiveValues.py: -------------------------------------------------------------------------------- 1 | array_type_size = {'f': 4, 'd': 8, 'l': 8, 'i': 4, 'b': 1, 'c' : 1} -------------------------------------------------------------------------------- /camorph/sphinx/camorph.rst: -------------------------------------------------------------------------------- 1 | ``camorph`` Module 2 | ================== 3 | .. automodule:: camorph 4 | :members: 5 | -------------------------------------------------------------------------------- /camorph/ext/RealityCapture/__init__.py: -------------------------------------------------------------------------------- 1 | from .RealityCapture import RealityCapture 2 | 3 | camorph_extension = RealityCapture -------------------------------------------------------------------------------- /camorph/lib/vis.rst: -------------------------------------------------------------------------------- 1 | ``visualizer`` Module 2 | ===================== 3 | 4 | .. automodule:: visualizer 5 | :members: 6 | -------------------------------------------------------------------------------- /camorph/__init__.py: -------------------------------------------------------------------------------- 1 | import camorph.ext as ext 2 | 3 | crucial_property_config = None 4 | imported_instances = ext.imported_instances 5 | 6 | -------------------------------------------------------------------------------- /camorph/lib/crucial_property.rst: -------------------------------------------------------------------------------- 1 | ``crucial_property`` module 2 | =========================== 3 | 4 | .. automodule:: crucial_property 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/sphinx/formats/NeRF/NeRF_coordinatesystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/NeRF/NeRF_coordinatesystem.png -------------------------------------------------------------------------------- /camorph/sphinx/formats/NeRF/NeRF_coordinatesystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/NeRF/NeRF_coordinatesystem.png -------------------------------------------------------------------------------- /docs/sphinx/formats/FBX/fbx_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/FBX/fbx_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXConnection.py: -------------------------------------------------------------------------------- 1 | class FBXConnection: 2 | 3 | def __init__(self, ctype, cnames): 4 | self.ctype = ctype 5 | self.cnames = cnames -------------------------------------------------------------------------------- /camorph/sphinx/formats/FBX/fbx_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/FBX/fbx_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /docs/sphinx/formats/COLMAP/colmap_coordinatesystem_cam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/COLMAP/colmap_coordinatesystem_cam.png -------------------------------------------------------------------------------- /docs/sphinx/formats/Unity/Unity_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/Unity/Unity_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/sphinx/formats/COLMAP/colmap_coordinatesystem_cam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/COLMAP/colmap_coordinatesystem_cam.png -------------------------------------------------------------------------------- /camorph/sphinx/formats/Unity/Unity_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/Unity/Unity_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXHierachyNode.py: -------------------------------------------------------------------------------- 1 | class FBXHierarchyNode: 2 | 3 | def __init__(self, id): 4 | self.id = id 5 | self.object = None 6 | self.children = [] -------------------------------------------------------------------------------- /docs/sphinx/formats/Meshroom/Meshrrom_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/Meshroom/Meshrrom_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/ext/COLMAP/model/__init__.py: -------------------------------------------------------------------------------- 1 | from .ColmapImage import ColmapImage 2 | from .ColmapCamera import ColmapCamera 3 | 4 | ColmapImage = ColmapImage 5 | ColmapCamera = ColmapCamera 6 | 7 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/Meshroom/Meshrrom_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/Meshroom/Meshrrom_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /docs/sphinx/formats/MPEG_OMAF/MPEG_OMAF_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/MPEG_OMAF/MPEG_OMAF_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/sphinx/formats/MPEG_OMAF/MPEG_OMAF_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/MPEG_OMAF/MPEG_OMAF_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXProperty.py: -------------------------------------------------------------------------------- 1 | class FBXProperty: 2 | 3 | def __init__(self, pname, ptype, pvalue): 4 | self.pname = pname 5 | self.ptype = ptype 6 | self.pvalue = pvalue 7 | -------------------------------------------------------------------------------- /docs/sphinx/formats/RealityCapture/RealityCapture_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/docs/sphinx/formats/RealityCapture/RealityCapture_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/sphinx/formats/RealityCapture/RealityCapture_coordinatesystem_cam-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-IIS/camorph/HEAD/camorph/sphinx/formats/RealityCapture/RealityCapture_coordinatesystem_cam-1.png -------------------------------------------------------------------------------- /camorph/lib/model.rst: -------------------------------------------------------------------------------- 1 | ``Camera`` Class 2 | ================ 3 | .. autoclass:: model.Camera.Camera 4 | :members: 5 | 6 | ``FileHandler`` Class 7 | ===================== 8 | .. autoclass:: model.FileHandler.FileHandler 9 | :members: 10 | -------------------------------------------------------------------------------- /camorph/lib/utils.rst: -------------------------------------------------------------------------------- 1 | ``math_utils`` module 2 | ===================== 3 | .. automodule:: utils.math_utils 4 | :members: 5 | 6 | ``file_utils`` module 7 | ===================== 8 | 9 | .. automodule:: utils.file_utils 10 | :members: 11 | 12 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/model/ColmapCamera.py: -------------------------------------------------------------------------------- 1 | class ColmapCamera: 2 | cam_id: int 3 | type: str 4 | res: (float, float) 5 | params: [] 6 | 7 | def __init__(self, cam_id, type, res, params): 8 | self.cam_id = cam_id 9 | self.type = type 10 | self.res = res 11 | self.params = params 12 | -------------------------------------------------------------------------------- /docs/lib/vis.md: -------------------------------------------------------------------------------- 1 | # `visualizer` Module 2 | 3 | 4 | ### visualizer.visualize(cams) 5 | This function takes a list of camorph cameras and visualizes them with matplotlib 6 | 7 | 8 | * **Parameters** 9 | 10 | **cams** (*list**[*[*Camera*](model.md#model.Camera.Camera)*]*) – A list of camorph Cameras 11 | 12 | 13 | 14 | * **Returns** 15 | 16 | None 17 | -------------------------------------------------------------------------------- /camorph/sphinx/formats.rst: -------------------------------------------------------------------------------- 1 | #################### 2 | File Formats 3 | #################### 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | :caption: File Formats: 8 | 9 | formats/FBX/FBX 10 | formats/Meshroom/Meshroom 11 | formats/COLMAP/COLMAP 12 | formats/RealityCapture/RealityCapture 13 | formats/Unity/Unity 14 | formats/MPEG_OMAF/MPEG_OMAF 15 | formats/NeRF/NeRF -------------------------------------------------------------------------------- /camorph/ext/FBX/model/FBXNodeObject.py: -------------------------------------------------------------------------------- 1 | class FBXNodeObject: 2 | 3 | def __init__(self, name, pname, ptype, pvalue, nested_objects = None): 4 | self.name = name 5 | self.pname = pname 6 | self.ptype = ptype 7 | self.pvalue = pvalue 8 | if nested_objects is not None: 9 | for obj in nested_objects: 10 | setattr(self, obj, nested_objects[obj]) -------------------------------------------------------------------------------- /camorph/sphinx/code.rst: -------------------------------------------------------------------------------- 1 | #################### 2 | Reference 3 | #################### 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | :caption: Camorph: 8 | 9 | camorph 10 | ../lib/vis 11 | ../lib/crucial_property 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :caption: Utilities: 16 | 17 | ../lib/utils 18 | 19 | 20 | .. toctree:: 21 | :maxdepth: 2 22 | :caption: Model: 23 | 24 | ../lib/model 25 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/model/ColmapImage.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyquaternion import Quaternion 3 | 4 | 5 | class ColmapImage: 6 | img_id: int 7 | q: Quaternion 8 | t: np.ndarray 9 | cam_id: int 10 | name: str 11 | 12 | def __init__(self, img_id, q, t, cam_id, name): 13 | self.img_id = img_id 14 | self.q = q 15 | self.t = t 16 | self.cam_id = cam_id 17 | self.name = name -------------------------------------------------------------------------------- /camorph/ext/FBX/model/__init__.py: -------------------------------------------------------------------------------- 1 | from .FBXNode import FBXNode 2 | from .FBXConnection import FBXConnection 3 | from .FBXNodeObject import FBXNodeObject 4 | from .FBXHierachyNode import FBXHierarchyNode 5 | from .FBXProperty import FBXProperty 6 | from .FBXPrimitiveValues import * 7 | 8 | FBXNode = FBXNode 9 | FBXConnection = FBXConnection 10 | FBXNodeAttribute = FBXNodeObject 11 | FBXHierachyNode = FBXHierachyNode 12 | FBXProperty = FBXProperty 13 | -------------------------------------------------------------------------------- /camorph/ext/__init__.py: -------------------------------------------------------------------------------- 1 | from .COLMAP import COLMAP 2 | from .FBX import FBX 3 | from .LLFF import LLFF 4 | from .Meshroom import Meshroom 5 | from .MPEG_OMAF import MPEG_OMAF 6 | from .NeRF import NeRF 7 | from .RealityCapture import RealityCapture 8 | from .Unity import Unity 9 | 10 | imported_instances = { 11 | "colmap": COLMAP(), 12 | "fbx": FBX(), 13 | "llff": LLFF(), 14 | "meshroom": Meshroom(), 15 | "mpeg_omaf": MPEG_OMAF(), 16 | "nerf": NeRF(), 17 | "reality_capture": RealityCapture(), 18 | "unity": Unity(), 19 | } -------------------------------------------------------------------------------- /docs/sphinx/code.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | # Camorph: 4 | 5 | 6 | * [`camorph` Module](camorph.md) 7 | 8 | 9 | * [`visualizer` Module](../lib/vis.md) 10 | 11 | 12 | * [`crucial_property` module](../lib/crucial_property.md) 13 | 14 | 15 | # Utilities: 16 | 17 | 18 | * [`math_utils` module](../lib/utils.md) 19 | 20 | 21 | * [`file_utils` module](../lib/utils.md#module-utils.file_utils) 22 | 23 | 24 | # Model: 25 | 26 | 27 | * [`Camera` Class](../lib/model.md) 28 | 29 | 30 | * [`FileHandler` Class](../lib/model.md#filehandler-class) 31 | -------------------------------------------------------------------------------- /camorph_cli.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Modify Camorph parameters here 4 | :: Possible format types: fbx, colmap, unity, nerf, reality_capture, mpeg_omaf, llff 5 | SET inputType=colmap 6 | SET inputFolder=\\path\to\your\input 7 | SET input=%inputFolder% 8 | 9 | SET outputType=nerf 10 | SET outputFolder=%inputFolder% 11 | SET output=%outputFolder%\CamorphJSON\transforms.json 12 | 13 | 14 | :: Camorph output 15 | :: Additional arguments: -ft (for colmap to switch between txt and bin), -pt (for pose traces) 16 | python -m camorph -if %inputType% -i "%input%" -of %outputType% -o "%output%" 17 | 18 | -------------------------------------------------------------------------------- /docs/lib/crucial_property.md: -------------------------------------------------------------------------------- 1 | # `crucial_property` module 2 | 3 | 4 | ### crucial_property.write_crucial_config_template(cams, path, properties=None) 5 | This function writes a config.json template file to the specified location. 6 | When trying to write to a format with missing crucial properties, camorph automatically 7 | writes a config.json file to the target path. 8 | 9 | 10 | * **Parameters** 11 | 12 | 13 | * **cams** – A list of cameras 14 | 15 | 16 | * **path** – The path where to write the config.json file 17 | 18 | 19 | * **properties** – Optional missing properties 20 | 21 | 22 | 23 | * **Returns** 24 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to camorph’s documentation! 2 | 3 | 4 | [Paper](https://www.int-arch-photogramm-remote-sens-spatial-inf-sci.net/XLVIII-2-W1-2022/29/2022/isprs-archives-XLVIII-2-W1-2022-29-2022.pdf)   [Code](https://github.com/Fraunhofer-IIS/camorph) 5 | 6 | 7 | This is the documentation for the python library camorph. 8 | 9 | * [About](sphinx/about.md) 10 | 11 | * [Autocomputation](sphinx/autocomputation.md) 12 | 13 | * [Code](sphinx/code.md) 14 | 15 | * [Crucial Properties](sphinx/crucial_properties.md) 16 | 17 | * [Supported Formats](sphinx/formats.md) 18 | 19 | * [Plugin](sphinx/plugin.md) -------------------------------------------------------------------------------- /docs/sphinx/formats/FBX/FBX.md: -------------------------------------------------------------------------------- 1 | # FBX 2 | 3 | FBX is a proprietary file format by Autodesk. Because of this, there is little information publicly available. 4 | 5 | ## Coordinate System 6 | 7 | The coordinate system in FBX can be set individually for each FBX file. 8 | The standard definition is y-up, x-right, 9 | z-front and the default camera orientation is x-front 10 | y-up 11 | 12 | 13 | 14 | ![image](./fbx_coordinatesystem_cam-1.png) 15 | 16 | ## File Structure 17 | 18 | Blender published their findings for the binary file structure of an FBX file in a [blogpost](https://code.blender.org/2013/08/fbx-binary-file-format-specification/) in 2014. 19 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import camorph.camorph as camorph 2 | 3 | # Example for converting from COLMAP to FBX (with visualization of the camera poses) 4 | cams = camorph.read_cameras('COLMAP',r'\\path\to\colmap') 5 | 6 | camorph.visualize(cams) 7 | 8 | camorph.write_cameras('fbx', r'\\path\to\file.fbx', cams) 9 | 10 | 11 | # Example for converting MPEG-I OMAF pose trace to NeRF JSON 12 | inputFile = r'\\path\to\input.json' 13 | addInputFile = r'\\path\to\posetrace.csv' 14 | outputFile = r'\\path\to\output.json' 15 | 16 | cams = camorph.read_cameras('mpeg_omaf', inputFile, addInputFile, posetrace="posetrace") 17 | 18 | camorph.write_cameras("nerf", outputFile, cams) 19 | 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = camorph 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /camorph/index.rst: -------------------------------------------------------------------------------- 1 | .. camorph documentation master file, created by 2 | sphinx-quickstart on Thu Feb 10 15:51:43 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to camorph's documentation! 7 | =================================== 8 | This is the documentation for the python library camorph 9 | 10 | 11 | .. toctree:: 12 | :hidden: 13 | :maxdepth: 2 14 | :caption: Camorph: 15 | 16 | sphinx/about 17 | sphinx/crucial_properties 18 | sphinx/autocomputation 19 | sphinx/plugin 20 | sphinx/code 21 | sphinx/formats 22 | sphinx/license 23 | 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | -------------------------------------------------------------------------------- /camorph/ext/Unity/model/UnityGameObject.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | 4 | class UnityGameObject(yaml.YAMLObject): 5 | yaml_tag = r'!u!1' 6 | 7 | def __init__(self, id, cam_id, transform_id, Name): 8 | self.id = id 9 | self.m_ObjectHideFlags = 0 10 | self.m_CorrespondingSourceObject = dict(fileID=0) 11 | self.m_PrefabInstance = dict(fileID=0) 12 | self.m_PrefabAsset = dict(fileID=0) 13 | self.serializedVersion = 6 14 | self.m_Component = [dict(component=dict(fileID=cam_id)), dict(component=dict(fileID=transform_id))] 15 | self.m_Layer = 0 16 | self.m_Name = Name 17 | self.m_TagString = Name 18 | self.m_Icon = dict(fileID=0) 19 | self.m_NavMeshLayer = 0 20 | self.m_StaticEditorFlags = 0 21 | self.m_IsActive = 1 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | [tool.setuptools.packages.find] 5 | exclude=["thirdPartyLegalNotices"] 6 | [project] 7 | name = "camorph" 8 | version = "1.0.0" 9 | description = "Camorph is a python library for converting different camera parameter representations into each other" 10 | readme = "README.md" 11 | requires-python = ">=3.8" 12 | keywords = ["camera parameter convention", "conversion"] 13 | license = {text = "BSD 3-Clause License"} 14 | classifiers = [ 15 | "Programming Language :: Python :: 3", 16 | ] 17 | dependencies = [ 18 | "matplotlib>=3.5.0", 19 | "numpy>=1.21.2", 20 | "Pillow>=9.2.0", 21 | "pyquaternion>=0.9.9", 22 | "PyYAML>=6.0", 23 | "rdflib>=6.1.1", 24 | ] 25 | [project.scripts] 26 | camorph = "camorph.cli:run" 27 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /camorph/ext/Unity/util/ReaderSyntacticUtil.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import re 3 | 4 | def get_unity_from_yaml(yaml_txt): 5 | # we could add a tag constructor for unity objects 6 | # but as the only relevant information is the internal id 7 | # this is some magic regex to replace the tag with a proper yaml attribute 8 | yaml_txt = re.sub("%TAG.*", "", yaml_txt) 9 | yaml_txt = re.sub(r"(--- )([^&]*)&(\d*)(.*\n)(.*\n)", "---\n \g<5> obj_id: \g<3>\n", yaml_txt) 10 | data = yaml.safe_load_all(yaml_txt) 11 | game_objs = [] 12 | cameras = [] 13 | transforms = [] 14 | for unity_obj in data: 15 | if 'GameObject' in unity_obj: 16 | game_objs.append(unity_obj['GameObject']) 17 | elif 'Camera' in unity_obj: 18 | cameras.append(unity_obj['Camera']) 19 | elif 'Transform' in unity_obj: 20 | if 'm_GameObject' in unity_obj['Transform']: 21 | transforms.append(unity_obj['Transform']) 22 | 23 | return game_objs, cameras, transforms -------------------------------------------------------------------------------- /camorph/sphinx/autocomputation.rst: -------------------------------------------------------------------------------- 1 | 2 | Autocomputation 3 | =============== 4 | 5 | Some parameters on camera representations are equivalent. For example, the focal length can be expressed in millimeters as well as in pixels. 6 | Camorph supports autocomputation of certain parameters. The :class:`Camera ` class has the 7 | attribute `autcompute` which is true by default.To disable autocompute, set this to false. 8 | 9 | The supported parameters are: 10 | - Focal length in pixels from focal length in millimeters, sensor size and resolution 11 | - Focal length in millimeters from focal length in pixels, sensor size and resolution 12 | - Focal length in millimeters from sensor size and field of view 13 | - Sensor size in millimeters from focal length in millimeters and field of view 14 | - Field of view in radians from focal length in millimeters and sensor size 15 | - Principal point in pixel coordinates from lens shift and resolution 16 | - lens shift in normalized coordinates from -0.5 to +0.5 from principal point and resolution -------------------------------------------------------------------------------- /camorph/ext/Unity/model/UnityTransform.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | from numpy.core.records import ndarray 3 | from pyquaternion import Quaternion 4 | 5 | from camorph.lib.utils import math_utils 6 | 7 | 8 | class UnityTransform(yaml.YAMLObject): 9 | yaml_tag = r'!u!4' 10 | def __init__(self, t: ndarray, r: Quaternion, id, game_object_id): 11 | self.id = id 12 | self.m_ObjectHideFlags = 0 13 | self.m_CorrespondingSourceObject = dict(fileID=0) 14 | self.m_PrefabInstance = dict(fileID=0) 15 | self.m_PrefabAsset = dict(fileID=0) 16 | self.m_GameObject = dict(fileID=game_object_id) 17 | self.m_LocalRotation = dict(x=float(r.x), y=float(r.y), z=float(r.z), w=float(r.w)) 18 | self.m_LocalPosition = dict(x=float(t[0]), y=float(t[1]), z=float(t[2])) 19 | self.m_LocalScale = dict(x=1, y=1, z=1) 20 | self.m_Children = [] 21 | self.m_Father = dict(fileID=0) 22 | self.m_RootOrder = 0 23 | e = math_utils.quaternion_to_euler(r) 24 | self.m_LocalEulerAnglesHint = dict(x=e[0], y=e[1], z=e[2]) 25 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/FBX/FBX.rst: -------------------------------------------------------------------------------- 1 | FBX 2 | === 3 | 4 | FBX is a proprietary file format by `Autodesk`. Because of this, there is little information publicly available. 5 | 6 | Coordinate System 7 | ----------------- 8 | The coordinate system in FBX can be set individually for each FBX file. 9 | The standard definition is y-up, x-right, 10 | z-front and the default camera orientation is x-front 11 | y-up 12 | 13 | .. image:: fbx_coordinatesystem_cam-1.png 14 | 15 | 16 | File Structure 17 | -------------- 18 | Blender published their findings for the binary file structure of an FBX file in a `blogpost `_ in 2014. 19 | 20 | .. attention:: \ \ 21 | 22 | When the FBX version is greater than 7500 which was released in 23 | 2016, some important changes need to be 24 | made: 25 | 26 | - The NULL blocks after nested nodes are 25 bytes, not 13 27 | 28 | - The node metadata fields ``EndOffset``, ``NumProperties`` and 29 | ``PropertyListLen`` switch from ``uint32`` to ``uint64`` 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/sphinx/autocomputation.md: -------------------------------------------------------------------------------- 1 | # Autocomputation 2 | 3 | Some parameters on camera representations are equivalent. For example, the focal length can be expressed in millimeters as well as in pixels. 4 | Camorph supports autocomputation of certain parameters. The [`Camera`](../lib/model.md#model.Camera.Camera) class has the 5 | attribute autcompute which is true by default.To disable autocompute, set this to false. 6 | 7 | The supported parameters are: 8 | 9 | 10 | * Focal length in pixels from focal length in millimeters, sensor size and resolution 11 | 12 | 13 | * Focal length in millimeters from focal length in pixels, sensor size and resolution 14 | 15 | 16 | * Focal length in millimeters from sensor size and field of view 17 | 18 | 19 | * Sensor size in millimeters from focal length in millimeters and field of view 20 | 21 | 22 | * Field of view in radians from focal length in millimeters and sensor size 23 | 24 | 25 | * Principal point in pixel coordinates from lens shift and resolution 26 | 27 | 28 | * lens shift in normalized coordinates from -0.5 to +0.5 from principal point and resolution 29 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/pyparsing.txt: -------------------------------------------------------------------------------- 1 | Applies to pyparsing 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/setuptools-scm.txt: -------------------------------------------------------------------------------- 1 | Applies to setuptools-scm 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/six.txt: -------------------------------------------------------------------------------- 1 | Applies to six 2 | 3 | Copyright (c) 2010-2020 Benjamin Peterson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/tomli.txt: -------------------------------------------------------------------------------- 1 | Applies to tomli 2 | 3 | MIT License 4 | 5 | Copyright (c) 2021 Taneli Hukkinen 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/fontools.txt: -------------------------------------------------------------------------------- 1 | Applies to fontools 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 Just van Rossum 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/pyYaml.txt: -------------------------------------------------------------------------------- 1 | Applies to pyYaml 2 | 3 | Copyright (c) 2017-2021 Ingy döt Net 4 | Copyright (c) 2006-2016 Kirill Simonov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /thirdPartyLegalNotices/PyQuaternion.txt: -------------------------------------------------------------------------------- 1 | Applies to PyQuaternion 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2015 Kieran Wynn 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /docs/sphinx/formats/MPEG_OMAF/MPEG_OMAF.md: -------------------------------------------------------------------------------- 1 | # MPEG OMAF 2 | 3 | The *Moving Picture Experts Group* developed the *Omnidirectional Media 4 | Format (OMAF)* in the standard *MPEG-I* to facilitate easy 5 | interoperability between different devices, components and systems. 6 | 7 | ## Coordinate System 8 | 9 | The standard *MPEG OMAF* coordinate system is z up, 10 | y right, x front. The camera orientation is 11 | x front and z up 12 | 13 | 14 | 15 | ![image](./MPEG_OMAF_coordinatesystem_cam-1.png) 16 | 17 | ## JSON File 18 | 19 | 20 | * The `cameras` array holds the important information for camorph 21 | 22 | 23 | * `Name` is a unique name of the camera 24 | 25 | 26 | * `Projection` is either `Perspective` or `Equirectangular`. Only 27 | perspective projections are subject of this thesis 28 | 29 | 30 | * `Position` is the position of the camera 31 | 32 | 33 | * `Resolution` is the Resolution of the camera 34 | 35 | 36 | * `Rotation` is the rotation of the camera represented as three Euler 37 | angles around $z,y,x$ with a rotational order of $x-y-z$ 38 | 39 | 40 | * `Focal` is the focal length in pixels in each direction 41 | 42 | 43 | * `Principle_point`(sic) is the principal point expressed as pixel 44 | coordinates 45 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/MPEG_OMAF/MPEG_OMAF.rst: -------------------------------------------------------------------------------- 1 | MPEG OMAF 2 | ========= 3 | The *Moving Picture Experts Group* developed the *Omnidirectional Media 4 | Format (OMAF)* in the standard *MPEG-I* to facilitate easy 5 | interoperability between different devices, components and systems. 6 | 7 | Coordinate System 8 | ----------------- 9 | The standard *MPEG OMAF* coordinate system is z up, 10 | y right, x front. The camera orientation is 11 | x front and z up 12 | 13 | .. image:: MPEG_OMAF_coordinatesystem_cam-1.png 14 | 15 | JSON File 16 | --------- 17 | 18 | - The ``cameras`` array holds the important information for camorph 19 | 20 | - ``Name`` is a unique name of the camera 21 | 22 | - ``Projection`` is either ``Perspective`` or ``Equirectangular``. Only 23 | perspective projections are subject of this thesis 24 | 25 | - ``Position`` is the position of the camera 26 | 27 | - ``Resolution`` is the Resolution of the camera 28 | 29 | - ``Rotation`` is the rotation of the camera represented as three Euler 30 | angles around :math:`z,y,x` with a rotational order of :math:`x-y-z` 31 | 32 | - ``Focal`` is the focal length in pixels in each direction 33 | 34 | - ``Principle_point``\ (sic) is the principal point expressed as pixel 35 | coordinates -------------------------------------------------------------------------------- /camorph/ext/Unity/util/ReaderSemanticUtil.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyquaternion import Quaternion 3 | 4 | 5 | def apply_transform_rec(cur_transform, transforms, translation=None, rotation=None): 6 | father_id = cur_transform['m_Father']['fileID'] 7 | if translation is None and rotation is None: 8 | translation, rotation = get_translation_and_rotation(cur_transform) 9 | if father_id != 0: 10 | father = next(x for x in transforms if x['obj_id'] == cur_transform['m_Father']['fileID']) 11 | f_translation, f_rotation = get_translation_and_rotation(father) 12 | translation = translation 13 | translation = f_rotation.rotate(translation) 14 | translation = translation + f_translation 15 | rotation = f_rotation * rotation 16 | return apply_transform_rec(father, transforms, translation, rotation) 17 | else: 18 | return translation, rotation 19 | 20 | 21 | def get_translation_and_rotation(transform): 22 | translation = np.asarray([transform['m_LocalPosition']['x'], transform['m_LocalPosition']['y'],transform['m_LocalPosition']['z']]) 23 | rotation = Quaternion(transform['m_LocalRotation']['w'], transform['m_LocalRotation']['x'], transform['m_LocalRotation']['y'], transform['m_LocalRotation']['z']) 24 | return translation, rotation -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.9 3 | # To update, run: 4 | # 5 | # pip-compile requirements.in 6 | # 7 | cycler==0.11.0 8 | # via matplotlib 9 | fonttools==4.37.4 10 | # via matplotlib 11 | isodate==0.6.1 12 | # via rdflib 13 | kiwisolver==1.4.4 14 | # via matplotlib 15 | matplotlib==3.5.0 16 | # via -r requirements.in 17 | numpy==1.21.2 18 | # via 19 | # -r requirements.in 20 | # matplotlib 21 | # pyquaternion 22 | packaging==21.3 23 | # via 24 | # matplotlib 25 | # setuptools-scm 26 | pillow==9.2.0 27 | # via 28 | # -r requirements.in 29 | # matplotlib 30 | pyparsing==3.0.9 31 | # via 32 | # matplotlib 33 | # packaging 34 | # rdflib 35 | pyquaternion==0.9.9 36 | # via -r requirements.in 37 | python-dateutil==2.8.2 38 | # via matplotlib 39 | pyyaml==6.0 40 | # via -r requirements.in 41 | rdflib==6.1.1 42 | # via -r requirements.in 43 | setuptools-scm==7.0.5 44 | # via matplotlib 45 | six==1.16.0 46 | # via 47 | # isodate 48 | # python-dateutil 49 | tomli==2.0.1 50 | # via setuptools-scm 51 | typing-extensions==4.3.0 52 | # via setuptools-scm 53 | 54 | # The following packages are considered to be unsafe in a requirements file: 55 | # setuptools 56 | -------------------------------------------------------------------------------- /camorph/sphinx/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Camorph is a python library to convert different representations of camera parameters into each other. 5 | 6 | Quickstart 7 | ========== 8 | 9 | .. code-block:: python 10 | 11 | import camorph.camorph as camorph 12 | 13 | 14 | cams = camorph.read_cameras('COLMAP',r'\\path\to\colmap') 15 | 16 | camorph.visualize(cams) 17 | 18 | camorph.write_cameras('fbx', r'\\path\to\file.fbx', cams) 19 | 20 | 21 | :meth:`camorph.read_cameras` takes a format name and path as a string and returns a list of :class:`Cameras `. 22 | 23 | :meth:`camorph.visualize` creates a visualization of :class:`Cameras ` with `Matplotlib `_. 24 | 25 | :meth:`camorph.write_cameras` takes a format name, a path as a string and a list of cameras and writes the output file(s) to the specified path. 26 | 27 | Currently supported formats: 28 | - Computer Graphics 29 | - :doc:`FBX ` 30 | - Photogrammetry 31 | - :doc:`COLMAP ` 32 | - :doc:`Meshroom ` 33 | - :doc:`Reality Capture ` 34 | - Game Engines 35 | - :doc:`Unity ` 36 | - Virtual Reality 37 | - :doc:`MPEG OMAF ` 38 | - Machine Learning 39 | - :doc:`NeRF ` -------------------------------------------------------------------------------- /camorph/lib/visualizer.py: -------------------------------------------------------------------------------- 1 | from camorph.lib.model.Camera import Camera 2 | import matplotlib.pyplot as plt 3 | from mpl_toolkits.mplot3d import Axes3D 4 | import numpy as np 5 | 6 | def visualize(cams: list[Camera], show=True): 7 | """ 8 | This function takes a list of camorph cameras and visualizes them with matplotlib 9 | 10 | :param cams: A list of camorph Cameras 11 | :type cams: list[Camera] 12 | :return: None 13 | """ 14 | x,y,z = zip(*[x.t for x in cams]) 15 | dir = np.array([0,0,-1]) 16 | up = np.array([0,1,0]) 17 | maxdist = max([np.linalg.norm(x.t) for x in cams]) 18 | dir = dir * maxdist * 0.2 19 | up = up * maxdist * 0.2 20 | #dir = np.array([1, 0, 0]) 21 | #up = np.array([0, 0, 1]) 22 | dirx,diry,dirz = zip(*[x.r.rotate(dir) for x in cams]) 23 | upx, upy, upz = zip(*[x.r.rotate(up) for x in cams]) 24 | fig = plt.figure() 25 | ax = fig.add_subplot(111, projection='3d') 26 | ax.quiver(x, y, z, dirx, diry, dirz, color='r', label='Camera Front') 27 | ax.quiver(x, y, z, upx, upy, upz, color='g', label='Camera Up') 28 | 29 | ax.set_xlim([-maxdist, maxdist]) 30 | ax.set_ylim([-maxdist, maxdist]) 31 | ax.set_zlim([-maxdist, maxdist]) 32 | 33 | ax.scatter(x,y,z) 34 | ax.set_xlabel('X') 35 | ax.set_ylabel('Y') 36 | ax.set_zlabel('Z') 37 | 38 | ax.legend() 39 | if show: 40 | plt.show() 41 | else: 42 | return fig -------------------------------------------------------------------------------- /camorph/ext/Unity/util/WriterSyntacticUtil.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import re 3 | 4 | import yaml 5 | import numpy as np 6 | 7 | from ..model.UnityCamera import UnityCamera 8 | from ..model.UnityGameObject import UnityGameObject 9 | from ..model.UnityTransform import UnityTransform 10 | from camorph.lib.model.Camera import Camera 11 | 12 | 13 | def build_file(cam_array: list[Camera]): 14 | out_game_objs = [] 15 | out_cams = [] 16 | out_transforms = [] 17 | out_txt = "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n" 18 | id_gen = itertools.count(1) 19 | for cam in cam_array: 20 | gid = next(id_gen) 21 | cid = next(id_gen) 22 | tid = next(id_gen) 23 | u_game_obj = UnityGameObject(gid, cid, tid, cam.name) 24 | u_cam = UnityCamera(cid, gid, cam.focal_length_mm[0], cam.sensor_size, float(np.degrees(cam.fov[0])), cam.projection_type) 25 | u_transform = UnityTransform(cam.t, cam.r, tid, gid) 26 | out_game_objs.append(dict(GameObject=u_game_obj)) 27 | out_cams.append(dict(Camera=u_cam)) 28 | out_transforms.append(dict(Transform=u_transform)) 29 | out_txt += yaml.dump(out_game_objs, default_flow_style=None) 30 | out_txt += (yaml.dump(out_cams)) 31 | out_txt += (yaml.dump(out_transforms)) 32 | out_txt = re.sub(r" {4}", " ", out_txt) 33 | out_txt = re.sub(r"- (.*)(!u)%21(\d*)([\S\s]*?)\n *id: *(\d*)", "--- \g<2>!\g<3> &\g<5>\n\g<1>\g<4>", out_txt) 34 | return out_txt 35 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/packaging.txt: -------------------------------------------------------------------------------- 1 | Applies to packaging 2 | 3 | Copyright (c) Donald Stufft and individual contributors. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/model/CameraModel.py: -------------------------------------------------------------------------------- 1 | class CameraModel: 2 | 3 | def __init__(self, model_id: int, model_name: str, num_params: int): 4 | self.model_id: int = model_id 5 | self.model_name: str = model_name 6 | self.num_params: int = num_params 7 | 8 | # from https://github.com/colmap/colmap/blob/1a4d0bad2e90aa65ce997c9d1779518eaed998d5/scripts/python/read_write_model.py 9 | CAMERA_MODELS = { 10 | CameraModel(model_id=0, model_name="SIMPLE_PINHOLE", num_params=3), 11 | CameraModel(model_id=1, model_name="PINHOLE", num_params=4), 12 | CameraModel(model_id=2, model_name="SIMPLE_RADIAL", num_params=4), 13 | CameraModel(model_id=3, model_name="RADIAL", num_params=5), 14 | CameraModel(model_id=4, model_name="OPENCV", num_params=8), 15 | CameraModel(model_id=5, model_name="OPENCV_FISHEYE", num_params=8), 16 | CameraModel(model_id=6, model_name="FULL_OPENCV", num_params=12), 17 | CameraModel(model_id=7, model_name="FOV", num_params=5), 18 | CameraModel(model_id=8, model_name="SIMPLE_RADIAL_FISHEYE", num_params=4), 19 | CameraModel(model_id=9, model_name="RADIAL_FISHEYE", num_params=5), 20 | CameraModel(model_id=10, model_name="THIN_PRISM_FISHEYE", num_params=12) 21 | } 22 | CAMERA_MODEL_IDS = dict([(camera_model.model_id, camera_model) 23 | for camera_model in CAMERA_MODELS]) 24 | CAMERA_MODEL_NAMES = dict([(camera_model.model_name, camera_model) 25 | for camera_model in CAMERA_MODELS]) 26 | 27 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/NeRF/NeRF.rst: -------------------------------------------------------------------------------- 1 | NeRF 2 | ========= 3 | *NeRF* s (Neural Radiance Fields) are a novel way of representing a 3D scene via a fully connected neural network, trained on a set of 2D Images. 4 | 5 | Coordinate System 6 | ----------------- 7 | The standard *NeRF* coordinate system is z up, 8 | x right, -y front. The camera orientation is 9 | -z front and y up 10 | 11 | .. image:: NeRF_coordinatesystem.png 12 | 13 | JSON File 14 | --------- 15 | There are many different version of *NeRF* JSON files. This one is a custom version based on InstantNGP by `NVIDIA`. 16 | 17 | The top level holds global information: 18 | 19 | - ``camera_angle_x`` and ``camera_angle_y`` are the respective fovs in radians 20 | 21 | - ``fl_x`` and ``fl_y`` are the respective focal lengths in pixels 22 | 23 | - ``k1``, ``k2``, ``k3``, ``p1``, ``p2``, ``p3`` are distortion parameters 24 | 25 | - ``cx`` and ``cy`` is the location of the principal point in pixels 26 | 27 | - ``w`` and ``h`` is the size of one image 28 | 29 | - ``aabb_scale`` the size of the axis aligned bounding box (currently unused) 30 | 31 | ``frames`` contains the information for each camera: 32 | 33 | - ``intrinsics`` holds individual intrinsic information about the camera if it is other than the global information 34 | 35 | - ``file_path`` is the path to the source image 36 | 37 | - ``rotation`` is a currently unused rotation parameter always set to 0, but required by some *NeRF* models 38 | 39 | - ``transform_matrix`` is the 4x4 transformation matrix -------------------------------------------------------------------------------- /docs/sphinx/formats/NeRF/NeRF.md: -------------------------------------------------------------------------------- 1 | # NeRF 2 | 3 | *NeRF* s (Neural Radiance Fields) are a novel way of representing a 3D scene via a fully connected neural network, trained on a set of 2D Images. 4 | 5 | ## Coordinate System 6 | 7 | The standard *NeRF* coordinate system is z up, 8 | x right, -y front. The camera orientation is 9 | -z front and y up 10 | 11 | 12 | 13 | ![image](./NeRF_coordinatesystem.png) 14 | 15 | ## JSON File 16 | 17 | There are many different version of *NeRF* JSON files. This one is a custom version based on InstantNGP by NVIDIA. 18 | 19 | The top level holds global information: 20 | 21 | 22 | * `camera_angle_x` and `camera_angle_y` are the respective fovs in radians 23 | 24 | 25 | * `fl_x` and `fl_y` are the respective focal lengths in pixels 26 | 27 | 28 | * `k1`, `k2`, `k3`, `p1`, `p2`, `p3` are distortion parameters 29 | 30 | 31 | * `cx` and `cy` is the location of the principal point in pixels 32 | 33 | 34 | * `w` and `h` is the size of one image 35 | 36 | 37 | * `aabb_scale` the size of the axis aligned bounding box (currently unused) 38 | 39 | `frames` contains the information for each camera: 40 | 41 | 42 | * `intrinsics` holds individual intrinsic information about the camera if it is other than the global information 43 | 44 | 45 | * `file_path` is the path to the source image 46 | 47 | 48 | * `rotation` is a currently unused rotation parameter always set to 0, but required by some *NeRF* models 49 | 50 | 51 | * `transform_matrix` is the 4x4 transformation matrix 52 | -------------------------------------------------------------------------------- /docs/sphinx/about.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Camorph is a python library to convert different representations of camera parameters into each other. 4 | 5 | # Quickstart 6 | 7 | ```python 8 | import camorph.camorph as camorph 9 | 10 | 11 | cams = camorph.read_cameras('COLMAP',r'\\path\to\colmap') 12 | 13 | camorph.visualize(cams) 14 | 15 | camorph.write_cameras('fbx', r'\\path\to\file.fbx', cams) 16 | ``` 17 | 18 | [`camorph.read_cameras()`](camorph.md#camorph.read_cameras) takes a format name and path as a string and returns a list of [`Cameras`](../lib/model.md#model.Camera.Camera). 19 | 20 | [`camorph.visualize()`](camorph.md#camorph.visualize) creates a visualization of [`Cameras`](../lib/model.md#model.Camera.Camera) with [Matplotlib](https://matplotlib.org/). 21 | 22 | [`camorph.write_cameras()`](camorph.md#camorph.write_cameras) takes a format name, a path as a string and a list of cameras and writes the output file(s) to the specified path. 23 | 24 | Currently supported formats: 25 | 26 | 27 | * Computer Graphics 28 | 29 | 30 | * [FBX](formats/FBX/FBX.md) 31 | 32 | 33 | * Photogrammetry 34 | 35 | 36 | * [COLMAP](formats/COLMAP/COLMAP.md) 37 | 38 | 39 | * [Meshroom](formats/Meshroom/Meshroom.md) 40 | 41 | 42 | * [Reality Capture](formats/RealityCapture/RealityCapture.md) 43 | 44 | 45 | * Game Engines 46 | 47 | 48 | * [Unity](formats/Unity/Unity.md) 49 | 50 | 51 | * Virtual Reality 52 | 53 | 54 | * [MPEG OMAF](formats/MPEG_OMAF/MPEG_OMAF.md) 55 | 56 | 57 | * Machine Learning 58 | 59 | 60 | * [NeRF](formats/NeRF/NeRF.md) 61 | -------------------------------------------------------------------------------- /camorph/ext/Meshroom/model/sfm.py: -------------------------------------------------------------------------------- 1 | from json import JSONEncoder 2 | 3 | 4 | class sfm: 5 | 6 | def __init__(self): 7 | self.version = ["1","2","4"] # Corresponds to Meshroom 2023.2.0 8 | self.feature_folders = [""] 9 | self.matchesFolders = [""] 10 | self.views = [] 11 | self.intrinsics = [] 12 | self.poses = [] 13 | 14 | 15 | class sfm_view: 16 | 17 | def __init__(self): 18 | self.viewId = '0' 19 | self.poseId = '0' 20 | self.intrinsicId = '0' 21 | self.resectionId = '0' 22 | self.path = "" 23 | self.width = '0' 24 | self.height = '0' 25 | self.metadata = None 26 | 27 | class sfm_intrinsics(dict): 28 | 29 | def __init__(self): 30 | super().__init__() 31 | self.intrinsicId = '0' 32 | self.width = '0' 33 | self.height = '0' 34 | self.sensorWidth = '0' 35 | self.sensorHeight = '0' 36 | self.serialNumber = '0' 37 | self.type = 'pinhole' 38 | self.initializationMode = 'calibrated' 39 | self.pxInitialFocalLength = '0' 40 | self.pxFocalLength = '0' 41 | self.principalPoint = ['0','0'] 42 | self.locked = '1' 43 | 44 | class sfm_pose: 45 | 46 | def __init__(self): 47 | self.poseId = '0' 48 | self.pose = sfm_transform() 49 | self.locked = '1' 50 | 51 | class sfm_transform: 52 | 53 | def __init__(self): 54 | transform_obj = dict() 55 | transform_obj['rotation'] = [] 56 | transform_obj['center'] = [] 57 | self.transform = transform_obj -------------------------------------------------------------------------------- /thirdPartyLegalNotices/Pillow.txt: -------------------------------------------------------------------------------- 1 | Applies to Pillow 2 | 3 | The Python Imaging Library (PIL) is 4 | 5 | Copyright © 1997-2011 by Secret Labs AB 6 | Copyright © 1995-2011 by Fredrik Lundh 7 | 8 | Pillow is the friendly PIL fork. It is 9 | 10 | Copyright © 2010-2022 by Alex Clark and contributors 11 | 12 | Like PIL, Pillow is licensed under the open source HPND License: 13 | 14 | By obtaining, using, and/or copying this software and/or its associated 15 | documentation, you agree that you have read, understood, and will comply 16 | with the following terms and conditions: 17 | 18 | Permission to use, copy, modify, and distribute this software and its 19 | associated documentation for any purpose and without fee is hereby granted, 20 | provided that the above copyright notice appears in all copies, and that 21 | both that copyright notice and this permission notice appear in supporting 22 | documentation, and that the name of Secret Labs AB or the author not be 23 | used in advertising or publicity pertaining to distribution of the software 24 | without specific, written prior permission. 25 | 26 | SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 27 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 28 | IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, 29 | INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 30 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 31 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 32 | PERFORMANCE OF THIS SOFTWARE. 33 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/cycler.txt: -------------------------------------------------------------------------------- 1 | Applies to cycler 2 | 3 | Copyright (c) 2015, matplotlib project 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the matplotlib project nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /thirdPartyLegalNotices/rdflib.txt: -------------------------------------------------------------------------------- 1 | Applies to rdflib 2 | 3 | BSD 3-Clause License 4 | 5 | Copyright (c) 2002-2022, RDFLib Team 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/Numpy.txt: -------------------------------------------------------------------------------- 1 | Applies to Numpy 2 | 3 | Copyright (c) 2005-2022, NumPy Developers. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following 15 | disclaimer in the documentation and/or other materials provided 16 | with the distribution. 17 | 18 | * Neither the name of the NumPy Developers nor the names of any 19 | contributors may be used to endorse or promote products derived 20 | from this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/isodate.txt: -------------------------------------------------------------------------------- 1 | Applies to isodate 2 | 3 | Copyright (c) 2021, Hugo van Kemenade and contributors 4 | Copyright (c) 2009-2018, Gerhard Weis and contributors 5 | Copyright (c) 2009, Gerhard Weis 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of the nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The Clear BSD License 2 | 3 | Copyright (c) 2022 Fraunhofer IIS 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted (subject to the limitations in the disclaimer below) provided that 8 | the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY 22 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 23 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 30 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /docs/sphinx/formats.md: -------------------------------------------------------------------------------- 1 | # File Formats 2 | 3 | # File Formats: 4 | 5 | 6 | * [FBX](formats/FBX/FBX.md) 7 | 8 | 9 | * [Coordinate System](formats/FBX/FBX.md#coordinate-system) 10 | 11 | 12 | * [File Structure](formats/FBX/FBX.md#file-structure) 13 | 14 | 15 | * [Meshroom](formats/Meshroom/Meshroom.md) 16 | 17 | 18 | * [Coordinate System](formats/Meshroom/Meshroom.md#coordinate-system) 19 | 20 | 21 | * [SfM File](formats/Meshroom/Meshroom.md#sfm-file) 22 | 23 | 24 | * [Camera Models](formats/Meshroom/Meshroom.md#camera-models) 25 | 26 | 27 | * [COLMAP](formats/COLMAP/COLMAP.md) 28 | 29 | 30 | * [Coordinate system](formats/COLMAP/COLMAP.md#coordinate-system) 31 | 32 | 33 | * [Cameras file](formats/COLMAP/COLMAP.md#cameras-file) 34 | 35 | 36 | * [Images file](formats/COLMAP/COLMAP.md#images-file) 37 | 38 | 39 | * [Camera models](formats/COLMAP/COLMAP.md#camera-models) 40 | 41 | 42 | * [Reality Capture](formats/RealityCapture/RealityCapture.md) 43 | 44 | 45 | * [Coordinate System](formats/RealityCapture/RealityCapture.md#coordinate-system) 46 | 47 | 48 | * [XMP Files](formats/RealityCapture/RealityCapture.md#xmp-files) 49 | 50 | 51 | * [Camera Models](formats/RealityCapture/RealityCapture.md#camera-models) 52 | 53 | 54 | * [Unity](formats/Unity/Unity.md) 55 | 56 | 57 | * [Coordinate System](formats/Unity/Unity.md#coordinate-system) 58 | 59 | 60 | * [Unity Scene Files](formats/Unity/Unity.md#unity-scene-files) 61 | 62 | 63 | * [MPEG OMAF](formats/MPEG_OMAF/MPEG_OMAF.md) 64 | 65 | 66 | * [Coordinate System](formats/MPEG_OMAF/MPEG_OMAF.md#coordinate-system) 67 | 68 | 69 | * [JSON File](formats/MPEG_OMAF/MPEG_OMAF.md#json-file) 70 | 71 | 72 | * [NeRF](formats/NeRF/NeRF.md) 73 | 74 | 75 | * [Coordinate System](formats/NeRF/NeRF.md#coordinate-system) 76 | 77 | 78 | * [JSON File](formats/NeRF/NeRF.md#json-file) 79 | -------------------------------------------------------------------------------- /camorph_win.yml: -------------------------------------------------------------------------------- 1 | name: camorph_win 2 | channels: 3 | - defaults 4 | dependencies: 5 | - blas=1.0=mkl 6 | - brotli=1.0.9=ha925a31_2 7 | - bzip2=1.0.8=he774522_0 8 | - ca-certificates=2022.2.1=haa95532_0 9 | - certifi=2020.6.20=pyhd3eb1b0_3 10 | - cycler=0.11.0=pyhd3eb1b0_0 11 | - fonttools=4.25.0=pyhd3eb1b0_0 12 | - freetype=2.10.4=hd328e21_0 13 | - icu=58.2=ha925a31_3 14 | - intel-openmp=2021.4.0=haa95532_3556 15 | - jpeg=9d=h2bbff1b_0 16 | - kiwisolver=1.3.1=py310hd77b12b_0 17 | - libffi=3.4.2=h604cdb4_1 18 | - libpng=1.6.37=h2a8f88b_0 19 | - libtiff=4.2.0=hd0e1b90_0 20 | - libwebp=1.2.2=h2bbff1b_0 21 | - lz4-c=1.9.3=h2bbff1b_1 22 | - matplotlib=3.5.1=py310haa95532_0 23 | - matplotlib-base=3.5.1=py310hd77b12b_0 24 | - mkl=2021.4.0=haa95532_640 25 | - mkl-service=2.4.0=py310h2bbff1b_0 26 | - mkl_fft=1.3.1=py310ha0764ea_0 27 | - mkl_random=1.2.2=py310h4ed8f06_0 28 | - munkres=1.1.4=py_0 29 | - numpy=1.21.5=py310h4c31df0_0 30 | - numpy-base=1.21.5=py310hedd7904_0 31 | - olefile=0.46=pyhd3eb1b0_0 32 | - openssl=1.1.1m=h2bbff1b_0 33 | - packaging=21.3=pyhd3eb1b0_0 34 | - pillow=8.4.0=py310hdc2b20a_0 35 | - pip=21.2.4=py310haa95532_0 36 | - pyparsing=3.0.4=pyhd3eb1b0_0 37 | - pyqt=5.9.2=py310hd77b12b_6 38 | - python=3.10.0=h96c0403_3 39 | - python-dateutil=2.8.2=pyhd3eb1b0_0 40 | - qt=5.9.7=vc14h73c81de_0 41 | - setuptools=58.0.4=py310haa95532_0 42 | - sip=4.19.13=py310hd77b12b_0 43 | - six=1.16.0=pyhd3eb1b0_1 44 | - sqlite=3.37.2=h2bbff1b_0 45 | - tk=8.6.11=h2bbff1b_0 46 | - tornado=6.1=py310h2bbff1b_0 47 | - tzdata=2021e=hda174b7_0 48 | - vc=14.2=h21ff451_1 49 | - vs2015_runtime=14.27.29016=h5e58377_2 50 | - wheel=0.37.1=pyhd3eb1b0_0 51 | - wincertstore=0.2=py310haa95532_2 52 | - xz=5.2.5=h62dcd97_0 53 | - zlib=1.2.11=h8cc25b3_4 54 | - zstd=1.4.9=h19a0ad4_0 55 | - pip: 56 | - isodate==0.6.1 57 | - pyquaternion==0.9.9 58 | - pyyaml==6.0 59 | - rdflib==6.1.1 60 | prefix: C:\ProgramData\Anaconda3\envs\camorph_win 61 | -------------------------------------------------------------------------------- /camorph/ext/Unity/model/UnityCamera.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | class UnityCamera(yaml.YAMLObject): 4 | yaml_tag = '!u!20' 5 | def __init__(self, id, game_object_id, focal_length = 50, sensor_size = (36,24),fov = 60, projection_type = 'perspective' ): 6 | self.id = id 7 | self.m_ObjectHideFlags = 0 8 | self.m_CorrespondingSourceObject = dict(fileID=0) 9 | self.m_PrefabInstance = dict(fileID=0) 10 | self.m_PrefabAsset = dict(fileID=0) 11 | self.m_GameObject = dict(fileID=game_object_id) 12 | self.m_Enabled = 1 13 | self.serializedVersion = 2 14 | self.m_ClearFlags = 2 15 | self.m_BackGroundColor = dict(r=0.19215687, g=0.3019608, b=0.4745098, a=0) 16 | self.m_projectionMatrixMode = 2 17 | self.m_GateFitMode = 2 18 | self.m_FOVAxisMode = 1 19 | self.m_SensorSize = dict(x=float(sensor_size[0]), y=float(sensor_size[1])) 20 | self.m_LensShift = dict(x=0, y=0) 21 | self.m_FocalLength = float(focal_length) 22 | self.m_NormalizedViewPortRect = dict( 23 | serializedVersion=2, 24 | x=0, 25 | y=0, 26 | width=1, 27 | height=1) 28 | setattr(self, 'near clip plane', 0.3) 29 | setattr(self, 'far clip plane', 1000) 30 | setattr(self, 'field of view', fov) 31 | self.orthographic = 0 if projection_type == 'perspective' else 1 32 | setattr(self, 'orthographic size', 5) 33 | self.m_Depth = -1 34 | self.m_CullingMask = dict( 35 | serializedVersion=2, 36 | m_Bits=4294967295) 37 | self.m_RenderingPath = -1 38 | self.m_TargetTexture = dict(fileID=0) 39 | self.m_TargetDisplay = 0 40 | self.m_TargetEye = 0 41 | self.m_HDR = 1 42 | self.m_AllowMSAA = 0 43 | self.m_AllowDynamicResolution = 0 44 | self.m_ForceIntoRT = 0 45 | self.m_OcclusionCulling = 0 46 | self.m_StereoConvergence = 10 47 | self.m_StereoSeparation = 0.022 48 | -------------------------------------------------------------------------------- /docs/sphinx/crucial_properties.md: -------------------------------------------------------------------------------- 1 | # Crucial Properties 2 | 3 | Some formats need properties other formats do not support. All photogrammetry formats 4 | need a source_image for example, which is generally not supported in Computer Graphics Formats. 5 | To supply missing crucial properties, you can specify a config.json in the target folder of the format you want to write. 6 | An example config.json would be: 7 | 8 | ```json 9 | { 10 | "type": "Camorph_config", 11 | "missing_properties": [ 12 | "source_image", 13 | "focal_length_px" 14 | ], 15 | "example": { 16 | "property": "Value", 17 | "global_property_path": { 18 | "path": "\\path\\to\\dir", 19 | "filter": "regex" 20 | } 21 | }, 22 | "global": { 23 | }, 24 | "values": [ 25 | { 26 | "name": "cam1" 27 | }, 28 | { 29 | "name": "cam2" 30 | }, 31 | { 32 | "name": "cam3" 33 | }, 34 | { 35 | "name": "cam4" 36 | }, 37 | ] 38 | } 39 | ``` 40 | 41 | `type` is always “Camorph_config” 42 | 43 | `missing_properties` is a hint which properties where missing when trying to convert to the desired output format 44 | 45 | All parameters in global are applied to all cameras. 46 | When a valid parameter + “_path” is supplied (for example source_images_path), camorph 47 | expects an object with path and filter parameters as content. 48 | The path parameter supply the path where camorph looks for matching files, while filter is a regular expression to 49 | filter the filenames. If there is no filter supplied, camorph will not filter files. 50 | Camorph will look for files matching the name parameter of the cameras. If there are none, camorph 51 | will rely on the order in which the operating system orders the files as well as the order of the internal 52 | camera array. 53 | 54 | Each camera in the list has an object in values. Individual properties can be supplied here. 55 | Properties in values override properties in global 56 | -------------------------------------------------------------------------------- /camorph/sphinx/crucial_properties.rst: -------------------------------------------------------------------------------- 1 | Crucial Properties 2 | ================== 3 | Some formats need properties other formats do not support. All photogrammetry formats 4 | need a source_image for example, which is generally not supported in Computer Graphics Formats. 5 | To supply missing crucial properties, you can specify a config.json in the target folder of the format you want to write. 6 | An example config.json would be: 7 | 8 | .. code-block:: json 9 | 10 | { 11 | "type": "Camorph_config", 12 | "missing_properties": [ 13 | "source_image", 14 | "focal_length_px" 15 | ], 16 | "example": { 17 | "property": "Value", 18 | "global_property_path": { 19 | "path": "\\path\\to\\dir", 20 | "filter": "regex" 21 | } 22 | }, 23 | "global": { 24 | }, 25 | "values": [ 26 | { 27 | "name": "cam1" 28 | }, 29 | { 30 | "name": "cam2" 31 | }, 32 | { 33 | "name": "cam3" 34 | }, 35 | { 36 | "name": "cam4" 37 | }, 38 | ] 39 | } 40 | 41 | `type` is always `"Camorph_config"` 42 | 43 | `missing_properties` is a hint which properties where missing when trying to convert to the desired output format 44 | 45 | All parameters in `global` are applied to all cameras. 46 | When a valid parameter + `"_path"` is supplied (for example `source_images_path`), camorph 47 | expects an object with `path` and `filter` parameters as content. 48 | The `path` parameter supply the path where camorph looks for matching files, while `filter` is a regular expression to 49 | filter the filenames. If there is no filter supplied, camorph will not filter files. 50 | Camorph will look for files matching the `name` parameter of the cameras. If there are none, camorph 51 | will rely on the order in which the operating system orders the files as well as the order of the internal 52 | camera array. 53 | 54 | Each camera in the list has an object in `values`. Individual properties can be supplied here. 55 | Properties in `values` override properties in `global` -------------------------------------------------------------------------------- /camorph/ext/Meshroom/util/WriterSyntacticUtil.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | 4 | from ..model.sfm import sfm, sfm_view, sfm_transform, sfm_pose, sfm_intrinsics 5 | from camorph.lib.model.Camera import Camera 6 | 7 | 8 | def build_file(cams: list[Camera]): 9 | sfm_json = sfm() 10 | 11 | for cam in cams: 12 | view = sfm_view() 13 | view.viewId = str(random.randint(0,65535)) 14 | view.poseId = view.viewId 15 | view.intrinsicId = str(random.randint(0, 65535)) 16 | view.resectionId = str(random.randint(0, 65536)) 17 | view.path = str(cam.source_image) 18 | view.width = str(int(cam.resolution[0])) 19 | view.height = str(int(cam.resolution[1])) 20 | 21 | intrinsic = sfm_intrinsics() 22 | intrinsic['intrinsicId'] = str(view.intrinsicId) 23 | intrinsic['width'] = str(int(view.width)) 24 | intrinsic['height'] = str(int(view.height)) 25 | intrinsic['sensorWidth'] = str(cam.sensor_size[0]) 26 | intrinsic['sensorHeight'] = str(cam.sensor_size[1]) 27 | intrinsic['serialNumber'] = str(-1) 28 | intrinsic['pxFocalLength'] = str(cam.focal_length_px[0]) 29 | intrinsic['pxInitialFocalLength'] = str(cam.focal_length_px[0]) 30 | 31 | # To also support newer Meshroom version 32 | intrinsic['focalLength'] = str(cam.focal_length_mm[0]) 33 | intrinsic['initialFocalLength'] = str(cam.focal_length_mm[0]) 34 | 35 | intrinsic['type'] = cam.model 36 | if cam.model == 'radial3' and cam.radial_distortion is not None: 37 | intrinsic['distortionParams'] = [str(item) for item in cam.radial_distortion] 38 | intrinsic['principalPoint'] = [str(item) for item in cam.principal_point] 39 | 40 | pose = sfm_pose() 41 | pose.poseId = view.poseId 42 | pose.pose.transform['rotation'] = [str(item) for sublist in cam.r.rotation_matrix.tolist() for item in sublist] 43 | pose.pose.transform['center'] = [str(item) for item in cam.t.tolist()] 44 | 45 | sfm_json.views.append(view) 46 | sfm_json.poses.append(pose) 47 | sfm_json.intrinsics.append(intrinsic) 48 | 49 | return json.dumps(sfm_json.__dict__, default=lambda o: o.__dict__, indent=4) -------------------------------------------------------------------------------- /docs/sphinx/formats/Unity/Unity.md: -------------------------------------------------------------------------------- 1 | # Unity 2 | 3 | *Unity* is a free to use multiplatform game engine. Although *Unity* is 4 | not simple to use, it is successfull in being a bridge between technical 5 | computer graphics and usability for ambitious users. It can also be used 6 | to create virtual reality experiences. 7 | 8 | ## Coordinate System 9 | 10 | The standard *Unity* coordinate system is y up, x right, -z front. The camera orientation is z front and y up. 11 | 12 | 13 | 14 | ![image](./Unity_coordinatesystem_cam-1.png) 15 | 16 | ## Unity Scene Files 17 | 18 | The storage format of *Unity* 19 | scenes are ASCII .unity files, which use a subset of the YAML language 20 | called *UnityYAML*. 21 | 22 | *Unity* uses tags at the start of a YAML document to define the object 23 | type and id: 24 | 25 | ```default 26 | --- !u!1 &6 27 | GameObject: 28 | ... 29 | ``` 30 | 31 | `!u!` marks the custom *Unity* prefix. The number after `!u!` 32 | indicates the type of the object. This thesis only focuses on three 33 | types: `GameObject (6)` which serves as the parent for the other two, 34 | `Transform (4)` which represents transformations and `Camera (20)` 35 | which represents a Camera. The number after `&` represents an unique 36 | id. 37 | 38 | The `GameObject` acts as the parent of the other objects and holds the 39 | information which transform corresponds to which camera. 40 | 41 | 42 | * `m_Component` is an array of `component`s which hold the ids of 43 | other objects in the scene. 44 | 45 | The `Transform` holds extrinsic parameters. 46 | 47 | 48 | * `m_LocalRotation` is the local rotation as a quaternion 49 | 50 | 51 | * `m_LocalPosition` is the local position as a vector 52 | 53 | 54 | * `m_LocalScale` is the local scale as a vector 55 | 56 | 57 | * `m_Father` is the id of the father transform 58 | 59 | 60 | * `m_Children` holds an array of ids of child transforms 61 | 62 | The `Camera` holds intrinsic parameters. 63 | 64 | 65 | * `m_SensorSize` is the sensor size in mm 66 | 67 | 68 | * `m_LensShift` is the lens shift relative to the sensor size 69 | 70 | 71 | * `m_FocalLength` is the focal length in mm 72 | 73 | 74 | * `orthographic` is a bool indicating whether the camera is a 75 | perspective or an orthographic camera 76 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/Unity/Unity.rst: -------------------------------------------------------------------------------- 1 | Unity 2 | ===== 3 | 4 | *Unity* is a free to use multiplatform game engine. Although *Unity* is 5 | not simple to use, it is successfull in being a bridge between technical 6 | computer graphics and usability for ambitious users. It can also be used 7 | to create virtual reality experiences. 8 | 9 | Coordinate System 10 | ----------------- 11 | The standard *Unity* coordinate system is y up, x right, -z front. The camera orientation is z front and y up. 12 | 13 | .. image:: Unity_coordinatesystem_cam-1.png 14 | 15 | Unity Scene Files 16 | ----------------- 17 | The storage format of *Unity* 18 | scenes are ASCII .unity files, which use a subset of the YAML language 19 | called *UnityYAML*. 20 | 21 | *Unity* uses tags at the start of a YAML document to define the object 22 | type and id: 23 | 24 | :: 25 | 26 | --- !u!1 &6 27 | GameObject: 28 | ... 29 | 30 | ``!u!`` marks the custom *Unity* prefix. The number after ``!u!`` 31 | indicates the type of the object. This thesis only focuses on three 32 | types: ``GameObject (6)`` which serves as the parent for the other two, 33 | ``Transform (4)`` which represents transformations and ``Camera (20)`` 34 | which represents a Camera. The number after ``&`` represents an unique 35 | id. 36 | 37 | The ``GameObject`` acts as the parent of the other objects and holds the 38 | information which transform corresponds to which camera. 39 | 40 | - ``m_Component`` is an array of ``component``\ s which hold the ids of 41 | other objects in the scene. 42 | 43 | The ``Transform`` holds extrinsic parameters. 44 | 45 | - ``m_LocalRotation`` is the local rotation as a quaternion 46 | 47 | - ``m_LocalPosition`` is the local position as a vector 48 | 49 | - ``m_LocalScale`` is the local scale as a vector 50 | 51 | - ``m_Father`` is the id of the father transform 52 | 53 | - ``m_Children`` holds an array of ids of child transforms 54 | 55 | The ``Camera`` holds intrinsic parameters. 56 | 57 | - ``m_SensorSize`` is the sensor size in mm 58 | 59 | - ``m_LensShift`` is the lens shift relative to the sensor size 60 | 61 | - ``m_FocalLength`` is the focal length in mm 62 | 63 | - ``orthographic`` is a bool indicating whether the camera is a 64 | perspective or an orthographic camera 65 | 66 | -------------------------------------------------------------------------------- /camorph/lib/utils/bin_writer_utils.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def get_uint8(i: int) -> bytes: 5 | """ 6 | This function encodes an integer to a uint8 7 | :param i: Integer 8 | :return: bytes 9 | """ 10 | return i.to_bytes(1, 'little') 11 | 12 | 13 | def get_uint32(i: int) -> bytes: 14 | """ 15 | This function encodes an integer to a uint32 16 | :param i: Integer 17 | :return: bytes 18 | """ 19 | return i.to_bytes(4, 'little') 20 | 21 | 22 | def get_uint64(i: int) -> bytes: 23 | """ 24 | This function encodes an integer to a uint64 25 | :param i: Integer 26 | :return: bytes 27 | """ 28 | return i.to_bytes(8, 'little') 29 | 30 | 31 | def get_int16(i: int) -> bytes: 32 | """ 33 | This function encodes an integer to a int16 34 | :param i: Integer 35 | :return: bytes 36 | """ 37 | return i.to_bytes(2, 'little', signed=True) 38 | 39 | 40 | def get_int32(i: int) -> bytes: 41 | """ 42 | This function encodes an integer to a int32 43 | :param i: Integer 44 | :return: bytes 45 | """ 46 | return i.to_bytes(4, 'little', signed=True) 47 | 48 | 49 | def get_int64(i: int) -> bytes: 50 | """ 51 | This function encodes an integer to a int64 52 | :param i: Integer 53 | :return: bytes 54 | """ 55 | return i.to_bytes(8, 'little', signed=True) 56 | 57 | 58 | def get_float32(f: float) -> bytes: 59 | """ 60 | This function encodes an float to a float32 61 | :param f: Float 62 | :return: bytes 63 | """ 64 | return bytearray(struct.pack(' bytes: 68 | """ 69 | This function encodes an float to a float64 70 | :param f: Float 71 | :return: bytes 72 | """ 73 | return bytearray(struct.pack(' bytes: 77 | """ 78 | This function encodes a bool to bytes 79 | :param b: Bool 80 | :return: bytes 81 | """ 82 | i = 1 if b == True else 0 83 | return i.to_bytes(1, 'little') 84 | 85 | 86 | def get_string(s: str) -> bytes: 87 | """ 88 | This function encodes a string to bytes 89 | :param s: String 90 | :return: bytes 91 | """ 92 | if len(s) == 0: 93 | return b'' 94 | return bytearray(s, 'Ascii') 95 | -------------------------------------------------------------------------------- /camorph/lib/utils/file_utils.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | 3 | 4 | def peek(f, length): 5 | """ 6 | This function peeks if the file can be read for a given length. 7 | 8 | :param f: File handle 9 | :param length: Length 10 | :return: bytes or str 11 | """ 12 | pos = f.tell() 13 | peeked = f.read(length) 14 | f.seek(pos) 15 | return peeked 16 | 17 | def extension(path: str) -> str: 18 | """ 19 | This function returns the extension of a filename. 20 | :param path: Filename 21 | :type path: str 22 | :return: str 23 | """ 24 | return path[path.rfind('.'):] 25 | 26 | def get_files_in_dir(path: str, extension = None): 27 | """ 28 | This function returns all files in a given directory with an optional extension match. 29 | :param path: Path to directory 30 | :type path: str 31 | :param extension: Optional extension 32 | :type extension: str 33 | :return: list[str] 34 | """ 35 | if extension is None: 36 | return [f for f in listdir(path)] 37 | else: 38 | return [f for f in listdir(path) if f.endswith(extension)] 39 | 40 | def fixed_list(ilist, length, optype = None): 41 | """ 42 | This function returns a list of a fixed length, regardless of number of elements in the input list. 43 | If the list is shorter than the length parameter, it will be padded with the default value for this type. 44 | When the list is empty, an optype has to be supplied. 45 | For example: 46 | >>> ilist = list[1.0] 47 | >>> fixed_list(ilist, 3) 48 | returns [1.0,0.0,0.0] 49 | 50 | :param ilist: List to extend or truncate 51 | :param length: Desired output length 52 | :param optype: When ilist is empty, this is the type with which the list is filled. 53 | :return: list[any] 54 | """ 55 | if ilist is None or len(ilist) == 0: 56 | if optype is not None: 57 | rlist = [optype()] 58 | else: 59 | raise Exception("If list is None or empty, a basetype must be supplied") 60 | else: 61 | rlist = ilist.copy() 62 | eltype = type(rlist[0]) 63 | listlen = len(rlist) 64 | if listlen >= length: 65 | return rlist[:length] 66 | else: 67 | rlist.extend([eltype() for x in range(length - listlen)]) 68 | return rlist 69 | -------------------------------------------------------------------------------- /docs/sphinx/plugin.md: -------------------------------------------------------------------------------- 1 | # Extensions 2 | 3 | Because there is an abundance of formats and new ones are always emerging, camorph offers easy exentisbility. 4 | All modules in the ext need to define a property camorph_extension in its __init__.py and set that to an instance 5 | of the [`FileHandler`](../lib/model.md#model.FileHandler.FileHandler) class. Camorph recognizes all modules with this property 6 | which can then be chosen in the [`camorph.read_cameras()`](camorph.md#camorph.read_cameras) and [`camorph.read_cameras()`](camorph.md#camorph.read_cameras) methods by theis respective [`name`](../lib/model.md#model.FileHandler.FileHandler.name). 7 | 8 | ## Writing Extensions 9 | 10 | Step by step guide to write an extension 11 | 12 | ### Step 1 13 | 14 | Create a new folder inside the ext folder, and create a __init__.py 15 | 16 | ### Step 2 17 | 18 | Create a new python file and name it something descriptive, preferably the name of the format. 19 | Then, create a new class with the same name as the module inside the python file, and derive it from 20 | the abstract class [`FileHandler`](../lib/model.md#model.FileHandler.FileHandler). For example, the file myformat.py inside the package 21 | myformat could look like this: 22 | 23 | ```python 24 | class myformat(FileHandler): 25 | """ 26 | Here you can provide a docstring for the sphinx documentation 27 | """ 28 | 29 | def crucial_properties(self) -> list[str]: 30 | return ['source_image','resolution','principal_point','focal_length_px'] 31 | 32 | def name(self): 33 | return "myformat" 34 | 35 | def file_number(self): 36 | return 1 37 | 38 | def read_file(self, *path): 39 | pass 40 | 41 | def write_file(self, camera_array, output_path, file_type = None): 42 | pass 43 | 44 | def coordinate_into(self, camera_array): 45 | pass 46 | 47 | def coordinate_from(self, camera_array): 48 | pass 49 | ``` 50 | 51 | ### Step 3 52 | 53 | In the __init__.py, create an attribute camorph_extension and set it to an instance of your new class 54 | 55 | ```python 56 | from .myformat import myformat 57 | 58 | camorph_extension = myformat() 59 | ``` 60 | 61 | ### Step 4 62 | 63 | Implement the new format according to the specification of the [`FileHandler`](../lib/model.md#model.FileHandler.FileHandler) class. 64 | Camorph will automatically load the module: 65 | 66 | ```python 67 | camorph.read_cameras('myformat', r'\path\to\myformat') 68 | ``` 69 | -------------------------------------------------------------------------------- /camorph/lib/model/FileHandler.py: -------------------------------------------------------------------------------- 1 | # This is an interface to be used for all new extensions 2 | 3 | from abc import ABC, abstractmethod 4 | from functools import wraps 5 | 6 | 7 | class FileHandler(ABC): 8 | """ 9 | This is the base class for all external FileHandlers. 10 | """ 11 | 12 | @abstractmethod 13 | def crucial_properties(self) -> list[(str, type)]: 14 | """ 15 | Define all crucial properties as a list of the property name, for example: 16 | return ['source_image', 'resolution'] 17 | """ 18 | pass 19 | 20 | @property 21 | @abstractmethod 22 | def name(self) -> str: 23 | """ 24 | The unique name of the FileHandler, for example COLMAP, fbx, etc. 25 | """ 26 | pass 27 | 28 | @property 29 | @abstractmethod 30 | def file_number(self) -> int: 31 | """ 32 | How many files the Handler needs to read or write. Return -1 if the number cannot be known beforehand (for example RealityCapture). 33 | """ 34 | pass 35 | 36 | @abstractmethod 37 | def read_file(self, *args, **kwargs): 38 | """ 39 | Read the given file 40 | 41 | :param args: The input parameters, usually file paths. 42 | :param kwargs: The input keyword args, like posetrace. 43 | :return: list[Camera] 44 | """ 45 | pass 46 | 47 | @abstractmethod 48 | def write_file(self, camera_array, output_path, file_type=None): 49 | """ 50 | Write the given list of cameras to a file. 51 | 52 | :param camera_array: The list of cameras 53 | :param output_path: The output path 54 | :param file_type: An optional string for different filetypes (for example binary or ascii) 55 | :return: None 56 | """ 57 | pass 58 | 59 | @abstractmethod 60 | def coordinate_from(self, camera_array): 61 | """ 62 | Convert cameras rotation and translation from this coordinate system into camorph coordinate system 63 | 64 | :param camera_array: Ths list of cameras 65 | :return: list[Camera] 66 | """ 67 | pass 68 | 69 | @abstractmethod 70 | def coordinate_into(self, camera_array): 71 | """ 72 | Convert cameras rotation and translation from camorph coordinate system into this coordinate system 73 | 74 | :param camera_array: Ths list of cameras 75 | :return: list[Camera] 76 | """ 77 | pass 78 | -------------------------------------------------------------------------------- /camorph_linux.yml: -------------------------------------------------------------------------------- 1 | name: camorph_linux 2 | channels: 3 | - defaults 4 | dependencies: 5 | - _libgcc_mutex=0.1=main 6 | - _openmp_mutex=5.1=1_gnu 7 | - blas=1.0=mkl 8 | - brotli=1.0.9=he6710b0_2 9 | - bzip2=1.0.8=h7b6447c_0 10 | - ca-certificates=2022.4.26=h06a4308_0 11 | - cycler=0.11.0=pyhd3eb1b0_0 12 | - dbus=1.13.18=hb2f20db_0 13 | - expat=2.4.4=h295c915_0 14 | - fontconfig=2.13.1=h6c09931_0 15 | - fonttools=4.25.0=pyhd3eb1b0_0 16 | - freetype=2.11.0=h70c0345_0 17 | - giflib=5.2.1=h7b6447c_0 18 | - glib=2.69.1=h4ff587b_1 19 | - gst-plugins-base=1.14.0=h8213a91_2 20 | - gstreamer=1.14.0=h28cd5cc_2 21 | - icu=58.2=he6710b0_3 22 | - intel-openmp=2021.4.0=h06a4308_3561 23 | - jpeg=9e=h7f8727e_0 24 | - lcms2=2.12=h3be6417_0 25 | - ld_impl_linux-64=2.38=h1181459_1 26 | - libffi=3.3=he6710b0_2 27 | - libgcc-ng=11.2.0=h1234567_0 28 | - libgomp=11.2.0=h1234567_0 29 | - libpng=1.6.37=hbc83047_0 30 | - libstdcxx-ng=11.2.0=h1234567_0 31 | - libtiff=4.2.0=h2818925_1 32 | - libuuid=1.0.3=h7f8727e_2 33 | - libwebp=1.2.2=h55f646e_0 34 | - libwebp-base=1.2.2=h7f8727e_0 35 | - libxcb=1.15=h7f8727e_0 36 | - libxml2=2.9.12=h74e7548_2 37 | - lz4-c=1.9.3=h295c915_1 38 | - matplotlib-base=3.5.1=py310ha18d171_1 39 | - mkl=2021.4.0=h06a4308_640 40 | - mkl_fft=1.3.1=py310hd6ae3a3_0 41 | - mkl_random=1.2.2=py310h00e6091_0 42 | - munkres=1.1.4=py_0 43 | - ncurses=6.3=h7f8727e_2 44 | - numpy-base=1.22.3=py310h9585f30_0 45 | - openssl=1.1.1o=h7f8727e_0 46 | - packaging=21.3=pyhd3eb1b0_0 47 | - pcre=8.45=h295c915_0 48 | - pyparsing=3.0.4=pyhd3eb1b0_0 49 | - pyqt=5.9.2=py310h295c915_6 50 | - python=3.10.4=h12debd9_0 51 | - python-dateutil=2.8.2=pyhd3eb1b0_0 52 | - qt=5.9.7=h5867ecd_1 53 | - readline=8.1.2=h7f8727e_1 54 | - six=1.16.0=pyhd3eb1b0_1 55 | - sqlite=3.38.3=hc218d9a_0 56 | - tk=8.6.11=h1ccaba5_1 57 | - tzdata=2022a=hda174b7_0 58 | - wheel=0.37.1=pyhd3eb1b0_0 59 | - xz=5.2.5=h7f8727e_1 60 | - zlib=1.2.12=h7f8727e_2 61 | - zstd=1.5.2=ha4553b6_0 62 | - pip: 63 | - certifi==2022.5.18.1 64 | - isodate==0.6.1 65 | - kiwisolver==1.4.2 66 | - matplotlib==3.5.1 67 | - mkl-fft==1.3.1 68 | - mkl-random==1.2.2 69 | - mkl-service==2.4.0 70 | - numpy==1.22.3 71 | - pillow==9.0.1 72 | - pip==21.2.4 73 | - pyquaternion==0.9.9 74 | - pyyaml==6.0 75 | - rdflib==6.1.1 76 | - setuptools==61.2.0 77 | - sip==4.19.13 78 | - tornado==6.1 79 | prefix: /usr/local/anaconda3/envs/camorph_linux 80 | 81 | -------------------------------------------------------------------------------- /camorph/lib/utils/bin_reader_utils.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | def read_uint8(f): 5 | """ 6 | This function reads a uint8 from a file 7 | :param f: File handle 8 | :return: int 9 | """ 10 | bytes = f.read(1) 11 | return int.from_bytes(bytes, 'little') 12 | 13 | 14 | def read_uint32(f): 15 | """ 16 | This function reads a uint32 from a file 17 | :param f: File handle 18 | :return: int 19 | """ 20 | bytes = f.read(4) 21 | return int.from_bytes(bytes, 'little') 22 | 23 | def read_uint64(f): 24 | """ 25 | This function reads a uint64 from a file 26 | :param f: File handle 27 | :return: int 28 | """ 29 | bytes = f.read(8) 30 | return int.from_bytes(bytes, 'little') 31 | 32 | def read_int16(f): 33 | """ 34 | This function reads a in16 from a file 35 | :param f: File handle 36 | :return: int 37 | """ 38 | bytes = f.read(2) 39 | return int.from_bytes(bytes, 'little', signed=True) 40 | 41 | 42 | def read_int32(f): 43 | """ 44 | This function reads a int32 from a file 45 | :param f: File handle 46 | :return: int 47 | """ 48 | bytes = f.read(4) 49 | return int.from_bytes(bytes, 'little', signed=True) 50 | 51 | 52 | def read_int64(f): 53 | """ 54 | This function reads a int64 from a file 55 | :param f: File handle 56 | :return: int 57 | """ 58 | bytes = f.read(8) 59 | return int.from_bytes(bytes, 'little', signed=True) 60 | 61 | 62 | def read_float32(f): 63 | """ 64 | This function reads a float32 from a file 65 | :param f: File handle 66 | :return: float 67 | """ 68 | bytes = f.read(4) 69 | return struct.unpack('` class. Camorph recognizes all modules with this property 9 | which can then be chosen in the :meth:`camorph.read_cameras` and :meth:`camorph.read_cameras` methods by theis respective :meth:`name `. 10 | 11 | Writing Extensions 12 | ================== 13 | 14 | Step by step guide to write an extension 15 | 16 | Step 1 17 | ------ 18 | 19 | Create a new folder inside the `ext` folder, and create a `__init__.py` 20 | 21 | Step 2 22 | ------ 23 | 24 | Create a new python file and name it something descriptive, preferably the name of the format. 25 | Then, create a new class with the same name as the module inside the python file, and derive it from 26 | the abstract class :class:`FileHandler `. For example, the file `myformat.py` inside the package 27 | `myformat` could look like this: 28 | 29 | .. code-block:: python 30 | 31 | class myformat(FileHandler): 32 | """ 33 | Here you can provide a docstring for the sphinx documentation 34 | """ 35 | 36 | def crucial_properties(self) -> list[str]: 37 | return ['source_image','resolution','principal_point','focal_length_px'] 38 | 39 | def name(self): 40 | return "myformat" 41 | 42 | def file_number(self): 43 | return 1 44 | 45 | def read_file(self, *path): 46 | pass 47 | 48 | def write_file(self, camera_array, output_path, file_type = None): 49 | pass 50 | 51 | def coordinate_into(self, camera_array): 52 | pass 53 | 54 | def coordinate_from(self, camera_array): 55 | pass 56 | 57 | Step 3 58 | ------ 59 | In the `__init__.py`, create an attribute `camorph_extension` and set it to an instance of your new class 60 | 61 | .. code-block:: python 62 | 63 | from .myformat import myformat 64 | 65 | camorph_extension = myformat() 66 | 67 | Step 4 68 | ------ 69 | Implement the new format according to the specification of the :class:`FileHandler ` class. 70 | Camorph will automatically load the module: 71 | 72 | .. code-block:: python 73 | 74 | camorph.read_cameras('myformat', r'\path\to\myformat') -------------------------------------------------------------------------------- /camorph/ext/Meshroom/util/ReaderSyntacticUtil.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import os 4 | from os.path import basename 5 | 6 | import numpy as np 7 | from pyquaternion import Quaternion 8 | from distutils.version import StrictVersion 9 | 10 | from camorph.lib.model.Camera import Camera 11 | from camorph.lib.utils import math_utils 12 | 13 | 14 | 15 | def read_sfm(input_path): 16 | with open(input_path, 'r') as f: 17 | sfm_json = json.loads(f.read()) 18 | poses = sfm_json['poses'] 19 | intrinsics = sfm_json['intrinsics'] 20 | cams = [] 21 | for view in sfm_json['views']: 22 | pose = next((p for p in poses if p['poseId'] == view['poseId']), None) 23 | intrinsic = next((i for i in intrinsics if i['intrinsicId'] == view['intrinsicId']), None) 24 | if pose is not None and intrinsic is not None: 25 | transform = pose['pose']['transform'] 26 | cam = Camera() 27 | cam.name = basename(view['path']).split('.')[0] 28 | cam.projection_type = 'perspective' 29 | cam.t = np.asarray([float(x) for x in transform['center']]) 30 | rotmat = np.asarray([float(x) for x in transform['rotation']]).reshape((3,3)) 31 | cam.r = Quaternion(matrix = rotmat) 32 | if intrinsic.get('pxFocalLength') is not None: 33 | cam.focal_length_px = [float(intrinsic['pxFocalLength']),float(intrinsic['pxFocalLength'])] 34 | elif intrinsic.get('focalLength') is not None: 35 | cam.focal_length_mm = [float(intrinsic['focalLength']),float(intrinsic['focalLength'])] 36 | 37 | cam.projection_type = 'perspective' 38 | cam.resolution = (float(view['width']), float(view['height'])) 39 | if StrictVersion('.'.join(sfm_json['version'])) > StrictVersion('1.2.0'): 40 | cam.principal_point = np.add(tuple([float(x) for x in intrinsic['principalPoint']]),np.multiply(cam.resolution,0.5)) 41 | else: 42 | cam.principal_point = tuple([float(x) for x in intrinsic['principalPoint']]) 43 | # Unfortunately, Meshroom sensor size cannot be trusted 44 | # Calculating based on image aspect ratio 45 | sensor_size_x = 36.0 46 | cam.sensor_size = (sensor_size_x, sensor_size_x * (cam.resolution[1]/cam.resolution[0])) 47 | # cam.sensor_size = (float(intrinsic['sensorWidth']), float(intrinsic['sensorHeight'])) 48 | cam.model = intrinsic['type'] 49 | if 'distortionParams' in intrinsic: 50 | cam.model = 'brown' 51 | cam.radial_distortion = [float(x) for x in intrinsic['distortionParams']] 52 | else: 53 | cam.model = 'pinhole' 54 | cam.source_image = view['path'] 55 | cams.append(cam) 56 | 57 | return cams 58 | -------------------------------------------------------------------------------- /camorph/ext/LLFF/LLFF.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | import numpy as np 4 | import os 5 | import warnings 6 | from camorph.lib.model.Camera import Camera 7 | from camorph.lib.model.FileHandler import FileHandler 8 | from camorph.lib.utils import math_utils 9 | from pyquaternion import Quaternion 10 | 11 | 12 | class LLFF(FileHandler): 13 | def __init__(self): 14 | pass 15 | 16 | def crucial_properties(self) -> list[(str, type)]: 17 | return ['focal_length_px', 'resolution', 'near_far_bounds'] 18 | 19 | def name(self): 20 | return "llff" 21 | 22 | def file_number(self): 23 | return 1 24 | 25 | def read_file(self, input_path: str, **kwargs): 26 | data = np.load(input_path) 27 | cams = [] 28 | 29 | for c in data: 30 | cam = Camera() 31 | cam.focal_length_px = [c[14],c[14]] 32 | cam.resolution = [c[9],c[4]] 33 | c_mat = c[:-2].reshape([3,5])[:,:-1] 34 | t = c_mat[:,-1] 35 | rm = c_mat[:,:-1] 36 | s = [np.linalg.norm(rm[:,x]) for x in range(0,3)] 37 | rm = np.asarray([[rm[0,0]/s[0],rm[0,1]/s[1],rm[0,2]/s[2]], 38 | [rm[1,0]/s[0],rm[1,1]/s[1],rm[1,2]/s[2]], 39 | [rm[2,0]/s[0],rm[2,1]/s[1],rm[2,2]/s[2]]]) 40 | cam.t = t 41 | cam.r = math_utils.rotation_matrix_to_quaternion(rm) 42 | cam.near_far_bounds = [c[15],c[16]] 43 | cams.append(cam) 44 | return self.coordinate_from(cams) 45 | 46 | def write_file(self, camera_array: list[Camera], output_path: str, file_type=None): 47 | cams = self.coordinate_into(camera_array) 48 | warnings.warn("LLFF relies on sorted image order. Cameras will be sorted after images when writing LLFF") 49 | cams = sorted(cams, key=lambda x : (os.path.basename(x.source_image),x.source_image)) 50 | data = [] 51 | for c in cams: 52 | r = c.r.rotation_matrix.copy() 53 | r = np.append(r,c.t[...,None],axis=1) 54 | r = np.append(r,[[c.resolution[1]],[c.resolution[0]],[c.focal_length_px[0]]],axis=1) 55 | r = np.append(r,c.near_far_bounds) 56 | data.append(r) 57 | data = np.asarray(data, dtype='float64') 58 | np.save(output_path,data) 59 | 60 | 61 | def coordinate_into(self, camera_array: list[Camera]): 62 | cam_arr = copy.deepcopy(camera_array) 63 | for cam in cam_arr: 64 | cam.t, cam.r = math_utils.convert_coordinate_systems(['-x', '-z', 'y'], cam.t, cam.r, cdir=[0, 0, -1], 65 | cup=[-1, 0, 0],transpose=True) 66 | return cam_arr 67 | 68 | def coordinate_from(self, camera_array: list[Camera]): 69 | cam_arr = copy.deepcopy(camera_array) 70 | for cam in cam_arr: 71 | cam.t, cam.r = math_utils.convert_coordinate_systems(['-x', '-z', 'y'], cam.t, cam.r, cdir=[0, 0, -1], 72 | cup=[-1, 0, 0]) 73 | return cam_arr 74 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/dateutil.txt: -------------------------------------------------------------------------------- 1 | Applies to dateutil 2 | 3 | Copyright 2017- Paul Ganssle 4 | Copyright 2017- dateutil contributors (see AUTHORS file) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | The above license applies to all contributions after 2017-12-01, as well as 19 | all contributions that have been re-licensed (see AUTHORS file for the list of 20 | contributors who have re-licensed their code). 21 | -------------------------------------------------------------------------------- 22 | dateutil - Extensions to the standard Python datetime module. 23 | 24 | Copyright (c) 2003-2011 - Gustavo Niemeyer 25 | Copyright (c) 2012-2014 - Tomi Pieviläinen 26 | Copyright (c) 2014-2016 - Yaron de Leeuw 27 | Copyright (c) 2015- - Paul Ganssle 28 | Copyright (c) 2015- - dateutil contributors (see AUTHORS file) 29 | 30 | All rights reserved. 31 | 32 | Redistribution and use in source and binary forms, with or without 33 | modification, are permitted provided that the following conditions are met: 34 | 35 | * Redistributions of source code must retain the above copyright notice, 36 | this list of conditions and the following disclaimer. 37 | * Redistributions in binary form must reproduce the above copyright notice, 38 | this list of conditions and the following disclaimer in the documentation 39 | and/or other materials provided with the distribution. 40 | * Neither the name of the copyright holder nor the names of its 41 | contributors may be used to endorse or promote products derived from 42 | this software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 45 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 46 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 47 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 48 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 49 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 50 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 51 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 52 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 53 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 54 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 | 56 | The above BSD License Applies to all code, even that also covered by Apache 2.0. -------------------------------------------------------------------------------- /camorph/ext/Meshroom/Mehsroom.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import json 3 | import warnings 4 | import os 5 | from .util import ReaderSyntacticUtil, WriterSyntacticUtil 6 | from camorph.lib.model.Camera import Camera 7 | from camorph.lib.model.FileHandler import FileHandler 8 | from camorph.lib.utils import math_utils 9 | 10 | 11 | 12 | class Meshroom(FileHandler): 13 | def __init__(self): 14 | pass 15 | 16 | def crucial_properties(self) -> list[(str, type)]: 17 | return ['source_image','resolution','sensor_size','focal_length_px'] 18 | 19 | def name(self): 20 | return "meshroom" 21 | 22 | def file_number(self): 23 | return 1 24 | 25 | def read_file(self, input_path: str, **kwargs): 26 | suffix = input_path[input_path.rfind('.'):] 27 | if suffix == '.mg': 28 | cams = [] 29 | with open(input_path, 'r') as f: 30 | meshroom_json = json.loads(f.read()) 31 | graph = meshroom_json['graph'] 32 | nodes = [] 33 | for key in graph: 34 | node = graph[key] 35 | node['name'] = key 36 | nodes.append(graph[key]) 37 | for node in nodes: 38 | if node['nodeType'] == 'StructureFromMotion': 39 | uid = node['uids']['0'] 40 | sfm_folder = os.path.join(input_path[:input_path.rfind(os.path.sep)], 'MeshroomCache', 'StructureFromMotion', str(uid), 'cameras.sfm') 41 | try: 42 | cams.extend(ReaderSyntacticUtil.read_sfm(sfm_folder)) 43 | except Exception: 44 | #raise Exception 45 | warnings.warn('sfm file does not exist for node {}'.format(node['name'])) 46 | elif suffix == '.sfm': 47 | cams = ReaderSyntacticUtil.read_sfm(input_path) 48 | else: 49 | raise Exception('Unsupported File Format ', suffix) 50 | return self.coordinate_from(cams) 51 | 52 | def write_file(self, camera_array: list[Camera], output_path: str, file_type = None): 53 | cam_arr = self.coordinate_into(camera_array) 54 | sfm_json = WriterSyntacticUtil.build_file(cam_arr) 55 | with open(output_path, "w+") as file: 56 | file.write(sfm_json) 57 | 58 | 59 | def coordinate_into(self, camera_array: list[Camera]): 60 | cam_arr = copy.deepcopy(camera_array) 61 | for cam in cam_arr: 62 | cam.t, cam.r = math_utils.convert_coordinate_systems(['y', 'z', 'x'], cam.t, cam.r, cdir=[0, 0, 1], 63 | cup=[0, -1, 0], transpose=True) 64 | return cam_arr 65 | 66 | def coordinate_from(self, camera_array: list[Camera]): 67 | cam_arr = copy.deepcopy(camera_array) 68 | for cam in cam_arr: 69 | cam.t, cam.r = math_utils.convert_coordinate_systems(['y', 'z', 'x'], cam.t, cam.r, cdir=[0, 0, 1], 70 | cup=[0, -1, 0]) 71 | return cam_arr 72 | -------------------------------------------------------------------------------- /docs/sphinx/camorph.md: -------------------------------------------------------------------------------- 1 | # `camorph` Module 2 | 3 | This module provides the main conversion methods. 4 | 5 | 6 | ### camorph.print_keys() 7 | Print all available keys for supported formats 8 | 9 | 10 | * **Returns** 11 | 12 | None 13 | 14 | 15 | 16 | ### camorph.read_cameras(format, \*args, \*\*kwargs) 17 | Read the cameras of format format 18 | 19 | 20 | * **Parameters** 21 | 22 | 23 | * **src** (*str*) – The source camera format. Currently possible: COLMAP, fbx, meshroom, unity, mpeg_omaf 24 | 25 | 26 | * **args** – The source path. The number of source path arguments is defined by [`model.FileHandler.FileHandler.file_number()`](../lib/model.md#model.FileHandler.FileHandler.file_number) 27 | 28 | 29 | 30 | * **Returns** 31 | 32 | A list of cameras in the camorph coordinate system convention defined in [`model.Camera.Camera`](../lib/model.md#model.Camera.Camera) 33 | 34 | 35 | 36 | * **Return type** 37 | 38 | list[[`model.Camera.Camera`](../lib/model.md#model.Camera.Camera)] 39 | 40 | 41 | 42 | ### camorph.set_crucial_property_config(crucial_property_config) 43 | Manually set a crucial property config.json See 44 | 45 | 46 | * **Parameters** 47 | 48 | **crucial_property_config** – The path to the config.json file 49 | 50 | 51 | 52 | * **Returns** 53 | 54 | None 55 | 56 | 57 | 58 | ### camorph.visualize(cams) 59 | Visualizes the list of [`model.Camera.Camera`](../lib/model.md#model.Camera.Camera) with matplotlib 60 | See `vis.Visualizer` 61 | 62 | 63 | * **Parameters** 64 | 65 | **cams** – The list of [`model.Camera.Camera`](../lib/model.md#model.Camera.Camera) to visualize 66 | 67 | 68 | 69 | * **Returns** 70 | 71 | None 72 | 73 | 74 | 75 | ### camorph.write_cameras(format, path, cams, crop=None, scale=None, imdir=None, check_images=False, file_type=None) 76 | Write cameras of format dest to dest_path 77 | 78 | 79 | * **Parameters** 80 | 81 | 82 | * **format** – The dest camera format. Currently possible: COLMAP, fbx, meshroom, unity, mpeg_omaf 83 | 84 | 85 | * **path** – The path where to write the cameras 86 | 87 | 88 | * **cams** – The list of [`model.Camera.Camera`](../lib/model.md#model.Camera.Camera) to write 89 | 90 | 91 | * **crop** – A list of type [[x1,y1],[x2,y2]] which specifies the pixel location of the cropping to be applied 92 | 93 | 94 | * **scale** – A float which represents the relative scale applied in range 95 | 96 | 97 | * **imdir** – A string to replace the basepath of the current images 98 | 99 | 100 | * **file_type** – An optional parameter if there are multiple file types (for example, COLMAP has bin and txt) 101 | 102 | 103 | 104 | * **Returns** 105 | 106 | None 107 | 108 | 109 | 110 | ### camorph.write_crucial_config_template(cams, path) 111 | This function writes a template config.json to the desired path 112 | 113 | 114 | * **Parameters** 115 | 116 | 117 | * **cams** – The list of [`model.Camera.Camera`](../lib/model.md#model.Camera.Camera) 118 | 119 | 120 | * **path** – The path where to write the config 121 | 122 | 123 | 124 | * **Returns** 125 | 126 | None 127 | -------------------------------------------------------------------------------- /camorph/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | import sphinx_rtd_theme 16 | 17 | sys.path.insert(0, os.path.abspath('.')) 18 | sys.path.insert(0, os.path.abspath('.\\lib')) 19 | #sys.path.insert(0, os.path.abspath('.\\lib\\utils')) 20 | #sys.path.insert(0, os.path.abspath('.\\lib\\model')) 21 | sys.path.insert(0, os.path.abspath('.\\ext')) 22 | #sys.path.insert(0, os.path.abspath('.\\ext\\COLMAP')) 23 | 24 | # -- Project information ----------------------------------------------------- 25 | 26 | project = 'camorph' 27 | copyright = '2022, Benjamin Brand' 28 | author = 'Benjamin Brand' 29 | 30 | # The full version, including alpha/beta/rc tags 31 | release = '02.04.2022' 32 | 33 | 34 | # -- General configuration --------------------------------------------------- 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be 37 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 38 | # ones. 39 | extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc', 'sphinx_rtd_theme', 'sphinxcontrib.confluencebuilder', 'sphinx_markdown_builder'] 40 | autodoc_typehints = "none" 41 | 42 | myst_enable_extensions = [ 43 | "amsmath", 44 | "attrs_inline", 45 | "colon_fence", 46 | "deflist", 47 | "dollarmath", 48 | "fieldlist", 49 | "html_admonition", 50 | "html_image", 51 | "inv_link", 52 | "linkify", 53 | "replacements", 54 | "smartquotes", 55 | "strikethrough", 56 | "substitution", 57 | "tasklist", 58 | ] 59 | 60 | # Add any paths that contain templates here, relative to this directory. 61 | templates_path = ['_templates'] 62 | 63 | # List of patterns, relative to source directory, that match files and 64 | # directories to ignore when looking for source files. 65 | # This pattern also affects html_static_path and html_extra_path. 66 | exclude_patterns = [] 67 | 68 | 69 | # -- Options for HTML output ------------------------------------------------- 70 | 71 | # The theme to use for HTML and HTML Help pages. See the documentation for 72 | # a list of builtin themes. 73 | # 74 | html_theme = "sphinx_rtd_theme" 75 | 76 | # Add any paths that contain custom static files (such as style sheets) here, 77 | # relative to this directory. They are copied after the builtin static files, 78 | # so a file named "default.css" will overwrite the builtin "default.css". 79 | html_static_path = ['_static'] 80 | 81 | # Confluence options 82 | confluence_publish = True 83 | confluence_space_key = 'BTCIAS' 84 | confluence_ask_password = True 85 | confluence_server_url = 'https://intern.iis.fhg.de/' 86 | confluence_server_user = 'brandbn' 87 | confluence_parent_page = 'brandbn::Bachelor_thesis_camorph::Documentation' -------------------------------------------------------------------------------- /thirdPartyLegalNotices/kiwi.txt: -------------------------------------------------------------------------------- 1 | Applies to Kiwi-solver 2 | 3 | ========================= 4 | The Kiwi licensing terms 5 | ========================= 6 | Kiwi is licensed under the terms of the Modified BSD License (also known as 7 | New or Revised BSD), as follows: 8 | 9 | Copyright (c) 2013, Nucleic Development Team 10 | 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | Redistributions of source code must retain the above copyright notice, this 17 | list of conditions and the following disclaimer. 18 | 19 | Redistributions in binary form must reproduce the above copyright notice, this 20 | list of conditions and the following disclaimer in the documentation and/or 21 | other materials provided with the distribution. 22 | 23 | Neither the name of the Nucleic Development Team nor the names of its 24 | contributors may be used to endorse or promote products derived from this 25 | software without specific prior written permission. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 28 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 30 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 31 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 33 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 34 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 35 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | 38 | About Kiwi 39 | ---------- 40 | Chris Colbert began the Kiwi project in December 2013 in an effort to 41 | create a blisteringly fast UI constraint solver. Chris is still the 42 | project lead. 43 | 44 | The Nucleic Development Team is the set of all contributors to the Nucleic 45 | project and its subprojects. 46 | 47 | The core team that coordinates development on GitHub can be found here: 48 | http://github.com/nucleic. The current team consists of: 49 | 50 | * Chris Colbert 51 | 52 | Our Copyright Policy 53 | -------------------- 54 | Nucleic uses a shared copyright model. Each contributor maintains copyright 55 | over their contributions to Nucleic. But, it is important to note that these 56 | contributions are typically only changes to the repositories. Thus, the Nucleic 57 | source code, in its entirety is not the copyright of any single person or 58 | institution. Instead, it is the collective copyright of the entire Nucleic 59 | Development Team. If individual contributors want to maintain a record of what 60 | changes/contributions they have specific copyright on, they should indicate 61 | their copyright in the commit message of the change, when they commit the 62 | change to one of the Nucleic repositories. 63 | 64 | With this in mind, the following banner should be used in any source code file 65 | to indicate the copyright and license terms: 66 | 67 | #------------------------------------------------------------------------------ 68 | # Copyright (c) 2013, Nucleic Development Team. 69 | # 70 | # Distributed under the terms of the Modified BSD License. 71 | # 72 | # The full license is in the file COPYING.txt, distributed with this software. 73 | #------------------------------------------------------------------------------ 74 | -------------------------------------------------------------------------------- /camorph/ext/Unity/Unity.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import math 3 | import warnings 4 | 5 | import numpy as np 6 | 7 | from .util import ReaderSyntacticUtil, ReaderSemanticUtil, WriterSyntacticUtil 8 | from camorph.lib.model.Camera import Camera 9 | from camorph.lib.utils import math_utils 10 | from camorph.lib.model.FileHandler import FileHandler 11 | 12 | 13 | class Unity(FileHandler): 14 | 15 | def crucial_properties(self) -> list[(str, type)]: 16 | return ['fov'] 17 | 18 | def name(self): 19 | return "unity" 20 | 21 | def file_number(self): 22 | return 1 23 | 24 | def read_file(self, input_path, **kwargs): 25 | with open(input_path, "r") as f: 26 | txt = f.read() 27 | game_objs, cameras, transforms = ReaderSyntacticUtil.get_unity_from_yaml(txt) 28 | # we dont support cameras in prefabs for now 29 | ret_cams = [] 30 | for cam in cameras: 31 | # find corresponding transform 32 | transform = next(x for x in transforms if x['m_GameObject']['fileID'] == cam['m_GameObject']['fileID']) 33 | game_obj = next(x for x in game_objs if x['obj_id'] == cam['m_GameObject']['fileID']) 34 | translation, rotation = ReaderSemanticUtil.apply_transform_rec(transform, transforms) 35 | c = Camera() 36 | c.r = rotation 37 | c.t = translation 38 | c.name = game_obj['m_Name'] 39 | 40 | c.projection_type = 'orthographic' if cam['orthographic'] == 1 else 'perspective' 41 | c.model = 'orthographic' if c.projection_type == 'orthographic' else 'pinhole' 42 | 43 | c.focal_length_mm = [cam['m_FocalLength'], cam['m_FocalLength']] 44 | c.sensor_size = [cam['m_SensorSize']['x'], cam['m_SensorSize']['y']] 45 | c.lens_shift = [cam['m_LensShift']['x'], cam['m_LensShift']['y']] 46 | 47 | fov_axis_mode = cam['m_FOVAxisMode'] 48 | if fov_axis_mode == 1: 49 | c.fov = (np.radians(cam['field of view']), c.sensor_size[1]*np.radians(cam['field of view'])/c.sensor_size[0]) 50 | else: 51 | c.fov = (c.sensor_size[0] * np.radians(cam['field of view']) / c.sensor_size[1]), np.radians(cam['field of view']) 52 | computed_fov = 2 * math.atan2(c.sensor_size[0], 2 * c.focal_length_mm[0]) 53 | epsilon = 0.01 54 | if abs(computed_fov - c.fov[0]) > epsilon: 55 | warnings.warn('Focal length and field of view from Unity do not match. Calculating focal length from field of view') 56 | c.focal_length_mm = [c.sensor_size[0] / (2 * math.atan2(c.fov[0], 2)), 57 | c.sensor_size[1] / (2 * math.atan2(c.fov[1], 2))] 58 | 59 | 60 | ret_cams.append(c) 61 | 62 | ret_cams = self.coordinate_from(ret_cams) 63 | return ret_cams 64 | 65 | def write_file(self, camera_array, output_path, file_type = None): 66 | camera_array = self.coordinate_into(camera_array) 67 | file = WriterSyntacticUtil.build_file(camera_array) 68 | path = output_path 69 | with open(path, 'w') as f: 70 | f.write(file) 71 | 72 | def coordinate_into(self, camera_array): 73 | cam_arr = copy.deepcopy(camera_array) 74 | for cam in cam_arr: 75 | cam.t, cam.r = math_utils.convert_coordinate_systems(['y', 'z', '-x'], cam.t, cam.r, tdir=[0, 0, 1], 76 | tup=[0, 1, 0], transpose = True) 77 | return cam_arr 78 | 79 | def coordinate_from(self, camera_array): 80 | cam_arr = copy.deepcopy(camera_array) 81 | for cam in cam_arr: 82 | cam.t, cam.r = math_utils.convert_coordinate_systems(['y', 'z', '-x'], cam.t, cam.r, cdir=[0, 0, 1], 83 | cup=[0, 1, 0]) 84 | 85 | return cam_arr 86 | -------------------------------------------------------------------------------- /camorph/cli.py: -------------------------------------------------------------------------------- 1 | from argparse import ArgumentParser 2 | 3 | import camorph.camorph as camorph 4 | import importlib.metadata 5 | import sys 6 | 7 | def run(run_as_module=False): 8 | argparser = ArgumentParser(description='Convert Cameras from different formats to each other') 9 | if not run_as_module: 10 | argparser.add_argument('--version', action='version', version=f'{importlib.metadata.version("camorph")}') 11 | argparser.add_argument('-i', '--input', metavar='input_path', nargs="+", type=str, required=True, 12 | help='the input path of the camera file(s) to read') 13 | argparser.add_argument('-if', '--input_format', metavar='input_format', type=str, required=True, 14 | help='the format of the input camera file(s)') 15 | argparser.add_argument('-o', '--output', metavar='output_path', type=str, default=None, 16 | help='the output path where the camera file(s) should be saved') 17 | argparser.add_argument('-of', '--output_format', metavar='output_format', type=str, default=None, 18 | help='the format of the output camera file(s)') 19 | argparser.add_argument('-v', '--visualize', action='store_true', 20 | help='when this parameter is present, the cameras will be visualized.') 21 | argparser.add_argument('-c', '--config', metavar='config', type=str, default=None, 22 | help='the path to a config.json file for missing crucial properties') 23 | argparser.add_argument('-ft', '--file_type', metavar='file_type', type=str, default=None, 24 | help='some formats support different types of output files, for example bin for binary and txt for ascii files') 25 | argparser.add_argument('-pt', '--posetrace', action='store_true', 26 | help='treat the input as a posetrace and ignore any source images.') 27 | argparser.add_argument('-cr', '--crop',metavar='crop', type=str, default=None, 28 | help='crop source image attributes by the specified top left and bottom right corner. Format: \"leftcorner_x,leftcorner_y,rightcorner_x,rightcorner_y\". ATTENTION: THIS DOES NOT MODIFY THE IMAGES, ONLY THE PROPERTIES IN THE FILE!') 29 | argparser.add_argument('-s', '--scale',metavar='scale', type=float, default=None, 30 | help='scale source image attributes by the specified factor. ATTENTION: THIS DOES NOT MODIFY THE IMAGES, ONLY THE PROPERTIES IN THE FILE!') 31 | argparser.add_argument('-id','--image-dir',metavar='image_dir', type=str, 32 | help='replace the directory for the source images with this.') 33 | argparser.add_argument('-ci','--check-images',dest='check_images', action='store_true', 34 | help='check if images exist and are of the right resolution.') 35 | 36 | cargs = argparser.parse_args() 37 | cams = camorph.read_cameras(cargs.input_format, *cargs.input, posetrace=cargs.posetrace) 38 | if cargs.visualize is True: 39 | camorph.visualize(cams) 40 | 41 | def getargs(args, name): 42 | if hasattr(args, name): 43 | return getattr(args,name) 44 | else: 45 | return None 46 | 47 | if cargs.config is not None: 48 | camorph.set_crucial_property_config(cargs.config) 49 | if hasattr(cargs,"output_format") and hasattr(cargs,"output"): 50 | ft = getargs(cargs,"file_type") 51 | c = getargs(cargs,"crop") 52 | if c is not None: 53 | c = [int(x) for x in c.split(',')] 54 | assert(len(c) == 4) 55 | c = [[c[0],c[1]],[c[2],c[3]]] 56 | s = getargs(cargs, "scale") 57 | imd = getargs(cargs, "image_dir") 58 | camorph.write_cameras(cargs.output_format, cargs.output, cams, crop=c, scale=s, check_images=cargs.check_images, 59 | imdir=imd, file_type=ft) 60 | -------------------------------------------------------------------------------- /docs/lib/model.md: -------------------------------------------------------------------------------- 1 | # `Camera` Class 2 | 3 | 4 | ### _class_ model.Camera.Camera() 5 | This class represents camera parameters in camorph 6 | 7 | 8 | #### autocompute() 9 | Automaic computation of certain parameters 10 | 11 | Type: bool 12 | 13 | 14 | #### focal_length_mm() 15 | Focal length in millimeters 16 | 17 | Type: float 18 | 19 | 20 | #### focal_length_px() 21 | Focal length in pixels 22 | 23 | Type: float 24 | 25 | 26 | #### fov() 27 | Field of view in radians 28 | 29 | Type: (float,float) 30 | 31 | 32 | #### lens_shift() 33 | Lens shift in normalized coordinates from -0.5 to 0.5 34 | 35 | Type: (float,float) 36 | 37 | 38 | #### model() 39 | Camera model. One of the following: pinhole, opencv_fisheye, orthographic, brown 40 | 41 | Type: str 42 | 43 | 44 | #### name() 45 | A unique name 46 | 47 | Type: str 48 | 49 | 50 | #### near_far_bounds() 51 | Near and far camera bounds 52 | 53 | Type: list[float] 54 | 55 | 56 | #### principal_point() 57 | Principal point in pixels as a tuple 58 | 59 | Type: (float,float) 60 | 61 | 62 | #### projection_type() 63 | Projection type, either perspective, orthogonal or equirectangular 64 | 65 | Type: str 66 | 67 | 68 | #### r() 69 | Rotation 70 | 71 | Type: Quaternion 72 | 73 | 74 | #### radial_distortion() 75 | Radial distortion coefficients 76 | 77 | Type: list[float] 78 | 79 | 80 | #### resolution() 81 | Resolution in pixels as a tuple 82 | 83 | Type: (float,float) 84 | 85 | 86 | #### sensor_size() 87 | Sensor size 88 | 89 | Type: (float,float) 90 | 91 | 92 | #### source_image() 93 | Path to a source image 94 | 95 | Type: str 96 | 97 | 98 | #### t() 99 | Translation 100 | 101 | Type: ndarray 102 | 103 | 104 | #### tangential_distortion() 105 | Tangential distortion coefficients 106 | 107 | Type: list[float] 108 | 109 | # `FileHandler` Class 110 | 111 | 112 | ### _class_ model.FileHandler.FileHandler() 113 | This is the base class for all external FileHandlers. 114 | 115 | 116 | #### _abstract_ coordinate_from(camera_array) 117 | Convert cameras rotation and translation from this coordinate system into camorph coordinate system 118 | 119 | 120 | * **Parameters** 121 | 122 | **camera_array** – Ths list of cameras 123 | 124 | 125 | 126 | * **Returns** 127 | 128 | list[Camera] 129 | 130 | 131 | 132 | #### _abstract_ coordinate_into(camera_array) 133 | Convert cameras rotation and translation from camorph coordinate system into this coordinate system 134 | 135 | 136 | * **Parameters** 137 | 138 | **camera_array** – Ths list of cameras 139 | 140 | 141 | 142 | * **Returns** 143 | 144 | list[Camera] 145 | 146 | 147 | 148 | #### _abstract_ crucial_properties() 149 | Define all crucial properties as a list of the property name, for example: 150 | return [‘source_image’, ‘resolution’] 151 | 152 | 153 | #### _abstract property_ file_number() 154 | How many files the Handler needs to read or write. Return -1 if the number cannot be known beforehand (for example RealityCapture). 155 | 156 | 157 | #### _abstract property_ name() 158 | The unique name of the FileHandler, for example COLMAP, fbx, etc. 159 | 160 | 161 | #### _abstract_ read_file(\*args, \*\*kwargs) 162 | Read the given file 163 | 164 | 165 | * **Parameters** 166 | 167 | 168 | * **args** – The input parameters, usually file paths. 169 | 170 | 171 | * **kwargs** – The input keyword args, like posetrace. 172 | 173 | 174 | 175 | * **Returns** 176 | 177 | list[Camera] 178 | 179 | 180 | 181 | #### _abstract_ write_file(camera_array, output_path, file_type=None) 182 | Write the given list of cameras to a file. 183 | 184 | 185 | * **Parameters** 186 | 187 | 188 | * **camera_array** – The list of cameras 189 | 190 | 191 | * **output_path** – The output path 192 | 193 | 194 | * **file_type** – An optional string for different filetypes (for example binary or ascii) 195 | 196 | 197 | 198 | * **Returns** 199 | 200 | None 201 | -------------------------------------------------------------------------------- /camorph/ext/FBX/util/ReaderSemanticUtil.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from pyquaternion import Quaternion 4 | 5 | import camorph.lib.utils.math_utils as math_utils 6 | 7 | # sys.path.append('') 8 | # sys.path.append("../..") # Adds higher directory to python modules path. 9 | 10 | import numpy as np 11 | from ..model import * 12 | 13 | 14 | # semantic helpers 15 | def build_hierarchy_tree(connections, objects, node): 16 | resolved_connections = [] 17 | for con in connections: 18 | if con.cnames[1] == node.id: 19 | if con.cnames[0] in objects: 20 | child_node = FBXHierarchyNode(con.cnames[0]) 21 | child_node.object = objects[con.cnames[0]] 22 | node.children.append(child_node) 23 | resolved_connections.append(con) 24 | 25 | for con in resolved_connections: 26 | connections.remove(con) 27 | 28 | for child in node.children: 29 | build_hierarchy_tree(connections, objects, child) 30 | 31 | # compute chained transforms and add connected objects 32 | def resolve_hierarchy(parent, transforms): 33 | for child in parent.children: 34 | obj = child.object[0] 35 | if obj is not None and obj.name == 'Model': 36 | properties70 = obj.Properties70[0] 37 | 38 | # Translation 39 | translation = np.array([0.0, 0.0, 0.0]) 40 | if 'Lcl Translation' in properties70: 41 | translation += np.array(properties70['Lcl Translation'][0].pvalue) 42 | 43 | # Rotation 44 | rotation = np.identity(3) 45 | if 'PreRotation' in properties70: 46 | rotation = rotation @ math_utils.euler_to_rotation_matrix( 47 | [np.radians(x) for x in properties70['PreRotation'][0].pvalue]) 48 | if 'Lcl Rotation' in properties70: 49 | rotation = rotation @ math_utils.euler_to_rotation_matrix( 50 | [np.radians(x) for x in properties70['Lcl Rotation'][0].pvalue]) 51 | 52 | # Scale 53 | scale = np.array([1.0,1.0,1.0]) 54 | if 'Lcl Scaling' in properties70: 55 | scale *= np.array(properties70['Lcl Scaling'][0].pvalue) 56 | 57 | # get parent transforms 58 | parent_translation = transforms[parent.id][2] 59 | parent_rotation = transforms[parent.id][3] 60 | 61 | # chain parent transforms 62 | translation = translation / scale 63 | translation = parent_rotation @ translation 64 | translation = translation + parent_translation 65 | rotation = parent_rotation @ rotation 66 | 67 | transforms[child.id] = [obj, obj.pvalue, translation, rotation, child.children] 68 | elif obj is not None and obj.name == 'Texture': 69 | parent.object[0].Properties70[0]['FileName'] = obj.FileName[0]['props'][0] 70 | else: 71 | transforms[child.id] = [obj, None, np.array([0, 0, 0]), np.identity(3), child.children] 72 | resolve_hierarchy(child, transforms) 73 | 74 | def get_coordinate_system(fbx_obj): 75 | def get_axis_from_dict(axis, axis_sign, dict): 76 | return dict[axis] if axis_sign > 0 else '-' + dict[axis] 77 | global_settings = fbx_obj['GlobalSettings']['Properties70'][0] 78 | up_axis = global_settings['UpAxis'][0].pvalue[0] 79 | up_axis_sign = global_settings['UpAxisSign'][0].pvalue[0] 80 | front_axis = global_settings['FrontAxis'][0].pvalue[0] 81 | front_axis_sign = global_settings['FrontAxisSign'][0].pvalue[0] 82 | coord_axis = global_settings['CoordAxis'][0].pvalue[0] 83 | coord_axis_sign = global_settings['CoordAxisSign'][0].pvalue[0] 84 | 85 | axis_dict = {0: 'x', 1: 'y', 2: 'z'} 86 | 87 | # we would need to look what vector is what direction 88 | # for example: what is x? x is right 89 | # then write what right is in our coordinate system: y 90 | # this is complicated, luckily for us we can go the other way round 91 | # which axes are x,y,z in the FBX coordinate system 92 | # then transpose/invert 93 | coordinate_system = [get_axis_from_dict(front_axis, front_axis_sign, axis_dict), 94 | get_axis_from_dict(coord_axis, coord_axis_sign, axis_dict), 95 | get_axis_from_dict(up_axis, up_axis_sign, axis_dict)] 96 | 97 | return coordinate_system 98 | 99 | -------------------------------------------------------------------------------- /camorph/ext/COLMAP/util/ReaderSyntacticUtil.py: -------------------------------------------------------------------------------- 1 | from pyquaternion import Quaternion 2 | import numpy as np 3 | import struct 4 | 5 | from camorph.lib.utils.file_utils import extension 6 | from ..model import * 7 | from ..model.CameraModel import CameraModel, CAMERA_MODEL_IDS 8 | 9 | 10 | # from https://github.com/colmap/colmap/blob/1a4d0bad2e90aa65ce997c9d1779518eaed998d5/scripts/python/read_write_model.py 11 | def read_next_bytes(fid, num_bytes, format_char_sequence, endian_character="<"): 12 | """Read and unpack the next bytes from a binary file. 13 | :param fid: 14 | :param num_bytes: Sum of combination of {2, 4, 8}, e.g. 2, 6, 16, 30, etc. 15 | :param format_char_sequence: List of {c, e, f, d, h, H, i, I, l, L, q, Q}. 16 | :param endian_character: Any of {@, =, <, >, !} 17 | :return: Tuple of read and unpacked values. 18 | """ 19 | data = fid.read(num_bytes) 20 | return struct.unpack(endian_character + format_char_sequence, data) 21 | 22 | 23 | 24 | 25 | def read_images_txt(path): 26 | with open(path, 'r') as f: 27 | img_arr = [] 28 | for x in f: 29 | if x[0] != '#': 30 | # IMAGE_ID QW QX QY QZ TX TY TZ CAMERA_ID NAME 31 | pars = x.split(' ') 32 | img_id = int(pars[0]) 33 | rot = Quaternion(float(pars[1]), float(pars[2]), float(pars[3]), float(pars[4])) 34 | t = np.asarray([float(pars[5]), float(pars[6]), float(pars[7])]) 35 | cam_id = int(pars[8]) 36 | name = pars[9] 37 | name = name.replace('\n','') 38 | # ignore features 39 | f.readline() 40 | img_arr.append(ColmapImage(img_id, rot, t, cam_id, name)) 41 | return img_arr 42 | 43 | # from https://github.com/colmap/colmap/blob/1a4d0bad2e90aa65ce997c9d1779518eaed998d5/scripts/python/read_write_model.py 44 | def read_images_bin(path_to_model_file: str): 45 | img_arr = [] 46 | with open(path_to_model_file, "rb") as fid: 47 | num_reg_images = read_next_bytes(fid, 8, "Q")[0] 48 | for _ in range(num_reg_images): 49 | binary_image_properties = read_next_bytes( 50 | fid, num_bytes=64, format_char_sequence="idddddddi") 51 | img_id = binary_image_properties[0] 52 | rot = np.array(binary_image_properties[1:5]) 53 | rot = Quaternion(rot[0], rot[1], rot[2], rot[3]) 54 | t = np.array(binary_image_properties[5:8]) 55 | cam_id = binary_image_properties[8] 56 | image_name = "" 57 | current_char = read_next_bytes(fid, 1, "c")[0] 58 | while current_char != b"\x00": # look for the ASCII 0 entry 59 | image_name += current_char.decode("utf-8") 60 | current_char = read_next_bytes(fid, 1, "c")[0] 61 | # ignore stuff wie do not need 62 | num_points2D = read_next_bytes(fid, num_bytes=8, 63 | format_char_sequence="Q")[0] 64 | read_next_bytes(fid, num_bytes=24 * num_points2D, 65 | format_char_sequence="ddq" * num_points2D) 66 | img_arr.append(ColmapImage(img_id, rot, t, cam_id, image_name)) 67 | return img_arr 68 | 69 | 70 | def read_images(path: str): 71 | return read_images_txt(path) if extension(path) == ".txt" else read_images_bin(path) 72 | 73 | 74 | def read_cameras_txt(path: str): 75 | with open(path, 'r') as f: 76 | cam_arr = [] 77 | for x in f: 78 | if x[0] != '#': 79 | pars = x.replace('\n','').split(' ') 80 | cam_arr.append(ColmapCamera(int(pars[0]), pars[1], (int(pars[2]), int(pars[3])), pars[4:])) 81 | 82 | return cam_arr 83 | 84 | 85 | # from https://github.com/colmap/colmap/blob/1a4d0bad2e90aa65ce997c9d1779518eaed998d5/scripts/python/read_write_model.py 86 | def read_cameras_bin(path_to_model_file): 87 | cam_arr = [] 88 | with open(path_to_model_file, "rb") as fid: 89 | num_cameras = read_next_bytes(fid, 8, "Q")[0] 90 | for _ in range(num_cameras): 91 | camera_properties = read_next_bytes( 92 | fid, num_bytes=24, format_char_sequence="iiQQ") 93 | camera_id = camera_properties[0] 94 | model_id = camera_properties[1] 95 | model_name = CAMERA_MODEL_IDS[camera_properties[1]].model_name 96 | width = camera_properties[2] 97 | height = camera_properties[3] 98 | num_params = CAMERA_MODEL_IDS[model_id].num_params 99 | params = read_next_bytes(fid, num_bytes=8 * num_params, 100 | format_char_sequence="d" * num_params) 101 | cam_arr.append(ColmapCamera(camera_id, model_name, (width, height), params)) 102 | return cam_arr 103 | 104 | 105 | def read_cameras(path: str): 106 | return read_cameras_txt(path) if extension(path) == ".txt" else read_cameras_bin(path) 107 | -------------------------------------------------------------------------------- /thirdPartyLegalNotices/matplotlib.txt: -------------------------------------------------------------------------------- 1 | 2 | Applicable to matplotlib 3 | 4 | License agreement for matplotlib versions 1.3.0 and later 5 | ========================================================= 6 | 7 | 1. This LICENSE AGREEMENT is between the Matplotlib Development Team 8 | ("MDT"), and the Individual or Organization ("Licensee") accessing and 9 | otherwise using matplotlib software in source or binary form and its 10 | associated documentation. 11 | 12 | 2. Subject to the terms and conditions of this License Agreement, MDT 13 | hereby grants Licensee a nonexclusive, royalty-free, world-wide license 14 | to reproduce, analyze, test, perform and/or display publicly, prepare 15 | derivative works, distribute, and otherwise use matplotlib 16 | alone or in any derivative version, provided, however, that MDT's 17 | License Agreement and MDT's notice of copyright, i.e., "Copyright (c) 18 | 2012- Matplotlib Development Team; All Rights Reserved" are retained in 19 | matplotlib alone or in any derivative version prepared by 20 | Licensee. 21 | 22 | 3. In the event Licensee prepares a derivative work that is based on or 23 | incorporates matplotlib or any part thereof, and wants to 24 | make the derivative work available to others as provided herein, then 25 | Licensee hereby agrees to include in any such work a brief summary of 26 | the changes made to matplotlib . 27 | 28 | 4. MDT is making matplotlib available to Licensee on an "AS 29 | IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 30 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND 31 | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 32 | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 33 | WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 34 | 35 | 5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 36 | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR 37 | LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING 38 | MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF 39 | THE POSSIBILITY THEREOF. 40 | 41 | 6. This License Agreement will automatically terminate upon a material 42 | breach of its terms and conditions. 43 | 44 | 7. Nothing in this License Agreement shall be deemed to create any 45 | relationship of agency, partnership, or joint venture between MDT and 46 | Licensee. This License Agreement does not grant permission to use MDT 47 | trademarks or trade name in a trademark sense to endorse or promote 48 | products or services of Licensee, or any third party. 49 | 50 | 8. By copying, installing or otherwise using matplotlib , 51 | Licensee agrees to be bound by the terms and conditions of this License 52 | Agreement. 53 | 54 | License agreement for matplotlib versions prior to 1.3.0 55 | ======================================================== 56 | 57 | 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the 58 | Individual or Organization ("Licensee") accessing and otherwise using 59 | matplotlib software in source or binary form and its associated 60 | documentation. 61 | 62 | 2. Subject to the terms and conditions of this License Agreement, JDH 63 | hereby grants Licensee a nonexclusive, royalty-free, world-wide license 64 | to reproduce, analyze, test, perform and/or display publicly, prepare 65 | derivative works, distribute, and otherwise use matplotlib 66 | alone or in any derivative version, provided, however, that JDH's 67 | License Agreement and JDH's notice of copyright, i.e., "Copyright (c) 68 | 2002-2011 John D. Hunter; All Rights Reserved" are retained in 69 | matplotlib alone or in any derivative version prepared by 70 | Licensee. 71 | 72 | 3. In the event Licensee prepares a derivative work that is based on or 73 | incorporates matplotlib or any part thereof, and wants to 74 | make the derivative work available to others as provided herein, then 75 | Licensee hereby agrees to include in any such work a brief summary of 76 | the changes made to matplotlib. 77 | 78 | 4. JDH is making matplotlib available to Licensee on an "AS 79 | IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 80 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND 81 | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 82 | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 83 | WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 84 | 85 | 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 86 | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR 87 | LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING 88 | MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF 89 | THE POSSIBILITY THEREOF. 90 | 91 | 6. This License Agreement will automatically terminate upon a material 92 | breach of its terms and conditions. 93 | 94 | 7. Nothing in this License Agreement shall be deemed to create any 95 | relationship of agency, partnership, or joint venture between JDH and 96 | Licensee. This License Agreement does not grant permission to use JDH 97 | trademarks or trade name in a trademark sense to endorse or promote 98 | products or services of Licensee, or any third party. 99 | 100 | 8. By copying, installing or otherwise using matplotlib, 101 | Licensee agrees to be bound by the terms and conditions of this License 102 | Agreement. -------------------------------------------------------------------------------- /docs/sphinx/formats/RealityCapture/RealityCapture.md: -------------------------------------------------------------------------------- 1 | # Reality Capture 2 | 3 | *Reality Capture* is a 3D reconstruction software developed by Epic 4 | Games, Inc. There are several possibilities to export cameras: XMP, csv 5 | and FBX. Camorph supports the XMP file format, as this is the 6 | only format supported for importing cameras. *Reality Capture* generates 7 | XMP files for every source image in the source image folder. When you 8 | create a new *Reality Capture* project, XMP files in the source image 9 | folder with the same name as the images are imported automatically 10 | 11 | ## Coordinate System 12 | 13 | *Reality Capture* supports alignment with all EPSG geodesic coordinate 14 | systems. In Cartesian world coordinates, it uses z up, 15 | x right, -y front. The default orientation for cameras 16 | is z front and -y up. Yaw, Pitch and Roll in *Reality 17 | Capture* correspond to a rotation around the $z$,$y$ and 18 | $x$-axis respectively. Usually, angles are measured in 19 | counterclockwise direction around an axis. But in *Reality Capture*, 20 | Yaw is measured clockwise. This means that yaw = 180 - yaw to correct 21 | for counterclockwise orientation.The resulting rotation matrix with $z$ = yaw = $\phi$, 22 | $y$ = pitch = $\theta$ and $x$ = roll = $\psi$ 23 | *Reality Capture* uses internally: 24 | 25 | $$ 26 | \begin{pmatrix} 27 | \cos{\psi}\cos{\phi} + \sin{\psi}\sin{\theta}\sin{\phi} & -\cos{\psi}\sin{\phi} + \cos{\phi}\sin{\psi}\sin{\theta} & -\cos{\theta}\sin{\psi} \\ 28 | -\cos{\theta}\sin{\phi} & -\cos{\theta}\cos{\phi} & -\sin{\theta} \\ 29 | \cos{\psi}\sin{\theta}\sin{\phi} - \cos{\phi}\sin{\psi} & \cos{\psi}\cos{\phi}\sin{\theta} + \sin{\psi}\sin{\phi} & -\cos{\psi}\cos{\theta} 30 | \end{pmatrix} 31 | $$ 32 | 33 | When comparing this matrix to “regular” rotation matrices, note that the only difference to the 34 | rotation matrix $R_{zxy}$ is that $\cos{\theta}$ is 35 | $-\cos{\theta}$. This occurs because 36 | $\theta = \pi - \theta_{RC}$, as mentioned above. This also 37 | means that the rotational order *Reality Capture* uses internally is 38 | $z-x-y$ , or $R_y \cdot R_x \cdot R_z$. This is only 39 | relevant when dealing with .csv files, as the exported angles are the 40 | angles displayed in *Reality Capture*. The XMP files already supply 41 | the “correct” rotational matrix. 42 | 43 | 44 | 45 | ![image](./RealityCapture_coordinatesystem_cam-1.png) 46 | 47 | ## XMP Files 48 | 49 | XMP is a file format based on the [Resource Description Framework](https://www.w3.org/RDF/) (rdf). 50 | 51 | The `rdf:Description` element has the following important 52 | attributes: 53 | 54 | 55 | * `DistortionModel` is the name of the distortion model type 56 | 57 | 58 | * `FocalLength35mm` is the focal length with respect the 135 film 59 | standard 60 | 61 | 62 | * `Skew` is the skew parameter in the intrinsic matrix 63 | 64 | 65 | * `AspectRatio` is the pixel aspect ratio, not the sensor aspect 66 | ratio, which is 1 most of the time 67 | 68 | 69 | * `PrincipalPointU` and `PrincipalPointV` are the principal point 70 | coordinates relative to the middle of the image plane and the 71 | resolution 72 | 73 | This element contains three additional elements: 74 | 75 | 76 | * `Rotation` which is the rotational matrix in row major order. 77 | It is the inverse of 78 | $R$, meaning the rotational matrix used in the projection 79 | equation. 80 | 81 | 82 | * `Position` is the center of the camera 83 | 84 | 85 | * `DistortionCoeficients`(sic) are six distortion parameters based 86 | on the `DistortionModel`. There are always six coefficients, with 87 | unused parameters being zero. 88 | 89 | ## Camera Models 90 | 91 | *Reality Capture* always stores six distortion coefficients. When the 92 | camera model does not support six parameters, the unused parameters are 93 | set to zero. The camera models supported by *Reality Capture* are: 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 106 | 107 | 112 | 113 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 132 | 133 | 138 | 139 | 144 | 145 | 146 | 147 | 148 | 149 | 154 | 155 | 160 | 161 | 166 | 167 | 168 | 169 | 170 | 171 | 176 | 177 | 182 | 183 | 188 | 189 | 190 | 191 | 192 | 193 | 198 | 199 | 204 | 205 | 210 | 211 | 212 | 213 | 214 | 215 | 220 | 221 | 226 | 227 | 232 | 233 | 234 | 235 | 236 | 237 |
102 | 103 | **Name** 104 | 105 | 108 | 109 | **Description** 110 | 111 | 114 | 115 | **Parameters** 116 | 117 |
128 | 129 | `brown3` 130 | 131 | 134 | 135 | Brown distortion model with three radial parameters 136 | 137 | 140 | 141 | k1, k2, k3, 0, 0, 0 142 | 143 |
150 | 151 | `brown4` 152 | 153 | 156 | 157 | Brown distortion model with four radial parameters 158 | 159 | 162 | 163 | k1, k2, k3, k4, 0, 0 164 | 165 |
172 | 173 | `brown3t2` 174 | 175 | 178 | 179 | Brown distortion model with three radial parameters and two tangential parameters 180 | 181 | 184 | 185 | k1, k2, k3, 0, t1, t2 186 | 187 |
194 | 195 | `brown4t2` 196 | 197 | 200 | 201 | Brown distortion model with four radial parameters and two tangential parameters 202 | 203 | 206 | 207 | k1, k2, k3, k4, t1, t2 208 | 209 |
216 | 217 | `division` 218 | 219 | 222 | 223 | Division distortion model 224 | 225 | 228 | 229 | k, 0, 0, 0, 0, 0 230 | 231 |
-------------------------------------------------------------------------------- /camorph/sphinx/formats/RealityCapture/RealityCapture.rst: -------------------------------------------------------------------------------- 1 | Reality Capture 2 | =============== 3 | 4 | *Reality Capture* is a 3D reconstruction software developed by Epic 5 | Games, Inc. There are several possibilities to export cameras: XMP, csv 6 | and FBX. Camorph supports the XMP file format, as this is the 7 | only format supported for importing cameras. *Reality Capture* generates 8 | XMP files for every source image in the source image folder. When you 9 | create a new *Reality Capture* project, XMP files in the source image 10 | folder with the same name as the images are imported automatically 11 | 12 | Coordinate System 13 | ----------------- 14 | 15 | | *Reality Capture* supports alignment with all EPSG geodesic coordinate 16 | systems. In Cartesian world coordinates, it uses z up, 17 | x right, -y front. The default orientation for cameras 18 | is z front and -y up. Yaw, Pitch and Roll in *Reality 19 | Capture* correspond to a rotation around the :math:`z`,\ :math:`y` and 20 | :math:`x`-axis respectively. Usually, angles are measured in 21 | counterclockwise direction around an axis. But in *Reality Capture*, 22 | Yaw is measured clockwise. This means that yaw = 180 - yaw to correct 23 | for counterclockwise orientation. 24 | | The resulting rotation matrix with :math:`z` = yaw = :math:`\phi`, 25 | :math:`y` = pitch = :math:`\theta` and :math:`x` = roll = :math:`\psi` 26 | *Reality Capture* uses internally: 27 | 28 | .. math:: 29 | 30 | \begin{pmatrix} 31 | \cos{\psi}\cos{\phi} + \sin{\psi}\sin{\theta}\sin{\phi} & -\cos{\psi}\sin{\phi} + \cos{\phi}\sin{\psi}\sin{\theta} & -\cos{\theta}\sin{\psi} \\ 32 | -\cos{\theta}\sin{\phi} & -\cos{\theta}\cos{\phi} & -\sin{\theta} \\ 33 | \cos{\psi}\sin{\theta}\sin{\phi} - \cos{\phi}\sin{\psi} & \cos{\psi}\cos{\phi}\sin{\theta} + \sin{\psi}\sin{\phi} & -\cos{\psi}\cos{\theta} 34 | \end{pmatrix} 35 | 36 | When comparing this matrix to "regular" rotation matrices, note that the only difference to the 37 | rotation matrix :math:`R_{zxy}` is that :math:`\cos{\theta}` is 38 | :math:`-\cos{\theta}`. This occurs because 39 | :math:`\theta = \pi - \theta_{RC}`, as mentioned above. This also 40 | means that the rotational order *Reality Capture* uses internally is 41 | :math:`z-x-y` , or :math:`R_y \cdot R_x \cdot R_z`. This is only 42 | relevant when dealing with .csv files, as the exported angles are the 43 | angles displayed in *Reality Capture*. The XMP files already supply 44 | the "correct" rotational matrix. 45 | 46 | .. image:: RealityCapture_coordinatesystem_cam-1.png 47 | 48 | XMP Files 49 | --------- 50 | XMP is a file format based on the `Resource Description Framework `_ (rdf). 51 | 52 | The ``rdf:Description`` element has the following important 53 | attributes: 54 | 55 | - ``DistortionModel`` is the name of the distortion model type 56 | 57 | - ``FocalLength35mm`` is the focal length with respect the 135 film 58 | standard 59 | 60 | - ``Skew`` is the skew parameter in the intrinsic matrix 61 | 62 | - ``AspectRatio`` is the pixel aspect ratio, not the sensor aspect 63 | ratio, which is 1 most of the time 64 | 65 | - ``PrincipalPointU`` and ``PrincipalPointV`` are the principal point 66 | coordinates relative to the middle of the image plane and the 67 | resolution 68 | 69 | This element contains three additional elements: 70 | 71 | - ``Rotation`` which is the rotational matrix in row major order. 72 | It is the inverse of 73 | :math:`R`, meaning the rotational matrix used in the projection 74 | equation. 75 | 76 | - ``Position`` is the center of the camera 77 | 78 | - ``DistortionCoeficients``\ (sic) are six distortion parameters based 79 | on the ``DistortionModel``. There are always six coefficients, with 80 | unused parameters being zero. 81 | 82 | Camera Models 83 | ------------- 84 | *Reality Capture* always stores six distortion coefficients. When the 85 | camera model does not support six parameters, the unused parameters are 86 | set to zero. The camera models supported by *Reality Capture* are: 87 | 88 | +--------------+--------------------------+------------------------+ 89 | | **Name** | **Description** | **Parameters** | 90 | +==============+==========================+========================+ 91 | | ``brown3`` | Brown distortion model | k1, k2, k3, 0, 0, 0 | 92 | | | with three radial | | 93 | | | parameters | | 94 | +--------------+--------------------------+------------------------+ 95 | | ``brown4`` | Brown distortion model | k1, k2, k3, k4, 0, 0 | 96 | | | with four radial | | 97 | | | parameters | | 98 | +--------------+--------------------------+------------------------+ 99 | | ``brown3t2`` | Brown distortion model | k1, k2, k3, 0, t1, t2 | 100 | | | with three radial | | 101 | | | parameters and two | | 102 | | | tangential parameters | | 103 | +--------------+--------------------------+------------------------+ 104 | | ``brown4t2`` | Brown distortion model | k1, k2, k3, k4, t1, t2 | 105 | | | with four radial | | 106 | | | parameters and two | | 107 | | | tangential parameters | | 108 | +--------------+--------------------------+------------------------+ 109 | | ``division`` | Division distortion | k, 0, 0, 0, 0, 0 | 110 | | | model | | 111 | +--------------+--------------------------+------------------------+ -------------------------------------------------------------------------------- /docs/lib/utils.md: -------------------------------------------------------------------------------- 1 | # `math_utils` module 2 | 3 | 4 | ### utils.math_utils.convert_coordinate_systems(crd, t, r, cdir=array([0, 0, - 1]), cup=array([0, 1, 0]), tdir=array([0, 0, - 1]), tup=array([0, 1, 0]), transpose=False) 5 | This function converts translation and rotation with up and front vectors between different coordinate systems 6 | crd is relative to the camorph coordinate system [‘x’, ‘y’, ‘z’] with z up, y right, x front 7 | for example, if the new coordinate system is y up, z front, x left (y up right handed): 8 | [‘x’, ‘z’, ‘y’] 9 | 10 | 11 | * **Parameters** 12 | 13 | 14 | * **crd** (*list**[**str**]*) – Relative coordinate axis to camorph coordinate system 15 | 16 | 17 | * **t** (*ndarray*) – Translation 18 | 19 | 20 | * **r** (*ndarray** or **Quaternion*) – Rotation 21 | 22 | 23 | * **cdir** (*ndarray*) – Front vector in the source coordinate system 24 | 25 | 26 | * **cup** (*ndarray*) – Up vector in the source coordinate system 27 | 28 | 29 | * **tdir** (*ndarray*) – Front vector in the target coordinate system 30 | 31 | 32 | * **tup** (*ndarray*) – Up vector in the target coordinate system 33 | 34 | 35 | * **transpose** (*bool*) – If the linear translation should be inverted. 36 | 37 | 38 | 39 | * **Returns** 40 | 41 | (ndarray, quaternion) 42 | 43 | 44 | 45 | ### utils.math_utils.euler_to_quaternion(vec, rot_order='xyz') 46 | This function converts a set of euler angles to a quaternion. 47 | 48 | 49 | * **Parameters** 50 | 51 | 52 | * **vec** (*ndarray*) – Vector as xyz 53 | 54 | 55 | * **rot_order** (*str*) – rotational order, one of ‘xyz’, ‘xzy’, ‘yxz’, ‘yzx’, ‘zyx’, ‘zxy’ 56 | 57 | 58 | 59 | * **Returns** 60 | 61 | PyQuaternion 62 | 63 | 64 | 65 | ### utils.math_utils.euler_to_rotation_matrix(vec, rotation_order='xyz') 66 | This function converts a set of euler angles to a rotation matrix with the given order 67 | 68 | 69 | * **Parameters** 70 | 71 | 72 | * **vec** (*ndarray*) – Vector as xyz 73 | 74 | 75 | * **rot_order** (*str*) – rotational order, one of ‘xyz’, ‘xzy’, ‘yxz’, ‘yzx’, ‘zyx’, ‘zxy’ 76 | 77 | 78 | 79 | * **Returns** 80 | 81 | ndarray 82 | 83 | 84 | 85 | ### utils.math_utils.inch_to_mm(inch) 86 | This function converts inch to mm. 87 | 88 | 89 | * **Parameters** 90 | 91 | **inch** (*float*) – Inch 92 | 93 | 94 | 95 | * **Returns** 96 | 97 | float 98 | 99 | 100 | 101 | ### utils.math_utils.mm_to_inch(mm) 102 | This function converts mm to inch. 103 | 104 | 105 | * **Parameters** 106 | 107 | **mm** (*float*) – Millimeter 108 | 109 | 110 | 111 | * **Returns** 112 | 113 | float 114 | 115 | 116 | 117 | ### utils.math_utils.quaternion_difference(q1, q2) 118 | This function returns the rotational difference between two quaternions 119 | 120 | 121 | * **Parameters** 122 | 123 | 124 | * **q1** (*PyQuaternion*) – First Quaternion 125 | 126 | 127 | * **q2** (*PyQuaternion*) – Second Quaternion 128 | 129 | 130 | 131 | * **Returns** 132 | 133 | PyQuaternion 134 | 135 | 136 | 137 | ### utils.math_utils.quaternion_to_euler(q, rot_order='xyz') 138 | This function converts a quaternion of euler angles to a quaternion. 139 | 140 | 141 | * **Parameters** 142 | 143 | 144 | * **q** (*PyQuaternion*) – Quaternion 145 | 146 | 147 | * **rot_order** (*str*) – rotational order, one of ‘xyz’, ‘yxz’ 148 | 149 | 150 | 151 | * **Returns** 152 | 153 | list[float] 154 | 155 | 156 | 157 | ### utils.math_utils.quaternion_to_rotation_matrix(q) 158 | This function converts a quaternion to the corresponding rotation matrix. 159 | 160 | 161 | * **Parameters** 162 | 163 | **q** (*PyQuaternion*) – Quaterion 164 | 165 | 166 | 167 | * **Returns** 168 | 169 | ndarray 170 | 171 | 172 | 173 | ### utils.math_utils.rotation_matrix_to_euler_angles(R, rot_order='xyz') 174 | This function converts a rotation matrix to a set of euler angles 175 | From Gregory G. Slabaugh “Computing Euler angles from a rotation matrix” 176 | Other orders are calculated in a similiar fashion. 177 | 178 | 179 | * **Parameters** 180 | 181 | 182 | * **R** (*ndarray*) – Rotation matrix 183 | 184 | 185 | * **rot_order** (*str*) – rotational order, one of ‘xyz’, ‘yxz’ 186 | 187 | 188 | 189 | * **Returns** 190 | 191 | ndarray 192 | 193 | 194 | # `file_utils` module 195 | 196 | 197 | ### utils.file_utils.extension(path) 198 | This function returns the extension of a filename. 199 | :param path: Filename 200 | :type path: str 201 | :return: str 202 | 203 | 204 | ### utils.file_utils.fixed_list(ilist, length, optype=None) 205 | This function returns a list of a fixed length, regardless of number of elements in the input list. 206 | If the list is shorter than the length parameter, it will be padded with the default value for this type. 207 | When the list is empty, an optype has to be supplied. 208 | For example: 209 | >>> ilist = list[1.0] 210 | >>> fixed_list(ilist, 3) 211 | returns [1.0,0.0,0.0] 212 | 213 | 214 | * **Parameters** 215 | 216 | 217 | * **ilist** – List to extend or truncate 218 | 219 | 220 | * **length** – Desired output length 221 | 222 | 223 | * **optype** – When ilist is empty, this is the type with which the list is filled. 224 | 225 | 226 | 227 | * **Returns** 228 | 229 | list[any] 230 | 231 | 232 | 233 | ### utils.file_utils.get_files_in_dir(path, extension=None) 234 | This function returns all files in a given directory with an optional extension match. 235 | :param path: Path to directory 236 | :type path: str 237 | :param extension: Optional extension 238 | :type extension: str 239 | :return: list[str] 240 | 241 | 242 | ### utils.file_utils.peek(f, length) 243 | This function peeks if the file can be read for a given length. 244 | 245 | 246 | * **Parameters** 247 | 248 | 249 | * **f** – File handle 250 | 251 | 252 | * **length** – Length 253 | 254 | 255 | 256 | * **Returns** 257 | 258 | bytes or str 259 | -------------------------------------------------------------------------------- /camorph/ext/MPEG_OMAF/MPEG_OMAF.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import json 3 | import csv 4 | import copy 5 | import numpy as np 6 | 7 | from camorph.lib.model.FileHandler import FileHandler 8 | from camorph.lib.model.Camera import Camera 9 | from camorph.lib.utils import math_utils 10 | 11 | 12 | class MPEG_OMAF(FileHandler): 13 | def __init__(self): 14 | pass 15 | 16 | def crucial_properties(self) -> list[(str, type)]: 17 | return ['resolution','focal_length_px','principal_point'] 18 | 19 | def name(self): 20 | return "mpeg_omaf" 21 | 22 | def file_number(self): 23 | return -1 24 | 25 | def read_file(self, input_path: str, *args, **kwargs): 26 | with open(input_path, 'r') as f: 27 | mpeg_json = json.loads(f.read()) 28 | cam_arr = [] 29 | # Z Up X Front 30 | for mcam in mpeg_json['cameras']: 31 | cam = Camera() 32 | r = mcam['Rotation'][::-1] 33 | cam.t = mcam['Position'] 34 | cam.r = math_utils.euler_to_quaternion(r) 35 | cam.projection_type = mcam['Projection'].lower() 36 | cam.resolution = mcam['Resolution'] 37 | cam.resolution = mcam['Resolution'] 38 | cam.name = mcam['Name'] 39 | if cam.projection_type == 'perspective': 40 | cam.model = 'pinhole' 41 | cam.focal_length_px = [mcam['Focal'][0], mcam['Focal'][1]] 42 | cam.principal_point = mcam['Principle_point'] 43 | else: 44 | cam.focal_length_px = [cam.resolution[0] / 2,cam.resolution[0] / 2] 45 | cam.principal_point = [x / 2 for x in cam.resolution] 46 | 47 | if cam.resolution is not None: 48 | if cam.resolution[0] > cam.resolution[1]: 49 | cam.sensor_size = (36, (36 / cam.resolution[0]) * cam.resolution[1]) 50 | else: 51 | cam.sensor_size = ((36 / cam.resolution[1]) * cam.resolution[0], 36) 52 | 53 | cam_arr.append(cam) 54 | if len(args) == 1 and type(args[0]) == str and args[0].split('.')[-1] == 'csv': 55 | basecam = next((c for c in cam_arr if c.name == 'viewport'), None) 56 | if basecam is None: 57 | raise ValueError("Camera array needs to contain a camera named viewport as base for the posetrace") 58 | posetrace_array = [] 59 | with open(args[0],'r') as f: 60 | posetrace_data = csv.reader(f) 61 | posetrace_data.__next__() # skip heading 62 | for idx, row in enumerate(posetrace_data): 63 | tr = np.resize(np.asarray(row, dtype='float32'),[2,3]) 64 | t = tr[0] 65 | r = tr[1][::-1] 66 | pc = copy.copy(basecam) 67 | pc.t += t 68 | pc.r = math_utils.euler_to_quaternion(r) 69 | pc.name = f'posestrace_{str(idx).zfill(3)}' 70 | 71 | if pc.source_image is None and 'posetrace' in kwargs and kwargs['posetrace']: 72 | pc.source_image = pc.name 73 | posetrace_array.append(pc) 74 | return self.coordinate_from(posetrace_array) 75 | 76 | else: 77 | cam_arr = list(filter(lambda x: x.name !='viewport', cam_arr)) 78 | return self.coordinate_from(cam_arr) 79 | 80 | def write_file(self, camera_array: list[Camera], output_path: str, file_type=None): 81 | # from colmap_to_json.py 82 | json_file = { 83 | 'Version': '2.0', 84 | 'Content_name': 'Colmap dataset', 85 | 'Fps': 1, 86 | 'Frames_number': 1, 87 | 'Informative': { 88 | 'Converted_by': 'camorph', 89 | 'Original_units': 'm', 90 | 'New_units': 'm'}, 91 | 'cameras': [] 92 | } 93 | camera_array = self.coordinate_into(camera_array) 94 | for cam in camera_array: 95 | if cam.projection_type not in ['perspective', 'equirectangular', None]: 96 | warnings.warn(f"Unsupported projection type {cam.projection_type}") 97 | continue 98 | json_cam = { 99 | 'Name': cam.name, 100 | 'Position': [float(x) for x in cam.t], 101 | 'Rotation': [float(x) for x in math_utils.quaternion_to_euler(cam.r)[::-1]], 102 | 'Depthmap': 1, 103 | 'Background': 0, 104 | 'Depth_range': [40.0,70.0], 105 | 'Resolution': cam.resolution, 106 | 'Projection': cam.projection_type.capitalize(), 107 | 'Focal': [cam.focal_length_px[0], cam.focal_length_px[1]], 108 | 'Principle_point': cam.principal_point, 109 | 'BitDepthColor': 8, 110 | 'BitDepthDepth': 16, 111 | 'ColorSpace': 'YUV420', 112 | 'DepthColorSpace': 'YUV420' 113 | } 114 | json_file['cameras'].append(json_cam) 115 | with open(output_path,'w') as f: 116 | json.dump(json_file, f, indent=4) 117 | 118 | def coordinate_into(self, camera_array: list[Camera]): 119 | cam_arr = copy.deepcopy(camera_array) 120 | for cam in cam_arr: 121 | cam.t, cam.r = math_utils.convert_coordinate_systems(['x', 'y', 'z'], cam.t, cam.r, tdir=[1, 0, 0], 122 | tup=[0, 0, 1]) 123 | return cam_arr 124 | 125 | def coordinate_from(self, camera_array: list[Camera]): 126 | cam_arr = copy.deepcopy(camera_array) 127 | for cam in cam_arr: 128 | cam.t, cam.r = math_utils.convert_coordinate_systems(['x', 'y', 'z'], cam.t, cam.r, cdir=[1, 0, 0], 129 | cup=[0, 0, 1]) 130 | return cam_arr 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Camorph 2 | 3 | 4 | [Paper](https://www.int-arch-photogramm-remote-sens-spatial-inf-sci.net/XLVIII-2-W1-2022/29/2022/isprs-archives-XLVIII-2-W1-2022-29-2022.pdf) 5 | 6 |
7 | 8 |
9 | 10 | This is the gitlab project for camorph, a camera parameter converting tool written in python 3.9. 11 | 12 | ## Release Notes 13 | ### v1.0.0 (July 10, 2025) 14 | * Clarified the meaning of the "MPEG-OMAF" format in the README 15 | 16 | ### v0.3.6 (April 29, 2025) 17 | * Merged PR regarding COLMAP image path fix 18 | * Merged PR regarding NeRF principal point fix 19 | ### v0.3.5 (September 2, 2024) 20 | * Fixed an issue with FULL_OPENCV models when writing binary COLMAP files 21 | ### v0.3.4 (June 20, 2024) 22 | * Fixed an issue with shallow copying in the read_file() for NeRF when using individual image resolutions 23 | 24 | ## Documentation 25 | 26 | Documentation can be found [here](docs/index.md). 27 | 28 | ## Quickstart 29 | 30 | Some quick tips on how to use camorph. 31 | 32 | ### Installation 33 | 34 | If you want to work on camorph, clone the repo and install the conda environment by running 35 | 36 | ``` 37 | conda env create -f camorph_win.yml 38 | ``` 39 | 40 | Otherwise, you can also install camorph with pip 41 | 42 | ``` 43 | pip install git+https://github.com/Fraunhofer-IIS/camorph 44 | ``` 45 | 46 | ### Usage as Python Library 47 | 48 | Use the library to convert camera parameter representations: 49 | 50 | ``` 51 | import camorph.camorph as camorph 52 | 53 | 54 | cams = camorph.read_cameras('COLMAP',r'\\path\to\colmap') 55 | 56 | camorph.visualize(cams) 57 | 58 | camorph.write_cameras('fbx', r'\\path\to\file.fbx', cams) 59 | ``` 60 | 61 | ``camorph.read_cameras()`` takes a format name and path as a string and returns a list of Cameras. 62 | 63 | ``camorph.visualize()`` creates a visualization of Cameras with Matplotlib. 64 | 65 | ``camorph.write_cameras()`` takes a format name, a path as a string and a list of cameras and writes the output file(s) to the specified path. 66 | 67 | ### Usage as CLI 68 | 69 | You can use the command line interface by calling 70 | ``` 71 | python -m camorph -h 72 | ``` 73 | or when installed with pip 74 | ``` 75 | camorph -h 76 | ``` 77 | This will output the help for the command line interface 78 | 79 | ``` 80 | usage: camorph [-h] -i input_path [input_path ...] -if input_format [-o output_path] [-of output_format] [-v] 81 | [-c config] [-ft file_type] [-pt] [-cr crop] [-s scale] [-id image_dir] [-ci] 82 | 83 | Convert Cameras from different formats to each other 84 | 85 | optional arguments: 86 | -h, --help show this help message and exit 87 | -i input_path [input_path ...], --input input_path [input_path ...] 88 | the input path of the camera file(s) to read 89 | -if input_format, --input_format input_format 90 | the format of the input camera file(s) 91 | -o output_path, --output output_path 92 | the output path where the camera file(s) should be saved 93 | -of output_format, --output_format output_format 94 | the format of the output camera file(s) 95 | -v, --visualize when this parameter is present, the cameras will be visualized. 96 | -c config, --config config 97 | the path to a config.json file for missing crucial properties 98 | -ft file_type, --file_type file_type 99 | some formats support different types of output files, for example bin for binary and txt for 100 | ascii files 101 | -pt, --posetrace treat the input as a posetrace and ignore any source images. 102 | -cr crop, --crop crop 103 | crop source image attributes by the specified top left and bottom right corner. Format: 104 | "leftcorner_x,leftcorner_y,rightcorner_x,rightcorner_y". ATTENTION: THIS DOES NOT MODIFY THE IMAGES, ONLY THE PROPERTIES IN THE FILE! 105 | -s scale, --scale scale 106 | scale source image attributes by the specified factor. ATTENTION: THIS DOES NOT MODIFY THE IMAGES, ONLY THE PROPERTIES IN THE FILE! 107 | -id image_dir, --image-dir image_dir 108 | replace the directory for the source images with this. 109 | -ci, --check-images check if images exist and are of the right resolution. 110 | ``` 111 | 112 | For example 113 | 114 | ``` 115 | camorph -i \path\to\json -if nerf -o \path\to\output -of fbx 116 | ``` 117 | 118 | ### Crucial Properties 119 | If there are missing crucial properties, camorph will automatically create a `config.json` in the target folder. 120 | To edit this file, please refer to the ["Crucial Properties" section of the documentation](docs/sphinx/crucial_properties.md) 121 | 122 | ### Pose Trace 123 | If you want to convert a camera animation, which does not have source images (sometimes also referred to as *pose trace*), you can add the `-pt` argument to ignore any source image requirements. 124 | 125 | ### Cropping and Scaling 126 | This option crops and scales the **attributes of images in the file**. For example, colmap stores the resolution of the images, which can be modified by this parameter. 127 | > ❗❗❗ **THIS DOES NOT MODIFY THE IMAGES THEMSELVES!** ❗❗❗ 128 | 129 | ## Currently Supported Formats 130 | 131 | - Computer Graphics 132 | - **FBX** (Key: "fbx") 133 | 134 | - Photogrammetry 135 | - **COLMAP** (Key: "colmap") 136 | - **Meshroom** (Key: "meshroom") 137 | - **Reality Capture** (Key: "reality_capture") 138 | - **Local Light Field Fusion** (Key: "llff") 139 | 140 | - Game Engines 141 | - **Unity** (Key: "unity") 142 | 143 | - Virtual Reality / Immersive Video 144 | - **MPEG-OMAF** (Key: "mpeg_omaf") -> This format is the JSON format used in the software packages RVS (https://gitlab.com/mpeg-i-visual/rvs) and TMIV (https://gitlab.com/mpeg-i-visual/tmiv) which rely on the MPEG-OMAF coordinate system; for short, we called it MPEG-OMAF. 145 | 146 | - Machine Learning 147 | - **NeRF** (Key: "nerf") 148 | 149 | ## Citation 150 | 151 | ``` 152 | @article{Brand2022CAMORPHAT, 153 | title={CAMORPH: A TOOLBOX FOR CONVERSION BETWEEN CAMERA PARAMETER 154 | CONVENTIONS}, 155 | author={B. Brand and Michel B{\"a}tz and Joachim Keinert}, 156 | journal={The International Archives of the Photogrammetry, Remote Sensing and Spatial Information Sciences}, 157 | year={2022} 158 | } 159 | ``` 160 | -------------------------------------------------------------------------------- /camorph/camorph.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides the main conversion methods. 3 | """ 4 | 5 | import os 6 | import sys 7 | import PIL.Image 8 | import copy 9 | from pathlib import Path 10 | 11 | import camorph 12 | import camorph.lib.crucial_property as cp 13 | from camorph.lib.model import Camera 14 | from camorph.lib import visualizer as vis 15 | 16 | 17 | 18 | 19 | def read_cameras(format: str, *args, **kwargs) -> list[Camera]: 20 | """ 21 | Read the cameras of format `format` 22 | 23 | :param src: The source camera format. Currently possible: COLMAP, fbx, meshroom, unity, mpeg_omaf 24 | :type src: str 25 | :param args: The source path. The number of source path arguments is defined by :meth:`model.FileHandler.FileHandler.file_number` 26 | :return: A list of cameras in the camorph coordinate system convention defined in :class:`model.Camera.Camera` 27 | :rtype: list[:class:`model.Camera.Camera`] 28 | """ 29 | src_inst = camorph.imported_instances[format.lower()] 30 | cams = src_inst.read_file(*args, **kwargs) 31 | return cams 32 | 33 | def convert(format, cams): 34 | dest_inst = camorph.imported_instances[format.lower()] 35 | return dest_inst.coordinate_into(cams) 36 | 37 | def write_cameras(format, path, cams, crop=None, scale=None, imdir=None, check_images=False, file_type=None) -> None: 38 | """ 39 | Write cameras of format `dest` to dest_path 40 | 41 | :param format: The dest camera format. Currently possible: COLMAP, fbx, meshroom, unity, mpeg_omaf 42 | :param path: The path where to write the cameras 43 | :param cams: The list of :class:`model.Camera.Camera` to write 44 | :param crop: A list of type [[x1,y1],[x2,y2]] which specifies the pixel location of the cropping to be applied 45 | :param scale: A float which represents the relative scale applied in range 46 | :param imdir: A string to replace the basepath of the current images 47 | :param file_type: An optional parameter if there are multiple file types (for example, COLMAP has bin and txt) 48 | :return: None 49 | """ 50 | 51 | splitpath = path.split(os.path.sep) 52 | 53 | # please dont try to supply folders with a dot in the name 54 | isfile = '.' in splitpath[-1] 55 | if isfile: 56 | folder = (os.path.sep).join(splitpath[:-1]) 57 | else: 58 | folder = path 59 | if not os.path.isdir(folder) and folder != '': 60 | os.mkdir(folder) 61 | 62 | dest_inst = camorph.imported_instances[format.lower()] 63 | 64 | # check for crucial properties of output format 65 | cp.pip_check_properties(dest_inst, cams, camorph.crucial_property_config, path) 66 | 67 | if imdir is not None: 68 | for cam in cams: 69 | basename = os.path.basename(cam.source_image) 70 | cam.source_image = os.path.join(imdir, basename) 71 | 72 | def is_none_or_empty(x): 73 | if x is None: 74 | return True 75 | elif isinstance(x,list): 76 | return all([i == 0 for i in x]) 77 | else: 78 | return x == 0 79 | 80 | # recalculate camera parameters for cropped images 81 | if crop is not None: 82 | print('cropping intrinsics...') 83 | # raise exception when distortion is not None or 0 84 | if type(crop) is not list or type(crop[0]) is not list or type(crop[1]) is not list: 85 | raise TypeError("Please supply a crop in the format [[x1,y1],[x2,y2]]") 86 | if crop[0][0] > crop[1][0] or crop[0][1] > crop[1][1]: 87 | raise ValueError("First crop point must be the upper right, the second must be lower left") 88 | n_imsize = [crop[1][0] - crop[0][0],crop[1][1]-crop[0][1]] 89 | for cam in cams: 90 | if not is_none_or_empty(cam.radial_distortion) or not is_none_or_empty(cam.tangential_distortion): 91 | raise ValueError("Images cannot be resized if the camera has radial or tangential distortion") 92 | cam.principal_point = [cam.principal_point[0] - crop[0][0], cam.principal_point[1] - crop[0][1]] 93 | cam.resolution = n_imsize 94 | cam.focal_length_mm = cam._focal_length_mm_1() 95 | cam.fov = cam._fov() 96 | cam.lens_shift = cam._lens_shift() 97 | 98 | # recalculate camera parameters for scaled images 99 | if scale is not None: 100 | print('scaling intrinsics...') 101 | if type(scale) is not float: 102 | raise TypeError("Please provide scale as a float") 103 | for cam in cams: 104 | cam.resolution = [int(x * scale) for x in cam.resolution] 105 | cam.principal_point = [x * scale for x in cam.principal_point] 106 | cam.focal_length_px = cam._focal_length_px() 107 | check_images = True 108 | 109 | # sanity check for images 110 | if check_images: 111 | print('checking images...') 112 | for cam in cams: 113 | if os.path.isabs(cam.source_image): 114 | im = PIL.Image.open(cam.source_image) 115 | else: 116 | im = PIL.Image.open(os.path.join(Path(path).parent,cam.source_image)) 117 | if not (im.height == cam.resolution[0] and im.width == cam.resolution[1]) and not (im.height == cam.resolution[1] and im.width == cam.resolution[0]): 118 | raise ValueError(f"Resolution of image and intrinsics does not match: image {im.height}x{im.width}, intrinsic {cam.resolution[0]}x{cam.resolution[1]}") 119 | 120 | # do not modify the source array 121 | cam_copy = copy.deepcopy(cams) 122 | 123 | dest_inst.write_file(cam_copy, path, file_type) 124 | 125 | def write_crucial_config_template(cams, path): 126 | """ 127 | This function writes a template config.json to the desired path 128 | 129 | :param cams: The list of :class:`model.Camera.Camera` 130 | :param path: The path where to write the config 131 | :return: None 132 | """ 133 | cp.write_crucial_config_template(cams, path) 134 | 135 | def visualize(cams, show=True): 136 | """ 137 | Visualizes the list of :class:`model.Camera.Camera` with matplotlib 138 | See :mod:`vis.Visualizer` 139 | 140 | :param cams: The list of :class:`model.Camera.Camera` to visualize 141 | :return: None 142 | """ 143 | return vis.visualize(cams, show) 144 | 145 | def set_crucial_property_config(crucial_property_config: str): 146 | """ 147 | Manually set a crucial property config.json\ 148 | See 149 | 150 | :param crucial_property_config: The path to the config.json file 151 | :return: None 152 | """ 153 | camorph.crucial_property_config = crucial_property_config 154 | 155 | def print_keys(): 156 | """ 157 | Print all available keys for supported formats 158 | 159 | :return: None 160 | """ 161 | for key in camorph.imported_instances: 162 | print(key) 163 | -------------------------------------------------------------------------------- /docs/sphinx/formats/Meshroom/Meshroom.md: -------------------------------------------------------------------------------- 1 | # Meshroom 2 | 3 | *Meshroom* is a 3D reconstruction software developed by Griwodz et. al. 4 | The software has a nodal architecture and stores intermediate results in 5 | a specific subfolder structure. Each nodes data is stored in a folder 6 | with its unique identifier as a name in a subfolder of the node type in 7 | the folder `MeshroomCache`. The reconstructed camera poses are stored 8 | in `cameras.sfm` in the `StructureFromMotion` folder. Known poses 9 | can be imported into Mehsroom by setting the input of a 10 | `FeatureExtraction` node to the custom `cameras.sfm` file. 11 | 12 | ## Coordinate System 13 | 14 | The standard *Meshroom* coordinate system is y up, x right, z front. The camera orientation is -z front and y up. 15 | 16 | 17 | 18 | ![image](./Meshrrom_coordinatesystem_cam-1.png) 19 | 20 | ## SfM File 21 | 22 | The SfM file uses the widespread JSON format. 23 | 24 | 25 | * `featuresFolders` and `matchesFolders` point to specific folders 26 | in the *Meshroom* folder structure, and should be left empty when 27 | writing a custom file. 28 | 29 | 30 | * `views` holds view objects which hold data of a source image used 31 | for reconstruction in *Meshroom*. 32 | 33 | 34 | * `viewId` is a unique identifier 35 | 36 | 37 | * `poseId` refers to a pose object in `poses` where extrinsic 38 | parameters are stored 39 | 40 | 41 | * `intrinsicId` refers to an intrinsic object in `intrinsics` 42 | where intrinsic parameters are stored 43 | 44 | 45 | * `path` refers to the file path of the source image 46 | 47 | 48 | * `width` and `height` are the width and height of the image 49 | 50 | 51 | * `metadata` holds metadata about the image which is not relevant 52 | in this thesis. 53 | 54 | 55 | * `intrinsics` holds intrinsic objects which store intrinsic 56 | parameters of a specific camera model 57 | 58 | 59 | * `intrinsicId` is a unique identifier 60 | 61 | 62 | * `width` and `height` the resolution of the camera 63 | 64 | 65 | * `sensorWidth` and `sensorHeight` the size of the sensor in mm 66 | 67 | 68 | * `serialNumber` the serial number of the camera the source image 69 | was taken with 70 | 71 | 72 | * `type` the name of the distortion model type 73 | 74 | 75 | * `initializationMode` one of `calibrated` for externally 76 | calibrated cameras, `estimated` for estimated cameras, 77 | `unknown` for unkown cameras with guess values or `none` for 78 | none 79 | 80 | 81 | * `pxInitialFocalLength` is the initial guess of the focal length 82 | in px 83 | 84 | 85 | * `pxFocalLength` is the focal length in px 86 | 87 | 88 | * `principalPoint` is the principal point in px 89 | 90 | 91 | * `distortionParams` are the distortion params of the camera model 92 | 93 | 94 | * `locked` is a bool if the intrinsic objects parameters are 95 | locked or can be modified 96 | 97 | 98 | * `poses` holds pose objects which store extrinsic parameters of a 99 | specific pose 100 | 101 | 102 | * `poseId` is a unique identifier 103 | 104 | 105 | * `pose` is a nested pose object 106 | 107 | 108 | * `transform` stores the rotation as `rotation` as a matrix 109 | in row major order and the translation as `center` as a 110 | three-dimensional vector 111 | 112 | 113 | * `locked` is a bool if the pose objects parameters are locked or 114 | can be modified 115 | 116 | ## Camera Models 117 | 118 | Meshroom supports the following camera models: 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 131 | 132 | 137 | 138 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 157 | 158 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 175 | 176 | 181 | 182 | 187 | 188 | 189 | 190 | 191 | 192 | 197 | 198 | 203 | 204 | 209 | 210 | 211 | 212 | 213 | 214 | 219 | 220 | 225 | 226 | 231 | 232 | 233 | 234 | 235 | 236 | 241 | 242 | 247 | 248 | 253 | 254 | 255 | 256 | 257 | 258 | 263 | 264 | 269 | 270 | 275 | 276 | 277 | 278 | 279 | 280 | 285 | 286 | 291 | 292 | 297 | 298 | 299 | 300 | 301 | 302 | 307 | 308 | 313 | 314 | 319 | 320 | 321 | 322 | 323 | 324 | 329 | 330 | 335 | 336 | 341 | 342 | 343 | 344 | 345 | 346 |
127 | 128 | **Name** 129 | 130 | 133 | 134 | **Description** 135 | 136 | 139 | 140 | **Parameters** 141 | 142 |
153 | 154 | `pinhole` 155 | 156 | 159 | 160 | Pinhole model without distortion coefficients 161 | 162 |
171 | 172 | `radial1` 173 | 174 | 177 | 178 | Brown model with one radial coefficient 179 | 180 | 183 | 184 | k1 185 | 186 |
193 | 194 | `radial3` 195 | 196 | 199 | 200 | Brown model with three radial coefficients 201 | 202 | 205 | 206 | k1, k2, k3 207 | 208 |
215 | 216 | `brown` 217 | 218 | 221 | 222 | Brown model with three radial and two tangential coefficients 223 | 224 | 227 | 228 | k1, k2, k3, t1, t2 229 | 230 |
237 | 238 | `fisheye` 239 | 240 | 243 | 244 | OpenCV fisheye distortion model with 4 coefficients 245 | 246 | 249 | 250 | k1, k2, k3, k4 251 | 252 |
259 | 260 | `equidistant_r3` 261 | 262 | 265 | 266 | Camera model used for fisheye optics with distortion provided in [1](#id2) 267 | 268 | 271 | 272 | w 273 | 274 |
281 | 282 | `3deanamorphic4` 283 | 284 | 287 | 288 | Anamorphic camera model with 4 coefficients used by 3D Equalizer 289 | 290 | 293 | 294 | cxx, cxy, cyx, cyy 295 | 296 |
303 | 304 | `3declassicld` 305 | 306 | 309 | 310 | Anamorphic camera model with 10 coefficients used by 3D Equalizer 311 | 312 | 315 | 316 | delta, epsilon, mux, muy, q 317 | 318 |
325 | 326 | `3deradial4` 327 | 328 | 331 | 332 | Radial camera model with used by 3D Equalizer 333 | 334 | 337 | 338 | c2, c4, u1, v1, u3, v3 339 | 340 |
347 | 348 |
349 | 350 |
[1]
351 | 352 |
353 | 354 | Devernay, Frédéric and Faugeras, Olivier. “Straight Lines Have to Be Straight Automatic Calibration and Removal of Distortion from Scenes of Structured Envi- ronments”. In: _Mach Vis Appl 13 (Aug. 2001)_. doi: 10.1007/PL00013269 355 | 356 |
357 | 358 |
-------------------------------------------------------------------------------- /docs/sphinx/formats/COLMAP/COLMAP.md: -------------------------------------------------------------------------------- 1 | # COLMAP 2 | 3 | is a 3D reconstruction software developed by Johannes L. Schönberger . 4 | The software stores its results in multiple files: A `cameras` file 5 | containing intrinsic parameters, an `images` file containing extrinsic 6 | parameters and a `points3d` file containing all reconstructed 3D 7 | points. The files can be in binary format for faster processing or in 8 | ASCII format for human readability. 9 | 10 | ## Coordinate system 11 | 12 | The default COLMAP coordinate system is z front, -x right and -y up. The default camera direction is z and camera up is -y: 13 | 14 | 15 | 16 | ![image](./colmap_coordinatesystem_cam.png) 17 | 18 | ## Cameras file 19 | 20 | Example cameras ASCII file: 21 | 22 | ```default 23 | camera_id camera_model image_width image_height params* 24 | 1 SIMPLE_PINHOLE 3072 2304 2559.81 1536 1152 25 | 2 PINHOLE 3072 2304 2560.56 2560.56 1536 1152 26 | 3 SIMPLE_RADIAL 3072 2304 2559.69 1536 1152 -0.0218531 27 | ``` 28 | 29 | ## Images file 30 | 31 | Example images ASCII file: 32 | 33 | ```default 34 | imageId, qw, qx, qy, qz, tx, ty, tz, cameraId, name 35 | points2d[] as (x,y,pointid) 36 | 1 0.9238795391929062 0.38268341623423263 -0.0 -0.0 -5.041089999999998 1.2708298199999997 1.4063895899999999 1 view1.png 37 | 0 0 0 0 0 0 0 0 0 38 | 2 0.7071067811865475 -0.0 0.7071067811865475 -0.0 0.0506335 1.94183 0.594955 2 view1.png 39 | 0 0 0 0 0 0 0 0 0 40 | 3 0.9238795391929062 -0.0 -0.0 -0.38268341623423263 -3.779291409999999 3.3067953399999994 0.042054899999999985 3 view1.png 41 | 0 0 0 0 0 0 0 0 0 42 | ``` 43 | 44 | Note that the rotation $R$ and the translation $t$ are given by: 45 | 46 | $$ 47 | R = R_c^{-1} \\ 48 | t = -Rt_c 49 | $$ 50 | 51 | where $R_c$ is the rotation matrix built from the quaternion in the images file, and $t_c$ is the translation vector in the images file 52 | ## Camera models 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 | 73 | 74 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 99 | 100 | 105 | 106 | 111 | 112 | 117 | 118 | 119 | 120 | 121 | 122 | 127 | 128 | 133 | 134 | 139 | 140 | 145 | 146 | 147 | 148 | 149 | 150 | 155 | 156 | 161 | 162 | 167 | 168 | 173 | 174 | 175 | 176 | 177 | 178 | 183 | 184 | 189 | 190 | 195 | 196 | 201 | 202 | 203 | 204 | 205 | 206 | 211 | 212 | 217 | 218 | 223 | 224 | 229 | 230 | 231 | 232 | 233 | 234 | 239 | 240 | 245 | 246 | 251 | 252 | 257 | 258 | 259 | 260 | 261 | 262 | 267 | 268 | 273 | 274 | 279 | 280 | 285 | 286 | 287 | 288 | 289 | 290 | 295 | 296 | 301 | 302 | 307 | 308 | 313 | 314 | 315 | 316 | 317 | 318 | 323 | 324 | 329 | 330 | 335 | 336 | 341 | 342 | 343 | 344 | 345 | 346 | 351 | 352 | 357 | 358 | 363 | 364 | 369 | 370 | 371 | 372 | 373 | 374 | 379 | 380 | 385 | 386 | 391 | 392 | 397 | 398 | 399 | 400 | 401 | 402 |
63 | 64 | **ID** 65 | 66 | 69 | 70 | **Name** 71 | 72 | 75 | 76 | **Description** 77 | 78 | 81 | 82 | **Parameters (in order)** 83 | 84 |
95 | 96 | 0 97 | 98 | 101 | 102 | SIMPLE_PINHOLE 103 | 104 | 107 | 108 | Simple model without distortion 109 | 110 | 113 | 114 | f, cx, cy 115 | 116 |
123 | 124 | 1 125 | 126 | 129 | 130 | PINHOLE 131 | 132 | 135 | 136 | Simple model without distortion but two focal length parameters 137 | 138 | 141 | 142 | fx, fy, cx, cy 143 | 144 |
151 | 152 | 2 153 | 154 | 157 | 158 | SIMPLE_RADIAL 159 | 160 | 163 | 164 | Radial model with one distortion parameter. Similar to VisualSfM, with the difference that the distortion is applied to the projections 165 | 166 | 169 | 170 | f, cx, cy, k 171 | 172 |
179 | 180 | 3 181 | 182 | 185 | 186 | RADIAL 187 | 188 | 191 | 192 | Radial model with two distortion parameters. The same model that Bundler uses 193 | 194 | 197 | 198 | f, cx, cy, k1, k2 199 | 200 |
207 | 208 | 4 209 | 210 | 213 | 214 | OPENCV 215 | 216 | 219 | 220 | A simple brown camera model with two radial and two tangential distortion parameters 221 | 222 | 225 | 226 | fx, fy, cx, cy, k1, k2, p1, p2 227 | 228 |
235 | 236 | 5 237 | 238 | 241 | 242 | OPENCV_FISHEYE 243 | 244 | 247 | 248 | An OpenCV fisheye camera model with two radial and two tangential distortion parameters 249 | 250 | 253 | 254 | fx, fy, cx, cy, k1, k2, k3, k4 255 | 256 |
263 | 264 | 6 265 | 266 | 269 | 270 | FULL_OPENCV 271 | 272 | 275 | 276 | The full OpenCV camera model with six radial and two tangential distortion parameters 277 | 278 | 281 | 282 | fx, fy, cx, cy, k1, k2, p1, p2, k3, k4, k5, k6 283 | 284 |
291 | 292 | 7 293 | 294 | 297 | 298 | FOV 299 | 300 | 303 | 304 | The field of view distortion model used in project tango and described by Klein et. al. 305 | 306 | 309 | 310 | fx, fy, cx, cy, omega 311 | 312 |
319 | 320 | 8 321 | 322 | 325 | 326 | SIMPLE _RADIAL_FISHEYE 327 | 328 | 331 | 332 | A simple OpenCV fisheye model with one radial distortion coefficient 333 | 334 | 337 | 338 | f, cx, cy, k 339 | 340 |
347 | 348 | 9 349 | 350 | 353 | 354 | RADIAL_FISHEYE 355 | 356 | 359 | 360 | A simple OpenCV fisheye model with two radial distortion coefficients 361 | 362 | 365 | 366 | f, cx, cy, k1, k2 367 | 368 |
375 | 376 | 10 377 | 378 | 381 | 382 | THIN _PRISM_FISHEYE 383 | 384 | 387 | 388 | A camera model with radial and tangential distortion coefficients and additional thin-prism distortion coefficients as described by Weng et. al. 389 | 390 | 393 | 394 | fx, fy, cx, cy, k1, k2, p1, p2, k3, k4, sx1, sy1 395 | 396 |
403 | 404 |
405 | 406 |
[1](#id1)
407 | 408 |
409 | 410 | Schönberger, Johannes Lutz and Frahm, Jan-Michael. “Structure-from-Motion Revisited”. In: Conference on Computer Vision and Pattern Recognition (CVPR). Las Vegas, NV, USA, June 2016. 411 | 412 |
413 | 414 |
[2](#id2)
415 | 416 |
417 | 418 | Schönberger, Johannes Lutz et al. “Pixelwise View Selection for Unstructured Multi-View Stereo”. In: European Conference on Computer Vision (ECCV). The Netherlands, Amsterdam, Oct. 2016 419 | 420 |
421 | 422 |
423 | 424 | -------------------------------------------------------------------------------- /camorph/sphinx/formats/Meshroom/Meshroom.rst: -------------------------------------------------------------------------------- 1 | Meshroom 2 | ======== 3 | 4 | *Meshroom* is a 3D reconstruction software developed by Griwodz et. al. 5 | The software has a nodal architecture and stores intermediate results in 6 | a specific subfolder structure. Each nodes data is stored in a folder 7 | with its unique identifier as a name in a subfolder of the node type in 8 | the folder ``MeshroomCache``. The reconstructed camera poses are stored 9 | in ``cameras.sfm`` in the ``StructureFromMotion`` folder. Known poses 10 | can be imported into Mehsroom by setting the input of a 11 | ``FeatureExtraction`` node to the custom ``cameras.sfm`` file. 12 | 13 | Coordinate System 14 | ----------------- 15 | 16 | The standard *Meshroom* coordinate system is y up, x right, z front. The camera orientation is -z front and y up. 17 | 18 | .. image:: Meshrrom_coordinatesystem_cam-1.png 19 | 20 | SfM File 21 | -------- 22 | The SfM file uses the widespread JSON format. 23 | 24 | - ``featuresFolders`` and ``matchesFolders`` point to specific folders 25 | in the *Meshroom* folder structure, and should be left empty when 26 | writing a custom file. 27 | 28 | - ``views`` holds view objects which hold data of a source image used 29 | for reconstruction in *Meshroom*. 30 | 31 | - ``viewId`` is a unique identifier 32 | 33 | - ``poseId`` refers to a pose object in ``poses`` where extrinsic 34 | parameters are stored 35 | 36 | - ``intrinsicId`` refers to an intrinsic object in ``intrinsics`` 37 | where intrinsic parameters are stored 38 | 39 | - ``path`` refers to the file path of the source image 40 | 41 | - ``width`` and ``height`` are the width and height of the image 42 | 43 | - ``metadata`` holds metadata about the image which is not relevant 44 | in this thesis. 45 | 46 | - ``intrinsics`` holds intrinsic objects which store intrinsic 47 | parameters of a specific camera model 48 | 49 | - ``intrinsicId`` is a unique identifier 50 | 51 | - ``width`` and ``height`` the resolution of the camera 52 | 53 | - ``sensorWidth`` and ``sensorHeight`` the size of the sensor in mm 54 | 55 | - ``serialNumber`` the serial number of the camera the source image 56 | was taken with 57 | 58 | - ``type`` the name of the distortion model type 59 | 60 | - ``initializationMode`` one of ``calibrated`` for externally 61 | calibrated cameras, ``estimated`` for estimated cameras, 62 | ``unknown`` for unkown cameras with guess values or ``none`` for 63 | none 64 | 65 | - ``pxInitialFocalLength`` is the initial guess of the focal length 66 | in px 67 | 68 | - ``pxFocalLength`` is the focal length in px 69 | 70 | - ``principalPoint`` is the principal point in px 71 | 72 | - ``distortionParams`` are the distortion params of the camera model 73 | 74 | - ``locked`` is a bool if the intrinsic objects parameters are 75 | locked or can be modified 76 | 77 | - ``poses`` holds pose objects which store extrinsic parameters of a 78 | specific pose 79 | 80 | - ``poseId`` is a unique identifier 81 | 82 | - ``pose`` is a nested pose object 83 | 84 | - ``transform`` stores the rotation as ``rotation`` as a matrix 85 | in row major order and the translation as ``center`` as a 86 | three-dimensional vector 87 | 88 | - ``locked`` is a bool if the pose objects parameters are locked or 89 | can be modified 90 | 91 | Camera Models 92 | ------------- 93 | Meshroom supports the following camera models: 94 | 95 | +--------------------+-----------------------+-----------------------+ 96 | | **Name** | **Description** | **Parameters** | 97 | +====================+=======================+=======================+ 98 | | ``pinhole`` | Pinhole model without | | 99 | | | distortion | | 100 | | | coefficients | | 101 | +--------------------+-----------------------+-----------------------+ 102 | | ``radial1`` | Brown model with one | k1 | 103 | | | radial coefficient | | 104 | +--------------------+-----------------------+-----------------------+ 105 | | ``radial3`` | Brown model with | k1, k2, k3 | 106 | | | three radial | | 107 | | | coefficients | | 108 | +--------------------+-----------------------+-----------------------+ 109 | | ``brown`` | Brown model with | k1, k2, k3, t1, t2 | 110 | | | three radial and two | | 111 | | | tangential | | 112 | | | coefficients | | 113 | +--------------------+-----------------------+-----------------------+ 114 | | ``fisheye`` | OpenCV fisheye | k1, k2, k3, k4 | 115 | | | distortion model with | | 116 | | | 4 | | 117 | | | coefficients | | 118 | | | | | 119 | +--------------------+-----------------------+-----------------------+ 120 | | ``equidistant_r3`` | Camera model used for | w | 121 | | | fisheye optics with | | 122 | | | distortion provided | | 123 | | | in [1]_ | | 124 | | | | | 125 | +--------------------+-----------------------+-----------------------+ 126 | | ``3deanamorphic4`` | Anamorphic camera | cxx, cxy, cyx, cyy | 127 | | | model with 4 | | 128 | | | coefficients used by | | 129 | | | 3D Equalizer | | 130 | | | | | 131 | +--------------------+-----------------------+-----------------------+ 132 | | ``3declassicld`` | Anamorphic camera | delta, epsilon, mux, | 133 | | | model with 10 | muy, q | 134 | | | coefficients used by | | 135 | | | 3D Equalizer | | 136 | | | | | 137 | +--------------------+-----------------------+-----------------------+ 138 | | ``3deradial4`` | Radial camera model | c2, c4, u1, v1, u3, | 139 | | | with used by 3D | v3 | 140 | | | Equalizer | | 141 | | | | | 142 | +--------------------+-----------------------+-----------------------+ 143 | 144 | .. [1] Devernay, Frédéric and Faugeras, Olivier. "Straight Lines Have to Be Straight 145 | Automatic Calibration and Removal of Distortion from Scenes of Structured Envi- 146 | ronments". In: *Mach Vis Appl 13 (Aug. 2001)*. doi: 10.1007/PL00013269 -------------------------------------------------------------------------------- /camorph/lib/model/Camera.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import math 3 | from typing import Union, Optional 4 | 5 | import numpy as np 6 | from numpy import ndarray 7 | from pyquaternion import Quaternion 8 | 9 | 10 | class Camera(): 11 | """ 12 | This class represents camera parameters in camorph 13 | """ 14 | 15 | 16 | def __init__(self): 17 | # Z 18 | # ^ 19 | # | 20 | # | 21 | # | 22 | # .-------------> Y 23 | # / 24 | # / c| 25 | # v a|--> 26 | # X m| 27 | # v 28 | 29 | 30 | 31 | self.r: Optional[Quaternion] = None 32 | """Rotation 33 | 34 | `Type`: Quaternion""" 35 | 36 | self.t: Optional[ndarray] = None 37 | """Translation 38 | 39 | `Type`: ndarray""" 40 | 41 | self.name: Optional[str] = None 42 | """A unique name 43 | 44 | `Type`: str""" 45 | 46 | self.autocompute: bool = True 47 | """Automaic computation of certain parameters 48 | 49 | `Type`: bool""" 50 | 51 | self.focal_length_px: Optional[list[float]] = None 52 | """Focal length in pixels 53 | 54 | `Type`: float""" 55 | 56 | self.focal_length_mm: Optional[list[float]] = None 57 | """Focal length in millimeters 58 | 59 | `Type`: float""" 60 | 61 | self.resolution: Union[(float, float), None] = None 62 | """Resolution in pixels as a tuple 63 | 64 | `Type`: (float,float)""" 65 | 66 | self.principal_point: Union[(float, float), None] = None 67 | """Principal point in pixels as a tuple 68 | 69 | `Type`: (float,float)""" 70 | 71 | self.fov: Union[(float, float), None] = None 72 | """Field of view in radians 73 | 74 | `Type`: (float,float)""" 75 | 76 | self.projection_type: Optional[str] = 'perspective' 77 | """Projection type, either `perspective`, `orthogonal` or `equirectangular` 78 | 79 | `Type`: str""" 80 | 81 | self.sensor_size: Union[(float, float), None] = None 82 | """Sensor size 83 | 84 | `Type`: (float,float)""" 85 | 86 | self.lens_shift: Union[(float, float), None] = None 87 | """Lens shift in normalized coordinates from -0.5 to 0.5 88 | 89 | `Type`: (float,float)""" 90 | 91 | self.model: Optional[str] = 'pinhole' 92 | """Camera model. One of the following: `pinhole`, `opencv_fisheye`, `orthographic`, `brown` 93 | 94 | `Type`: str""" 95 | 96 | self.radial_distortion: Optional[list[float]] = None 97 | """Radial distortion coefficients 98 | 99 | `Type`: list[float]""" 100 | 101 | self.tangential_distortion: Optional[list[float]] = None 102 | """Tangential distortion coefficients 103 | 104 | `Type`: list[float]""" 105 | 106 | self.source_image: Optional[str] = None 107 | """Path to a source image 108 | 109 | `Type`: str""" 110 | 111 | self.mask: Optional[str] = None 112 | """Path to a mask 113 | 114 | `Type`: str""" 115 | 116 | self.exif: Optional[dict] = None 117 | """Exif information of the camera 118 | 119 | `Type`: dict""" 120 | 121 | self.near_far_bounds: Optional[list] = None 122 | """Near and far camera bounds 123 | 124 | `Type`: list[float] 125 | """ 126 | 127 | 128 | 129 | def _has_or_none(self, *args): 130 | """ 131 | This function returns False if one of the parameters in args is not bound or None 132 | :param args: List of parameters 133 | :return: bool 134 | """ 135 | for param in args: 136 | if getattr(self, param, None) is None: 137 | return False 138 | return True 139 | 140 | def _focal_length_mm_1(self): 141 | """ 142 | This function computes the focal length in mm 143 | :return: float 144 | """ 145 | return [x * self.sensor_size[idx] / self.resolution[idx] for idx, x in enumerate(self.focal_length_px)] 146 | 147 | def _focal_length_mm_2(self): 148 | """ 149 | This function computes the focal length in mm 150 | :return: float 151 | """ 152 | return [x / (2 * math.atan2(self.fov[0], 2)) for idx, x in enumerate(self.sensor_size[0])] 153 | 154 | def _focal_length_px(self): 155 | """ 156 | This function computes the focal length in px 157 | :return: float 158 | """ 159 | return [x * self.resolution[idx] / self.sensor_size[idx] for idx, x in enumerate(self.focal_length_mm) ] 160 | 161 | def _sensor_size(self): 162 | """ 163 | This function computes the sensor size in mm 164 | :return: (float, float) 165 | """ 166 | return 2 * self.focal_length_mm[0] * math.tan(self.fov[0]), 2 * self.focal_length_mm[1] * math.tan(self.fov[1]) 167 | 168 | def _fov(self): 169 | """ 170 | This function computes the fov in radians 171 | :return: (float, float) 172 | """ 173 | return 2 * math.atan2(self.sensor_size[0], 2 * self.focal_length_mm[0]), 2 * math.atan2(self.sensor_size[1], 174 | 2 * self.focal_length_mm[1]) 175 | 176 | def _principal_point(self): 177 | """ 178 | This function computes the principal point in px 179 | :return: (float, float) 180 | """ 181 | return (0.5 + self.lens_shift[0]) * self.resolution[0], (0.5 + self.lens_shift[1]) * self.resolution[1] 182 | 183 | def _lens_shift(self): 184 | """ 185 | This function computes the lens shift 186 | :return: 187 | """ 188 | return self.principal_point[0] / self.resolution[0] - 0.5, self.principal_point[1] / self.resolution[1] - 0.5 189 | 190 | def __setattr__(self, key, value): 191 | super(Camera, self).__setattr__(key, value) 192 | if hasattr(self, 'autocompute') and not self.autocompute: 193 | return 194 | if hasattr(self, 'focal_length_mm') and self.focal_length_mm is None and key != 'focal_length_mm' and\ 195 | self._has_or_none('focal_length_px', 'sensor_size', 'resolution'): 196 | self.focal_length_mm = self._focal_length_mm_1() 197 | if hasattr(self, 'focal_length_mm') and self.focal_length_mm is None and key != 'focal_length_mm' and\ 198 | self._has_or_none('sensor_size', 'fov'): 199 | self.focal_length_mm = self._focal_length_mm_2() 200 | if hasattr(self, 'focal_length_px') and self.focal_length_px is None and key != 'focal_length_px' and\ 201 | self._has_or_none('focal_length_mm', 'resolution','sensor_size'): 202 | self.focal_length_px = self._focal_length_px() 203 | if hasattr(self, 'sensor_size') and self.sensor_size is None and key != 'sensor_size' and\ 204 | self._has_or_none('focal_length_mm', 'fov'): 205 | self.sensor_size = self._sensor_size() 206 | if hasattr(self, 'fov') and self.fov is None and key != 'fov' and\ 207 | self._has_or_none('sensor_size', 'focal_length_mm'): 208 | self.fov = self._fov() 209 | if hasattr(self, 'principal_point') and self.principal_point is None and key != 'principal_point' and\ 210 | self._has_or_none('lens_shift', 'resolution'): 211 | self.principal_point = self._principal_point() 212 | if hasattr(self, 'lens_shift') and self.lens_shift is None and key != 'lens_shift' and \ 213 | self._has_or_none('principal_point', 'resolution'): 214 | self.lens_shift = self._lens_shift() 215 | 216 | -------------------------------------------------------------------------------- /camorph/lib/crucial_property.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import warnings 4 | import os 5 | from os.path import isdir, dirname, isfile, basename 6 | 7 | import numpy as np 8 | 9 | from camorph.lib.model import Camera 10 | from camorph.lib.model.FileHandler import FileHandler 11 | 12 | """ 13 | This module is used to check if the crucial properties of a destination FileHandler have been set 14 | when trying to write to a file. For example, all photogrammetry formats (COLMAP, RealityCapture, Mehsroom) need 15 | source_image property. This can be set by supplying a config.json file. Camorph automatically looks for 16 | a config.json file in the target directory. 17 | """ 18 | 19 | 20 | def cmd_check_properties(inst: FileHandler, cams: list[Camera]) -> None: 21 | pass 22 | 23 | 24 | def pip_check_properties(inst: FileHandler, cams: list[Camera], crucial_property_config: str, dest_path: str) -> None: 25 | # This function checks if crucial properties are set, and sets them via a config 26 | crucial_properties = inst.crucial_properties() 27 | crucial_property_config = os.path.join(dest_path, 'config.json') if isdir(dest_path) else os.path.join(dirname(dest_path), 'config.json') 28 | 29 | cpc = None 30 | if isfile(crucial_property_config): 31 | print('Found config.json') 32 | with open(crucial_property_config, 'r') as f: 33 | cpc = json.load(f) 34 | 35 | # set all attributes in config.json 36 | if 'values' in cpc: 37 | for val in cpc['values']: 38 | for prop in val: 39 | try: 40 | set_config_property(next(cam for cam in cams if cam.name == val['name']), prop, val[prop]) 41 | except StopIteration: 42 | raise Exception("At least one camera supplied in the config file was not found in the source file. Are you using a wrong config.json ?") 43 | 44 | for prop in cpc['global']: 45 | if prop[prop.rfind('_'):] == '_path': 46 | prop = prop[:prop.rfind('_')] 47 | p = cpc['global'][prop + '_path']['path'] 48 | if 'filter' in cpc['global'][prop + '_path']: 49 | regex = cpc['global'][prop + '_path']['filter'] 50 | else: 51 | regex = '.*' 52 | files = [os.path.relpath(os.path.join(p,f),os.path.dirname(dest_path)) for f in os.listdir(p) if bool(re.match(regex, basename(f)))] 53 | # os.listdir() returns random order when path is a network path 54 | files = sorted(files) 55 | 56 | unmatched_files = [] 57 | for file in files: 58 | # match files according to camera names 59 | filename = basename(file) 60 | try: 61 | found_cam = next( 62 | (cam for cam in cams if (cam.name[:cam.name.rfind('.')] == filename[:filename.rfind('.')] if '.' in cam.name else cam.name == filename[:filename.rfind('.')])), 63 | None) 64 | except TypeError: 65 | # cam.name is None and cant be iterated 66 | found_cam = None 67 | if found_cam is not None and hasattr(found_cam,prop) and getattr(found_cam,prop) is None: 68 | set_config_property(found_cam, prop, file) 69 | else: 70 | unmatched_files.append(file) 71 | if len(unmatched_files) > 0: 72 | warnings.warn("Not all files match Camera names, resorting to order of files in os") 73 | idx = 0 74 | for cam in cams: 75 | if getattr(cam,prop) is None: 76 | try: 77 | set_config_property(cam, prop, unmatched_files[idx]) 78 | idx += 1 79 | except IndexError: 80 | raise Exception(f"There are less files than cameras found:" 81 | f" {len(files)} files, {len(cams)} cameras") 82 | else: 83 | for cam in cams: 84 | if isinstance(cpc['global'][prop], list): 85 | set_config_property(cam, prop, np.asarray(cpc['global'][prop])) 86 | else: 87 | set_config_property(cam, prop, cpc['global'][prop]) 88 | 89 | # check if any cam doesn't have this attribute or if it is None 90 | unset_properties = [] 91 | unset_json_properties = dict() 92 | for prop in crucial_properties: 93 | for cam in cams: 94 | if not hasattr(cam, prop) or getattr(cam, prop) is None: 95 | if cpc is not None: 96 | if prop not in unset_json_properties: 97 | unset_json_properties[prop] = [] 98 | unset_json_properties[prop].append(cam.name) 99 | else: 100 | if prop not in unset_properties: 101 | unset_properties.append(prop) 102 | 103 | # check if json was supplied, but not all properties were set 104 | if len(unset_json_properties) > 0: 105 | errstring = "Not all crucial properties have been set in config.json. The following properties are missing: \n" 106 | for key in unset_json_properties: 107 | errstring += key + ' on cameras ' + ','.join([str(x) for x in unset_json_properties[key]]) + '\n' 108 | errstring += 'Camorph can automatically compute some parameters. Please refer to the documentation.' 109 | raise Exception(errstring) 110 | 111 | # check if unset properties exist 112 | if len(unset_properties) > 0: 113 | if not isfile(crucial_property_config): 114 | write_crucial_config_template(cams, crucial_property_config, unset_properties) 115 | raise Exception("Crucial properties are missing and no config.json file was found in the output folder. A " 116 | "config.json template has been generated. The following curcial properties are missing: " + 117 | ' '.join([str(x) for x in unset_properties])) 118 | else: 119 | raise Exception( 120 | "This error should not occur. There are no unset json properties but a config.json exists. Please " 121 | "contact support") 122 | 123 | 124 | def set_config_property(cam, property, value): 125 | # name property cannot be set as it is used as a unique identifier for cameras 126 | if property == 'name': 127 | return 128 | if hasattr(cam, property) and getattr(cam, property) is not None: 129 | warnings.warn('Config.json is overriding existing property ' + property + ' of value ' + str(getattr(cam, property)) + ' with value ' + str(value)) 130 | setattr(cam, property, value) 131 | 132 | def write_crucial_config_template(cams, path, properties=None): 133 | """ 134 | This function writes a config.json template file to the specified location. 135 | When trying to write to a format with missing crucial properties, camorph automatically 136 | writes a config.json file to the target path. 137 | 138 | :param cams: A list of cameras 139 | :param path: The path where to write the config.json file 140 | :param properties: Optional missing properties 141 | :return: 142 | """ 143 | if properties is None: 144 | properties = [] 145 | template = dict() 146 | template['type'] = "camorph_config" 147 | template['missing_properties'] = [] 148 | for prop in properties: 149 | template['missing_properties'].append(prop) 150 | template['example'] = {"property_name": "Value", "global_path": {"path": r"\path\to\dir", "filter": "regex"}} 151 | template['global'] = {} 152 | template['values'] = [] 153 | for c in cams: 154 | template['values'].append({'name': c.name}) 155 | with open(path, 'w') as f: 156 | json.dump(template, f, indent=4) 157 | --------------------------------------------------------------------------------