This repository has been archived by the owner on Oct 17, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwebcam.py
executable file
·381 lines (310 loc) · 15.9 KB
/
webcam.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import Qt
# Import Camera OpenCV wrapper to easily manage video captures with OpenCV
# This is the home-made module Camera.py
import Camera
class Empty :
""" Convenience class to handle various objects """
def __init__(self):
pass
class MainWindow(QtGui.QMainWindow):
""" Displays video frames, controls, and other various things """
def __init__(self):
""" Initializes window """
# Call parent constructor
QtGui.QMainWindow.__init__(self)
# Define widget containers
self.gui = Empty()
# Contains Qt's signals
self.signals = Empty()
# FPS configuration
self.fps = 15 # fps
# Easy access to custom Qt's slots
self.slots = {
'camChangeHue': QtCore.SLOT('camChangeHue(int)'),
'camChangeContrast': QtCore.SLOT('camChangeContrast(int)'),
'camChangeSaturation': QtCore.SLOT('camChangeSaturation(int)'),
'camChangeBrightness': QtCore.SLOT('camChangeBrightness(int)')
}
# Set window options
self.setWindowTitle('VideoBot')
# Builds widgets, bindings and some other init stuff
self.init()
def init(self):
# Create widgets
self.buildGui()
# Setup logic
self.buildBindings()
# Initialize camera
self.initCam()
def buildGui(self):
""" Calls GUI's builders (separated to improve code readability) """
# Defines main namespace
self.gui.main = Empty()
# Defines the main layout
self.gui.main.layout = QtGui.QVBoxLayout()
# Create tabs
self.buildGuiVideo()
self.buildGuiTabs()
# Includes main layout in a basic widget and add it to MainWindow
# as the “CentralWidget”
self.gui.main.container = QtGui.QWidget()
self.gui.main.container.setLayout(self.gui.main.layout)
self.setCentralWidget(self.gui.main.container)
def buildGuiVideo(self):
""" Setup video-related GUI stuff """
# Defines video namespace
self.gui.video = Empty()
# Creates QLabel where we'll place frames
self.gui.video.frame = QtGui.QLabel()
# Add the label to the main layout
self.gui.main.layout.addWidget(self.gui.video.frame)
def buildGuiTabs(self):
""" Setup bottom tabs """
# Creates tabs namespace
self.gui.tabs = Empty()
# Creates the QTab widget
self.gui.tabs.tabs = QtGui.QTabWidget()
# Populates tabs
self.buildGuiTabControls()
self.buildGuiTabImg()
# Add tabs to the main layout
self.gui.main.layout.addWidget(self.gui.tabs.tabs)
def buildGuiTabImg(self):
""" Creates image settings controls """
# Creates the tabs.img namespace
self.gui.tabs.img = Empty()
# Creates the Grid layout and the future blank widget container
self.gui.tabs.img.layout = QtGui.QGridLayout()
self.gui.tabs.img.container = QtGui.QWidget()
# Creates hue namespace
self.gui.tabs.img.hue = Empty()
# Creates hue slider
self.gui.tabs.img.hue.slider = QtGui.QSlider(Qt.Horizontal)
self.gui.tabs.img.hue.slider.setRange(0, 100)
# Creates hue spinbox
self.gui.tabs.img.hue.spin = QtGui.QSpinBox()
self.gui.tabs.img.hue.spin.setRange(0, 100)
self.gui.tabs.img.hue.spin.setSuffix('%')
# Add them to the layout
self.gui.tabs.img.layout.addWidget(QtGui.QLabel('Hue'), 1, 0)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.hue.slider, 1, 1)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.hue.spin, 1, 2)
# Creates contrast namespace
self.gui.tabs.img.contrast = Empty()
# Creates contrast slider
self.gui.tabs.img.contrast.slider = QtGui.QSlider(Qt.Horizontal)
self.gui.tabs.img.contrast.slider.setRange(-50, 50)
# Creates contrast spinbox
self.gui.tabs.img.contrast.spin = QtGui.QSpinBox()
self.gui.tabs.img.contrast.spin.setRange(-50, 50)
self.gui.tabs.img.contrast.spin.setSuffix('%')
# Add them to the layout
self.gui.tabs.img.layout.addWidget(QtGui.QLabel('Contrast'), 2, 0)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.contrast.slider, 2, 1)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.contrast.spin, 2, 2)
# Creates satutration namespace
self.gui.tabs.img.saturation = Empty()
# Creates saturation slider
self.gui.tabs.img.saturation.slider = QtGui.QSlider(Qt.Horizontal)
self.gui.tabs.img.saturation.slider.setMaximum(100)
# Creates saturation spinbox
self.gui.tabs.img.saturation.spin = QtGui.QSpinBox()
self.gui.tabs.img.saturation.spin.setRange(0, 100)
self.gui.tabs.img.saturation.spin.setSuffix('%')
# Add them to the layout
self.gui.tabs.img.layout.addWidget(QtGui.QLabel('Saturation'), 3, 0)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.saturation.slider, 3, 1)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.saturation.spin, 3, 2)
# Creates brightness namespace
self.gui.tabs.img.brightness = Empty()
# Creates brightness slider
self.gui.tabs.img.brightness.slider = QtGui.QSlider(Qt.Horizontal)
self.gui.tabs.img.brightness.slider.setRange(-50, 50)
# Creates brightness spinbox
self.gui.tabs.img.brightness.spin = QtGui.QSpinBox()
self.gui.tabs.img.brightness.spin.setRange(-50, 50)
self.gui.tabs.img.brightness.spin.setSuffix('%')
# Add them to the layout
self.gui.tabs.img.layout.addWidget(QtGui.QLabel('Brightness'), 4, 0)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.brightness.slider, 4, 1)
self.gui.tabs.img.layout.addWidget(self.gui.tabs.img.brightness.spin, 4, 2)
# Add layout to the blank widget container and add it to the tabs layout
self.gui.tabs.img.container.setLayout(self.gui.tabs.img.layout)
self.gui.tabs.tabs.addTab(self.gui.tabs.img.container, 'Video settings')
def buildGuiTabControls(self):
""" Creates the contols tab """
# Creates the tabs.controls namespace
self.gui.tabs.controls = Empty()
# Creates a layout and a blank widget
self.gui.tabs.controls.layout = QtGui.QFormLayout()
self.gui.tabs.controls.container = QtGui.QWidget()
# Add some widgets here...
# Set layout to the container, and add it to the tabs layout
self.gui.tabs.controls.container.setLayout(self.gui.tabs.controls.layout)
self.gui.tabs.tabs.addTab(self.gui.tabs.controls.container, 'Controls')
def buildBindings(self):
""" Connects Qt's signals and slots """
# Setting up hue stuff. Moving slider or changing its value updates
# cam's settings. Also connects spinbox to the slider to easily
# configure it.
self.connect(self.gui.tabs.img.hue.slider,
QtCore.SIGNAL('sliderMoved(int)'),
self.slots['camChangeHue'])
self.connect(self.gui.tabs.img.hue.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.slots['camChangeHue'])
self.connect(self.gui.tabs.img.hue.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.hue.spin,
QtCore.SLOT('setValue(int)'))
self.connect(self.gui.tabs.img.hue.spin,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.hue.slider,
QtCore.SLOT('setValue(int)'))
# Setting up contrast stuff. Moving slider or changing its value updates
# cam's settings. Also connects spinbox to the slider to easily
# configure it.
self.connect(self.gui.tabs.img.contrast.slider,
QtCore.SIGNAL('sliderMoved(int)'),
self.slots['camChangeContrast'])
self.connect(self.gui.tabs.img.contrast.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.slots['camChangeContrast'])
self.connect(self.gui.tabs.img.contrast.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.contrast.spin,
QtCore.SLOT('setValue(int)'))
self.connect(self.gui.tabs.img.contrast.spin,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.contrast.slider,
QtCore.SLOT('setValue(int)'))
# Setting up brightness stuff. Moving slider or changing its value
# updates cam's settings. Also connects spinbox to the slider to easily
# configure it.
self.connect(self.gui.tabs.img.brightness.slider,
QtCore.SIGNAL('sliderMoved(int)'),
self.slots['camChangeBrightness'])
self.connect(self.gui.tabs.img.brightness.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.slots['camChangeBrightness'])
self.connect(self.gui.tabs.img.brightness.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.brightness.spin,
QtCore.SLOT('setValue(int)'))
self.connect(self.gui.tabs.img.brightness.spin,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.brightness.slider,
QtCore.SLOT('setValue(int)'))
# Setting up saturation stuff. Moving slider or changing its value
# updates cam's settings. Also connects spinbox to the slider to easily
# configure it.
self.connect(self.gui.tabs.img.saturation.slider,
QtCore.SIGNAL('sliderMoved(int)'),
self.slots['camChangeSaturation'])
self.connect(self.gui.tabs.img.saturation.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.slots['camChangeSaturation'])
self.connect(self.gui.tabs.img.saturation.slider,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.saturation.spin,
QtCore.SLOT('setValue(int)'))
self.connect(self.gui.tabs.img.saturation.spin,
QtCore.SIGNAL('valueChanged(int)'),
self.gui.tabs.img.saturation.slider,
QtCore.SLOT('setValue(int)'))
# Setting up the timer to periodically retrieve a cam's frame and
# display it.
# Interval between two requests is defined by self.fps (see
# self.__init__)
self.gui.video.timer = QtCore.QTimer(self)
self.connect(self.gui.video.timer, QtCore.SIGNAL('timeout()'), self.shot)
self.gui.video.timer.start(1000 / self.fps)
def initCam(self):
""" Initializes camera """
# Open camera with Cam OpenCV wrapper
self.gui.video.device = Camera.Cam(-1)
# Immediately grab and display a frame
self.shot()
# Retrieve HSBC parameters
self.camUpdateHsbc()
def camUpdateHsbc(self):
""" Updates image parameters sliders with cam's actual current values """
# Retrieve cam's HSBC (not the bank) parameters and set them to the
# matching controls (see self.buildguiTabImg()).
# Because sliders does only work with integers, we convert decimals
# values (defined between 0 and 1) to percent. The reverse conversion
# is done in slots self.camChange*()
self.gui.tabs.img.hue.slider.setValue(round(self.gui.video.device.hue(), 2) * 100)
self.gui.tabs.img.saturation.slider.setValue(round(self.gui.video.device.saturation(), 2) * 100)
# For these two, we want values from -50% to +50%. We substract 50 to
# the optained percentage to do it.
self.gui.tabs.img.brightness.slider.setValue(round(self.gui.video.device.brightness(), 2) * 100 - 50)
# For the contrast, the most small is the value, the most contrasted is
# the image. To fit common contrast settings, we revert this value.
self.gui.tabs.img.contrast.slider.setValue(round(self.gui.video.device.contrast(), 2) * -100 + 50)
def shot(self):
""" Grabs a frame from camera and display it"""
# Because I haven't found any efficient OpenCV → PyQt converter, and
# because properly “translating” IPLImages to QPixmaps pixel by pixel
# heavily unefficient, we simply write frames to a temporary JPEG file
# and re-open it with Qt.
# Grabs a frame and save it in the temp JPEG file
self.gui.video.device.save('.frame.tmp.jpg')
# Opens the temp image, convert it into a QPixmap, and display it in the
# label created on self.builGuiVideo()
self.gui.video.frame.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage('.frame.tmp.jpg')))
# Retrieve HSBC parameters (useless, since no updates but these made here)
#self.camUpdateHsbc()
@QtCore.pyqtSlot()
def saveFrame(self):
""" Allows user to save a frame as a image (This is also a PyQt slot) """
# Needs to be completed
# Ask the user for the destination file
dest = QtGui.QFileDialog.getSaveFileName(self, "Save frame as...", '', 'Images (*.png *.gif *.jpg *.jpeg)')
# Grab and save a frame in the given file
self.gui.video.device.save(str(dest))
@QtCore.pyqtSlot(int)
def camChangeHue(self, value):
""" Sets cam's hue value in percent (This is also a PyQt slot) """
# Because QSliders does not work with decimal values, we use percent
# instead. So we need to get a float between 0 and 1 then we set the
# value
self.gui.video.device.setHue(value / 100.0)
@QtCore.pyqtSlot(int)
def camChangeContrast(self, value):
""" Sets cam's contrast value in percent (This is also a PyQt slot) """
# Because QSliders does not work with decimal values, we use percent
# instead. So we need to get a float between 0 and 1 then we set the
# value.
# And because the range is from -50% to +50%, we add 50 to get a positive
# value
self.gui.video.device.setContrast((value * -1 + 50.0) / 100.0)
@QtCore.pyqtSlot(int)
def camChangeSaturation(self, value):
""" Sets cam's saturation value in percent (This is also a PyQt slot) """
# Because QSliders does not work with decimal values, we use percent
# instead. So we need to get a float between 0 and 1 then we set the
# value
self.gui.video.device.setSaturation(value / 100.0)
@QtCore.pyqtSlot(int)
def camChangeBrightness(self, value):
""" Sets cam's brightness value in percent (This is also a PyQt slot) """
# Because QSliders does not work with decimal values, we use percent
# instead. So we need to get a float between 0 and 1 then we set the
# value.
# And because the range is from -50% to +50%, we add 50 to get a positive
# value.
self.gui.video.device.setBrightness((value + 50.0) / 100.0)
if __name__ == '__main__' :
# Initialize application
app = QtGui.QApplication(sys.argv)
app.setApplicationName("camview")
# Instanciate the main window and display it
main = MainWindow()
main.show()
# Runs application
sys.exit(app.exec_())