geopic-tag-reader 1.3.3__tar.gz → 1.4.0__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 (58) hide show
  1. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/CHANGELOG.md +9 -1
  2. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/PKG-INFO +1 -1
  3. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/develop.md +14 -0
  4. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/quality_score.md +5 -5
  5. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/__init__.py +1 -1
  6. geopic_tag_reader-1.4.0/geopic_tag_reader/camera.py +148 -0
  7. geopic_tag_reader-1.4.0/geopic_tag_reader/cameras.csv +3775 -0
  8. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/main.py +3 -0
  9. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/reader.py +80 -21
  10. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  11. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.po +209 -0
  12. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  13. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.po +52 -39
  14. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  15. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.po +207 -0
  16. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/geopic_tag_reader.pot +48 -35
  17. geopic_tag_reader-1.4.0/geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  18. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.po +56 -27
  19. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/pyproject.toml +1 -1
  20. geopic_tag_reader-1.3.3/geopic_tag_reader/camera.py +0 -60
  21. geopic_tag_reader-1.3.3/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  22. geopic_tag_reader-1.3.3/geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  23. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/.gitignore +0 -0
  24. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/.gitlab-ci.yml +0 -0
  25. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/.pre-commit-config.yaml +0 -0
  26. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/CODE_OF_CONDUCT.md +0 -0
  27. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/LICENSE +0 -0
  28. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/Makefile +0 -0
  29. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/README.md +0 -0
  30. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/images/quality_score.png +0 -0
  31. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/images/quality_score_viewer.png +0 -0
  32. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/index.md +0 -0
  33. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/install.md +0 -0
  34. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/tech/api_reference.md +0 -0
  35. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/docs/tech/cli.md +0 -0
  36. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/i18n.py +0 -0
  37. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/model.py +0 -0
  38. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/py.typed +0 -0
  39. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/sequence.py +0 -0
  40. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  41. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.po +0 -0
  42. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  43. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.po +0 -0
  44. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  45. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.po +0 -0
  46. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  47. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.po +0 -0
  48. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/hu/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  49. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/hu/LC_MESSAGES/geopic_tag_reader.po +0 -0
  50. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/ko/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  51. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/ko/LC_MESSAGES/geopic_tag_reader.po +0 -0
  52. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/nl/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  53. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/nl/LC_MESSAGES/geopic_tag_reader.po +0 -0
  54. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/pl/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  55. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/translations/pl/LC_MESSAGES/geopic_tag_reader.po +0 -0
  56. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/geopic_tag_reader/writer.py +0 -0
  57. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/mkdocs.yml +0 -0
  58. {geopic_tag_reader-1.3.3 → geopic_tag_reader-1.4.0}/pytest.ini +0 -0
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.4.0] - 2025-01-06
11
+
12
+ ### Added
13
+
14
+ - Camera general metadata (sensor width, 360° devices, GPS average accuracy) is made available through a single CSV file named `cameras.csv`. This make info centralized and will lighten camera info in Panoramax API.
15
+ - New fields are offered in GeoPicTags: `sensor_width, gps_accuracy, field_of_view`
16
+
10
17
  ## [1.3.3] - 2024-11-24
11
18
 
12
19
  ### Added
@@ -244,7 +251,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
244
251
 
245
252
  - EXIF tag reading methods extracted from [Panoramax/GeoVisio API](https://gitlab.com/panoramax/server/api)
246
253
 
247
- [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.3...main
254
+ [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.4.0...main
255
+ [1.4.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.3...1.4.0
248
256
  [1.3.3]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.2...1.3.3
249
257
  [1.3.2]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.1...1.3.2
250
258
  [1.3.1]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.0...1.3.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: geopic-tag-reader
3
- Version: 1.3.3
3
+ Version: 1.4.0
4
4
  Summary: GeoPicTagReader
5
5
  Author-email: Adrien PAVIE <panieravide@riseup.net>
6
6
  Requires-Python: >=3.8
@@ -69,6 +69,20 @@ pip install -e .[dev]
69
69
  pre-commit install
70
70
  ```
71
71
 
72
+ ### Add camera information
73
+
74
+ Many fallback information are made available through the `geopic_tag_reader/cameras.csv` file, which is structured this way:
75
+
76
+ - `make`: name of camera builder (as noted in `Make` EXIF field in picture)
77
+ - `model`: name of camera model (as noted in `Model` EXIF field in picture)
78
+ - `sensor_width`: the width of camera sensor (in millimeters)
79
+ - `is_360`: set to `1` if camera is a 360° device, left empty or set to `0` otherwise
80
+ - `gps_accuracy`: average GPS accuracy (in meters) if the camera has an embed GPS and pictures don't contain GPS horizontal positioning error in their metadata
81
+
82
+ If a camera is missing in this list, feel free to offer a pull request or send us these information through an issue or an email so we can expand the list.
83
+
84
+ Also note that more generic information about GPS accuracy from various camera brands is present in `geopic_tag_reader/camera.py` file (`GPS_ACCURACY_MAKE` constant). This allows us to say _"all smartphones from this brand have this GPS accuracy"_, feel free to contact us if we missed some brands.
85
+
72
86
  ## Make a release
73
87
 
74
88
  ```bash
@@ -58,11 +58,11 @@ This indicator allows to have an idea of how high the picture resolution is, ind
58
58
 
59
59
  | Grade | Density for 360° | Density for non 360° |
60
60
  | ----- | ---------------- | --------------------------- |
61
- | 5/5 | >= 38 px/deg | Not possible |
62
- | 4/5 | >= 20 px/deg | >= 60 px/deg |
63
- | 3/5 | >= 15px/deg | >= 38px/deg |
64
- | 2/5 | < 15px/deg | >= 15 px/deg |
65
- | 1/5 | Unknown value | < 15 px/deg or unknown value |
61
+ | 5/5 | >= 30 px/deg | Not possible |
62
+ | 4/5 | >= 15 px/deg | >= 30 px/deg |
63
+ | 3/5 | < 15px/deg | >= 15px/deg |
64
+ | 2/5 | Not possible | >= 10 px/deg |
65
+ | 1/5 | Unknown value | < 10 px/deg or unknown value |
66
66
 
67
67
  !!! info
68
68
 
@@ -2,4 +2,4 @@
2
2
  GeoPicTagReader
3
3
  """
4
4
 
5
- __version__ = "1.3.3"
5
+ __version__ = "1.4.0"
@@ -0,0 +1,148 @@
1
+ from typing import Optional, Dict, List
2
+ import importlib.resources
3
+ import csv
4
+ from dataclasses import dataclass
5
+
6
+
7
+ # Per-make GPS estimated accuracy (in meters)
8
+ GPS_ACCURACY_MAKE = {
9
+ # Diff GPS
10
+ "stfmani": 2,
11
+ "trimble": 2,
12
+ "imajing": 2,
13
+ # Good GPS
14
+ "gopro": 4,
15
+ "insta360": 4,
16
+ "garmin": 4,
17
+ "viofo": 4,
18
+ "xiaoyi": 4,
19
+ "blackvue": 4,
20
+ "tectectec": 4,
21
+ "arashi vision": 4,
22
+ # Smartphone GPS
23
+ "samsung": 5,
24
+ "xiaomi": 5,
25
+ "huawei": 5,
26
+ "ricoh": 5,
27
+ "lenovo": 5,
28
+ "motorola": 5,
29
+ "oneplus": 5,
30
+ "apple": 5,
31
+ "google": 5,
32
+ "sony": 5,
33
+ "wiko": 5,
34
+ "asus": 5,
35
+ "cubot": 5,
36
+ "lge": 5,
37
+ "fairphone": 5,
38
+ "realme": 5,
39
+ "symphony": 5,
40
+ "crosscall": 5,
41
+ "htc": 5,
42
+ "homtom": 5,
43
+ "hmd global": 5,
44
+ "oppo": 5,
45
+ "ulefone": 5,
46
+ }
47
+
48
+
49
+ @dataclass
50
+ class CameraMetadata:
51
+ is_360: bool = False
52
+ sensor_width: Optional[float] = None
53
+ gps_accuracy: Optional[float] = None
54
+
55
+
56
+ CAMERAS: Dict[str, Dict[str, CameraMetadata]] = {} # Make -> Model -> Metadata
57
+
58
+
59
+ def get_cameras() -> Dict[str, Dict[str, CameraMetadata]]:
60
+ """
61
+ Retrieve general metadata about cameras
62
+ """
63
+
64
+ if len(CAMERAS) > 0:
65
+ return CAMERAS
66
+
67
+ # Cameras.csv file is a composite of various sources:
68
+ # - Wikipedia's list of 360° cameras ( https://en.wikipedia.org/wiki/List_of_omnidirectional_(360-degree)_cameras )
69
+ # - OpenSfM's sensor widths ( https://github.com/mapillary/OpenSfM/blob/main/opensfm/data/sensor_data.json )
70
+
71
+ with importlib.resources.open_text("geopic_tag_reader", "cameras.csv") as camerasCsv:
72
+ camerasReader = csv.DictReader(camerasCsv, delimiter=";")
73
+ for camera in camerasReader:
74
+ make = camera["make"].lower()
75
+ model = camera["model"].lower()
76
+ sensorWidth = float(camera["sensor_width"]) if camera["sensor_width"] != "" else None
77
+ is360 = camera["is_360"] == "1"
78
+ gpsAccuracy = float(camera["gps_accuracy"]) if camera["gps_accuracy"] != "" else None
79
+
80
+ # Override GPS Accuracy with Make one if necessary
81
+ if gpsAccuracy is None and make in GPS_ACCURACY_MAKE:
82
+ gpsAccuracy = GPS_ACCURACY_MAKE[make]
83
+
84
+ # Append to general list
85
+ if not make in CAMERAS:
86
+ CAMERAS[make] = {}
87
+
88
+ CAMERAS[make][model] = CameraMetadata(is360, sensorWidth, gpsAccuracy)
89
+
90
+ return CAMERAS
91
+
92
+
93
+ def find_camera(make: Optional[str] = None, model: Optional[str] = None) -> Optional[CameraMetadata]:
94
+ """
95
+ Finds camera metadata based on make and model.
96
+
97
+ >>> find_camera()
98
+
99
+ >>> find_camera("GoPro")
100
+
101
+ >>> find_camera("GoPro", "Max")
102
+ CameraMetadata(is_360=True, sensor_width=6.17, gps_accuracy=4)
103
+ >>> find_camera("GoPro", "Max 360")
104
+ CameraMetadata(is_360=True, sensor_width=6.17, gps_accuracy=4)
105
+ """
106
+
107
+ # Check make and model are defined
108
+ if not make or not model:
109
+ return None
110
+
111
+ # Find make
112
+ cameras = get_cameras()
113
+ matchMake = next((m for m in cameras.keys() if m in make.lower()), None)
114
+ if matchMake is None:
115
+ return None
116
+
117
+ # Find model
118
+ return next((cameras[matchMake][matchModel] for matchModel in cameras[matchMake].keys() if model.lower().startswith(matchModel)), None)
119
+
120
+
121
+ def is_360(make: Optional[str] = None, model: Optional[str] = None, width: Optional[str] = None, height: Optional[str] = None) -> bool:
122
+ """
123
+ Checks if given camera is equirectangular (360°) based on its make, model and dimensions (width, height).
124
+
125
+ >>> is_360()
126
+ False
127
+ >>> is_360("GoPro")
128
+ False
129
+ >>> is_360("GoPro", "Max 360")
130
+ True
131
+ >>> is_360("GoPro", "Max 360", "2048", "1024")
132
+ True
133
+ >>> is_360("GoPro", "Max 360", "1024", "768")
134
+ False
135
+ >>> is_360("RICOH", "THETA S", "5376", "2688")
136
+ True
137
+ """
138
+
139
+ # Check make and model are defined
140
+ camera = find_camera(make, model)
141
+ if not camera:
142
+ return False
143
+
144
+ # Check width and height are equirectangular
145
+ if not ((width is None or height is None) or int(width) == 2 * int(height)):
146
+ return False
147
+
148
+ return camera.is_360