@@ -15,9 +15,9 @@ which takes on the input raw camera output image and display your own output.
15
15
pip install pypylon-opencv-viewer
16
16
```
17
17
18
- ## Usage
18
+ ## Initialization
19
19
20
- To start working, launch Jupyter notebook and connect to Basler camera. Here is an example how you can do it:
20
+ To start working, launch Jupyter notebook and connect to Basler camera. Here is an example how we can do it:
21
21
``` python
22
22
from pypylon import pylon
23
23
@@ -36,94 +36,239 @@ if info is not None:
36
36
camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateDevice(info))
37
37
camera.Open()
38
38
```
39
+ When our camera is connected and open, we can initialize our viewer with it:
39
40
40
- Now we can start working with our viewer. Basically we need 3 things: connected camera, features we want to work with
41
- (you can find them in [ official Basler documentation] ( https://docs.baslerweb.com/#t=en%2Ffeatures.htm&rhsearch=sdk ) , for
42
- now this library supports only boolean and numeric features) and image processing function we want to apply on grabbing
43
- images. Image processing function is not a requirement, you don't have to specify one, in this case you'll get raw
44
- camera output.
45
-
46
- #### List of features
47
-
48
- Features - list of dicts.
49
-
50
- Dict structure:
51
- 1 . ` name ` - camera pylon feature name, example: "GainRaw" (required)
52
- 1 . ` type ` - widget input type, allowed values ` int ` , ` float ` , ` bool ` , ` int_text ` , ` float_text ` (optional, default: "int")
53
- 1 . ` value ` - widget input value (optional, default: current camera feature value)
54
- 1 . ` max ` - maximum widget input value, only numeric widget types (optional, default: camera feature max value)
55
- 1 . ` min ` - minimum widget input value, only numeric widget types (optional, default: camera feature min value)
56
- 1 . ` step ` - step of allowed input value (optional, default: camera feature increment, if not exist =1)
41
+ ``` python
42
+ from pypylon_opencv_viewer import BaslerOpenCVViewer
43
+ viewer = BaslerOpenCVViewer(camera)
44
+ ```
45
+
46
+ ### Configuration
47
+ Next step is to configure created viewer using method ` set_configuration ` , where passed value is dictionary with the following items:
48
+
49
+ features : list of dicts (required)
50
+ List of widgets configuration stored in
51
+ dictionaries with items:
52
+ name : str (required)
53
+ Camera pylon feature name, example: "GainRaw"
54
+ type : str (required)
55
+ widget input type, allowed values are {"int", "float", "bool", "int_text", "float_text", "choice_text"}
56
+ value : number or bool (optional, default: current camera feature value)
57
+ widget input value
58
+ max : number (optional, default: camera feature max value)
59
+ maximum widget input value, only numeric widget types
60
+ min : number (optional, default: camera feature min value)
61
+ minimum widget input value, only numeric widget types
62
+ step : number (optional, default: camera feature increment)
63
+ step of allowed input value
64
+ options: list, mandatory for type "choice_text",
65
+ sets values in list as options for ToggleButtons
66
+ unit: str (optional, default empty)
67
+ string shown at the end of label in the form "Label [unit]:"
68
+ dependency: dict, (optional, default empty)
69
+ defines how other widgets must be set to be this widget enabled
70
+ layout : dict (optional, default: {"width": '100%', "height": '50px', "align_items": "center"})
71
+ values are passed to widget's layout
72
+ style: dict, (optional, default {'description_width': 'initial'})
73
+ values are passed to widget's style
74
+
75
+ Example:
76
+ "features": {
77
+ "name": "GainRaw",
78
+ "type": "int",
79
+ "value": 20,
80
+ "max": 63,
81
+ "min": 10,
82
+ "step": 1,
83
+ "layout": {"width":"99%", "height": "50px")
84
+ "style": {"button_width": "90px"}
85
+ }
86
+ features_layout: list of tuples (optional, default is one widget per row)
87
+ List of features' widgets' name for reordering. Each tuple represents one row
88
+ Example:
89
+ "* features_layout": [
90
+ ("Height", "Width"),
91
+ ("OffsetX", "CenterX"),
92
+ ("ExposureAuto", "ExposureTimeAbs"),
93
+ ("AcquisitionFrameCount", "AcquisitionLineRateAbs")
94
+ ],
95
+ actions_layout: list of tuples (optional, default is one widget per row)
96
+ List of actions' widgets' name for reordering. Each tuple represents one row.
97
+ Available widgets are StatusLabel, SaveConfig, LoadConfig, ContinuousShot, SingleShot, "UserSet"
98
+ * Example:
99
+ "action_layout": [
100
+ ("StatusLabel"),
101
+ ("SaveConfig", "LoadConfig", "ContinuousShot", "SingleShot"),
102
+ ("UserSet")
103
+ ]
104
+ default_user_set: string (optional, default is None)
105
+ If value is None, widget for selecting UserSet is displayed.
106
+ Otherwise is set to given value in ["UserSet1", "UserSet2", "UserSet3"]
107
+ * Example:
108
+ "default_user_set": "UserSet3"
109
+
110
+ The only required and also most important item in the dictionary above is a list of features you want to control. Their names can be found in [ official Basler documentation] ( https://docs.baslerweb.com/#t=en%2Ffeatures.htm&rhsearch=sdk ) .
57
111
58
112
Example configuration you can see below:
59
113
60
114
``` python
61
- # List of features to create wigets
62
- features = [
63
- {
64
- " name" : " GainRaw" ,
65
- " type" : " int"
66
- },
67
- {
68
- " name" : " Height" ,
69
- " type" : " int_text" ,
70
- " max" : 1000 ,
71
- " min" : 100 ,
72
- " step" : " 5"
73
- },
74
- {
75
- " name" : " Width" ,
76
- " type" : " int_text" ,
77
- " max" : 1000 ,
78
- " min" : 100 ,
79
- " step" : " 5"
80
- },
81
- {
82
- " name" : " AcquisitionFrameRateEnable" ,
83
- " type" : " bool"
84
- },
85
- {
86
- " name" : " AcquisitionFrameRateAbs" ,
87
- " type" : " int" ,
88
- " max" : 60 ,
89
- " min" : 10
90
- }
91
- ]
115
+ # Example of configuration for basic RGB camera's features
116
+ VIEWER_CONFIG_RGB_MATRIX = {
117
+ " features" : [
118
+ {
119
+ " name" : " GainRaw" ,
120
+ " type" : " int" ,
121
+ " step" : 1 ,
122
+ },
123
+ {
124
+ " name" : " Height" ,
125
+ " type" : " int" ,
126
+ " value" : 1080 ,
127
+ " unit" : " px" ,
128
+ " step" : 2 ,
129
+ },
130
+ {
131
+ " name" : " Width" ,
132
+ " type" : " int" ,
133
+ " value" : 1920 ,
134
+ " unit" : " px" ,
135
+ " step" : 2 ,
136
+ },
137
+ {
138
+ " name" : " CenterX" ,
139
+ " type" : " bool" ,
140
+ },
141
+ {
142
+ " name" : " CenterY" ,
143
+ " type" : " bool" ,
144
+
145
+ },
146
+ {
147
+ " name" : " OffsetX" ,
148
+ " type" : " int" ,
149
+ " dependency" : {" CenterX" : False },
150
+ " unit" : " px" ,
151
+ " step" : 2 ,
152
+ },
153
+ {
154
+ " name" : " OffsetY" ,
155
+ " type" : " int" ,
156
+ " dependency" : {" CenterY" : False },
157
+ " unit" : " px" ,
158
+ " step" : 2 ,
159
+ },
160
+ {
161
+ " name" : " AcquisitionFrameRateAbs" ,
162
+ " type" : " int" ,
163
+ " unit" : " fps" ,
164
+ " dependency" : {" AcquisitionFrameRateEnable" : True },
165
+ " max" : 150 ,
166
+ " min" : 1 ,
167
+ },
168
+ {
169
+ " name" : " AcquisitionFrameRateEnable" ,
170
+ " type" : " bool" ,
171
+ },
172
+ {
173
+ " name" : " ExposureAuto" ,
174
+ " type" : " choice_text" ,
175
+ " options" : [" Off" , " Once" , " Continuous" ],
176
+ " style" : {" button_width" : " 90px" }
177
+ },
178
+ {
179
+ " name" : " ExposureTimeAbs" ,
180
+ " type" : " int" ,
181
+ " dependency" : {" ExposureAuto" : " Off" },
182
+ " unit" : " μs" ,
183
+ " step" : 100 ,
184
+ " max" : 35000 ,
185
+ " min" : 500 ,
186
+ },
187
+ {
188
+ " name" : " BalanceWhiteAuto" ,
189
+ " type" : " choice_text" ,
190
+ " options" : [" Off" , " Once" , " Continuous" ],
191
+ " style" : {" button_width" : " 90px" }
192
+ },
193
+ ],
194
+ " features_layout" : [
195
+ (" Height" , " Width" ),
196
+ (" OffsetX" , " CenterX" ),
197
+ (" OffsetY" , " CenterY" ),
198
+ (" ExposureAuto" , " ExposureTimeAbs" ),
199
+ (" AcquisitionFrameRateAbs" , " AcquisitionFrameRateEnable" ),
200
+ (" BalanceWhiteAuto" , " GainRaw" )
201
+ ],
202
+ " actions_layout" : [
203
+ (" StatusLabel" ),
204
+ (" SaveConfig" , " LoadConfig" , " ContinuousShot" , " SingleShot" ),
205
+ (" UserSet" )
206
+ ],
207
+ " default_user_set" : " UserSet3" ,
208
+ }
209
+ viewer.set_configuration(VIEWER_CONFIG_RGB_MATRIX )
210
+
92
211
```
93
212
94
- #### Example image processing function
95
- Just example image processing function, which negatives the image. Image has to be the only argument in it.
96
- If you want some image to be shown, you have to do it yourself inside the function. DON'T DESTROY
97
- ALL OpenCV windows or wait for key pressed in it.
98
213
99
- ``` python
100
- import numpy as np
101
- import cv2
214
+ #### Image processing function
215
+ We can also define image processing function that we want to apply on grabbed images using method ` set_impro_function ` . If we don't specify one, we will get raw camera output.
102
216
217
+ The given function must either return processed image:
218
+ ``` python
219
+ def impro (img ):
220
+ return np.hstack([img, (255 - img)])
221
+ viewer.set_impro_function(impro)
222
+ ```
223
+ or display it using cv2.namedWindow. In this case we must specify ` own_window=True ` to disable showing of default window.
224
+ ``` python
103
225
def impro (img ):
104
226
cv2.namedWindow(' 1' , cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_NORMAL )
105
227
cv2.resizeWindow(' 1' , 1080 , 720 )
106
228
cv2.imshow(" 1" , np.hstack([img, (255 - img)]))
229
+ viewer.set_impro_function(impro, own_window = True )
107
230
```
231
+ In both cases, DON'T DESTROY ALL OpenCV windows or wait for key pressed in it!
108
232
109
233
#### Viewer
110
- We have prepared all required parts. Now we just set them to the viewer object and launch image grabbing:
111
- ` run_interaction_continuous_shot ` for continuous or ` run_interaction_single_shot ` for single shot.
112
- Also you can press 'S' button to save raw camera image or impro function return value to ` image_folder ` .
113
- ``` python
114
- from pypylon_opencv_viewer import BaslerOpenCVViewer
115
-
116
- viewer = BaslerOpenCVViewer(camera)
117
- viewer.set_features(features)
118
- viewer.set_impro_function(impro)
119
- viewer.run_interaction_continuous_shot(image_folder = ' ~/Documents/images' )
120
- ```
234
+ We have already created our viewer and set its configuration. Now we can display defined widgets using method ` show_interactive_panel `
235
+ with parameters ` image_folder ` and ` window_size ` .
236
+ The panel contains 4 buttons:
237
+ 1 . Save configuration - save current values of features to camera's inner memory (UserSet)
238
+ 1 . Load configuration - load values of features from camera's inner memory (UserSet) to the widgets
239
+ 1 . Continuous shot - start streaming frames from the camera
240
+ 1 . Single shot - grab a one frame
121
241
122
- Now we see some similar image, we can setup camera features values. Push ` Run interaction ` to let it go .
123
- To close OpenCV windows just push 'Q ' on your keyboard. You don't have to launch this cell once more to try the same
242
+ Also we can press 's' key to save raw camera image or impro function return value (but only when own_window=False) to ` image_folder ` .
243
+ To close OpenCV windows just push 'q ' on the keyboard. We don't have to launch this cell once more to try the same
124
244
procedure with the image, just change wanted values and push the button. That's it!
125
- ![ Basler OpenCV viewer] ( https://raw.githubusercontent.com/mbalatsko/pypylon-opencv-viewer/master/images/wiget.PNG )
126
- ![ Basler OpenCV viewer] ( https://raw.githubusercontent.com/mbalatsko/pypylon-opencv-viewer/master/images/opened.PNG )
245
+
246
+ For configuration above we should see this interactive panel:
247
+ ![ Basler OpenCV viewer] ( images/widget.png )
248
+
249
+ #### Example
250
+ We can use our viewer along with more complex image processing function for detection of numbers:
251
+ ``` python
252
+ def impro (img ):
253
+ img_rgb = img.copy()
254
+ img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY )
255
+ _, img_gray = cv2.threshold(img_gray, 170 , 255 , cv2.THRESH_BINARY )
256
+ img_gray = 255 - img_gray
257
+ _, contours, _ = cv2.findContours(img_gray, cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE )
258
+ selected_contours = []
259
+ for c in contours:
260
+ contour_area = cv2.contourArea(c)
261
+ x,y,w,h = cv2.boundingRect(c)
262
+ bounding_rect_area = w* h
263
+ if (contour_area > 80 and contour_area/ bounding_rect_area < 0.75 ):
264
+ selected_contours.append(c)
265
+
266
+ cv2.drawContours(img_rgb, selected_contours, - 1 , (0 ,0 ,255 ), thickness = cv2.FILLED )
267
+ img = cv2.putText(img, " Original" , (10 , 100 ), cv2.FONT_HERSHEY_SIMPLEX , 4 , (255 ,0 ,0 ), 8 )
268
+ img_rgb = cv2.putText(img_rgb, " Found numbers" , (10 , 100 ), cv2.FONT_HERSHEY_SIMPLEX , 4 , (255 ,0 ,0 ), 8 )
269
+ return np.hstack([img, img_rgb])
270
+ ```
271
+ ![ Number detection] ( images/impro-function-example.png )
127
272
128
273
#### Save or get image from camera
129
274
0 commit comments