-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcamera.py
More file actions
311 lines (271 loc) · 12.3 KB
/
Copy pathcamera.py
File metadata and controls
311 lines (271 loc) · 12.3 KB
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
import cv2
import numpy as np
from ctypes import cast, POINTER, c_ubyte
from time import sleep
from PyQt6.QtCore import *
from debugging import debugging, dbgVideo
from helperFunctions.connect import connection
from timeit import default_timer as timer
from helperFunctions.timer import MyTimer
from helperFunctions.createFile import createFile
from datetime import datetime
# conditonal imports
if not debugging:
from egrabber import *
def fpsToCyclePeriod(fps):
"""Converts FPS to CyclePeriod and vice versa"""
return 1e6/fps # 1e6/(2 * fps) is originally set in artemis
camNames = [] # Cameras fetched from the grabber
class Camera(QRunnable):
def __init__(self, grabber, name, timeKeeper):
super(Camera, self).__init__()
self.grabber = grabber # Euresys grabber
self.exposure = self.grabber.remote.get('ExposureTime') # Camera exposure
self.signals = CameraSignals() # Thread connection
self.cameraName = name # Camera model and vendor
self.running = True # Controls run function
self.display_zoom = 1 # Make image smaller or bigger
self.recording = False # Controls recording
self.saveResolution = 1472 # Defines the height
self.videoOutput = 'mp4' # Video format
self.savePath=None # Path to save video
self.expName=None # Name of the experiment to save files
self.timeKeeper = timeKeeper # Is this camera taking care of time?
self.recordingStatus='Null' # Shows info about recording
self.everRecorded = False # To create a new container
self.start_drag = None # Will be change to a point (x,y). Based on resized img
self.end_drag=None # Point
self.areaOfInteres = None # List of int representing dimensions: [x1, x2, y1, y2]
self.hoverinOn = None # Point where mouse is hovering
self.lowerThresh = 0 # Image thresholding
self.higherThresh=255 # Image thresholding
self.timer=None # Timer for camera
self.capturing = False # Controls when a picture is taken
self.imgFormat = 'png' # Saves image in this format
self.showThresh = False # Wehater or not to threshold the image
self.fps=10
try:
self.fps=min(self.fps, fpsToCyclePeriod(self.grabber.device.get('CycleMinimumPeriod'))) # Current fps limited to 10
except GenTLException as err:
print('ERROR: Cannot fetch grabber FPS for {}: {}. Fallback to fps {}'.format(self.cameraName, err, self.fps))
def settings(self):
"""Get camera settings by camera name"""
# return {'fps': self.fps, 'exposure': self.exposure, 'roi': self.areaOfInteres
# , 'displaying': {'scale': self.display_zoom, 'threshold': {'low': self.lowerThresh, 'hight': self.higherThresh}}
# , 'recording': {'frameHeight': self.saveResolution, 'format': self.videoOutput, 'path': self.savePath} # timeout
# }
st = {'fps': self.fps, 'exposure': self.exposure
, 'displaying': {'scale': self.display_zoom, 'threshold': {'low': self.lowerThresh, 'hight': self.higherThresh}}
, 'recording': {'frameHeight': self.saveResolution, 'format': self.videoOutput}
}
if self.areaOfInteres:
st['roi'] = self.areaOfInteres
if self.savePath:
st['recording']['path'] = self.savePath
return st
def loadSettings(self, st):
"""Get camera settings by camera name"""
self.fps = st['fps']
try:
self.grabber.device.set('CycleMinimumPeriod', fpsToCyclePeriod(self.fps))
print('{} FPS set to: {}'.format(self.cameraName, self.fps))
except GenTLException as err:
print('ERROR: failed to set grabber FPS for {}: '.format(self.cameraName) + str(err))
self.exposure = st['exposure']
try:
self.grabber.remote.set('ExposureTime', self.exposure)
print('{} exposure set to: {}'.format(self.cameraName, self.exposure))
except GenTLException as err:
print('ERROR: failed to set grabber exposure for {}: '.format(self.cameraName) + str(err))
self.areaOfInteres = st.get('roi')
self.display_zoom = st['displaying']['scale']
self.lowerThresh = st['displaying']['threshold']['low']
self.higherThresh = st['displaying']['threshold']['hight']
self.saveResolution = st['recording']['frameHeight']
self.videoOutput = st['recording']['format']
self.savePath = st['recording'].get('path')
# Takes list of grabber from gui
def run(self):
#print('Camera starting')
# Local variables
lastTime = 0
measuredTime = 1
allTimes = []
# Open camera
if debugging:
cap = cv2.VideoCapture(dbgVideo)
else:
# Create 3 buffers for the grabber as listed in the eGrabber Programmer Guide
self.grabber.realloc_buffers(3)
# Start the grabber
self.grabber.start()
# Capture and display loop
while self.running:
# Get image
if debugging:
ret, img = cap.read()
sleep(1)
timeStamp=datetime.now()
if not ret:
print('error reading video')
break
else:
with Buffer(self.grabber) as buffer:
# Get address, width, and height of image in buffer
ptr = buffer.get_info(BUFFER_INFO_BASE, INFO_DATATYPE_PTR)
w = buffer.get_info(BUFFER_INFO_WIDTH, INFO_DATATYPE_SIZET)
h = buffer.get_info(BUFFER_INFO_DELIVERED_IMAGEHEIGHT, INFO_DATATYPE_SIZET)
timeStamp = buffer.get_info(BUFFER_INFO_TIMESTAMP, INFO_DATATYPE_UINT64)
# Convert image to BGR format
bgr = buffer.convert('BGR8')
# Resize and display the image (using opencv and numpy)
data = cast(bgr.get_address(), POINTER(c_ubyte * bgr.get_buffer_size())).contents
img = np.frombuffer(data, count=bgr.get_buffer_size(), dtype=np.uint8).reshape((h,w,3))
# Crop image
img = self.cropImage(img)
h, w, channels = img.shape
# Show current pixelColor
infoImg = self.hoveringColors(img.copy(),h)
# Display image
disImg=self.drawRect(infoImg)
try:
disImg = cv2.resize(disImg, (int(w * self.display_zoom), int(h * self.display_zoom)))
except:
print('Problem dimensions visualizing: ', w,h)
if self.showThresh:
# Threshold image
threshImg = self.threshold(img.copy())
try:
threshImg = cv2.resize(threshImg, (int(w * self.display_zoom), int(h * self.display_zoom)))
except:
print('Problem dimensions thresholding: ', w,h)
# Send image to gui
self.signals.images.emit(('Thresholded '+self.cameraName,threshImg))
# Emit images
if self.capturing:
blank = np.full((int(h * self.display_zoom),int(w * self.display_zoom),3),255, np.uint8)
self.signals.images.emit((self.cameraName,blank))
else:
self.signals.images.emit((self.cameraName,disImg))
# Image to save
# Resize
factor = self.saveResolution/h
if factor>1:
saveW = w
saveH=h
else:
saveW = w*factor
saveH=self.saveResolution
outImg = cv2.resize(img, (int(saveW), int(saveH)))
if self.recording and self.fps !=0:
self.recordVideo(outImg,saveW,saveH,timeStamp)
if self.capturing:
self.capturing=False
# Create file name
fileName=self.expName+'_'+self.cameraName
name = createFile(self.savePath,fileName,self.imgFormat)
cv2.imwrite(name, outImg)
# Measure fps
timeNow=timer()
timePassed= timeNow-lastTime
allTimes.append(timeStamp)
if timePassed> measuredTime:
# Set new time
lastTime=timeNow
# Update fps
if len(allTimes)>2:
self.fps = (len(allTimes)*1000000)/(allTimes[-1]-allTimes[0])
allTimes=[]
# Send signal
if self.timeKeeper:
self.signals.updateInfo.emit()
if not debugging:
self.grabber.stop()
if self.everRecorded:
self.out.release()
self.everRecorded = False
print('end of camera ', self.cameraName)
# Uses opencv to record video into path
def recordVideo(self, img, w,h, timeStamp):
# Create container to record
if not self.everRecorded:
videoName=self.expName+'_'+self.cameraName
name = createFile(self.savePath,videoName,self.videoOutput)
if self.videoOutput =='avi':
fourcc='MJPG'
else:
fourcc='mp4v'
# Create video holder
self.out =cv2.VideoWriter(name,cv2.VideoWriter_fourcc(*fourcc),self.fps, (int(w),int(h)))
self.everRecorded=True
self.timer = MyTimer()
self.timer.start()
# Create file with timeStamps
timeName='times_'+self.expName+'_'+self.cameraName
self.timeName=createFile(self.savePath,timeName,'txt')
# Save image in path
f = open(self.timeName,'a')
f.write(str(timeStamp))
f.write('\n')
self.out.write(img)
# Draws a rectangle in image when user is selecting area wanted
def drawRect(self,img):
if self.start_drag is not None:
if self.end_drag is None:
self.end_drag = (self.start_drag[0]+5,self.start_drag[1]+5)
# Change values based on zoom
start = (int(self.start_drag[0]/self.display_zoom),int(self.start_drag[1]/self.display_zoom))
end = (int(self.end_drag[0]/self.display_zoom),int(self.end_drag[1]/self.display_zoom))
# Draw rectangle
img = cv2.rectangle(img, start, end, (102,255,102), 8)
return img
# Saves area selected by the user as area of interest
def setZoom(self):
if self.areaOfInteres is None:
x1 = int(self.start_drag[0]/self.display_zoom)
y1=int(self.start_drag[1]/self.display_zoom)
x2=int(self.end_drag[0]/self.display_zoom)
y2=int(self.end_drag[1]/self.display_zoom)
if x2<x1:
c=x1
x1=x2
x2=c
if y2<y1:
c = y1
y1=y2
y2=c
y = y2-y1
x = x2 - x1
if x <32 or y<32:
self.areaOfInteres = None
else:
self.areaOfInteres = [x1,x2,y1,y2]
# Crops image to area selected
def cropImage(self,img):
if self.areaOfInteres is not None:
x1,x2,y1,y2=self.areaOfInteres
img=img[y1:y2, x1:x2]
return img
# Saves last mouse position on image
def hovering(self,x,y):
self.hoverinOn= [int(y/self.display_zoom), int(x/self.display_zoom)]
# Prints pixel color intensity on image
def hoveringColors(self,img,h):
if self.hoverinOn is not None:
try:
(b, g, r) = img[self.hoverinOn[0],self.hoverinOn[1]]
except:
b,g,r=0,0,0
text='B:'+str(b)+' G:'+str(g)+' R:'+str(r)
img=cv2.putText(img,text,(15,h),cv2.FONT_HERSHEY_SIMPLEX,3,(102,255,102),2)
return img
# Thresholds image based on current threshold values
def threshold(self,img):
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,lowImg = cv2.threshold(img.copy(),self.lowerThresh,255,cv2.THRESH_BINARY)
ret,highImg = cv2.threshold(img.copy(),self.higherThresh,255,cv2.THRESH_BINARY_INV)
img = cv2.bitwise_and(lowImg, highImg)
return img
class CameraSignals(QObject):
images = pyqtSignal(object)
updateInfo = pyqtSignal()