pyopenrivercam 0.8.5__tar.gz → 0.8.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/CHANGELOG.md +12 -0
  2. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/PKG-INFO +1 -1
  3. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/index.rst +8 -2
  4. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/__init__.py +1 -1
  5. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/cameraconfig.py +32 -43
  6. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/cross_section.py +1 -1
  7. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cli/cli_elements.py +11 -2
  8. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cli/cli_utils.py +60 -10
  9. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cli/main.py +18 -1
  10. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cv.py +76 -25
  11. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/conftest.py +3 -2
  12. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_cameraconfig.py +8 -13
  13. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_cli.py +2 -0
  14. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.github/ISSUE_TEMPLATE/issue-report.md +0 -0
  15. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.github/workflows/documentation.yml +0 -0
  16. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.github/workflows/pypi-publish.yml +0 -0
  17. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.github/workflows/pypitest-publish.yml +0 -0
  18. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.github/workflows/tests.yml +0 -0
  19. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.gitignore +0 -0
  20. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/.pre-commit-config.yaml +0 -0
  21. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/Dockerfile +0 -0
  22. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/LICENSE +0 -0
  23. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/README.md +0 -0
  24. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/TRADEMARK.md +0 -0
  25. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/Makefile +0 -0
  26. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/ChuoKikuu_GCPs.jpg +0 -0
  27. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/GCPs_interactive.jpg +0 -0
  28. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/Geul_GCPs.jpg +0 -0
  29. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/bbox_interactive.jpg +0 -0
  30. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/camera_calib.gif +0 -0
  31. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/example_geul_small.jpg +0 -0
  32. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/site_schematic_cs.svg +0 -0
  33. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/site_schematic_planar.svg +0 -0
  34. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_edge.gif +0 -0
  35. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_norm.gif +0 -0
  36. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_norm_proj.gif +0 -0
  37. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_orig.gif +0 -0
  38. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_stable.gif +0 -0
  39. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/video_unstable.gif +0 -0
  40. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/wark_cam_config.jpg +0 -0
  41. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/wark_cam_config_persp.jpg +0 -0
  42. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/wark_cam_persp.jpg +0 -0
  43. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/wark_discharge.jpg +0 -0
  44. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_images/wark_streamplot.jpg +0 -0
  45. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/logo.png +0 -0
  46. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/logo.svg +0 -0
  47. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/orc_favicon.svg +0 -0
  48. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/orc_logo_bw.svg +0 -0
  49. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/orc_logo_color.svg +0 -0
  50. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/orc_logo_grey.svg +0 -0
  51. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_static/theme-localdevices.css +0 -0
  52. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/_templates/accessor_method.rst +0 -0
  53. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/api.rst +0 -0
  54. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/conf.py +0 -0
  55. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/index.rst +0 -0
  56. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/installation.rst +0 -0
  57. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/intro.rst +0 -0
  58. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/make.bat +0 -0
  59. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/ngwerere.jpg +0 -0
  60. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/quickstart.rst +0 -0
  61. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/requirements.txt +0 -0
  62. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/api.rst +0 -0
  63. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/api.rst +0 -0
  64. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/api_bbox.rst +0 -0
  65. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/api_gcps.rst +0 -0
  66. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/api_geo.rst +0 -0
  67. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/api_lens_calib.rst +0 -0
  68. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/cam_config.py +0 -0
  69. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/cli.rst +0 -0
  70. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/cli_bbox.rst +0 -0
  71. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/cli_gcps.rst +0 -0
  72. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/camera_config/cli_geo.rst +0 -0
  73. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cli.rst +0 -0
  74. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cross_section/cross_section.jpg +0 -0
  75. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cross_section/index.rst +0 -0
  76. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cross_section/polygon_samples.jpg +0 -0
  77. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cross_section/pyplots/cross.py +0 -0
  78. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/cross_section/wrong_estimate.jpg +0 -0
  79. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/frames/frames.ipynb +0 -0
  80. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/frames/index.rst +0 -0
  81. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/index.rst +0 -0
  82. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/plot/index.rst +0 -0
  83. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/plot/plot_example.jpg +0 -0
  84. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/plot/plot_example.py +0 -0
  85. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/plot/plot_example.yml +0 -0
  86. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/transect/index.rst +0 -0
  87. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/velocimetry/index.rst +0 -0
  88. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/docs/user-guide/video/index.rst +0 -0
  89. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/environment.yml +0 -0
  90. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/envs/pyorc-dev.yml +0 -0
  91. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/envs/pyorc-test.yml +0 -0
  92. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/01_Camera_Configuration_single_video.ipynb +0 -0
  93. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/02_Process_velocimetry.ipynb +0 -0
  94. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/03_Plotting_and_masking_velocimetry_results.ipynb +0 -0
  95. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/04_Extracting_crosssection_velocities_and_discharge.ipynb +0 -0
  96. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/05_Camera_calibration.ipynb +0 -0
  97. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/06_Estimating_water_levels_optically.ipynb +0 -0
  98. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/camera_calib/camera_calib_720p.mkv +0 -0
  99. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/geul/dk_cam_config.json +0 -0
  100. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/geul/dk_control.mp4 +0 -0
  101. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/cross_section1.geojson +0 -0
  102. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/cross_section2.geojson +0 -0
  103. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere.json +0 -0
  104. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere.yml +0 -0
  105. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_20191103.mp4 +0 -0
  106. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_cross_section.csv +0 -0
  107. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_cross_section_2.csv +0 -0
  108. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_gcps.geojson +0 -0
  109. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_masked.nc +0 -0
  110. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_piv.nc +0 -0
  111. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/examples/ngwerere/ngwerere_test.yml +0 -0
  112. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/__init__.py +0 -0
  113. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/frames.py +0 -0
  114. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/mask.py +0 -0
  115. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/orcbase.py +0 -0
  116. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/plot.py +0 -0
  117. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/transect.py +0 -0
  118. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/velocimetry.py +0 -0
  119. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/api/video.py +0 -0
  120. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cli/__init__.py +0 -0
  121. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/cli/log.py +0 -0
  122. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/const.py +0 -0
  123. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/helpers.py +0 -0
  124. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/plot_helpers.py +0 -0
  125. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/project.py +0 -0
  126. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/pyorc.sh +0 -0
  127. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/sample_data.py +0 -0
  128. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/service/__init__.py +0 -0
  129. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/service/camera_config.py +0 -0
  130. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/service/velocimetry.py +0 -0
  131. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/velocimetry/__init__.py +0 -0
  132. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/velocimetry/ffpiv.py +0 -0
  133. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyorc/velocimetry/openpiv.py +0 -0
  134. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/pyproject.toml +0 -0
  135. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/__init__.py +0 -0
  136. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_cross_section.py +0 -0
  137. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_frames.py +0 -0
  138. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_mask.py +0 -0
  139. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_plot_helpers.py +0 -0
  140. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_sample_data.py +0 -0
  141. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_transect.py +0 -0
  142. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_velocimetry.py +0 -0
  143. {pyopenrivercam-0.8.5 → pyopenrivercam-0.8.6}/tests/test_video.py +0 -0
@@ -1,3 +1,15 @@
1
+ ## [0.8.6] = 2025-05-16
2
+ ### Added
3
+ - added options `--k1`, `--k2` and `--focal_length` to command line interface for cases where
4
+ focal length, and distortion coefficients are already known
5
+ ### Changed
6
+ - optimization of intrinsics can now also be done with partly already known data. If k1 and k2 are known
7
+ these can be passed as camera
8
+ ### Deprecated
9
+ ### Removed
10
+ ### Fixed
11
+
12
+
1
13
  ## [0.8.5] = 2025-03-25
2
14
  ### Added
3
15
  - option `--cross` can now also be provided at service level. Only relevant
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyopenrivercam
3
- Version: 0.8.5
3
+ Version: 0.8.6
4
4
  Summary: pyorc: free and open-source image-based surface velocity and discharge.
5
5
  Author-email: Hessel Winsemius <winsemius@rainbowsensing.com>
6
6
  Requires-Python: >=3.9
@@ -93,7 +93,7 @@ middle of the FOV, then velocities at the edges are likely to be overestimated.
93
93
  optimize the radial distortion. Therefore we strongly recommend that you measure 6 or more control points in case
94
94
  you use a lens with significant radial distortion.
95
95
 
96
- You can also provide a camera intrinsic matrix and distortion coefficients in the API if you have these, or optimize
96
+ You can also provide a camera intrinsic matrix and distortion coefficients in the CLI or API if you have these, or optimize
97
97
  the intrinsic matrix and distortion coefficients using a checkerboard pattern. More on this is described below.
98
98
 
99
99
  Preparing a video for camera calibration
@@ -142,7 +142,13 @@ Lens calibration method
142
142
  .. note::
143
143
 
144
144
  At the moment, manual lens calibration is only available at API level. If you require a command-line option
145
- for lens calibration, then please contact us at info@rainbowsensing.com.
145
+ for lens calibration, then please contact us at info@rainbowsensing.com. If you have the focal length, and
146
+ k1 and k2 radial distortion parameters available, then you can enter these in the command line interface
147
+ using the `--focal_length`, `--k1`, `--k2` parameters. The units of the focal length should be in pixels.
148
+ For focal length, and distortion parameters, we use the OpenCV methods, so please ensure you use
149
+ the same as input here. For more information please read:
150
+ `the OpenCV instructions <https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html>`_
151
+
146
152
 
147
153
  .. tab-item:: API
148
154
 
@@ -1,6 +1,6 @@
1
1
  """pyorc: free and open-source image-based surface velocity and discharge."""
2
2
 
3
- __version__ = "0.8.5"
3
+ __version__ = "0.8.6"
4
4
 
5
5
  from .api import CameraConfig, CrossSection, Frames, Transect, Velocimetry, Video, get_camera_config, load_camera_config # noqa
6
6
  from .project import * # noqa
@@ -47,7 +47,6 @@ class CameraConfig:
47
47
  lens_position: Optional[List[float]] = None,
48
48
  corners: Optional[List[List[float]]] = None,
49
49
  gcps: Optional[Dict[str, Union[List, float]]] = None,
50
- lens_pars: Optional[Dict[str, float]] = None,
51
50
  calibration_video: Optional[str] = None,
52
51
  is_nadir: Optional[bool] = False,
53
52
  stabilize: Optional[List[List]] = None,
@@ -90,10 +89,6 @@ class CameraConfig:
90
89
  the same vertical reference as the measured bathymetry and other survey points,
91
90
  "crs": int, str or CRS object, CRS in which "dst" points are measured. If None, a local coordinate system is
92
91
  assumed (e.g. from spirit level).
93
- lens_pars : dict, optional
94
- Lens parameters, containing: "k1": float, barrel lens distortion parameter (default: 0.),
95
- "c": float, optical center (default: 2.),
96
- "focal_length": float, focal length (default: width of image frame)
97
92
  calibration_video : str, optional
98
93
  local path to video file containing a checkerboard pattern. Must be 9x6 if called directly, otherwise use
99
94
  ``.calibrate_camera`` explicitly and provide ``chessboard_size`` explicitly. When used, an automated camera
@@ -141,13 +136,13 @@ class CameraConfig:
141
136
  if self.is_nadir:
142
137
  # with nadir, no perspective can be constructed, hence, camera matrix and dist coeffs will be set
143
138
  # to default values
144
- self.camera_matrix = cv._get_cam_mtx(self.height, self.width)
139
+ self.camera_matrix = cv.get_cam_mtx(self.height, self.width)
145
140
  self.dist_coeffs = cv.DIST_COEFFS
146
141
  # camera pars are incomplete and need to be derived
147
142
  else:
148
- self.set_intrinsic(camera_matrix=camera_matrix, lens_pars=lens_pars)
143
+ self.set_intrinsic(camera_matrix=camera_matrix)
149
144
  else:
150
- # camera matrix and dist coeffs can also be set hard, this overrules the lens_pars option
145
+ # camera matrix and dist coeffs can also be set hard
151
146
  self.camera_matrix = camera_matrix
152
147
  self.dist_coeffs = dist_coeffs
153
148
  if calibration_video is not None:
@@ -201,6 +196,27 @@ class CameraConfig:
201
196
  def dist_coeffs(self, dist_coeffs):
202
197
  self._dist_coeffs = dist_coeffs.tolist() if isinstance(dist_coeffs, np.ndarray) else dist_coeffs
203
198
 
199
+ @property
200
+ def focal_length(self):
201
+ """Get focal length."""
202
+ if not self.camera_matrix:
203
+ return None
204
+ return self.camera_matrix[0][0]
205
+
206
+ @property
207
+ def k1(self):
208
+ """Get first distortion coefficient."""
209
+ if not self.dist_coeffs:
210
+ return None
211
+ return self.dist_coeffs[0]
212
+
213
+ @property
214
+ def k2(self):
215
+ """Get second distortion coefficient."""
216
+ if not self.dist_coeffs:
217
+ return None
218
+ return self.dist_coeffs[1]
219
+
204
220
  @property
205
221
  def gcps_dest(self):
206
222
  """Get destination coordinates of GCPs.
@@ -855,7 +871,6 @@ class CameraConfig:
855
871
  self,
856
872
  camera_matrix: Optional[List[List]] = None,
857
873
  dist_coeffs: Optional[List[List]] = None,
858
- lens_pars: Optional[Dict[str, float]] = None,
859
874
  ):
860
875
  """Set lens and distortion parameters.
861
876
 
@@ -871,12 +886,13 @@ class CameraConfig:
871
886
  Distortion coefficients to be used for the camera. If not provided, it will use default values or those
872
887
  derived from GCPs if available.
873
888
 
874
- lens_pars : Optional[Dict[str, float]]
875
- Lens parameters to be set. These will override any default settings or those derived from GCPs if provided.
876
-
877
889
  """
878
- # first set a default estimate from pose if 3D gcps are available
879
- self.set_lens_pars() # default parameters use width of frame
890
+ if camera_matrix is not None and dist_coeffs is not None:
891
+ # both are provided by user, so no fitting needed
892
+ self.camera_matrix = camera_matrix
893
+ self.dist_coeffs = dist_coeffs
894
+ return
895
+
880
896
  if hasattr(self, "gcps"):
881
897
  if len(self.gcps["src"]) >= 4:
882
898
  self.camera_matrix, self.dist_coeffs, err = cv.optimize_intrinsic(
@@ -886,36 +902,9 @@ class CameraConfig:
886
902
  self.height,
887
903
  self.width,
888
904
  lens_position=self.lens_position,
905
+ camera_matrix=camera_matrix,
906
+ dist_coeffs=dist_coeffs,
889
907
  )
890
- if lens_pars is not None:
891
- # override with lens parameter set by user
892
- self.set_lens_pars(**lens_pars)
893
- if camera_matrix is not None and dist_coeffs is not None:
894
- # override with
895
- self.camera_matrix = camera_matrix
896
- self.dist_coeffs = dist_coeffs
897
-
898
- def set_lens_pars(self, k1: Optional[float] = 0.0, c: Optional[float] = 2.0, focal_length: Optional[float] = None):
899
- """Set the lens parameters of the given CameraConfig.
900
-
901
- Parameters
902
- ----------
903
- k1 : float, optional
904
- lens curvature [-], zero (default) means no curvature
905
- c : float, optional
906
- optical centre [1/n], where n is the fraction of the lens diameter, 2.0 (default) means in the
907
- centre.
908
- focal_length : float, optional
909
- focal length [mm], typical values could be 2.8, or 4 (default).
910
-
911
-
912
- """
913
- assert isinstance(k1, (int, float)), "k1 must be a float"
914
- assert isinstance(c, (int, float)), "c must be a float"
915
- if focal_length is not None:
916
- assert isinstance(focal_length, (int, float, None)), "f must be a float"
917
- self.dist_coeffs = cv._get_dist_coefs(k1)
918
- self.camera_matrix = cv._get_cam_mtx(self.height, self.width, c=c, focal_length=focal_length)
919
908
 
920
909
  def set_gcps(
921
910
  self, src: List[List], dst: List[List], z_0: float, h_ref: Optional[float] = None, crs: Optional[Any] = None
@@ -986,7 +986,7 @@ class CrossSection:
986
986
  lens_position_xy = self.camera_config.estimate_lens_position()[0:2]
987
987
  dists = [((p.x - lens_position_xy[0]) ** 2 + (p.y - lens_position_xy[1]) ** 2) ** 0.5 for p in points]
988
988
  points = self.get_csl_point(h=h, camera=True, swap_y_coords=swap_y_coords) # find camera positions
989
- x, y = points[np.argmin(dists)].xy
989
+ x, y = points[np.argmax(dists)].xy
990
990
  x, y = float(x[0]), float(y[0])
991
991
 
992
992
  # only plot text in 2D camera perspective at farthest point
@@ -360,7 +360,7 @@ class AoiSelect(BaseSelect):
360
360
  class GcpSelect(BaseSelect):
361
361
  """Selector tool to provide source GCP coordinates to pyOpenRiverCam."""
362
362
 
363
- def __init__(self, img, dst, crs=None, lens_position=None, logger=logging):
363
+ def __init__(self, img, dst, crs=None, camera_matrix=None, dist_coeffs=None, lens_position=None, logger=logging):
364
364
  """Set up interactive GCP selection plot."""
365
365
  super(GcpSelect, self).__init__(img, dst, crs=crs, logger=logger)
366
366
  # make empty plot
@@ -389,6 +389,8 @@ class GcpSelect(BaseSelect):
389
389
  self.ax_geo.legend()
390
390
  self.ax.legend()
391
391
  self.lens_position = lens_position
392
+ self.camera_matrix_input = camera_matrix
393
+ self.dist_coeffs_input = dist_coeffs
392
394
  # add dst coords in the intended CRS
393
395
  if crs is not None and use_cartopy:
394
396
  self.dst_crs = helpers.xyz_transform(self.dst, 4326, crs)
@@ -407,7 +409,14 @@ class GcpSelect(BaseSelect):
407
409
  self.title.set_text("Fitting pose and camera parameters...")
408
410
  self.ax.figure.canvas.draw()
409
411
  src_fit, dst_fit, camera_matrix, dist_coeffs, rvec, tvec, err = cli_utils.get_gcps_optimized_fit(
410
- self.src, self.dst_crs, self.height, self.width, c=2.0, lens_position=self.lens_position
412
+ self.src,
413
+ self.dst_crs,
414
+ self.height,
415
+ self.width,
416
+ c=2.0,
417
+ camera_matrix=self.camera_matrix_input,
418
+ dist_coeffs=self.dist_coeffs_input,
419
+ lens_position=self.lens_position,
411
420
  )
412
421
  self.p_fit.set_data(*list(zip(*src_fit)))
413
422
  self.camera_matrix = camera_matrix
@@ -4,6 +4,7 @@ import hashlib
4
4
  import json
5
5
  import logging
6
6
  import os
7
+ from typing import Optional
7
8
 
8
9
  import click
9
10
  import cv2
@@ -57,15 +58,39 @@ def get_corners_interactive(
57
58
 
58
59
 
59
60
  def get_gcps_interactive(
60
- fn, dst, crs=None, crs_gcps=None, frame_sample=0, lens_position=None, rotation=None, logger=logging
61
+ fn,
62
+ dst,
63
+ crs=None,
64
+ crs_gcps=None,
65
+ frame_sample=0,
66
+ focal_length=None,
67
+ k1=None,
68
+ k2=None,
69
+ lens_position=None,
70
+ rotation=None,
71
+ logger=logging,
61
72
  ):
62
73
  """Select GCP points in interactive display using first selected video frame."""
63
74
  vid = Video(fn, start_frame=frame_sample, end_frame=frame_sample + 1, rotation=rotation)
64
75
  # get first frame
65
76
  frame = vid.get_frame(0, method="rgb")
77
+ # construct camera matrix and distortion coefficients
78
+ # parse items
79
+ camera_matrix, dist_coeffs = parse_lens_params(
80
+ height=frame.shape[0], width=frame.shape[1], focal_length=focal_length, k1=k1, k2=k2
81
+ )
82
+
66
83
  if crs_gcps is not None:
67
84
  dst = helpers.xyz_transform(dst, crs_from=crs_gcps, crs_to=4326)
68
- selector = GcpSelect(frame, dst, crs=crs, lens_position=lens_position, logger=logger)
85
+ selector = GcpSelect(
86
+ frame,
87
+ dst,
88
+ crs=crs,
89
+ camera_matrix=camera_matrix,
90
+ dist_coeffs=dist_coeffs,
91
+ lens_position=lens_position,
92
+ logger=logger,
93
+ )
69
94
  plt.show(block=True)
70
95
  return selector.src, selector.camera_matrix, selector.dist_coeffs
71
96
 
@@ -90,21 +115,16 @@ def get_file_hash(fn):
90
115
  return hash256
91
116
 
92
117
 
93
- def get_gcps_optimized_fit(src, dst, height, width, c=2.0, lens_position=None):
118
+ def get_gcps_optimized_fit(src, dst, height, width, c=2.0, camera_matrix=None, dist_coeffs=None, lens_position=None):
94
119
  """Fit intrinsic and extrinsic parameters on provided set of src and dst points."""
95
120
  # optimize cam matrix and dist coeffs with provided control points
96
121
  if np.array(dst).shape == (4, 2):
97
122
  _dst = np.c_[np.array(dst), np.zeros(4)]
98
123
  else:
99
124
  _dst = np.array(dst)
125
+ print(camera_matrix)
100
126
  camera_matrix, dist_coeffs, err = cv.optimize_intrinsic(
101
- src,
102
- _dst,
103
- height,
104
- width,
105
- c=c,
106
- lens_position=lens_position,
107
- # dist_coeffs=cv.DIST_COEFFS
127
+ src, _dst, height, width, c=c, lens_position=lens_position, camera_matrix=camera_matrix, dist_coeffs=dist_coeffs
108
128
  )
109
129
  # once optimized, solve the perspective, and estimate the GCP locations with the perspective rot/trans
110
130
  coord_mean = np.array(_dst).mean(axis=0)
@@ -152,6 +172,29 @@ def parse_corners(ctx, param, value):
152
172
  return [[int(x), int(y)] for x, y in corners]
153
173
 
154
174
 
175
+ def parse_lens_params(
176
+ height: int,
177
+ width: int,
178
+ focal_length: Optional[float] = None,
179
+ k1: Optional[float] = None,
180
+ k2: Optional[float] = None,
181
+ ):
182
+ """Parse lens parameters to camera matrix and distortion coefficients vector."""
183
+ if focal_length is not None:
184
+ camera_matrix = cv.get_cam_mtx(height, width, c=2.0, focal_length=focal_length)
185
+ else:
186
+ camera_matrix = None
187
+ if k1 is not None or k2 is not None:
188
+ dist_coeffs = cv.DIST_COEFFS.copy()
189
+ if k1 is not None:
190
+ dist_coeffs[0][0] = k1
191
+ if k2 is not None:
192
+ dist_coeffs[1][0] = k2
193
+ else:
194
+ dist_coeffs = None
195
+ return camera_matrix, dist_coeffs
196
+
197
+
155
198
  def validate_file(ctx, param, value):
156
199
  """Validate existence of file."""
157
200
  if value is not None:
@@ -283,6 +326,13 @@ def read_shape_as_gdf(fn=None, geojson=None, gdf=None):
283
326
  else:
284
327
  gdf = gpd.read_file(fn)
285
328
  crs = gdf.crs if hasattr(gdf, "crs") else None
329
+ # also read raw json, and check if crs attribute exists
330
+ with open(fn, "r") as f:
331
+ raw_json = json.load(f)
332
+ if "crs" not in raw_json:
333
+ # override the crs
334
+ crs = None
335
+ gdf = gdf.set_crs(None, allow_override=True)
286
336
  # check if all geometries are points
287
337
  assert all([isinstance(geom, Point) for geom in gdf.geometry]), (
288
338
  "shapefile may only contain geometries of type " '"Point"'
@@ -99,6 +99,17 @@ def cli(ctx, info, license):
99
99
  help="Coordinate reference system in which destination GCP points (--dst) are measured",
100
100
  )
101
101
  @click.option("--resolution", type=float, help="Target resolution [m] for ortho-projection.")
102
+ @click.option("--focal_length", type=float, help="Focal length [pix] of lens.")
103
+ @click.option(
104
+ "--k1",
105
+ type=float,
106
+ help="First lens radial distortion coefficient k1 [-]. See also https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html",
107
+ )
108
+ @click.option(
109
+ "--k2",
110
+ type=float,
111
+ help="Second lens radial distortion coefficient k2 [-]. See also https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html",
112
+ )
102
113
  @click.option(
103
114
  "--window_size", type=int, help="Target window size [px] for interrogation window for Particle Image Velocimetry"
104
115
  )
@@ -148,6 +159,9 @@ def camera_config(
148
159
  z_0: Optional[float],
149
160
  h_ref: Optional[float],
150
161
  crs_gcps: Optional[Union[str, int]],
162
+ focal_length: Optional[float],
163
+ k1: Optional[float],
164
+ k2: Optional[float],
151
165
  resolution: Optional[float],
152
166
  window_size: Optional[int],
153
167
  lens_position: Optional[List[float]],
@@ -207,6 +221,9 @@ def camera_config(
207
221
  crs=crs,
208
222
  crs_gcps=crs_gcps,
209
223
  frame_sample=frame_sample,
224
+ focal_length=focal_length,
225
+ k1=k1,
226
+ k2=k2,
210
227
  lens_position=lens_position,
211
228
  rotation=rotation,
212
229
  logger=logger,
@@ -297,7 +314,7 @@ def camera_config(
297
314
  type=click.Path(exists=True, resolve_path=True, dir_okay=False, file_okay=True),
298
315
  help="Cross section file (*.geojson). If you provide this, you may add water level retrieval settings to the"
299
316
  " recipe section video: water_level: For more information see"
300
- " https://localdevices.github.io/pyorc/user-guide/video/index.html",
317
+ " [PyORC docs](https://localdevices.github.io/pyorc/user-guide/video/index.html)",
301
318
  callback=cli_utils.parse_cross_section_gdf,
302
319
  required=False,
303
320
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  import copy
4
4
  import os
5
+ import warnings
5
6
 
6
7
  import cv2
7
8
  import numpy as np
@@ -151,7 +152,7 @@ def _get_dist_coefs(k1):
151
152
  return dist
152
153
 
153
154
 
154
- def _get_cam_mtx(height, width, c=2.0, focal_length=None):
155
+ def get_cam_mtx(height, width, c=2.0, focal_length=None):
155
156
  """Compute camera matrix based on the given parameters for height, width, scaling factor, and focal length.
156
157
 
157
158
  Parameters
@@ -958,7 +959,7 @@ def get_polygon_pixels(img, pol, reverse_y=False):
958
959
  return img[mask == 255]
959
960
 
960
961
 
961
- def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
962
+ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None, camera_matrix=None, dist_coeffs=None):
962
963
  """Optimize the intrinsic parameters of a camera model.
963
964
 
964
965
  The function finds optimal intrinsic camera parameters, including focal length and distortion coefficients, by
@@ -980,6 +981,11 @@ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
980
981
  Center parameter of the camera matrix.
981
982
  lens_position : array_like, optional
982
983
  The assumed position of the lens in the 3D space.
984
+ camera_matrix : Optional[List[List]]
985
+ Predefined camera matrix. If not provided focal length will be fitted and camera matrix returned
986
+ dist_coeffs : Optional[List[List]]
987
+ Distortion coefficients to be used for the camera. If not provided, the first two (k1, k2)
988
+ distortion coefficients are fitted on data.
983
989
 
984
990
  Returns
985
991
  -------
@@ -989,7 +995,7 @@ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
989
995
 
990
996
  """
991
997
 
992
- def error_intrinsic(x, src, dst, height, width, c=2.0, lens_position=None, dist_coeffs=DIST_COEFFS):
998
+ def error_intrinsic(x, src, dst, height, width, c=2.0, lens_position=None, camera_matrix=None, dist_coeffs=None):
993
999
  """Compute the reprojection error for the intrinsic parameters of a camera model.
994
1000
 
995
1001
  This function optimizes for the focal length and first two distortion coefficients based on the source and
@@ -1014,6 +1020,8 @@ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
1014
1020
  center parameter of camera matrix.
1015
1021
  lens_position : array_like, optional
1016
1022
  The assumed position of the lens in the 3D space.
1023
+ camera_matrix : array_like, optional
1024
+ camera matrix [3x3]
1017
1025
  dist_coeffs : array_like, optional
1018
1026
  Distortion coefficients.
1019
1027
 
@@ -1024,24 +1032,43 @@ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
1024
1032
  the camera position error if the lens position is provided.
1025
1033
 
1026
1034
  """
1035
+ param_nr = 0
1036
+ # set the parameters
1037
+ if camera_matrix is None:
1038
+ f = x[param_nr] * width
1039
+ camera_matrix_sample = get_cam_mtx(height, width, c=c, focal_length=f)
1040
+ param_nr += 1
1041
+ else:
1042
+ # take the existing camera matrix
1043
+ camera_matrix_sample = camera_matrix.copy()
1044
+ if dist_coeffs is None:
1045
+ dist_coeffs_sample = DIST_COEFFS.copy()
1046
+ k1 = x[param_nr]
1047
+ k2 = x[param_nr + 1]
1048
+ dist_coeffs_sample[0][0] = k1
1049
+ dist_coeffs_sample[1][0] = k2
1050
+ else:
1051
+ # take the existing distortion coefficients
1052
+ dist_coeffs_sample = dist_coeffs.copy()
1053
+ k1 = dist_coeffs_sample[0][0]
1054
+ k2 = dist_coeffs_sample[1][0]
1055
+
1056
+ # initialize error
1027
1057
  err = 100
1028
1058
  cam_err = None
1029
- f = x[0] * width # only one parameter to optimize for now, can easily be extended!
1030
- dist_coeffs[0][0] = float(x[1])
1031
- dist_coeffs[1][0] = float(x[2])
1032
- # dist_coeffs[4][0] = float(x[3])
1033
- # dist_coeffs[3][0] = float(x[4])
1059
+
1060
+ # reduce problem space to centered around gcp average
1034
1061
  coord_mean = np.array(dst).mean(axis=0)
1035
1062
  _dst = np.float64(np.array(dst) - coord_mean)
1036
1063
  zs = np.zeros(4) if len(_dst[0]) == 2 else np.array(_dst)[:, -1]
1037
1064
  if lens_position is not None:
1038
1065
  _lens_pos = np.array(lens_position) - coord_mean
1039
1066
 
1040
- camera_matrix = _get_cam_mtx(height, width, c=c, focal_length=f)
1041
- success, rvec, tvec = solvepnp(_dst, src, camera_matrix, dist_coeffs)
1067
+ # camera_matrix = _get_cam_mtx(height, width, c=c, focal_length=f)
1068
+ success, rvec, tvec = solvepnp(_dst, src, camera_matrix_sample, dist_coeffs_sample)
1042
1069
  if success:
1043
1070
  # estimate destination locations from pose
1044
- dst_est = unproject_points(src, zs, rvec, tvec, camera_matrix, dist_coeffs)
1071
+ dst_est = unproject_points(src, zs, rvec, tvec, camera_matrix_sample, dist_coeffs_sample)
1045
1072
  # src_est = np.array([list(point[0]) for point in src_est])
1046
1073
  dist_xy = np.array(_dst)[:, 0:2] - np.array(dst_est)[:, 0:2]
1047
1074
  dist = (dist_xy**2).sum(axis=1) ** 0.5
@@ -1054,27 +1081,51 @@ def optimize_intrinsic(src, dst, height, width, c=2.0, lens_position=None):
1054
1081
  err = float(0.1 * cam_err + gcp_err) if cam_err is not None else gcp_err
1055
1082
  return err # assuming gcp pixel distance is about 5 cm
1056
1083
 
1057
- if len(dst) == 4:
1058
- bnds_k1 = (-0.0, 0.0)
1059
- bnds_k2 = (-0.0, 0.0)
1084
+ # determine optimization bounds
1085
+ bounds = []
1086
+ if camera_matrix is not None and dist_coeffs is not None:
1087
+ # both are already known, so nothing to do
1088
+ return camera_matrix, dist_coeffs, None
1089
+ if camera_matrix is None:
1090
+ bounds.append([float(0.25), float(2)])
1091
+ if len(dst) > 4 and dist_coeffs is None:
1092
+ bounds.append([-0.9, 0.9]) # k1
1093
+ bounds.append([-0.5, 0.5]) # k2
1060
1094
  else:
1061
- # bnds_k1 = (-0.2501, -0.25)
1062
- bnds_k1 = (-0.9, 0.9)
1063
- bnds_k2 = (-0.5, 0.5)
1064
- # bnds_k1 = (-0.0, 0.0)
1065
- # bnds_k2 = (-0.0, 0.0)
1095
+ # set a warning if dist_coeffs is provided without sufficient ground control
1096
+ if dist_coeffs:
1097
+ warnings.warn(
1098
+ "You are trying to optimize distortion coefficients with only 4 GCPs. "
1099
+ "This would lead to overfitting, setting distortion coefficients to zero.",
1100
+ stacklevel=2,
1101
+ )
1102
+ dist_coeffs = DIST_COEFFS.copy()
1103
+ # if len(dst) == 4:
1104
+ # bnds_k1 = (-0.0, 0.0)
1105
+ # bnds_k2 = (-0.0, 0.0)
1106
+ # else:
1107
+ # # bnds_k1 = (-0.2501, -0.25)
1108
+ # bnds_k1 = (-0.9, 0.9)
1109
+ # bnds_k2 = (-0.5, 0.5)
1110
+ # bnds_k1 = (-0.0, 0.0)
1111
+ # bnds_k2 = (-0.0, 0.0)
1066
1112
  opt = optimize.differential_evolution(
1067
1113
  error_intrinsic,
1068
1114
  # bounds=[(float(0.25), float(2)), bnds_k1],#, (-0.5, 0.5)],
1069
- bounds=[(float(0.25), float(2)), bnds_k1, bnds_k2],
1115
+ bounds=bounds,
1070
1116
  # bounds=[(1710./width, 1714./width), bnds_k1, bnds_k2],
1071
- args=(src, dst, height, width, c, lens_position, DIST_COEFFS),
1117
+ args=(src, dst, height, width, c, lens_position, camera_matrix, dist_coeffs),
1072
1118
  atol=0.001, # one mm
1073
1119
  )
1074
- camera_matrix = _get_cam_mtx(height, width, focal_length=opt.x[0] * width)
1075
- dist_coeffs = DIST_COEFFS
1076
- dist_coeffs[0][0] = opt.x[1]
1077
- dist_coeffs[1][0] = opt.x[2]
1120
+ param_nr = 0
1121
+ if camera_matrix is None:
1122
+ camera_matrix = get_cam_mtx(height, width, focal_length=opt.x[param_nr] * width)
1123
+ # move to next parameter
1124
+ param_nr += 1
1125
+ if dist_coeffs is None:
1126
+ dist_coeffs = DIST_COEFFS
1127
+ dist_coeffs[0][0] = opt.x[param_nr]
1128
+ dist_coeffs[1][0] = opt.x[param_nr + 1]
1078
1129
  # dist_coeffs[4][0] = opt.x[3]
1079
1130
  # dist_coeffs[3][0] = opt.x[4]
1080
1131
  # print(f"CAMERA MATRIX: {camera_matrix}")
@@ -166,13 +166,14 @@ def camera_matrix():
166
166
 
167
167
 
168
168
  @pytest.fixture()
169
- def cam_config(gcps, lens_position, lens_pars, corners):
169
+ def cam_config(gcps, lens_position, dist_coeffs, camera_matrix, corners):
170
170
  return pyorc.CameraConfig(
171
171
  height=1080,
172
172
  width=1920,
173
173
  gcps=gcps,
174
174
  lens_position=lens_position,
175
- lens_pars=lens_pars,
175
+ dist_coeffs=dist_coeffs,
176
+ camera_matrix=camera_matrix,
176
177
  corners=corners,
177
178
  window_size=25,
178
179
  resolution=0.01,
@@ -175,13 +175,6 @@ def test_set_bbox_from_corners(cur_cam_config, cur_corners, cur_bbox, request):
175
175
  assert np.allclose(cur_cam_config.bbox.bounds, cur_bbox.bounds)
176
176
 
177
177
 
178
- def test_set_lens_pars(cam_config, lens_pars, camera_matrix, dist_coeffs):
179
- # check if this works
180
- cam_config.set_lens_pars(**lens_pars)
181
- assert np.allclose(cam_config.camera_matrix, camera_matrix)
182
- assert np.allclose(cam_config.dist_coeffs, dist_coeffs)
183
-
184
-
185
178
  def test_set_gcps(cam_config, gcps):
186
179
  cam_config.set_gcps(**gcps)
187
180
  assert cam_config.gcps == gcps
@@ -203,13 +196,15 @@ def test_estimate_lens_position(cam_config):
203
196
  assert np.allclose(lens_pos, [6.42731099e05, 8.30429131e06, 1.18996749e03])
204
197
 
205
198
 
206
- def test_optimize_intrinsic(cam_config):
199
+ @pytest.mark.parametrize("cur_cam_config", ["cam_config", "cam_config_6gcps"])
200
+ def test_optimize_intrinsic(cur_cam_config, request):
201
+ cur_cam_config = request.getfixturevalue(cur_cam_config)
207
202
  camera_matrix, dist_coeffs, err = cv.optimize_intrinsic(
208
- cam_config.gcps["src"],
209
- cam_config.gcps_dest,
210
- cam_config.height,
211
- cam_config.width,
212
- lens_position=cam_config.lens_position,
203
+ cur_cam_config.gcps["src"],
204
+ cur_cam_config.gcps_dest,
205
+ cur_cam_config.height,
206
+ cur_cam_config.width,
207
+ lens_position=cur_cam_config.lens_position,
213
208
  )
214
209
  print(camera_matrix, dist_coeffs, err)
215
210
 
@@ -65,6 +65,8 @@ def test_cli_cam_config_video(cli_obj, vid_file, gcps_src, gcps_dst, lens_positi
65
65
  json.dumps(lens_position),
66
66
  "--resolution",
67
67
  "0.03",
68
+ "--focal_length",
69
+ "1500",
68
70
  "--window_size",
69
71
  "25",
70
72
  "--corners",
File without changes
File without changes