-
Notifications
You must be signed in to change notification settings - Fork 24
/
ProcessingThread.py
206 lines (179 loc) · 9.96 KB
/
ProcessingThread.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
from PyQt5.QtCore import QThread, QMutex, QTime, qDebug, QMutexLocker, pyqtSignal
from PyQt5.QtGui import QImage
from queue import Queue
import cv2
from MatToQImage import matToQImage
from Structures import *
from Config import *
class ProcessingThread(QThread):
newFrame = pyqtSignal(QImage)
updateStatisticsInGUI = pyqtSignal(ThreadStatisticsData)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
def __init__(self, sharedImageBuffer, deviceUrl, cameraId, parent=None):
super(QThread, self).__init__(parent)
self.sharedImageBuffer = sharedImageBuffer
self.cameraId = cameraId
# Save Device Url
self.deviceUrl = deviceUrl
# Initialize members
self.doStopMutex = QMutex()
self.processingMutex = QMutex()
self.t = QTime()
self.processingTime = 0
self.doStop = False
self.enableFrameProcessing = False
self.sampleNumber = 0
self.fpsSum = 0.0
self.fps = Queue()
self.currentROI = QRect()
self.imgProcFlags = ImageProcessingFlags()
self.imgProcSettings = ImageProcessingSettings()
self.statsData = ThreadStatisticsData()
self.frame = None
self.currentFrame = None
def run(self):
while True:
##############################
# Stop thread if doStop=True #
##############################
self.doStopMutex.lock()
if self.doStop:
self.doStop = False
self.doStopMutex.unlock()
break
self.doStopMutex.unlock()
################################
################################
# Save processing time
self.processingTime = self.t.elapsed()
# Start timer (used to calculate processing rate)
self.t.start()
with QMutexLocker(self.processingMutex):
# Get frame from queue, store in currentFrame, set ROI
# self.currentFrame = Mat(self.sharedImageBuffer.getByDeviceUrl(self.deviceUrl).get().clone(),
# self.currentROI)
self.currentFrame = self.sharedImageBuffer.getByDeviceUrl(self.deviceUrl).get()[
self.currentROI.y():(self.currentROI.y() + self.currentROI.height()),
self.currentROI.x():(self.currentROI.x() + self.currentROI.width())].copy()
# Example of how to grab a frame from another stream (where Device Url=1)
# Note: This requires stream synchronization to be ENABLED (in the Options menu of MainWindow)
# and frame processing for the stream you are grabbing FROM to be DISABLED.
# if sharedImageBuffer.containsImageBufferForDeviceUrl(1):
# # Grab frame from another stream (connected to camera with Device Url=1)
# Mat frameFromAnotherStream = Mat(sharedImageBuffer.getByDeviceUrl(1).getFrame(), currentROI)
# # Linear blend images together using OpenCV and save the result to currentFrame. Note: beta=1-alpha
# addWeighted(frameFromAnotherStream, 0.5, currentFrame, 0.5, 0.0, currentFrame)
##################################
# PERFORM IMAGE PROCESSING BELOW #
##################################
# Grayscale conversion (in-place operation)
if self.imgProcFlags.grayscaleOn and (
self.currentFrame.shape[2] == 3 or self.currentFrame.shape[2] == 4):
self.currentFrame = cv2.cvtColor(self.currentFrame, cv2.COLOR_BGR2GRAY)
# Smooth (in-place operations)
if self.imgProcFlags.smoothOn:
if self.imgProcSettings.smoothType == 0:
# BLUR
self.currentFrame = cv2.blur(self.currentFrame,
(self.imgProcSettings.smoothParam1,
self.imgProcSettings.smoothParam2))
elif self.imgProcSettings.smoothType == 1:
# GAUSSIAN
self.currentFrame = cv2.GaussianBlur(self.currentFrame,
(self.imgProcSettings.smoothParam1,
self.imgProcSettings.smoothParam2),
sigmaX=self.imgProcSettings.smoothParam3,
sigmaY=self.imgProcSettings.smoothParam4)
elif self.imgProcSettings.smoothType == 2:
# MEDIAN
self.currentFrame = cv2.medianBlur(self.currentFrame, self.imgProcSettings.smoothParam1)
# Dilate
if self.imgProcFlags.dilateOn:
self.currentFrame = cv2.dilate(self.currentFrame, self.kernel,
iterations=self.imgProcSettings.dilateNumberOfIterations)
# Erode
if self.imgProcFlags.erodeOn:
self.currentFrame = cv2.erode(self.currentFrame, self.kernel,
iterations=self.imgProcSettings.erodeUrlOfIterations)
# Flip
if self.imgProcFlags.flipOn:
self.currentFrame = cv2.flip(self.currentFrame, self.imgProcSettings.flipCode)
# Canny edge detection
if self.imgProcFlags.cannyOn:
self.currentFrame = cv2.Canny(self.currentFrame,
threshold1=self.imgProcSettings.cannyThreshold1,
threshold2=self.imgProcSettings.cannyThreshold2,
apertureSize=self.imgProcSettings.cannyApertureSize,
L2gradient=self.imgProcSettings.cannyL2gradient)
##################################
# PERFORM IMAGE PROCESSING ABOVE #
##################################
# Convert Mat to QImage
self.frame = matToQImage(self.currentFrame)
# Inform GUI thread of new frame (QImage)
self.newFrame.emit(self.frame)
# Update statistics
self.updateFPS(self.processingTime)
self.statsData.nFramesProcessed += 1
# Inform GUI of updated statistics
self.updateStatisticsInGUI.emit(self.statsData)
qDebug("Stopping processing thread...")
def doShowImage(self, val):
with QMutexLocker(self.processingMutex):
self.doShow = val
def updateFPS(self, timeElapsed):
# Add instantaneous FPS value to queue
if timeElapsed > 0:
self.fps.put(1000 / timeElapsed)
# Increment sample number
self.sampleNumber += 1
# Maximum size of queue is DEFAULT_PROCESSING_FPS_STAT_QUEUE_LENGTH
if self.fps.qsize() > PROCESSING_FPS_STAT_QUEUE_LENGTH:
self.fps.get()
# Update FPS value every DEFAULT_PROCESSING_FPS_STAT_QUEUE_LENGTH samples
if self.fps.qsize() == PROCESSING_FPS_STAT_QUEUE_LENGTH and self.sampleNumber == PROCESSING_FPS_STAT_QUEUE_LENGTH:
# Empty queue and store sum
while not self.fps.empty():
self.fpsSum += self.fps.get()
# Calculate average FPS
self.statsData.averageFPS = self.fpsSum / PROCESSING_FPS_STAT_QUEUE_LENGTH
# Reset sum
self.fpsSum = 0.0
# Reset sample number
self.sampleNumber = 0
def stop(self):
with QMutexLocker(self.doStopMutex):
self.doStop = True
def updateBoxesBufferMax(self, boxesBufferMax):
with QMutexLocker(self.processingMutex):
self.boxesBufferMax = boxesBufferMax
def updateImageProcessingFlags(self, imgProcFlags):
with QMutexLocker(self.processingMutex):
self.imgProcFlags.grayscaleOn = imgProcFlags.grayscaleOn
self.imgProcFlags.smoothOn = imgProcFlags.smoothOn
self.imgProcFlags.dilateOn = imgProcFlags.dilateOn
self.imgProcFlags.erodeOn = imgProcFlags.erodeOn
self.imgProcFlags.flipOn = imgProcFlags.flipOn
self.imgProcFlags.cannyOn = imgProcFlags.cannyOn
def updateImageProcessingSettings(self, imgProcSettings):
with QMutexLocker(self.processingMutex):
self.imgProcSettings.smoothType = imgProcSettings.smoothType
self.imgProcSettings.smoothParam1 = imgProcSettings.smoothParam1
self.imgProcSettings.smoothParam2 = imgProcSettings.smoothParam2
self.imgProcSettings.smoothParam3 = imgProcSettings.smoothParam3
self.imgProcSettings.smoothParam4 = imgProcSettings.smoothParam4
self.imgProcSettings.dilateNumberOfIterations = imgProcSettings.dilateNumberOfIterations
self.imgProcSettings.erodeUrlOfIterations = imgProcSettings.erodeUrlOfIterations
self.imgProcSettings.flipCode = imgProcSettings.flipCode
self.imgProcSettings.cannyThreshold1 = imgProcSettings.cannyThreshold1
self.imgProcSettings.cannyThreshold2 = imgProcSettings.cannyThreshold2
self.imgProcSettings.cannyApertureSize = imgProcSettings.cannyApertureSize
self.imgProcSettings.cannyL2gradient = imgProcSettings.cannyL2gradient
def setROI(self, roi):
with QMutexLocker(self.processingMutex):
self.currentROI.setX(roi.x())
self.currentROI.setY(roi.y())
self.currentROI.setWidth(roi.width())
self.currentROI.setHeight(roi.height())
def getCurrentROI(self):
return QRect(self.currentROI.x(), self.currentROI.y(), self.currentROI.width(), self.currentROI.height())