Skip to content

Commit 7ebf1be

Browse files
authored
Merge pull request #304 from zivid/MISC-2024-12-05-implement-measure-scene-conditions
Implement Camera.measure_scene_conditions
2 parents d7e8cb7 + ec5fede commit 7ebf1be

File tree

10 files changed

+221
-1
lines changed

10 files changed

+221
-1
lines changed

.flake8

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ per-file-ignores =
3131
camera_intrinsics.py:D101,D102,D106,D107
3232
frame_info.py:D101,D102,D106,D107
3333
network_configuration.py:D101,D102,D106,D107
34+
scene_conditions.py:D101,D102,D106,D107

continuous-integration/code-generation/datamodel_frontend_generator.py

+1
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ def generate_all_datamodels(dest_dir: Path) -> None:
839839
),
840840
(_zivid.CameraIntrinsics, "camera_intrinsics.py", []),
841841
(_zivid.NetworkConfiguration, "network_configuration.py", []),
842+
(_zivid.SceneConditions, "scene_conditions.py", []),
842843
]:
843844
_generate_datamodel_frontend(
844845
internal_class=internal_class,

modules/_zivid/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
infield_correction,
6767
Matrix4x4,
6868
NetworkConfiguration,
69+
SceneConditions,
6970
data_model,
7071
PixelMapping,
7172
projection,

modules/zivid/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
from zivid.camera_intrinsics import CameraIntrinsics
2626
from zivid.matrix4x4 import Matrix4x4
2727
from zivid.network_configuration import NetworkConfiguration
28+
from zivid.scene_conditions import SceneConditions

modules/zivid/camera.py

+16
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
_to_internal_network_configuration,
1111
_to_network_configuration,
1212
)
13+
from zivid.scene_conditions import _to_scene_conditions
1314
from zivid.settings import Settings, _to_internal_settings
1415
from zivid.settings2d import Settings2D, _to_internal_settings2d
1516

@@ -177,6 +178,21 @@ def release(self):
177178
else:
178179
impl.release()
179180

181+
def measure_scene_conditions(self):
182+
"""Measure and analyze the conditions of the scene.
183+
184+
The returned value will report if noticeable ambient light flicker indicative of a 50 Hz or 60 Hz power grid
185+
was detected. If light flicker is detected in the scene, it is recommended to use capture settings that are
186+
optimized for that power grid frequency.
187+
188+
`measure_scene_conditions` will raise a RuntimeException if the camera status (see `CameraState.Status`) is not
189+
"connected".
190+
191+
Returns:
192+
The current scene conditions.
193+
"""
194+
return _to_scene_conditions(self.__impl.measure_scene_conditions())
195+
180196
def __enter__(self):
181197
return self
182198

modules/zivid/scene_conditions.py

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"""Auto generated, do not edit."""
2+
3+
# pylint: disable=too-many-lines,protected-access,too-few-public-methods,too-many-arguments,line-too-long,missing-function-docstring,missing-class-docstring,redefined-builtin,too-many-branches,too-many-boolean-expressions
4+
import _zivid
5+
6+
7+
class SceneConditions:
8+
9+
class AmbientLight:
10+
11+
class FlickerClassification:
12+
13+
grid50hz = "grid50hz"
14+
grid60hz = "grid60hz"
15+
noFlicker = "noFlicker"
16+
unknownFlicker = "unknownFlicker"
17+
18+
_valid_values = {
19+
"grid50hz": _zivid.SceneConditions.AmbientLight.FlickerClassification.grid50hz,
20+
"grid60hz": _zivid.SceneConditions.AmbientLight.FlickerClassification.grid60hz,
21+
"noFlicker": _zivid.SceneConditions.AmbientLight.FlickerClassification.noFlicker,
22+
"unknownFlicker": _zivid.SceneConditions.AmbientLight.FlickerClassification.unknownFlicker,
23+
}
24+
25+
@classmethod
26+
def valid_values(cls):
27+
return list(cls._valid_values.keys())
28+
29+
def __init__(
30+
self,
31+
flicker_classification=_zivid.SceneConditions.AmbientLight.FlickerClassification().value,
32+
):
33+
34+
if isinstance(
35+
flicker_classification,
36+
_zivid.SceneConditions.AmbientLight.FlickerClassification.enum,
37+
):
38+
self._flicker_classification = (
39+
_zivid.SceneConditions.AmbientLight.FlickerClassification(
40+
flicker_classification
41+
)
42+
)
43+
elif isinstance(flicker_classification, str):
44+
self._flicker_classification = (
45+
_zivid.SceneConditions.AmbientLight.FlickerClassification(
46+
self.FlickerClassification._valid_values[flicker_classification]
47+
)
48+
)
49+
else:
50+
raise TypeError(
51+
"Unsupported type, expected: str, got {value_type}".format(
52+
value_type=type(flicker_classification)
53+
)
54+
)
55+
56+
@property
57+
def flicker_classification(self):
58+
if self._flicker_classification.value is None:
59+
return None
60+
for key, internal_value in self.FlickerClassification._valid_values.items():
61+
if internal_value == self._flicker_classification.value:
62+
return key
63+
raise ValueError(
64+
"Unsupported value {value}".format(value=self._flicker_classification)
65+
)
66+
67+
@flicker_classification.setter
68+
def flicker_classification(self, value):
69+
if isinstance(value, str):
70+
self._flicker_classification = (
71+
_zivid.SceneConditions.AmbientLight.FlickerClassification(
72+
self.FlickerClassification._valid_values[value]
73+
)
74+
)
75+
elif isinstance(
76+
value, _zivid.SceneConditions.AmbientLight.FlickerClassification.enum
77+
):
78+
self._flicker_classification = (
79+
_zivid.SceneConditions.AmbientLight.FlickerClassification(value)
80+
)
81+
else:
82+
raise TypeError(
83+
"Unsupported type, expected: str, got {value_type}".format(
84+
value_type=type(value)
85+
)
86+
)
87+
88+
def __eq__(self, other):
89+
if self._flicker_classification == other._flicker_classification:
90+
return True
91+
return False
92+
93+
def __str__(self):
94+
return str(_to_internal_scene_conditions_ambient_light(self))
95+
96+
def __init__(
97+
self,
98+
ambient_light=None,
99+
):
100+
101+
if ambient_light is None:
102+
ambient_light = self.AmbientLight()
103+
if not isinstance(ambient_light, self.AmbientLight):
104+
raise TypeError(
105+
"Unsupported type: {value}".format(value=type(ambient_light))
106+
)
107+
self._ambient_light = ambient_light
108+
109+
@property
110+
def ambient_light(self):
111+
return self._ambient_light
112+
113+
@ambient_light.setter
114+
def ambient_light(self, value):
115+
if not isinstance(value, self.AmbientLight):
116+
raise TypeError("Unsupported type {value}".format(value=type(value)))
117+
self._ambient_light = value
118+
119+
@classmethod
120+
def load(cls, file_name):
121+
return _to_scene_conditions(_zivid.SceneConditions(str(file_name)))
122+
123+
def save(self, file_name):
124+
_to_internal_scene_conditions(self).save(str(file_name))
125+
126+
@classmethod
127+
def from_serialized(cls, value):
128+
return _to_scene_conditions(_zivid.SceneConditions.from_serialized(str(value)))
129+
130+
def serialize(self):
131+
return _to_internal_scene_conditions(self).serialize()
132+
133+
def __eq__(self, other):
134+
if self._ambient_light == other._ambient_light:
135+
return True
136+
return False
137+
138+
def __str__(self):
139+
return str(_to_internal_scene_conditions(self))
140+
141+
142+
def _to_scene_conditions_ambient_light(internal_ambient_light):
143+
return SceneConditions.AmbientLight(
144+
flicker_classification=internal_ambient_light.flicker_classification.value,
145+
)
146+
147+
148+
def _to_scene_conditions(internal_scene_conditions):
149+
return SceneConditions(
150+
ambient_light=_to_scene_conditions_ambient_light(
151+
internal_scene_conditions.ambient_light
152+
),
153+
)
154+
155+
156+
def _to_internal_scene_conditions_ambient_light(ambient_light):
157+
internal_ambient_light = _zivid.SceneConditions.AmbientLight()
158+
159+
internal_ambient_light.flicker_classification = (
160+
_zivid.SceneConditions.AmbientLight.FlickerClassification(
161+
ambient_light._flicker_classification.value
162+
)
163+
)
164+
165+
return internal_ambient_light
166+
167+
168+
def _to_internal_scene_conditions(scene_conditions):
169+
internal_scene_conditions = _zivid.SceneConditions()
170+
171+
internal_scene_conditions.ambient_light = (
172+
_to_internal_scene_conditions_ambient_light(scene_conditions.ambient_light)
173+
)
174+
return internal_scene_conditions

src/ReleasableCamera.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace ZividPython
2626
.def("write_user_data", &ReleasableCamera::writeUserData)
2727
.def_property_readonly("user_data", &ReleasableCamera::userData)
2828
.def_property_readonly("network_configuration", &ReleasableCamera::networkConfiguration)
29-
.def("apply_network_configuration", &ReleasableCamera::applyNetworkConfiguration);
29+
.def("apply_network_configuration", &ReleasableCamera::applyNetworkConfiguration)
30+
.def("measure_scene_conditions", &ReleasableCamera::measureSceneConditions);
3031
}
3132
} // namespace ZividPython

src/Wrapper.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ ZIVID_PYTHON_MODULE // NOLINT
4141
ZIVID_PYTHON_WRAP_DATA_MODEL(module, FrameInfo);
4242
ZIVID_PYTHON_WRAP_DATA_MODEL(module, CameraIntrinsics);
4343
ZIVID_PYTHON_WRAP_DATA_MODEL(module, NetworkConfiguration);
44+
ZIVID_PYTHON_WRAP_DATA_MODEL(module, SceneConditions);
4445

4546
ZIVID_PYTHON_WRAP_CLASS_AS_SINGLETON(module, Application);
4647
ZIVID_PYTHON_WRAP_CLASS_AS_RELEASABLE(module, Camera);

src/include/ZividPython/ReleasableCamera.h

+4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#pragma once
22

33
#include <Zivid/Camera.h>
4+
45
#include <ZividPython/Releasable.h>
56
#include <ZividPython/ReleasableFrame.h>
67
#include <ZividPython/ReleasableFrame2D.h>
78
#include <ZividPython/Wrappers.h>
89

10+
#include <Zivid/SceneConditions.h>
11+
912
namespace ZividPython
1013
{
1114
class ReleasableCamera : public Releasable<Zivid::Camera>
@@ -27,6 +30,7 @@ namespace ZividPython
2730
ZIVID_PYTHON_FORWARD_1_ARGS(applyNetworkConfiguration,
2831
const Zivid::NetworkConfiguration &,
2932
networkConfiguration)
33+
ZIVID_PYTHON_FORWARD_0_ARGS(measureSceneConditions)
3034
};
3135

3236
void wrapClass(pybind11::class_<ReleasableCamera> pyClass);

test/test_camera.py

+20
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,23 @@ def test_state(shared_file_camera):
104104
state = shared_file_camera.state
105105
assert state
106106
assert isinstance(state, zivid.CameraState)
107+
108+
109+
@pytest.mark.physical_camera
110+
def test_measure_scene_conditions(physical_camera):
111+
from zivid import SceneConditions
112+
113+
scene_conditions = physical_camera.measure_scene_conditions()
114+
assert scene_conditions
115+
assert isinstance(scene_conditions, SceneConditions)
116+
assert (
117+
scene_conditions.ambient_light.flicker_classification
118+
in SceneConditions.AmbientLight.FlickerClassification.valid_values()
119+
)
120+
121+
122+
def test_measure_scene_conditions_fails_with_file_camera(file_camera):
123+
with pytest.raises(
124+
RuntimeError, match="This camera cannot measure surrounding conditions"
125+
):
126+
file_camera.measure_scene_conditions()

0 commit comments

Comments
 (0)