geopic-tag-reader 1.3.3__py3-none-any.whl → 1.4.0__py3-none-any.whl
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.
- geopic_tag_reader/__init__.py +1 -1
- geopic_tag_reader/camera.py +118 -30
- geopic_tag_reader/cameras.csv +3775 -0
- geopic_tag_reader/main.py +3 -0
- geopic_tag_reader/reader.py +80 -21
- geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.mo +0 -0
- geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.po +209 -0
- geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
- geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.po +52 -39
- geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.mo +0 -0
- geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.po +207 -0
- geopic_tag_reader/translations/geopic_tag_reader.pot +48 -35
- geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.mo +0 -0
- geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.po +56 -27
- {geopic_tag_reader-1.3.3.dist-info → geopic_tag_reader-1.4.0.dist-info}/METADATA +1 -1
- {geopic_tag_reader-1.3.3.dist-info → geopic_tag_reader-1.4.0.dist-info}/RECORD +19 -14
- {geopic_tag_reader-1.3.3.dist-info → geopic_tag_reader-1.4.0.dist-info}/LICENSE +0 -0
- {geopic_tag_reader-1.3.3.dist-info → geopic_tag_reader-1.4.0.dist-info}/WHEEL +0 -0
- {geopic_tag_reader-1.3.3.dist-info → geopic_tag_reader-1.4.0.dist-info}/entry_points.txt +0 -0
geopic_tag_reader/__init__.py
CHANGED
geopic_tag_reader/camera.py
CHANGED
|
@@ -1,30 +1,123 @@
|
|
|
1
1
|
from typing import Optional, Dict, List
|
|
2
|
+
import importlib.resources
|
|
3
|
+
import csv
|
|
4
|
+
from dataclasses import dataclass
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
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,
|
|
25
46
|
}
|
|
26
47
|
|
|
27
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
|
+
|
|
28
121
|
def is_360(make: Optional[str] = None, model: Optional[str] = None, width: Optional[str] = None, height: Optional[str] = None) -> bool:
|
|
29
122
|
"""
|
|
30
123
|
Checks if given camera is equirectangular (360°) based on its make, model and dimensions (width, height).
|
|
@@ -44,17 +137,12 @@ def is_360(make: Optional[str] = None, model: Optional[str] = None, width: Optio
|
|
|
44
137
|
"""
|
|
45
138
|
|
|
46
139
|
# Check make and model are defined
|
|
47
|
-
|
|
140
|
+
camera = find_camera(make, model)
|
|
141
|
+
if not camera:
|
|
48
142
|
return False
|
|
49
143
|
|
|
50
144
|
# Check width and height are equirectangular
|
|
51
145
|
if not ((width is None or height is None) or int(width) == 2 * int(height)):
|
|
52
146
|
return False
|
|
53
147
|
|
|
54
|
-
|
|
55
|
-
matchMake = next((m for m in EQUIRECTANGULAR_MODELS.keys() if make.lower() == m.lower()), None)
|
|
56
|
-
if matchMake is None:
|
|
57
|
-
return False
|
|
58
|
-
|
|
59
|
-
# Find model
|
|
60
|
-
return any(model.lower().startswith(m.lower()) for m in EQUIRECTANGULAR_MODELS[matchMake])
|
|
148
|
+
return camera.is_360
|