@@ -38,34 +38,84 @@ def __init__(self):
3838 Cameras .logger .info (f"Camera found: { camPath } " )
3939
4040 # Get supported resolutions using v4l2-ctl and a little regex
41- supportedResolutions = sorted ( # Sort values
41+ formatParams = subprocess .run (
42+ [
43+ "v4l2-ctl" ,
44+ "-d" ,
45+ path ,
46+ "--list-formats-ext" ,
47+ ],
48+ capture_output = True ,
49+ ).stdout .decode ("utf-8" )
50+
51+ supportedResolutions = sorted ( # Sort values
4252 list (
43- set ( # Unique values
53+ set ( # Unique values
4454 map (
4555 lambda x : (
4656 int (x .split ("x" )[0 ]),
4757 int (x .split ("x" )[1 ]),
4858 ),
4959 re .findall (
5060 "[0-9]+x[0-9]+" ,
51- subprocess .run (
52- [
53- "v4l2-ctl" ,
54- "-d" ,
55- path ,
56- "--list-formats-ext" ,
57- ],
58- capture_output = True ,
59- ).stdout .decode ("utf-8" ),
61+ formatParams ,
6062 ),
6163 )
6264 )
6365 )
6466 )
6567
68+ formats = set (
69+ map (
70+ lambda x : re .search ("'....'" , x ).group ().strip ("'" ),
71+ re .findall (
72+ ": '....'" ,
73+ formatParams ,
74+ ),
75+ )
76+ )
77+
78+ settingParams = subprocess .run (
79+ ["v4l2-ctl" , "-d" , path , "--list-ctrls-menus" ],
80+ capture_output = True ,
81+ ).stdout .decode ("utf-8" )
82+
83+ exposureRange = tuple (
84+ map (
85+ lambda x : int (x .split ("=" )[- 1 ]),
86+ re .search (
87+ "exposure_absolute .* min=[0-9]+ max=[0-9]+ step=[0-9]+" ,
88+ settingParams ,
89+ )
90+ .group ()
91+ .split ()[- 3 :],
92+ )
93+ )
94+
95+ brightnessRange = tuple (
96+ map (
97+ lambda x : int (x .split ("=" )[- 1 ]),
98+ re .search (
99+ "brightness .* min=[0-9]+ max=[0-9]+ step=[0-9]+" ,
100+ settingParams ,
101+ )
102+ .group ()
103+ .split ()[- 3 :],
104+ )
105+ )
106+
66107 Cameras .logger .info (
67108 f"Supported resolutions: { supportedResolutions } "
68109 )
110+ Cameras .logger .info (
111+ f"Supported formats: { formats } "
112+ )
113+ Cameras .logger .info (
114+ f"Supported exposures (min, max, step): { exposureRange } "
115+ )
116+ Cameras .logger .info (
117+ f"Supported brightnesses (min, max, step): { brightnessRange } "
118+ )
69119
70120 # Disable buffer so we always pull the latest image
71121 cam .set (cv2 .CAP_PROP_BUFFERSIZE , 1 )
@@ -81,6 +131,9 @@ def __init__(self):
81131 # Initialize CameraInfo object
82132 self .info [camPath ] = CameraInfo (cam , camPath , supportedResolutions )
83133 self .info [camPath ].resolution = self .getResolutions ()[camPath ]
134+ self .info [camPath ].exposureRange = exposureRange
135+ self .info [camPath ].brightnessRange = brightnessRange
136+ self .info [camPath ].validFormats = formats
84137
85138 # Attempt to import config from file
86139 self .importConfig (camPath )
@@ -89,13 +142,13 @@ def __init__(self):
89142 writeConfig (
90143 self .cleanIdentifier (camPath ),
91144 self .getResolutions ()[camPath ],
92- self .getGains ()[camPath ],
145+ self .getBrightnesss ()[camPath ],
93146 self .getExposures ()[camPath ],
94147 )
95148
96149 else :
97150 Cameras .logger .error ("Unknown platform!" )
98-
151+
99152 def setCalibration (self , identifier , K , D ):
100153 self .info [identifier ].K = K
101154 self .info [identifier ].D = D
@@ -118,6 +171,16 @@ def getFrames(self):
118171 frames [identifier ] = img
119172 return frames
120173
174+ # Grab frames from each camera specifically for processing
175+ def getFramesForProcessing (self ):
176+ frames = {}
177+ connections = {}
178+ for identifier , camInfo in self .info .items ():
179+ flag , img = camInfo .cam .read ()
180+ frames [identifier ] = img
181+ connections [identifier ] = True if flag else False
182+ return (connections , frames )
183+
121184 # Sets resolution, video format, and FPS
122185 def setResolution (self , identifier , resolution ):
123186 if resolution is None :
@@ -127,8 +190,10 @@ def setResolution(self, identifier, resolution):
127190 # os.system(f"v4l2-ctl -d /dev/v4l/by-path/{identifier} --set-fmt-video=width={resolution[0]},height={resolution[1]}")
128191 self .info [identifier ].cam .set (cv2 .CAP_PROP_FRAME_HEIGHT , resolution [1 ])
129192 self .info [identifier ].cam .set (cv2 .CAP_PROP_FRAME_WIDTH , resolution [0 ])
130- self .info [identifier ].cam .set (cv2 .CAP_PROP_FOURCC , cv2 .VideoWriter_fourcc (* "MJPG" ))
131- self .info [identifier ].cam .set (cv2 .CAP_PROP_FPS , 30 )
193+ self .info [identifier ].cam .set (
194+ cv2 .CAP_PROP_FOURCC , cv2 .VideoWriter_fourcc (* ("MJPG" if "MJPG" in self .info [identifier ].validFormats else "GREY" ))
195+ )
196+ self .info [identifier ].cam .set (cv2 .CAP_PROP_FPS , 30 ) # Lower can be better
132197 resolution = tuple (resolution )
133198
134199 # Test if resolution got set
@@ -145,31 +210,35 @@ def setResolution(self, identifier, resolution):
145210 writeConfig (
146211 self .cleanIdentifier (identifier ),
147212 resolution ,
148- self .getGains ()[identifier ],
213+ self .getBrightnesss ()[identifier ],
149214 self .getExposures ()[identifier ],
150215 )
151216
152217 Cameras .logger .info (f"Resolution set to { resolution } for { identifier } " )
153218 return True
154219
155- def setGain (self , identifier , gain ):
156- if gain is None :
157- Cameras .logger .info ("Gain not set" )
220+ def setBrightness (self , identifier , brightness ):
221+ if brightness is None :
222+ Cameras .logger .info ("Brightness not set" )
158223 return False
159224
160- # Set gain through command line
161- os .system (f"v4l2-ctl -d /dev/v4l/by-path/{ identifier } --set-ctrl gain={ gain } " )
225+ # Set brightness through command line
226+ returned = os .system (
227+ f"v4l2-ctl -d /dev/v4l/by-path/{ identifier } --set-ctrl brightness={ brightness } "
228+ )
162229
163230 # Check if it set, if so write it to a file
164- if self .info [identifier ].cam .get (cv2 .CAP_PROP_GAIN ) != gain :
165- Cameras .logger .warning (f"Gain not set: { gain } not accepted" )
231+ if returned != 0 :
232+ Cameras .logger .warning (
233+ f"Brightness not set: { brightness } not accepted on camera { identifier } "
234+ )
166235 return False
167236 else :
168- Cameras .logger .info (f"Gain set to { gain } " )
237+ Cameras .logger .info (f"Brightness set to { brightness } " )
169238 writeConfig (
170239 self .cleanIdentifier (identifier ),
171240 self .getResolutions ()[identifier ],
172- gain ,
241+ brightness ,
173242 self .getExposures ()[identifier ],
174243 )
175244 return True
@@ -180,38 +249,40 @@ def setExposure(self, identifier, exposure):
180249 return False
181250
182251 # Set exposure with a command
183- os .system (
184- f"v4l2-ctl -d /dev/v4l/by-path/{ identifier } --set-ctrl exposure_auto=1 --set-ctrl exposure_absolute= { exposure } "
252+ returned = os .system (
253+ f"v4l2-ctl -d /dev/v4l/by-path/{ identifier } --set-ctrl exposure_absolute= { exposure } " # --set-ctrl exposure_auto=1
185254 )
186255
187256 # Check if it set, if so write it to a file
188- if self .info [identifier ].cam .get (cv2 .CAP_PROP_EXPOSURE ) != exposure :
189- Cameras .logger .warning (f"Exposure not set: { exposure } not accepted" )
257+ if returned != 0 :
258+ Cameras .logger .warning (
259+ f"Exposure not set: { exposure } not accepted on camera { identifier } "
260+ )
190261 return False
191262 else :
192263 Cameras .logger .info (f"Exposure set to { exposure } " )
193264
194265 writeConfig (
195266 self .cleanIdentifier (identifier ),
196267 self .getResolutions ()[identifier ],
197- self .getGains ()[identifier ],
268+ self .getBrightnesss ()[identifier ],
198269 exposure ,
199270 )
200271 return True
201272
202273 # Return a dictionary of all camera exposures
203274 def getExposures (self ):
204- exposure = {}
205- for identifier , camInfo in self . info . items ():
206- exposure [ identifier ] = camInfo . cam . get ( cv2 . CAP_PROP_EXPOSURE )
207- return exposure
208-
209- # Return a dictionary of all camera gains
210- def getGains (self ):
211- gain = {}
212- for identifier , camInfo in self . info . items ():
213- gain [ identifier ] = camInfo . cam . get ( cv2 . CAP_PROP_GAIN )
214- return gain
275+ return {
276+ identifier : camInfo . cam . get ( cv2 . CAP_PROP_EXPOSURE )
277+ for identifier , camInfo in self . info . items ( )
278+ }
279+
280+ # Return a dictionary of all camera brightnesss
281+ def getBrightnesss (self ):
282+ return {
283+ identifier : camInfo . cam . get ( cv2 . CAP_PROP_BRIGHTNESS )
284+ for identifier , camInfo in self . info . items ( )
285+ }
215286
216287 # Return a dictionary of all camera resolutions
217288 def getResolutions (self ):
@@ -222,7 +293,13 @@ def getResolutions(self):
222293 int (camInfo .cam .get (cv2 .CAP_PROP_FRAME_HEIGHT )),
223294 )
224295
225- return resolution
296+ return {
297+ identifier : (
298+ int (camInfo .cam .get (cv2 .CAP_PROP_FRAME_WIDTH )),
299+ int (camInfo .cam .get (cv2 .CAP_PROP_FRAME_HEIGHT )),
300+ )
301+ for identifier , camInfo in self .info .items ()
302+ }
226303
227304 # Find a calibration for the camera
228305 def importCalibration (self , identifier ):
@@ -246,7 +323,7 @@ def importCalibration(self, identifier):
246323 f"Calibration not found for camera { identifier } at resolution { resolution } "
247324 )
248325 return False
249-
326+
250327 def importConfig (self , camPath ):
251328 # Attempt to import config from file
252329 Cameras .logger .info (f"Attempting to import config for { camPath } " )
@@ -267,15 +344,13 @@ def importConfig(self, camPath):
267344 camPath , config ["Resolution" ]
268345 ): # Calls self.importCalibration iff resolution was set
269346 self .importCalibration (camPath )
270- self .setGain (camPath , config ["Gain " ])
347+ self .setBrightness (camPath , config ["Brightness " ])
271348 self .setExposure (camPath , config ["Exposure" ])
272349
273350 else :
274351 self .importCalibration (camPath )
275- Cameras .logger .warning (
276- f"Camera config not found for camera { camPath } "
277- )
278-
352+ Cameras .logger .warning (f"Camera config not found for camera { camPath } " )
353+
279354 return config
280355
281356 @staticmethod
0 commit comments