def __init__(self): super(MainWindow, self).__init__() # self.setGeometry(5,60,700,500) self.setWindowTitle('Video Processing') self.background_color = self.palette().color(QtGui.QPalette.Background) # if os.name is 'posix': # is a mac or linux # scriptDir = os.path.dirname(sys.argv[0]) # else: # is a windows # scriptDir = os.getcwd() # This is the main Window self.main_Widget = QtWidgets.QWidget(self) self.setCentralWidget(self.main_Widget) # Elements of the main window # image viewer self.displayImage = ImageViewer() # Menu bar __ Top - Main options self.menuBar = QtWidgets.QMenuBar(self) self.setStyleSheet(""" QMenuBar { font-size:18px; background : transparent; } """) # Tool bar __ Top - Functions to analyze the current image self.toolBar_Top = QtWidgets.QToolBar(self) # Tool bar __ Bottom - Play/pause buttons self.toolBar_Bottom = QtWidgets.QToolBar(self) # Frame Slider _ Bottom - Easily move between frames self.slider_Bottom = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(100) self.slider_Bottom.setValue(1) self.slider_Bottom.setTickInterval(1) self.slider_Bottom.setEnabled(False) self.slider_Bottom.valueChanged.connect(self.slidervaluechange) # Status Bar _ Bottom - Show the current frame number self.frameLabel = QtWidgets.QLabel('') self.frameLabel.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom = QtWidgets.QStatusBar() self.statusBar_Bottom.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom.addPermanentWidget(self.frameLabel) # Definition of Variables self.video_handler = None self.video_name = None # name and location of video file self.current_frame = 0 # what is the current frame self.timer = QtCore.QTimer() # controls video playback # initialize the User Interface self.initUI() self.show()
class ShowExample(QtWidgets.QMainWindow): def __init__(self): super(ShowExample, self).__init__() self.setWindowTitle('Example') #self._new_window = None if os.name is 'posix': #is a mac or linux scriptDir = os.path.dirname(sys.argv[0]) else: #is a windows scriptDir = os.getcwd() img_Qt = QtGui.QImage(scriptDir + os.path.sep + 'include' +os.path.sep +'icons'+ os.path.sep + 'Facial-Nerve-Center.jpg') pixmap = QtGui.QPixmap.fromImage(img_Qt) self._view_photo = ImageViewer() self._view_photo.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(230,230,230))) self._view_photo.setPhoto(pixmap) self.label_title = QtWidgets.QLabel() self.label_title.setText('Sample text') self.label_title.setWordWrap(True) self.label_title.setFont(QtGui.QFont("Times",weight=QtGui.QFont.Bold)) self.label_content = QtWidgets.QLabel() self.label_content .setText('Sample text') self.label_content .setWordWrap(True) #self.label.setMaximumWidth(500) self.main_Widget = QtWidgets.QWidget(self) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.label_title) layout.addWidget(self.label_content) layout.addWidget(self._view_photo) self.main_Widget.setLayout(layout) self.setCentralWidget(self.main_Widget)
class MainWindow(QtWidgets.QMainWindow): def __init__(self): super(MainWindow, self).__init__() # self.setGeometry(5,60,700,500) self.setWindowTitle('Video Processing') self.background_color = self.palette().color(QtGui.QPalette.Background) # if os.name is 'posix': # is a mac or linux # scriptDir = os.path.dirname(sys.argv[0]) # else: # is a windows # scriptDir = os.getcwd() # This is the main Window self.main_Widget = QtWidgets.QWidget(self) self.setCentralWidget(self.main_Widget) # Elements of the main window # image viewer self.displayImage = ImageViewer() # Menu bar __ Top - Main options self.menuBar = QtWidgets.QMenuBar(self) self.setStyleSheet(""" QMenuBar { font-size:18px; background : transparent; } """) # Tool bar __ Top - Functions to analyze the current image self.toolBar_Top = QtWidgets.QToolBar(self) # Tool bar __ Bottom - Play/pause buttons self.toolBar_Bottom = QtWidgets.QToolBar(self) # Frame Slider _ Bottom - Easily move between frames self.slider_Bottom = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(100) self.slider_Bottom.setValue(1) self.slider_Bottom.setTickInterval(1) self.slider_Bottom.setEnabled(False) self.slider_Bottom.valueChanged.connect(self.slidervaluechange) # Status Bar _ Bottom - Show the current frame number self.frameLabel = QtWidgets.QLabel('') self.frameLabel.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom = QtWidgets.QStatusBar() self.statusBar_Bottom.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom.addPermanentWidget(self.frameLabel) # Definition of Variables self.video_handler = None self.video_name = None # name and location of video file self.current_frame = 0 # what is the current frame self.timer = QtCore.QTimer() # controls video playback # initialize the User Interface self.initUI() self.show() def initUI(self): # Populate the different menus file_menu = self.menuBar.addMenu("&File") load_video = file_menu.addAction("Load Video File") load_video.setShortcut("Ctrl+F") load_video.setStatusTip('Load video file, accepted formats : .mp4, .avi, .mov') load_video.triggered.connect(self.openvideofile) load_landmarks = file_menu.addAction("Load Landmarks File") load_landmarks.setShortcut("Ctrl+L") load_landmarks.setStatusTip('Load landmark file, accepted formats : .csv') # load_landmarks.triggered.connect(self.load_file) quit_program = file_menu.addAction("Quit") quit_program.setShortcut("Ctrl+Q") # quit_program.triggered.connect(self.load_file) video_menu = self.menuBar.addMenu("&Video") play_video = video_menu.addAction("Play Video") play_video.setShortcut("Ctrl+P") play_video.setStatusTip('Play video at given playback speed') # play_video.triggered.connect(self.load_file) stop_video = video_menu.addAction("Stop Video") stop_video.setShortcut("Ctrl+S") stop_video.setStatusTip('Stop video playback') # stop_video.triggered.connect(self.load_file) jump_to_frame = video_menu.addAction("Jump to Frame") jump_to_frame.setShortcut("Ctrl+J") jump_to_frame.setStatusTip('Jump to certain frame') # jump_to_frame.triggered.connect(self.load_file) playback_settings = video_menu.addAction("Playback Settings") playback_settings.setShortcut("Ctrl+P") playback_settings.setStatusTip('Define video playback settings') # playback_settings.triggered.connect(self.load_file) landmarks_menu = self.menuBar.addMenu("&Landmarks") # process_current_frame = landmarks_menu.addAction("Process Current Frame") # process_current_frame.setShortcut("Ctrl+C") # process_current_frame.setStatusTip('Determine facial landmarks for current frame') # # process_current_frame.triggered.connect(self.load_file) process_some_frame = landmarks_menu.addAction("Process Frames") process_some_frame.setShortcut("Ctrl+S") process_some_frame.setStatusTip('Determine facial landmarks for some frames in the video') # process_some_frame.triggered.connect(self.load_file) process_all_frame = landmarks_menu.addAction("Process All Frames") process_all_frame.setShortcut("Ctrl+A") process_all_frame.setStatusTip('Determine facial landmarks for all frames in the video') # process_all_frame.triggered.connect(self.load_file) process_settings = landmarks_menu.addAction("Process Frames Settings") process_settings.setShortcut("Ctrl+L") process_settings.setStatusTip('Determine facial landmarks for all frames in the video') # process_settings.triggered.connect(self.load_file) # fill the top toolbar process_current_frame = QtWidgets.QAction('Process current frame', self) process_current_frame.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # process_current_frame.connect(self.match_iris) toggle_landmark = QtWidgets.QAction('Show/Hide facial landmarks', self) toggle_landmark.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # toggle_landmark.connect(self.match_iris) manual_adjustment = QtWidgets.QAction('Manually adjust landmarks position in current frame', self) manual_adjustment.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # manual_adjustment.connect(self.match_iris) landmark_settings = QtWidgets.QAction('Adjust landmark visualization settings', self) landmark_settings.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # landmark_settings.connect(self.match_iris) show_metrics = QtWidgets.QAction('Display facial metrics in current frame', self) show_metrics.setIcon(QtGui.QIcon('./icons/facial-metrics.png')) # show_metrics.connect(self.match_iris) snapshot = QtWidgets.QAction('Save snapshot of current view', self) snapshot.setIcon(QtGui.QIcon('./icons/profile.png')) # snapshot.connect(self.match_iris) self.toolBar_Top.addActions((process_current_frame, toggle_landmark, manual_adjustment, landmark_settings, show_metrics, snapshot)) self.toolBar_Top.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar_Top.actions(): widget = self.toolBar_Top.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar_Top.setMinimumSize(self.toolBar_Top.sizeHint()) self.toolBar_Top.setStyleSheet('QToolBar{spacing:8px;}') # fill the bottom toolbar play_action = QtWidgets.QAction('Play', self) play_action.setShortcut('Shift+S') play_action.setIcon(QtGui.QIcon('./icons/play-arrow.png')) play_action.triggered.connect(self.playvideo) stop_action = QtWidgets.QAction('Stop', self) stop_action.setShortcut('Shift+Z') stop_action.setIcon(QtGui.QIcon('./icons/pause.png')) stop_action.triggered.connect(self.stopvideo) fastforward_action = QtWidgets.QAction('Fast Forward', self) fastforward_action.setShortcut('Shift+D') fastforward_action.setIcon(QtGui.QIcon('./icons/fast-forward.png')) # fastforward_action.triggered.connect(self.match_iris) rewind_action = QtWidgets.QAction('Rewind', self) rewind_action.setShortcut('Shift+A') rewind_action.setIcon(QtGui.QIcon('./icons/rewind.png')) # rewind_action.triggered.connect(self.match_iris) # spacer widget for left left_spacer = QtWidgets.QWidget(self) left_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # spacer widget for right right_spacer = QtWidgets.QWidget(self) right_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # fill the bottom toolbar self.toolBar_Bottom.addWidget(left_spacer) self.toolBar_Bottom.addActions((rewind_action, play_action, stop_action, fastforward_action)) self.toolBar_Bottom.addWidget(right_spacer) self.toolBar_Bottom.setIconSize(QtCore.QSize(35, 35)) # for action in self.toolBar_Bottom.actions(): # widget = self.toolBar_Bottom.widgetForAction(action) # widget.setFixedSize(35, 35) self.toolBar_Bottom.setMinimumSize(self.toolBar_Bottom.sizeHint()) self.toolBar_Bottom.setStyleSheet('QToolBar{spacing:8px;}') # Create the layout # The main window has 6 components in the following order: # Menu Bar - Tool Bar - Image viewer - Tool Bar - Slider - Status Bar layout = QtWidgets.QVBoxLayout() layout.addWidget(self.menuBar) layout.addWidget(self.toolBar_Top) layout.addWidget(self.displayImage) layout.addWidget(self.toolBar_Bottom) layout.addWidget(self.slider_Bottom) self.setStatusBar(self.statusBar_Bottom) # Set the defined layout in the main window self.main_Widget.setLayout(layout) self.setGeometry(300, 100, 600, 800) def openvideofile(self): # load a file using the widget name, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Load Video File', '', "Video files (*.mp4 *.avi *.mov *.MP4 *.AVI *.MOV)") if not name: pass else: name = os.path.normpath(name) # change window name to match the file name self.setWindowTitle('Video Processing - ' + name.split(os.path.sep)[-1]) # self.video_handler = VideoHandler(name) # image = self.video_handler.readfirstframe() # self.displayImage._opencvimage = image # self.displayImage.update_view() # # update the frame number # self.current_frame = 0 # # put the frame information in the app # self.frameLabel.setText('Frame :'+str(int(self.current_frame)+1)+'/'+str(self.video_handler.video_length)) # # update the slider # self.slider_Bottom.setMinimum(1) # self.slider_Bottom.setMaximum(self.video_handler.video_length) # self.slider_Bottom.blockSignals(True) # self.slider_Bottom.setValue(1) # self.slider_Bottom.blockSignals(False) # self.slider_Bottom.setEnabled(True) # name = os.path.normpath(name) # # Remove previous video handlers to avoid taking odd frames # self.video_handle = None # # change window name to match the file name # self.setWindowTitle('Video Processing - ' + name.split(os.path.sep)[-1]) # # # user provided a video, open it using OpenCV video_handler = cv2.VideoCapture(name) # read the video success, image = video_handler.read() # get the first frame if success: # if the frame exists then show the image self.displayImage._opencvimage = image self.displayImage.update_view() num_frames = int(video_handler.get(cv2.CAP_PROP_FRAME_COUNT)) video_length = num_frames video_fps = int(video_handler.get(cv2.CAP_PROP_FPS)) video_fps = video_fps self.current_frame = 0 # put the frame information in the app self.frameLabel.setText('Frame :'+str(int(self.current_frame)+1)+'/'+str(video_length)) # update the slider self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(video_length) self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(1) self.slider_Bottom.blockSignals(False) self.slider_Bottom.setEnabled(True) video_handler.release() def playvideo(self): # verify that the video handler is not empty if self.video_handler is not None: self.timer.timeout.connect(self.nextframefunction) self.timer.start(1000.0/self.video_handler.playbackspeed) def nextframefunction(self): # verify that we are not at the last frame if self.current_frame <= self.video_handler.video_length - 1: # verify the there are frames in the frames in the queue if self.video_handler.more(): image = self.video_handler.read() self.displayImage._opencvimage = image self.displayImage.update_view() self.current_frame += 1 # put the frame information in the app self.frameLabel.setText( 'Frame :' + str(int(self.current_frame) + 1) + '/' + str(self.video_handler.video_length)) # update the slider self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(self.current_frame + 1) self.slider_Bottom.blockSignals(False) else: # finished the video self.timer.stop() else: # reached the end of the video self.timer.stop() @pyqtSlot def putframe(self,image): # # verify that we are not in the last frame # if self.current_frame <= self.video_length - 1: # success, image = self.video_handle.read() # if success: # # if the frame exists then show the image # self.displayImage._opencvimage = image # self.displayImage.update_view() # # update the frame number at the bottom of the app # self.frameLabel.setText('Frame : ' + str(int(self.current_frame)+1) + '/' + str(self.video_length)) # # update the slider # self.slider_Bottom.blockSignals(True) # self.slider_Bottom.setValue(self.current_frame + 1) # self.slider_Bottom.blockSignals(False) # # update frame number # self.current_frame += 1. # else: # if it doens't then stop the time # self.timer.stop() # # else: # reached the end of the video # self.timer.stop() def stopvideo(self): # stop video if video is playing if self.timer.isActive(): # verify is the video is running # activate slider and stop playback self.video_handler.stop() self.timer.stop() def slidervaluechange(self): pass # slider_value = self.slider_Bottom.value() # self.video_handle.set(cv2.CAP_PROP_POS_FRAMES, slider_value) # success, image = self.video_handle.read() # if success: # # if the frame exists then show the image # self.displayImage._opencvimage = image # self.displayImage.update_view() # # update the frame number at the bottom of the app # self.frameLabel.setText('Frame : ' + str(int(self.current_frame) + 1) + '/' + str(self.video_length)) # # update frame number # self.current_frame = slider_value if __name__ == '__main__': __spec__ = "ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>)" freeze_support() if not QtWidgets.QApplication.instance(): app = QtWidgets.QApplication(sys.argv) else: app = QtWidgets.QApplication.instance() app.setStyle(QtWidgets.QStyleFactory.create('Cleanlooks')) GUI = MainWindow() GUI.show() app.exec_()
class Emotrics(QtWidgets.QDialog): def __init__(self, FileName): super(Emotrics, self).__init__() #self.setGeometry(5,60,700,500) self.setWindowTitle('Emotrics') scriptDir = os.path.dirname(os.path.realpath(sys.argv[0])) self.setWindowIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'meei_3WR_icon.ico')) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinMaxButtonsHint) self._new_window = None self._file_name = FileName self._Patient = None self._tab1_results = None self._tab2_results = None self._tab3_results = None self._toggle_landmaks = True self._toggle_lines = True self._Scale = 1 #this variable carries the scale of the image if it #needs to be resized, if Scale = 1 then the origina #image was used for processing. If Scale > 1 then #the original image was too large and a resized image #was used for processing # create Thread to take care of the landmarks and iris estimation self.thread_landmarks = QtCore.QThread() # no parent! #initialize the User Interface self.initUI() def initUI(self): #local directory scriptDir = os.getcwd( ) #os.path.dirname(os.path.realpath(sys.argv[0])) #image #read the image from file #img_Qt = QtGui.QImage(scriptDir + os.path.sep + 'include' +os.path.sep +'icon_color'+ os.path.sep + 'Facial-Nerve-Center.jpg') #img_show = QtGui.QPixmap.fromImage(img_Qt) #the image will be displayed in the custom ImageViewer self.displayImage = ImageViewer() #self.displayImage.setPhoto(img_show) #toolbar fitAction = QtWidgets.QAction('Fit image to window', self) fitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'fit_to_size_icon.png')) fitAction.triggered.connect(self.displayImage.show_entire_image) eyeAction = QtWidgets.QAction('Match iris diameter', self) eyeAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon.png')) eyeAction.triggered.connect(self.match_iris) eyeLoad = QtWidgets.QAction('Import iris position and diameter', self) eyeLoad.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon_import.png')) eyeLoad.triggered.connect(self.load_iris) centerAction = QtWidgets.QAction('Find face center', self) centerAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'center_icon.png')) centerAction.triggered.connect(self.face_center) toggleAction = QtWidgets.QAction('Toggle landmarks', self) toggleAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'toggle-icon.png')) toggleAction.triggered.connect(self.toggle_landmarks) measuresAction = QtWidgets.QAction('Facial metrics', self) measuresAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'ruler_icon.png')) measuresAction.triggered.connect(self.create_new_window) saveAction = QtWidgets.QAction('Save results', self) saveAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'save_icon.png')) saveAction.triggered.connect(self.save_results) snapshotAction = QtWidgets.QAction('Save current view', self) snapshotAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'snapshot_icon.png')) snapshotAction.triggered.connect(self.save_snapshot) exitAction = QtWidgets.QAction('Exit', self) exitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'exit_icon.png')) exitAction.triggered.connect(self.close_app) #create the toolbar and add the actions self.toolBar = QtWidgets.QToolBar(self) self.toolBar.addActions( (fitAction, eyeAction, eyeLoad, centerAction, toggleAction, measuresAction, snapshotAction, saveAction, exitAction)) #set the size of each icon to 50x50 self.toolBar.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar.actions(): widget = self.toolBar.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar.setMinimumSize(self.toolBar.sizeHint()) self.toolBar.setStyleSheet('QToolBar{spacing:5px;}') #the main window consist of the toolbar and the ImageViewer layout = QtWidgets.QVBoxLayout() layout.addWidget(self.toolBar) layout.addWidget(self.displayImage) self.setLayout(layout) self.load_file(self._file_name) self.displayImage.update_view() self.show() def create_new_window(self): #this creates a new window to display all the facial metrics, there #are two modes, one if there is no Patient (self._Patient = None) #and another if there is a patient (two photos) if self._Patient is None: if self.displayImage._shape is not None: #if the measurements window is already open then close it if self._new_window is not None: self._new_window.close() self._new_window = None #compute the facial metrics using the landmarks MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual = get_measurements_from_data( self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye) #send all the information the the appropiate places in the window self._tab1_results = CustomTabResult() #filling t_new_window_tab1_results he info for the right self._tab1_results._CE_right.setText('{0:.2f}'.format( MeasurementsRight.CommissureExcursion)) self._tab1_results._SA_right.setText('{0:.2f}'.format( MeasurementsRight.SmileAngle)) self._tab1_results._DS_right.setText('{0:.2f}'.format( MeasurementsRight.DentalShow)) self._tab1_results._MRD1_right.setText('{0:.2f}'.format( MeasurementsRight.MarginalReflexDistance1)) self._tab1_results._MRD2_right.setText('{0:.2f}'.format( MeasurementsRight.MarginalReflexDistance2)) self._tab1_results._BH_right.setText('{0:.2f}'.format( MeasurementsRight.BrowHeight)) #filling the info for the left self._tab1_results._CE_left.setText('{0:.2f}'.format( MeasurementsLeft.CommissureExcursion)) self._tab1_results._SA_left.setText('{0:.2f}'.format( MeasurementsLeft.SmileAngle)) self._tab1_results._DS_left.setText('{0:.2f}'.format( MeasurementsLeft.DentalShow)) self._tab1_results._MRD1_left.setText('{0:.2f}'.format( MeasurementsLeft.MarginalReflexDistance1)) self._tab1_results._MRD2_left.setText('{0:.2f}'.format( MeasurementsLeft.MarginalReflexDistance2)) self._tab1_results._BH_left.setText('{0:.2f}'.format( MeasurementsLeft.BrowHeight)) #deviation self._tab1_results._CE_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommissureExcursion)) self._tab1_results._SA_dev.setText('{0:.2f}'.format( MeasurementsDeviation.SmileAngle)) self._tab1_results._MRD1_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance1)) self._tab1_results._MRD2_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance2)) self._tab1_results._BH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.BrowHeight)) self._tab1_results._DS_dev.setText('{0:.2f}'.format( MeasurementsDeviation.DentalShow)) self._tab1_results._CH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommisureHeightDeviation)) self._tab1_results._UVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.UpperLipHeightDeviation)) self._tab1_results._LVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.LowerLipHeightDeviation)) self._tab1_results._CE_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.CommissureExcursion)) self._tab1_results._SA_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.SmileAngle)) self._tab1_results._MRD1_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance1)) self._tab1_results._MRD2_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance2)) self._tab1_results._BH_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.BrowHeight)) self._tab1_results._DS_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.DentalShow)) delimiter = os.path.sep temp = self._file_name.split(delimiter) photo_name = temp[-1] photo_name = photo_name[0:-4] self._tab1_results._tab_name = photo_name #say to the window that presents the results that there is only 1 tab self._new_window = ShowResults(self._tab1_results) #show the window with the results self._new_window.show() self.displayImage.update_view() else: #here there is a patient and so the result window will have three tabs if (self._Patient.FirstPhoto._shape is not None) and (self._Patient.SecondPhoto._shape is not None): #if the measurements window is already open then close it if self._new_window is not None: self._new_window.close() self._new_window = None #compute the facial metrics for the first photo and fill the information MeasurementsLeftFirst, MeasurementsRightFirst, MeasurementsDeviation, MeasurementsPercentual = get_measurements_from_data( self._Patient.FirstPhoto._shape, self._Patient.FirstPhoto._lefteye, self._Patient.FirstPhoto._righteye) self._tab1_results = CustomTabResult() #filling t_new_window_tab1_results he info for the right self._tab1_results._CE_right.setText('{0:.2f}'.format( MeasurementsRightFirst.CommissureExcursion)) self._tab1_results._SA_right.setText('{0:.2f}'.format( MeasurementsRightFirst.SmileAngle)) self._tab1_results._DS_right.setText('{0:.2f}'.format( MeasurementsRightFirst.DentalShow)) self._tab1_results._MRD1_right.setText('{0:.2f}'.format( MeasurementsRightFirst.MarginalReflexDistance1)) self._tab1_results._MRD2_right.setText('{0:.2f}'.format( MeasurementsRightFirst.MarginalReflexDistance2)) self._tab1_results._BH_right.setText('{0:.2f}'.format( MeasurementsRightFirst.BrowHeight)) #filling the info for the left self._tab1_results._CE_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.CommissureExcursion)) self._tab1_results._SA_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.SmileAngle)) self._tab1_results._DS_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.DentalShow)) self._tab1_results._MRD1_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.MarginalReflexDistance1)) self._tab1_results._MRD2_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.MarginalReflexDistance2)) self._tab1_results._BH_left.setText('{0:.2f}'.format( MeasurementsLeftFirst.BrowHeight)) #deviation self._tab1_results._CE_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommissureExcursion)) self._tab1_results._SA_dev.setText('{0:.2f}'.format( MeasurementsDeviation.SmileAngle)) self._tab1_results._MRD1_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance1)) self._tab1_results._MRD2_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance2)) self._tab1_results._BH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.BrowHeight)) self._tab1_results._DS_dev.setText('{0:.2f}'.format( MeasurementsDeviation.DentalShow)) self._tab1_results._CH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommisureHeightDeviation)) self._tab1_results._UVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.UpperLipHeightDeviation)) self._tab1_results._LVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.LowerLipHeightDeviation)) self._tab1_results._CE_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.CommissureExcursion)) self._tab1_results._SA_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.SmileAngle)) self._tab1_results._MRD1_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance1)) self._tab1_results._MRD2_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance2)) self._tab1_results._BH_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.BrowHeight)) self._tab1_results._DS_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.DentalShow)) self._tab1_results._tab_name = self._Patient.FirstPhoto._ID #compute the facial metrics for the second photo and fill the information MeasurementsLeftSecond, MeasurementsRightSecond, MeasurementsDeviation, MeasurementsPercentual = get_measurements_from_data( self._Patient.SecondPhoto._shape, self._Patient.SecondPhoto._lefteye, self._Patient.SecondPhoto._righteye) self._tab2_results = CustomTabResult() #filling t_new_window_tab1_results he info for the right self._tab2_results._CE_right.setText('{0:.2f}'.format( MeasurementsRightSecond.CommissureExcursion)) self._tab2_results._SA_right.setText('{0:.2f}'.format( MeasurementsRightSecond.SmileAngle)) self._tab2_results._DS_right.setText('{0:.2f}'.format( MeasurementsRightSecond.DentalShow)) self._tab2_results._MRD1_right.setText('{0:.2f}'.format( MeasurementsRightSecond.MarginalReflexDistance1)) self._tab2_results._MRD2_right.setText('{0:.2f}'.format( MeasurementsRightSecond.MarginalReflexDistance2)) self._tab2_results._BH_right.setText('{0:.2f}'.format( MeasurementsRightSecond.BrowHeight)) #filling the info for the left self._tab2_results._CE_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.CommissureExcursion)) self._tab2_results._SA_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.SmileAngle)) self._tab2_results._DS_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.DentalShow)) self._tab2_results._MRD1_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.MarginalReflexDistance1)) self._tab2_results._MRD2_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.MarginalReflexDistance2)) self._tab2_results._BH_left.setText('{0:.2f}'.format( MeasurementsLeftSecond.BrowHeight)) #deviation self._tab2_results._CE_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommissureExcursion)) self._tab2_results._SA_dev.setText('{0:.2f}'.format( MeasurementsDeviation.SmileAngle)) self._tab2_results._MRD1_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance1)) self._tab2_results._MRD2_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance2)) self._tab2_results._BH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.BrowHeight)) self._tab2_results._DS_dev.setText('{0:.2f}'.format( MeasurementsDeviation.DentalShow)) self._tab2_results._CH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommisureHeightDeviation)) self._tab2_results._UVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.UpperLipHeightDeviation)) self._tab2_results._LVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.LowerLipHeightDeviation)) self._tab2_results._CE_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.CommissureExcursion)) self._tab2_results._SA_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.SmileAngle)) self._tab2_results._MRD1_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance1)) self._tab2_results._MRD2_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance2)) self._tab2_results._BH_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.BrowHeight)) self._tab2_results._DS_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.DentalShow)) self._tab2_results._tab_name = self._Patient.SecondPhoto._ID #compute the the different between both photos and fill the information self._tab3_results = CustomTabResult() #filling tab3_results with the difference between the two photos self._tab3_results._CE_right.setText('{0:.2f}'.format( -MeasurementsRightFirst.CommissureExcursion + MeasurementsRightSecond.CommissureExcursion)) self._tab3_results._SA_right.setText( '{0:.2f}'.format(-MeasurementsRightFirst.SmileAngle + MeasurementsRightSecond.SmileAngle)) self._tab3_results._DS_right.setText( '{0:.2f}'.format(-MeasurementsRightFirst.DentalShow + MeasurementsRightSecond.DentalShow)) self._tab3_results._MRD1_right.setText('{0:.2f}'.format( -MeasurementsRightFirst.MarginalReflexDistance1 + MeasurementsRightSecond.MarginalReflexDistance1)) self._tab3_results._MRD2_right.setText('{0:.2f}'.format( -MeasurementsRightFirst.MarginalReflexDistance2 + MeasurementsRightSecond.MarginalReflexDistance2)) self._tab3_results._BH_right.setText( '{0:.2f}'.format(-MeasurementsRightFirst.BrowHeight + MeasurementsRightSecond.BrowHeight)) #filling the info for the left self._tab3_results._CE_left.setText('{0:.2f}'.format( -MeasurementsLeftFirst.CommissureExcursion + MeasurementsLeftSecond.CommissureExcursion)) self._tab3_results._SA_left.setText( '{0:.2f}'.format(-MeasurementsLeftFirst.SmileAngle + MeasurementsLeftSecond.SmileAngle)) self._tab3_results._DS_left.setText( '{0:.2f}'.format(-MeasurementsLeftFirst.DentalShow + MeasurementsLeftSecond.DentalShow)) self._tab3_results._MRD1_left.setText('{0:.2f}'.format( -MeasurementsLeftFirst.MarginalReflexDistance1 + MeasurementsLeftSecond.MarginalReflexDistance1)) self._tab3_results._MRD2_left.setText('{0:.2f}'.format( -MeasurementsLeftFirst.MarginalReflexDistance2 + MeasurementsLeftSecond.MarginalReflexDistance2)) self._tab3_results._BH_left.setText( '{0:.2f}'.format(-MeasurementsLeftFirst.BrowHeight + MeasurementsLeftSecond.BrowHeight)) #say to the window that presents the results that there are 3 tabs self._new_window = ShowResults(self._tab1_results, self._tab2_results, self._tab3_results) #show the window with the results self._new_window.show() def match_iris(self): #make both iris have the same diameter as the bigger one if self.displayImage._lefteye is not None: if self.displayImage._lefteye[2] < self.displayImage._righteye[2]: self.displayImage._lefteye[2] = self.displayImage._righteye[2] elif self.displayImage._lefteye[2] > self.displayImage._righteye[2]: self.displayImage._righteye[2] = self.displayImage._lefteye[2] elif self.displayImage._lefteye[2] == self.displayImage._righteye[ 2]: pass self._toggle_lines = True self.displayImage._points = None self.displayImage.set_update_photo() def face_center(self): #find a line connecting the center of both iris and then fit a perperdicular #line in the middle if self.displayImage._shape is not None: if self._toggle_lines == True: self._toggle_lines = False points = estimate_lines(self.displayImage._opencvimage, self.displayImage._lefteye, self.displayImage._righteye) self.displayImage._points = points self.displayImage.set_update_photo() else: self.displayImage._points = None self.displayImage.set_update_photo() self._toggle_lines = True def load_file(self, name): # #load a file using the widget # name,_ = QtWidgets.QFileDialog.getOpenFileName( # self,'Load Image', # '',"Image files (*.png *.jpg *.jpeg *.tif *.tiff *.PNG *.JPG *.JPEG *.TIF *.TIFF)") # # if not name: # pass # else: #the user will load an single image so get rid of Patient and the # changephotoAction in the toolbar self._Patient = None #if windows then transform / to \ (python stuffs) name = os.path.normpath(name) self._file_name = name #if the measurements window is open then close it if self._new_window is not None: self._new_window.close() #load image self.displayImage._opencvimage = cv2.imread(name) #if the photo was already processed then get the information for the #txt file, otherwise process the photo using the landmark ans pupil #localization algorithms file_txt = name[:-4] file_txt = (file_txt + '.txt') if os.path.isfile(file_txt): shape, lefteye, righteye, boundingbox = get_info_from_txt(file_txt) self.displayImage._lefteye = lefteye self.displayImage._righteye = righteye self.displayImage._shape = shape self.displayImage._boundingbox = boundingbox self.displayImage._points = None self.displayImage.update_view() else: #if the image is too large then it needs to be resized.... h, w, d = self.displayImage._opencvimage.shape #if the image is too big then we need to resize it so that the landmark #localization process can be performed in a reasonable time self._Scale = 1 #start from a clear initial scale if h > 1500 or w > 1500: if h >= w: h_n = 1500 self._Scale = h / h_n w_n = int(np.round(w / self._Scale, 0)) #self.displayImage._opencvimage=cv2.resize(self.displayImage._opencvimage, (w_n, h_n), interpolation=cv2.INTER_AREA) temp_image = cv2.resize(self.displayImage._opencvimage, (w_n, h_n), interpolation=cv2.INTER_AREA) #self._image = image else: w_n = 1500 self._Scale = w / w_n h_n = int(np.round(h / self._Scale, 0)) #self.displayImage._opencvimage=cv2.resize(self.displayImage._opencvimage, (w_n, h_n), interpolation=cv2.INTER_AREA) temp_image = cv2.resize(self.displayImage._opencvimage, (w_n, h_n), interpolation=cv2.INTER_AREA) #self._image = image # #now that the image has been reduced, ask the user if the image # #should be saved for continue the processing, otherwise the # #processing cannot continue with the large image # # #get the image name (separete it from the path) # delimiter = os.path.sep # split_name=name.split(delimiter) # # #the variable 'name' contains the file name and the path, we now # #get the file name and assign it to the photo object # file_name = split_name[-1] # new_file_name = file_name[:-4]+'_small.png' # # choice = QtWidgets.QMessageBox.information(self, 'Large Image', # 'The image is too large to process.\n\nPressing OK will create a new file\n%s\nin the current folder. This file will be used for processing.\nOtherwise, click Close to finalize the App.'%new_file_name, # QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Close, QtWidgets.QMessageBox.Ok) # # if choice == QtWidgets.QMessageBox.Close : # self.close() # app.exec_() # else: # #create a new, smaller image and use that for processing # name = name[:-4]+'_small.png' # self._file_name = name # cv2.imwrite(name,self.displayImage._opencvimage) else: #the image is of appropiate dimensions so no need for modification temp_image = self.displayImage._opencvimage.copy() #pass #get the landmarks using dlib, and the and the iris #using Dougman's algorithm #This is done in a separate thread to prevent the gui from #freezing and crashing #create worker, pass the image to the worker #self.landmarks = GetLandmarks(self.displayImage._opencvimage) self.landmarks = GetLandmarks(temp_image) #move worker to new thread self.landmarks.moveToThread(self.thread_landmarks) #start the new thread where the landmark processing will be performed self.thread_landmarks.start() #Connect Thread started signal to Worker operational slot method self.thread_landmarks.started.connect(self.landmarks.getlandmarks) #connect signal emmited by landmarks to a function self.landmarks.landmarks.connect(self.ProcessShape) #define the end of the thread self.landmarks.finished.connect(self.thread_landmarks.quit) def ProcessShape(self, shape, numFaces, lefteye, righteye, boundingbox): if numFaces == 1: if self._Scale is not 1: #in case that a smaller image was used for #processing, then update the landmark #position with the scale factor for k in range(0, 68): shape[k] = [ int(np.round(shape[k, 0] * self._Scale, 0)), int(np.round(shape[k, 1] * self._Scale, 0)) ] for k in range(0, 3): lefteye[k] = int(np.round(lefteye[k] * self._Scale, 0)) righteye[k] = int(np.round(righteye[k] * self._Scale, 0)) for k in range(0, 4): boundingbox[k] = int( np.round(boundingbox[k] * self._Scale, 0)) self.displayImage._shape = shape self.displayImage._lefteye = lefteye self.displayImage._righteye = righteye self.displayImage._boundingbox = boundingbox # self.displayImage._points = None elif numFaces == 0: #no face in image then shape is None self.displayImage._shape = None #inform the user QtWidgets.QMessageBox.warning( self, "Warning", "No face in the image.\nIf the image does contain a face plase modify the brightness and try again.", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) elif numFaces > 1: #multiple faces in image then shape is None self.displayImage._shape = None #inform the user QtWidgets.QMessageBox.warning( self, "Warning", "Multiple faces in the image.\nPlease load an image with a single face.", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) self.displayImage.update_view() def load_iris(self): #load a file using the widget name, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Load Iris Position and Diameter', '', "Image files (*.png *.jpg *.jpeg *.tif *.tiff *.PNG *.JPG *.JPEG *.TIF *.TIFF)" ) if not name: pass else: #if windows then transform / to \ (python stuffs) name = os.path.normpath(name) #if the measurements window is open then close it, the measures will be updated with the new eyes position if self._new_window is not None: self._new_window.close() #if the photo was already processed then get the information for the #txt file, otherwise process the photo using the landmark ans pupil #localization algorithms file_txt = name[:-4] file_txt = (file_txt + '.txt') if os.path.isfile(file_txt): shape, lefteye, righteye, _ = get_info_from_txt(file_txt) dx_left = lefteye[0] - shape[27, 0] dy_left = shape[27, 1] - lefteye[1] dx_right = shape[27, 0] - righteye[0] dy_right = shape[27, 1] - righteye[1] self.displayImage._lefteye = [ self.displayImage._shape[27, 0] + dx_left, self.displayImage._shape[27, 1] - dy_left, lefteye[2] ] self.displayImage._righteye = [ self.displayImage._shape[27, 0] - dx_right, self.displayImage._shape[27, 1] - dy_right, lefteye[2] ] self.displayImage.set_update_photo() else: QtWidgets.QMessageBox.warning( self, "Warning", "Iris information for this photograph is not avaliable", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) # self.displayImage._lefteye = lefteye # self.displayImage._righteye = righteye # self.displayImage.set_update_photo() def toggle_landmarks(self): #Hide - show the landmarks if self._toggle_landmaks is True: self._toggle_landmaks = False self.displayImage.set_update_photo(self._toggle_landmaks) elif self._toggle_landmaks is False: self._toggle_landmaks = True self.displayImage.set_update_photo(self._toggle_landmaks) def save_snapshot(self): #save the current view if self.displayImage._opencvimage is not None: proposed_name = self._file_name[:-4] + '-landmarks' name, _ = QtWidgets.QFileDialog.getSaveFileName( self, 'Save File', proposed_name, 'png (*.png);;jpg (*.jpg);; jpeg (*.jpeg)') if not name: pass else: #if shape then add shape to image temp_image = self.displayImage._opencvimage.copy() #draw 68 landmark points if self.displayImage._shape is not None: temp_image = mark_picture(temp_image, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._points) save_snaptshot_to_file(temp_image, name) def save_results(self): #save the results in a txt and xls files. There are two modes, one if #there is no patient and another is the is a patient (two photos) if self._Patient is None: #this implies that there is a single photo if self._file_name is not None: if self.displayImage._shape is not None: save_txt_file(self._file_name, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._boundingbox) MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual = get_measurements_from_data( self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye) save_xls_file(self._file_name, MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual) else: #this implies that the user created a patient and wants to analize two photos save_txt_file(self._Patient.FirstPhoto._file_name, self._Patient.FirstPhoto._shape, self._Patient.FirstPhoto._lefteye, self._Patient.FirstPhoto._righteye, self._Patient.FirstPhoto._boundingbox) save_txt_file(self._Patient.SecondPhoto._file_name, self._Patient.SecondPhoto._shape, self._Patient.SecondPhoto._lefteye, self._Patient.SecondPhoto._righteye, self._Patient.SecondPhoto._boundingbox) save_xls_file_patient(self._file_name, self._Patient) def close_app(self): #ask is the user really wants to close the app choice = QtWidgets.QMessageBox.question( self, 'Message', 'Do you want to exit?', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if choice == QtWidgets.QMessageBox.Yes: self.close() app.exec_() else: pass def closeEvent(self, event): #we need to close all the windows before closing the program if self._new_window is not None: self._new_window.close() event.accept()
def initUI(self): #local directory scriptDir = os.getcwd( ) #os.path.dirname(os.path.realpath(sys.argv[0])) #image #read the image from file #img_Qt = QtGui.QImage(scriptDir + os.path.sep + 'include' +os.path.sep +'icon_color'+ os.path.sep + 'Facial-Nerve-Center.jpg') #img_show = QtGui.QPixmap.fromImage(img_Qt) #the image will be displayed in the custom ImageViewer self.displayImage = ImageViewer() #self.displayImage.setPhoto(img_show) #toolbar fitAction = QtWidgets.QAction('Fit image to window', self) fitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'fit_to_size_icon.png')) fitAction.triggered.connect(self.displayImage.show_entire_image) eyeAction = QtWidgets.QAction('Match iris diameter', self) eyeAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon.png')) eyeAction.triggered.connect(self.match_iris) eyeLoad = QtWidgets.QAction('Import iris position and diameter', self) eyeLoad.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon_import.png')) eyeLoad.triggered.connect(self.load_iris) centerAction = QtWidgets.QAction('Find face center', self) centerAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'center_icon.png')) centerAction.triggered.connect(self.face_center) toggleAction = QtWidgets.QAction('Toggle landmarks', self) toggleAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'toggle-icon.png')) toggleAction.triggered.connect(self.toggle_landmarks) measuresAction = QtWidgets.QAction('Facial metrics', self) measuresAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'ruler_icon.png')) measuresAction.triggered.connect(self.create_new_window) saveAction = QtWidgets.QAction('Save results', self) saveAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'save_icon.png')) saveAction.triggered.connect(self.save_results) snapshotAction = QtWidgets.QAction('Save current view', self) snapshotAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'snapshot_icon.png')) snapshotAction.triggered.connect(self.save_snapshot) exitAction = QtWidgets.QAction('Exit', self) exitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'exit_icon.png')) exitAction.triggered.connect(self.close_app) #create the toolbar and add the actions self.toolBar = QtWidgets.QToolBar(self) self.toolBar.addActions( (fitAction, eyeAction, eyeLoad, centerAction, toggleAction, measuresAction, snapshotAction, saveAction, exitAction)) #set the size of each icon to 50x50 self.toolBar.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar.actions(): widget = self.toolBar.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar.setMinimumSize(self.toolBar.sizeHint()) self.toolBar.setStyleSheet('QToolBar{spacing:5px;}') #the main window consist of the toolbar and the ImageViewer layout = QtWidgets.QVBoxLayout() layout.addWidget(self.toolBar) layout.addWidget(self.displayImage) self.setLayout(layout) self.load_file(self._file_name) self.displayImage.update_view() self.show()
class MainWindow(QtWidgets.QMainWindow): def __init__(self): super(MainWindow, self).__init__() # self.setGeometry(5,60,700,500) self.setWindowTitle('Video Processing') self.background_color = self.palette().color(QtGui.QPalette.Background) # if os.name is 'posix': # is a mac or linux # scriptDir = os.path.dirname(sys.argv[0]) # else: # is a windows # scriptDir = os.getcwd() # This is the main Window self.main_Widget = QtWidgets.QWidget(self) self.setCentralWidget(self.main_Widget) # Elements of the main window # image viewer self.displayImage = ImageViewer() # Menu bar __ Top - Main options self.menuBar = QtWidgets.QMenuBar(self) self.setStyleSheet(""" QMenuBar { font-size:18px; background : transparent; } """) # Tool bar __ Top - Functions to analyze the current image self.toolBar_Top = QtWidgets.QToolBar(self) # Tool bar __ Bottom - Play/pause buttons self.toolBar_Bottom = QtWidgets.QToolBar(self) # Frame Slider _ Bottom - Easily move between frames self.slider_Bottom = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(100) self.slider_Bottom.setValue(1) self.slider_Bottom.setTickInterval(1) self.slider_Bottom.setEnabled(False) self.slider_Bottom.valueChanged.connect(self.slidervaluechange) self.slider_Bottom.sliderReleased.connect(self.slidervaluefinal) # Status Bar _ Bottom - Show the current frame number self.frameLabel = QtWidgets.QLabel('') self.frameLabel.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom = QtWidgets.QStatusBar() self.statusBar_Bottom.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom.addPermanentWidget(self.frameLabel) # Definition of Variables self.video_handler = None self.video_name = None # name and location of video file self.current_frame = 0 # what is the current frame self.timer = QtCore.QTimer() # controls video playback self.jump_frames = 1 # number of frames to jump with fastforward or rewind buttons # initialize the User Interface self.initUI() self.show() def initUI(self): # Populate the different menus file_menu = self.menuBar.addMenu("&File") load_video = file_menu.addAction("Load Video File") load_video.setShortcut("Ctrl+F") load_video.setStatusTip( 'Load video file, accepted formats : .mp4, .avi, .mov') load_video.triggered.connect(self.openvideofile) load_landmarks = file_menu.addAction("Load Landmarks File") load_landmarks.setShortcut("Ctrl+L") load_landmarks.setStatusTip( 'Load landmark file, accepted formats : .csv') # load_landmarks.triggered.connect(self.load_file) quit_program = file_menu.addAction("Quit") quit_program.setShortcut("Ctrl+Q") # quit_program.triggered.connect(self.load_file) video_menu = self.menuBar.addMenu("&Video") play_video = video_menu.addAction("Play Video") play_video.setShortcut("Ctrl+P") play_video.setStatusTip('Play video at given playback speed') # play_video.triggered.connect(self.load_file) stop_video = video_menu.addAction("Stop Video") stop_video.setShortcut("Ctrl+S") stop_video.setStatusTip('Stop video playback') # stop_video.triggered.connect(self.load_file) jump_to_frame = video_menu.addAction("Jump to Frame") jump_to_frame.setShortcut("Ctrl+J") jump_to_frame.setStatusTip('Jump to certain frame') # jump_to_frame.triggered.connect(self.load_file) playback_settings = video_menu.addAction("Playback Settings") playback_settings.setShortcut("Ctrl+P") playback_settings.setStatusTip('Define video playback settings') # playback_settings.triggered.connect(self.load_file) landmarks_menu = self.menuBar.addMenu("&Landmarks") # process_current_frame = landmarks_menu.addAction("Process Current Frame") # process_current_frame.setShortcut("Ctrl+C") # process_current_frame.setStatusTip('Determine facial landmarks for current frame') # # process_current_frame.triggered.connect(self.load_file) process_some_frame = landmarks_menu.addAction("Process Frames") process_some_frame.setShortcut("Ctrl+S") process_some_frame.setStatusTip( 'Determine facial landmarks for some frames in the video') # process_some_frame.triggered.connect(self.load_file) process_all_frame = landmarks_menu.addAction("Process All Frames") process_all_frame.setShortcut("Ctrl+A") process_all_frame.setStatusTip( 'Determine facial landmarks for all frames in the video') # process_all_frame.triggered.connect(self.load_file) process_settings = landmarks_menu.addAction("Process Frames Settings") process_settings.setShortcut("Ctrl+L") process_settings.setStatusTip( 'Determine facial landmarks for all frames in the video') # process_settings.triggered.connect(self.load_file) # fill the top toolbar process_current_frame = QtWidgets.QAction('Process current frame', self) process_current_frame.setIcon( QtGui.QIcon('./icons/facial-analysis.png')) # process_current_frame.connect(self.match_iris) toggle_landmark = QtWidgets.QAction('Show/Hide facial landmarks', self) toggle_landmark.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # toggle_landmark.connect(self.match_iris) manual_adjustment = QtWidgets.QAction( 'Manually adjust landmarks position in current frame', self) manual_adjustment.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # manual_adjustment.connect(self.match_iris) landmark_settings = QtWidgets.QAction( 'Adjust landmark visualization settings', self) landmark_settings.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # landmark_settings.connect(self.match_iris) show_metrics = QtWidgets.QAction( 'Display facial metrics in current frame', self) show_metrics.setIcon(QtGui.QIcon('./icons/facial-metrics.png')) # show_metrics.connect(self.match_iris) snapshot = QtWidgets.QAction('Save snapshot of current view', self) snapshot.setIcon(QtGui.QIcon('./icons/profile.png')) # snapshot.connect(self.match_iris) self.toolBar_Top.addActions( (process_current_frame, toggle_landmark, manual_adjustment, landmark_settings, show_metrics, snapshot)) self.toolBar_Top.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar_Top.actions(): widget = self.toolBar_Top.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar_Top.setMinimumSize(self.toolBar_Top.sizeHint()) self.toolBar_Top.setStyleSheet('QToolBar{spacing:8px;}') # fill the bottom toolbar play_action = QtWidgets.QAction('Play', self) play_action.setShortcut('Shift+S') play_action.setIcon(QtGui.QIcon('./icons/play-arrow.png')) play_action.triggered.connect(self.playvideo) #play_action.setToolTip(self, 'FPS ='+str(self.video_handler.playbackspeed)) stop_action = QtWidgets.QAction('Stop', self) stop_action.setShortcut('Shift+Z') stop_action.setIcon(QtGui.QIcon('./icons/pause.png')) stop_action.triggered.connect(self.stopvideo) fastforward_action = QtWidgets.QAction('Fast Forward', self) fastforward_action.setShortcut('Shift+D') fastforward_action.setIcon(QtGui.QIcon('./icons/fast-forward.png')) fastforward_action.triggered.connect(self.fastforward) rewind_action = QtWidgets.QAction('Rewind', self) rewind_action.setShortcut('Shift+A') rewind_action.setIcon(QtGui.QIcon('./icons/rewind.png')) rewind_action.triggered.connect(self.rewind) # spacer widget for left left_spacer = QtWidgets.QWidget(self) left_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # spacer widget for right right_spacer = QtWidgets.QWidget(self) right_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # fill the bottom toolbar self.toolBar_Bottom.addWidget(left_spacer) self.toolBar_Bottom.addActions( (rewind_action, play_action, stop_action, fastforward_action)) self.toolBar_Bottom.addWidget(right_spacer) self.toolBar_Bottom.setIconSize(QtCore.QSize(35, 35)) # for action in self.toolBar_Bottom.actions(): # widget = self.toolBar_Bottom.widgetForAction(action) # widget.setFixedSize(35, 35) self.toolBar_Bottom.setMinimumSize(self.toolBar_Bottom.sizeHint()) self.toolBar_Bottom.setStyleSheet('QToolBar{spacing:8px;}') # Create the layout # The main window has 6 components in the following order: # Menu Bar - Tool Bar - Image viewer - Tool Bar - Slider - Status Bar layout = QtWidgets.QVBoxLayout() layout.addWidget(self.menuBar) layout.addWidget(self.toolBar_Top) layout.addWidget(self.displayImage) layout.addWidget(self.toolBar_Bottom) layout.addWidget(self.slider_Bottom) self.setStatusBar(self.statusBar_Bottom) # Set the defined layout in the main window self.main_Widget.setLayout(layout) self.setGeometry(300, 100, 600, 800) def openvideofile(self): # load a file using the widget name, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Load Video File', '', "Video files (*.mp4 *.avi *.mov *.MP4 *.AVI *.MOV)") if not name: pass else: name = os.path.normpath(name) # Remove previous video handlers to avoid taking odd frames self.video_handler = None # change window name to match the file name self.setWindowTitle('Video Processing - ' + name.split(os.path.sep)[-1]) # user provided a video, open it using OpenCV self.video_handler = VideoInformation(name) # read the video success, image = self.video_handler.read() # get the first frame if success: # if the frame exists then show the image # video was successfully loaded and will be presented to the user, now we will verify if a csv file with # with the same name as the video exits. This file should contain the landmark and bounding box # information cvs_file_name = name[:-3] + 'csv' if os.path.exists(cvs_file_name): self.video_handler.video_landmarks_filename = cvs_file_name try: self.video_handler.video_landmarks_DF = pd.read_csv( self.video_handler.video_landmarks_filename) # self.video_handler.video_landmarks_DF.set_index('Frame_number', inplace=True) except ValueError: QtWidgets.QMessageBox.critical( 0, "Error", "Landmark file appears to exist but cannot be loaded" ) self.video_handler.video_landmarks_filename = None self.video_handler.video_landmarks_DF = None self.updateviewer(image, 0) self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(self.video_handler.video_length) self.slider_Bottom.setEnabled(True) def playvideo(self): # verify that the video handler is not empty if self.video_handler is not None: self.timer.timeout.connect(self.nextframefunction) self.timer.start(1000.0 / self.video_handler.playbackspeed) def nextframefunction(self): # verify that we are not at the last frame if self.current_frame <= self.video_handler.video_length: success, image = self.video_handler.read() # get a new frame if success: frame_number = self.current_frame + 1 self.updateviewer(image, frame_number) else: # reached the end of the video self.timer.stop() def stopvideo(self): # stop video if video is playing if self.timer.isActive(): # verify is the video is running self.timer.stop() def fastforward(self): if self.timer.isActive(): # verify is the video is running # top video playback before moving slider self.timer.stop() next_frame = self.current_frame + self.jump_frames if next_frame > self.video_handler.video_length - 1: next_frame = self.video_handler.video_length - 1 pass else: self.video_handler.video_handler.set(cv2.CAP_PROP_POS_FRAMES, next_frame) success, image = self.video_handler.read() if success: self.updateviewer(image, next_frame) else: print(next_frame, self.current_frame) def rewind(self): if self.timer.isActive(): # verify is the video is running # top video playback before moving slider self.timer.stop() previous_frame = self.current_frame - self.jump_frames if previous_frame < 0: previous_frame = 0 pass else: self.video_handler.video_handler.set(cv2.CAP_PROP_POS_FRAMES, previous_frame) success, image = self.video_handler.read() if success: self.updateviewer(image, previous_frame) def updateviewer(self, image, frame_number): self.displayImage._opencvimage = image if self.video_handler.video_landmarks_DF is not None: try: frame_information = self.video_handler.video_landmarks_DF.loc[ self.video_handler.video_landmarks_DF['Frame_number'] == frame_number].values except ValueError: frame_information = [] if len(frame_information) > 0: shape = np.array([frame_information[0][6:]]) shape = np.reshape(shape.astype(np.int), (-1, 2)) self.displayImage._shape = shape self.displayImage.update_view() self.current_frame = frame_number self.frameLabel.setText('Frame : ' + str(int(self.current_frame) + 1) + '/' + str(self.video_handler.video_length)) self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(frame_number + 1) self.slider_Bottom.blockSignals(False) def slidervaluechange(self): if self.timer.isActive(): # verify is the video is running # top video playback before moving slider self.timer.stop() # get slider position and update frame number slider_position = self.slider_Bottom.value() self.current_frame = slider_position - 1 self.frameLabel.setText('Frame : ' + str(int(self.current_frame) + 1) + '/' + str(self.video_handler.video_length)) self.is_slider_moving = True def slidervaluefinal(self): # adjust view only when the slider reaches its final position self.video_handler.video_handler.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame) success, image = self.video_handler.read() if success: self.updateviewer(image, self.current_frame)
class MainWindow(QtWidgets.QMainWindow): def __init__(self): super(MainWindow, self).__init__() # self.setGeometry(5,60,700,500) self.setWindowTitle('Video Processing') self.background_color = self.palette().color(QtGui.QPalette.Background) # if os.name is 'posix': # is a mac or linux # scriptDir = os.path.dirname(sys.argv[0]) # else: # is a windows # scriptDir = os.getcwd() # This is the main Window self.main_Widget = QtWidgets.QWidget(self) self.setCentralWidget(self.main_Widget) # Elements of the main window # image viewer self.displayImage = ImageViewer() # Menu bar __ Top - Main options self.menuBar = QtWidgets.QMenuBar(self) self.setStyleSheet(""" QMenuBar { font-size:18px; background : transparent; } """) # Tool bar __ Top - Functions to analyze the current image self.toolBar_Top = QtWidgets.QToolBar(self) # Tool bar __ Bottom - Play/pause buttons self.toolBar_Bottom = QtWidgets.QToolBar(self) # Frame Slider _ Bottom - Easily move between frames self.slider_Bottom = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(100) self.slider_Bottom.setValue(1) self.slider_Bottom.setTickInterval(1) self.slider_Bottom.setEnabled(False) self.slider_Bottom.valueChanged.connect(self.slidervaluechange) # Status Bar _ Bottom - Show the current frame number self.frameLabel = QtWidgets.QLabel('') self.frameLabel.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom = QtWidgets.QStatusBar() self.statusBar_Bottom.setFont(QtGui.QFont("Times", 10)) self.statusBar_Bottom.addPermanentWidget(self.frameLabel) # Definition of Variables self.video_handler = None self.video_name = None # name and location of video file self.current_frame = 0 # what is the current frame self.timer = QtCore.QTimer() # controls video playback # threads used to process data and help in the visualization self.thread_slider = QThread() # no parent self.class_slider = UpdateSlider() # initialize the User Interface self.initUI() self.show() def initUI(self): # Populate the different menus file_menu = self.menuBar.addMenu("&File") load_video = file_menu.addAction("Load Video File") load_video.setShortcut("Ctrl+F") load_video.setStatusTip('Load video file, accepted formats : .mp4, .avi, .mov') load_video.triggered.connect(self.openvideofile) load_landmarks = file_menu.addAction("Load Landmarks File") load_landmarks.setShortcut("Ctrl+L") load_landmarks.setStatusTip('Load landmark file, accepted formats : .csv') # load_landmarks.triggered.connect(self.load_file) quit_program = file_menu.addAction("Quit") quit_program.setShortcut("Ctrl+Q") # quit_program.triggered.connect(self.load_file) video_menu = self.menuBar.addMenu("&Video") play_video = video_menu.addAction("Play Video") play_video.setShortcut("Ctrl+P") play_video.setStatusTip('Play video at given playback speed') # play_video.triggered.connect(self.load_file) stop_video = video_menu.addAction("Stop Video") stop_video.setShortcut("Ctrl+S") stop_video.setStatusTip('Stop video playback') # stop_video.triggered.connect(self.load_file) jump_to_frame = video_menu.addAction("Jump to Frame") jump_to_frame.setShortcut("Ctrl+J") jump_to_frame.setStatusTip('Jump to certain frame') # jump_to_frame.triggered.connect(self.load_file) playback_settings = video_menu.addAction("Playback Settings") playback_settings.setShortcut("Ctrl+P") playback_settings.setStatusTip('Define video playback settings') # playback_settings.triggered.connect(self.load_file) landmarks_menu = self.menuBar.addMenu("&Landmarks") # process_current_frame = landmarks_menu.addAction("Process Current Frame") # process_current_frame.setShortcut("Ctrl+C") # process_current_frame.setStatusTip('Determine facial landmarks for current frame') # # process_current_frame.triggered.connect(self.load_file) process_some_frame = landmarks_menu.addAction("Process Frames") process_some_frame.setShortcut("Ctrl+S") process_some_frame.setStatusTip('Determine facial landmarks for some frames in the video') # process_some_frame.triggered.connect(self.load_file) process_all_frame = landmarks_menu.addAction("Process All Frames") process_all_frame.setShortcut("Ctrl+A") process_all_frame.setStatusTip('Determine facial landmarks for all frames in the video') # process_all_frame.triggered.connect(self.load_file) process_settings = landmarks_menu.addAction("Process Frames Settings") process_settings.setShortcut("Ctrl+L") process_settings.setStatusTip('Determine facial landmarks for all frames in the video') # process_settings.triggered.connect(self.load_file) # fill the top toolbar process_current_frame = QtWidgets.QAction('Process current frame', self) process_current_frame.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # process_current_frame.connect(self.match_iris) toggle_landmark = QtWidgets.QAction('Show/Hide facial landmarks', self) toggle_landmark.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # toggle_landmark.connect(self.match_iris) manual_adjustment = QtWidgets.QAction('Manually adjust landmarks position in current frame', self) manual_adjustment.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # manual_adjustment.connect(self.match_iris) landmark_settings = QtWidgets.QAction('Adjust landmark visualization settings', self) landmark_settings.setIcon(QtGui.QIcon('./icons/facial-analysis.png')) # landmark_settings.connect(self.match_iris) show_metrics = QtWidgets.QAction('Display facial metrics in current frame', self) show_metrics.setIcon(QtGui.QIcon('./icons/facial-metrics.png')) # show_metrics.connect(self.match_iris) snapshot = QtWidgets.QAction('Save snapshot of current view', self) snapshot.setIcon(QtGui.QIcon('./icons/profile.png')) # snapshot.connect(self.match_iris) self.toolBar_Top.addActions((process_current_frame, toggle_landmark, manual_adjustment, landmark_settings, show_metrics, snapshot)) self.toolBar_Top.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar_Top.actions(): widget = self.toolBar_Top.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar_Top.setMinimumSize(self.toolBar_Top.sizeHint()) self.toolBar_Top.setStyleSheet('QToolBar{spacing:8px;}') # fill the bottom toolbar play_action = QtWidgets.QAction('Play', self) play_action.setShortcut('Shift+S') play_action.setIcon(QtGui.QIcon('./icons/play-arrow.png')) play_action.triggered.connect(self.playvideo) stop_action = QtWidgets.QAction('Stop', self) stop_action.setShortcut('Shift+Z') stop_action.setIcon(QtGui.QIcon('./icons/pause.png')) stop_action.triggered.connect(self.stopvideo) fastforward_action = QtWidgets.QAction('Fast Forward', self) fastforward_action.setShortcut('Shift+D') fastforward_action.setIcon(QtGui.QIcon('./icons/fast-forward.png')) # fastforward_action.triggered.connect(self.match_iris) rewind_action = QtWidgets.QAction('Rewind', self) rewind_action.setShortcut('Shift+A') rewind_action.setIcon(QtGui.QIcon('./icons/rewind.png')) # rewind_action.triggered.connect(self.match_iris) # spacer widget for left left_spacer = QtWidgets.QWidget(self) left_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # spacer widget for right right_spacer = QtWidgets.QWidget(self) right_spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # fill the bottom toolbar self.toolBar_Bottom.addWidget(left_spacer) self.toolBar_Bottom.addActions((rewind_action, play_action, stop_action, fastforward_action)) self.toolBar_Bottom.addWidget(right_spacer) self.toolBar_Bottom.setIconSize(QtCore.QSize(35, 35)) # for action in self.toolBar_Bottom.actions(): # widget = self.toolBar_Bottom.widgetForAction(action) # widget.setFixedSize(35, 35) self.toolBar_Bottom.setMinimumSize(self.toolBar_Bottom.sizeHint()) self.toolBar_Bottom.setStyleSheet('QToolBar{spacing:8px;}') # Create the layout # The main window has 6 components in the following order: # Menu Bar - Tool Bar - Image viewer - Tool Bar - Slider - Status Bar layout = QtWidgets.QVBoxLayout() layout.addWidget(self.menuBar) layout.addWidget(self.toolBar_Top) layout.addWidget(self.displayImage) layout.addWidget(self.toolBar_Bottom) layout.addWidget(self.slider_Bottom) self.setStatusBar(self.statusBar_Bottom) # Set the defined layout in the main window self.main_Widget.setLayout(layout) self.setGeometry(300, 100, 600, 800) def openvideofile(self): # load a file using the widget name, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Load Video File', '', "Video files (*.mp4 *.avi *.mov *.MP4 *.AVI *.MOV)") if not name: pass else: name = os.path.normpath(name) # Remove previous video handlers to avoid taking odd frames self.video_handler = None # change window name to match the file name self.setWindowTitle('Video Processing - ' + name.split(os.path.sep)[-1]) # user provided a video, open it using OpenCV self.video_handler = VideoInformation(name) # read the video success, image = self.video_handler.read() # get the first frame # video was read sucessfully, update this information in the class taking care of the slider self.class_slider.video_handler = self.video_handler.video_handler self.class_slider.image_viewer = self.displayImage if success: # if the frame exists then show the image self.displayImage._opencvimage = image self.displayImage.update_view() self.current_frame = 0 # put the frame information in the app self.frameLabel.setText('Frame :'+str(int(self.current_frame)+1)+'/'+str(self.video_handler.video_length)) # update the slider self.slider_Bottom.setMinimum(1) self.slider_Bottom.setMaximum(self.video_handler.video_length) self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(1) self.slider_Bottom.blockSignals(False) self.slider_Bottom.setEnabled(True) # videocap.release() def playvideo(self): # verify that the video handler is not empty if self.video_handler is not None: self.timer.timeout.connect(self.nextframefunction) self.timer.start(1000.0/self.video_handler.playbackspeed) def nextframefunction(self): # verify that we are not at the last frame if self.current_frame < self.video_handler.video_length - 1: success, image = self.video_handler.read() # get the first frame self.displayImage._opencvimage = image self.displayImage.update_view() self.current_frame += 1 # put the frame information in the app self.frameLabel.setText( 'Frame :' + str(int(self.current_frame) + 1) + '/' + str(self.video_handler.video_length)) # update the slider self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(self.current_frame + 1) self.slider_Bottom.blockSignals(False) else: # reached the end of the video self.timer.stop() def stopvideo(self): # stop video if video is playing if self.timer.isActive(): # verify is the video is running self.timer.stop() def slidervaluechange(self): """ this functions read the slider, updates the view and then updates the slider according to the frame that is being displayed. Everything occurs in a new thread, so it is usually is slow as firing up and endings threads takes time. However, this is the best solution to prevent the UI from frozen, as the process of reading 'certain' frames from a cv2 video object is very slow and blocking """ if self.timer.isActive(): # verify is the video is running # top video playback before moving slider self.timer.stop() # read slider value from slider slider_value = self.slider_Bottom.value() # move the class that updates the viewer and slider to a new thread self.class_slider.moveToThread(self.thread_slider) # star the thread self.thread_slider.start() # update the view, pass the slider position to the function self.thread_slider.started.connect(lambda: self.class_slider.updateviewer(slider_value)) # if the view was successfully updated, update the slider position self.class_slider.frame_number.connect(self.updateviwefromslider) # end the thread self.class_slider.finished.connect(self.thread_slider.quit) @pyqtSlot(int) def updateviwefromslider(self, frame_number): if frame_number is not None: self.current_frame = frame_number - 1 self.frameLabel.setText( 'Frame : ' + str(int(self.current_frame) + 1) + '/' + str(self.video_handler.video_length)) #self.slider_Bottom.blockSignals(True) self.slider_Bottom.setValue(int(self.current_frame) + 1)
class Emotrics(QtWidgets.QDialog): def __init__(self, photograph, CalibrationType, CalibrationValue): super(Emotrics, self).__init__() #self.setGeometry(5,60,700,500) self.setWindowTitle(photograph._Tag) scriptDir = os.getcwd( ) #os.path.dirname(os.path.realpath(sys.argv[0])) self.setWindowIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'meei_3WR_icon.ico')) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinMaxButtonsHint) self._file_name = photograph._file_name self._new_window = None self._tab1_results = None self._toggle_landmaks = True self._toggle_lines = True self._photograph = photograph self._Scale = 1 #this variable carries the scale of the image if it #needs to be resized, if Scale = 1 then the original #image was used for processing. If Scale > 1 then #the original image was too large and a resized image #was used for processing # create Thread to take care of the landmarks and iris estimation #self.thread_landmarks = QtCore.QThread() # no parent! self._CalibrationType = CalibrationType self._CalibrationValue = CalibrationValue #initialize the User Interface self.initUI() def initUI(self): #local directory scriptDir = os.getcwd() #image #read the image from file img_Qt = QtGui.QImage(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'Facial-Nerve-Center.jpg') img_show = QtGui.QPixmap.fromImage(img_Qt) #the image will be displayed in the custom ImageViewer self.displayImage = ImageViewer() self.displayImage.setPhoto(img_show) #toolbar fitAction = QtWidgets.QAction('Fit image to window', self) fitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'fit_to_size_icon.png')) fitAction.triggered.connect(self.displayImage.show_entire_image) eyeAction = QtWidgets.QAction('Match iris diameter', self) eyeAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon.png')) eyeAction.triggered.connect(self.match_iris) eyeLoad = QtWidgets.QAction('Import iris position and diameter', self) eyeLoad.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'eye_icon_import.png')) eyeLoad.triggered.connect(self.load_iris) centerAction = QtWidgets.QAction('Find face center', self) centerAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'center_icon.png')) centerAction.triggered.connect(self.face_center) toggleAction = QtWidgets.QAction('Toggle landmarks', self) toggleAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'toggle-icon.png')) toggleAction.triggered.connect(self.toggle_landmarks) measuresAction = QtWidgets.QAction('Facial metrics', self) measuresAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'ruler_icon.png')) measuresAction.triggered.connect(self.create_new_window) saveAction = QtWidgets.QAction('Save results', self) saveAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'save_icon.png')) saveAction.triggered.connect(self.save_results) snapshotAction = QtWidgets.QAction('Save current view', self) snapshotAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'snapshot_icon.png')) snapshotAction.triggered.connect(self.save_snapshot) exitAction = QtWidgets.QAction('Exit', self) exitAction.setIcon( QtGui.QIcon(scriptDir + os.path.sep + 'include' + os.path.sep + 'icon_color' + os.path.sep + 'exit_icon.png')) exitAction.triggered.connect(self.close_app) #create the toolbar and add the actions self.toolBar = QtWidgets.QToolBar(self) self.toolBar.addActions( (fitAction, eyeAction, eyeLoad, centerAction, toggleAction, measuresAction, saveAction, snapshotAction, exitAction)) #set the size of each icon to 50x50 self.toolBar.setIconSize(QtCore.QSize(50, 50)) for action in self.toolBar.actions(): widget = self.toolBar.widgetForAction(action) widget.setFixedSize(50, 50) self.toolBar.setMinimumSize(self.toolBar.sizeHint()) self.toolBar.setStyleSheet('QToolBar{spacing:5px;}') #the main window consist of the toolbar and the ImageViewer layout = QtWidgets.QVBoxLayout() layout.addWidget(self.toolBar) layout.addWidget(self.displayImage) self.setLayout(layout) self.load_file(self._photograph) self.displayImage.update_view() self.show() def create_new_window(self): #this creates a new window to display all the facial metrics, there if self.displayImage._shape is not None: #if the measurements window is already open then close it if self._new_window is not None: self._new_window.close() self._new_window = None #compute the facial metrics using the landmarks MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual, _ = get_measurements_from_data( self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self._CalibrationType, self._CalibrationValue) #send all the information the the appropiate places in the window self._tab1_results = CustomTabResult() #filling t_new_window_tab1_results he info for the right self._tab1_results._CE_right.setText('{0:.2f}'.format( MeasurementsRight.CommissureExcursion)) self._tab1_results._SA_right.setText('{0:.2f}'.format( MeasurementsRight.SmileAngle)) self._tab1_results._DS_right.setText('{0:.2f}'.format( MeasurementsRight.DentalShow)) self._tab1_results._MRD1_right.setText('{0:.2f}'.format( MeasurementsRight.MarginalReflexDistance1)) self._tab1_results._MRD2_right.setText('{0:.2f}'.format( MeasurementsRight.MarginalReflexDistance2)) self._tab1_results._BH_right.setText('{0:.2f}'.format( MeasurementsRight.BrowHeight)) #filling the info for the left self._tab1_results._CE_left.setText('{0:.2f}'.format( MeasurementsLeft.CommissureExcursion)) self._tab1_results._SA_left.setText('{0:.2f}'.format( MeasurementsLeft.SmileAngle)) self._tab1_results._DS_left.setText('{0:.2f}'.format( MeasurementsLeft.DentalShow)) self._tab1_results._MRD1_left.setText('{0:.2f}'.format( MeasurementsLeft.MarginalReflexDistance1)) self._tab1_results._MRD2_left.setText('{0:.2f}'.format( MeasurementsLeft.MarginalReflexDistance2)) self._tab1_results._BH_left.setText('{0:.2f}'.format( MeasurementsLeft.BrowHeight)) #deviation self._tab1_results._CE_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommissureExcursion)) self._tab1_results._SA_dev.setText('{0:.2f}'.format( MeasurementsDeviation.SmileAngle)) self._tab1_results._MRD1_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance1)) self._tab1_results._MRD2_dev.setText('{0:.2f}'.format( MeasurementsDeviation.MarginalReflexDistance2)) self._tab1_results._BH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.BrowHeight)) self._tab1_results._DS_dev.setText('{0:.2f}'.format( MeasurementsDeviation.DentalShow)) self._tab1_results._CH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.CommisureHeightDeviation)) self._tab1_results._UVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.UpperLipHeightDeviation)) self._tab1_results._LVH_dev.setText('{0:.2f}'.format( MeasurementsDeviation.LowerLipHeightDeviation)) self._tab1_results._CE_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.CommissureExcursion)) self._tab1_results._SA_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.SmileAngle)) self._tab1_results._MRD1_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance1)) self._tab1_results._MRD2_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.MarginalReflexDistance2)) self._tab1_results._BH_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.BrowHeight)) self._tab1_results._DS_dev_p.setText('{0:.2f}'.format( MeasurementsPercentual.DentalShow)) delimiter = os.path.sep temp = self._file_name.split(delimiter) photo_name = temp[-1] photo_name = photo_name[0:-4] self._tab1_results._tab_name = photo_name #say to the window that presents the results that there is only 1 tab self._new_window = ShowResults(self._tab1_results) #show the window with the results self._new_window.show() def match_iris(self): #make both iris have the same diameter as the bigger one if self.displayImage._lefteye is not None: if self.displayImage._lefteye[2] < self.displayImage._righteye[2]: self.displayImage._lefteye[2] = self.displayImage._righteye[2] elif self.displayImage._lefteye[2] > self.displayImage._righteye[2]: self.displayImage._righteye[2] = self.displayImage._lefteye[2] elif self.displayImage._lefteye[2] == self.displayImage._righteye[ 2]: pass self._toggle_lines = True self.displayImage._points = None self.displayImage.set_update_photo() def face_center(self): #find a line connecting the center of both iris and then fit a perperdicular #line in the middle if self.displayImage._shape is not None: if self._toggle_lines == True: self._toggle_lines = False points = estimate_lines(self.displayImage._opencvimage, self.displayImage._lefteye, self.displayImage._righteye) self.displayImage._points = points self.displayImage.set_update_photo() else: self.displayImage._points = None self.displayImage.set_update_photo() self._toggle_lines = True def load_file(self, photograph): if self._new_window is not None: self._new_window.close() self.displayImage._opencvimage = self._photograph._photo self.displayImage._lefteye = self._photograph._lefteye self.displayImage._righteye = self._photograph._righteye self.displayImage._shape = self._photograph._shape self.displayImage._boundingbox = self._photograph._boundingbox self.displayImage._points = self._photograph._points self.displayImage.update_view() def load_iris(self): #load a file using the widget name, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Load Iris Position and Diameter', '', "Image files (*.png *.jpg *.jpeg *.tif *.tiff *.PNG *.JPG *.JPEG *.TIF *.TIFF)" ) if not name: pass else: #if windows then transform / to \ (python stuffs) name = os.path.normpath(name) #if the measurements window is open then close it, the measures will be updated with the new eyes position if self._new_window is not None: self._new_window.close() #if the photo was already processed then get the information for the #txt file, otherwise process the photo using the landmark ans pupil #localization algorithms file_txt = name[:-4] file_txt = (file_txt + '.txt') if os.path.isfile(file_txt): shape, lefteye, righteye, _ = get_info_from_txt(file_txt) dx_left = lefteye[0] - shape[27, 0] dy_left = shape[27, 1] - lefteye[1] dx_right = shape[27, 0] - righteye[0] dy_right = shape[27, 1] - righteye[1] self.displayImage._lefteye = [ self.displayImage._shape[27, 0] + dx_left, self.displayImage._shape[27, 1] - dy_left, lefteye[2] ] self.displayImage._righteye = [ self.displayImage._shape[27, 0] - dx_right, self.displayImage._shape[27, 1] - dy_right, lefteye[2] ] self.displayImage.set_update_photo() else: QtWidgets.QMessageBox.warning( self, "Warning", "Iris information for this photograph is not avaliable", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) # self.displayImage._lefteye = lefteye # self.displayImage._righteye = righteye # self.displayImage.set_update_photo() def toggle_landmarks(self): #Hide - show the landmarks if self._toggle_landmaks is True: self._toggle_landmaks = False self.displayImage.set_update_photo(self._toggle_landmaks) elif self._toggle_landmaks is False: self._toggle_landmaks = True self.displayImage.set_update_photo(self._toggle_landmaks) def save_snapshot(self): #save the current view if self.displayImage._opencvimage is not None: proposed_name = self._file_name[:-4] + '-landmarks' name, _ = QtWidgets.QFileDialog.getSaveFileName( self, 'Save File', proposed_name, 'png (*.png);;jpg (*.jpg);; jpeg (*.jpeg)') if not name: pass else: #if shape then add shape to image temp_image = self.displayImage._opencvimage.copy() #draw 68 landmark points if self.displayImage._shape is not None: temp_image = mark_picture(temp_image, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._points) cv2.imwrite(temp_image, name) def save_results(self): #save the results in a txt and xls files. if self._file_name is not None: if self.displayImage._shape is not None: MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual, _ = get_measurements_from_data( self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self._CalibrationType, self._CalibrationValue) # temp = SaveWindow(self, self._file_name, self._photograph._Tag, MeasurementsLeft, MeasurementsRight, MeasurementsDeviation, MeasurementsPercentual) temp.exec_() if temp._acceptSave: save_txt_file(self._file_name, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._boundingbox) def validate_exit(self): exit = True #save landmarks file if not available if self.displayImage._shape is not None: # only save if there is a shape file_txt = self._file_name[0:-4] + '.txt' if not (os.path.isfile(file_txt)): #the file doesn'e exists, save it save_txt_file(self._file_name, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._boundingbox) else: #the is a file available, compare with current values and save only if there are changes shape, lefteye, righteye, _ = get_info_from_txt(file_txt) if (shape == self.displayImage._shape).all() and ( lefteye == self.displayImage._lefteye) and ( righteye == self.displayImage._righteye): #no change move forward pass else: buttonReply = QMessageBox.question( self, 'Save changes', "Landamarks positions were modified.<br>Do you want to save the changes?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Cancel) if buttonReply == QMessageBox.Yes: save_txt_file(self._file_name, self.displayImage._shape, self.displayImage._lefteye, self.displayImage._righteye, self.displayImage._boundingbox) elif buttonReply == QMessageBox.No: pass elif buttonReply == QMessageBox.Cancel: exit = False return exit def close_app(self): self.close() def closeEvent(self, event): exit = self.validate_exit() if exit: #we need to close all the windows before closing the program if self._new_window is not None: self._new_window.close() event.accept() self.close() else: event.ignore()