def ShowImage(imKey, chMap, parent=None, brightness=1.0, scale=1.0, contrast=None): from imageviewer import ImageViewer imgs = FetchImage(imKey) frame = ImageViewer(imgs=imgs, chMap=chMap, img_key=imKey, parent=parent, title=str(imKey), brightness=brightness, scale=scale, contrast=contrast) frame.Show(True) return frame
def build(self): """Kivy build() Override Methode.""" global last_activity_time, ConfigObject, ImageViewerObject #global ScreenSaver self.settings_cls = MySettingsWithTabbedPanel #Window.size = (800, 480) def on_motion(self, etype, motionevent): global last_activity_time #global ScreenSaver last_activity_time = time.time() ## Catch 1st touch when screensaver is active #if ScreenSaver.display_state is False: # return(True) Window.bind(on_motion=on_motion) ConfigObject = self.config sm = ScreenManager(transition=NoTransition()) sm.add_widget(Mp3PiAppLayout()) #sm.add_widget(SettingsScreen()) #sm.add_widget(SaverScreen()) ImageViewerObject = ImageViewer() sm.add_widget(ImageViewerObject) return (sm)
def __init__(self, master, grid_c, grid_r): self.frame = Frame(master) self.frame.columnconfigure(0, weight=1) self.frame.columnconfigure(1, weight=1) self.frame.rowconfigure(0, weight=1) self.frame.rowconfigure(1, weight=1) self.img_area1 = ImageViewer(self.frame, 0, 0) self.img_area2 = ImageViewer(self.frame, 1, 0) self.img_area3 = ImageViewer(self.frame, 0, 1) self.img_area4 = ImageViewer(self.frame, 1, 1) # Layout self.frame.grid(column=grid_c, row=grid_r, rowspan=2, sticky=N + S + E + W)
def __init__(self, parent=None): super().__init__('Viewer', parent) self.setAllowedAreas(Qt.NoDockWidgetArea) self.image_list = [] self.index = 0 self.filters = [] self.init_filters() self.image_viewer = ImageViewer() self.image_viewer.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.scroll_area = QScrollArea() self.scroll_area.setWidget(self.image_viewer) self.setWidget(self.scroll_area) self.hide() self.setFloating(True) self.context_menu = QMenu(self) self.pix_is_ready.connect(self.image_viewer.show)
def __init__(self): super(UserWindow, self).__init__() self.ctr_frame = QtGui.QWidget() self.specTable = QtGui.QTableView() self.specModel = QtGui.QStandardItemModel(self) self.specTable1 = QtGui.QTableView() self.specModel1 = QtGui.QStandardItemModel(self) self.specList = self.createSpecTable() self.specList1 = self.createSpecTable1() self.image = ImageViewer(None, name) self.initUI()
def __init__(self, tmpDir): self.tmpDir = tmpDir self.tagSetting = TagSetting() self.taggedDataList = TaggedDataList() self.fileTagChanged = False self.curPage = 0 self.imageViewer = ImageViewer(self.tmpDir) self.window = Tk() self.window.title("Manga Tag Program") self.labelDir = Label(self.window, text='Input Directory:') self.dirVar = StringVar() self.entryDir = Entry(self.window, textvariable=self.dirVar, width=40) self.labelSearch = Label(self.window, text='Input keywords:') self.searchVar = StringVar() self.entrySearch = Entry(self.window, textvariable=self.searchVar, width=20) self.buttonSearch = Button(self.window, text="Search", command=self.searchFileWithKeyword) self.labelDir.grid(row=1, column=1) self.entryDir.grid(row=1, column=2, columnspan=2) self.labelSearch.grid(row=1, column=4) self.entrySearch.grid(row=1, column=5) self.buttonSearch.grid(row=1, column=6) self.listPath = Listbox(self.window, height=12, width=100, borderwidth=4) self.listPath.grid(row=2, column=1, columnspan=6) self.buttonOpenDir = Button(self.window, text="Open Directory", width=16, command=self.openDirectory) self.labelCurDir = Label(self.window, text="Current Directory:", width=20) self.textCurDir = Text(self.window, height=1, width=60) self.buttonOpenDir.grid(row=3, column=1) self.labelCurDir.grid(row=3, column=2) self.currentDir = "" self.textCurDir.grid(row=3, column=3, columnspan=4) self.textCurDir.config(state=DISABLED) self.buttonOpenFile = Button(self.window, text="Open File", width=16, command=self.openFile) self.labelCurFile = Label(self.window, text="Current File:", width=20) self.textCurFile = Text(self.window, height=1, width=60) self.buttonOpenFile.grid(row=4, column=1) self.labelCurFile.grid(row=4, column=2) self.currentFile = "" self.textCurFile.grid(row=4, column=3, columnspan=4) self.textCurFile.config(state=DISABLED) self.labelListTag = Label(self.window, text="List of Tags:", width=20) self.labelListFileTag = Label(self.window, text="List of File Tags:", width=20) self.labelListTag.grid(row=5, column=1, columnspan=2) self.labelListFileTag.grid(row=5, column=4, columnspan=3) self.listTag = Listbox(self.window, height=12, width=40, borderwidth=4) self.listFileTag = Listbox(self.window, height=12, width=40, borderwidth=4) self.listTag.grid(row=6, column=1, rowspan=6, columnspan=2) self.listFileTag.grid(row=6, column=4, rowspan=6, columnspan=3) self.buttonSearchWithTag = Button(self.window, text="↑ Find File with Tag", width=20, command=self.searchFileWithTag) self.buttonAddTag = Button(self.window, text="<---┐Add Tag", width=20, command=self.addTag) self.tagVar = StringVar() self.entryTag = Entry(self.window, textvariable=self.tagVar, width=20) self.buttonDeleteTag = Button(self.window, text="<-x--Delete Tag", width=20, command=self.deleteTag) self.buttonAddFileTag = Button(self.window, text="---Add to File--->", width=20, command=self.addFileTag) self.buttonDeleteFileTag = Button(self.window, text="Delete File Tag--x-->", width=20, command=self.deleteFileTag) self.buttonSearchWithTag.grid(row=6, column=3) self.buttonAddTag.grid(row=7, column=3) self.entryTag.grid(row=8, column=3) self.buttonDeleteTag.grid(row=9, column=3) self.buttonAddFileTag.grid(row=10, column=3) self.buttonDeleteFileTag.grid(row=11, column=3) self.textMessage = Text(self.window, height=4, width=100) self.textMessage.grid(row=12, column=1, rowspan=2, columnspan=6) self.loadTags() self.addMessage(self.textMessage, self.taggedDataList.readFile()) self.entryDir.focus_get() self.window.mainloop() if self.fileTagChanged: self.fileTagChanged = False self.taggedDataList.writeFile()
class TagGUI: def __init__(self, tmpDir): self.tmpDir = tmpDir self.tagSetting = TagSetting() self.taggedDataList = TaggedDataList() self.fileTagChanged = False self.curPage = 0 self.imageViewer = ImageViewer(self.tmpDir) self.window = Tk() self.window.title("Manga Tag Program") self.labelDir = Label(self.window, text='Input Directory:') self.dirVar = StringVar() self.entryDir = Entry(self.window, textvariable=self.dirVar, width=40) self.labelSearch = Label(self.window, text='Input keywords:') self.searchVar = StringVar() self.entrySearch = Entry(self.window, textvariable=self.searchVar, width=20) self.buttonSearch = Button(self.window, text="Search", command=self.searchFileWithKeyword) self.labelDir.grid(row=1, column=1) self.entryDir.grid(row=1, column=2, columnspan=2) self.labelSearch.grid(row=1, column=4) self.entrySearch.grid(row=1, column=5) self.buttonSearch.grid(row=1, column=6) self.listPath = Listbox(self.window, height=12, width=100, borderwidth=4) self.listPath.grid(row=2, column=1, columnspan=6) self.buttonOpenDir = Button(self.window, text="Open Directory", width=16, command=self.openDirectory) self.labelCurDir = Label(self.window, text="Current Directory:", width=20) self.textCurDir = Text(self.window, height=1, width=60) self.buttonOpenDir.grid(row=3, column=1) self.labelCurDir.grid(row=3, column=2) self.currentDir = "" self.textCurDir.grid(row=3, column=3, columnspan=4) self.textCurDir.config(state=DISABLED) self.buttonOpenFile = Button(self.window, text="Open File", width=16, command=self.openFile) self.labelCurFile = Label(self.window, text="Current File:", width=20) self.textCurFile = Text(self.window, height=1, width=60) self.buttonOpenFile.grid(row=4, column=1) self.labelCurFile.grid(row=4, column=2) self.currentFile = "" self.textCurFile.grid(row=4, column=3, columnspan=4) self.textCurFile.config(state=DISABLED) self.labelListTag = Label(self.window, text="List of Tags:", width=20) self.labelListFileTag = Label(self.window, text="List of File Tags:", width=20) self.labelListTag.grid(row=5, column=1, columnspan=2) self.labelListFileTag.grid(row=5, column=4, columnspan=3) self.listTag = Listbox(self.window, height=12, width=40, borderwidth=4) self.listFileTag = Listbox(self.window, height=12, width=40, borderwidth=4) self.listTag.grid(row=6, column=1, rowspan=6, columnspan=2) self.listFileTag.grid(row=6, column=4, rowspan=6, columnspan=3) self.buttonSearchWithTag = Button(self.window, text="↑ Find File with Tag", width=20, command=self.searchFileWithTag) self.buttonAddTag = Button(self.window, text="<---┐Add Tag", width=20, command=self.addTag) self.tagVar = StringVar() self.entryTag = Entry(self.window, textvariable=self.tagVar, width=20) self.buttonDeleteTag = Button(self.window, text="<-x--Delete Tag", width=20, command=self.deleteTag) self.buttonAddFileTag = Button(self.window, text="---Add to File--->", width=20, command=self.addFileTag) self.buttonDeleteFileTag = Button(self.window, text="Delete File Tag--x-->", width=20, command=self.deleteFileTag) self.buttonSearchWithTag.grid(row=6, column=3) self.buttonAddTag.grid(row=7, column=3) self.entryTag.grid(row=8, column=3) self.buttonDeleteTag.grid(row=9, column=3) self.buttonAddFileTag.grid(row=10, column=3) self.buttonDeleteFileTag.grid(row=11, column=3) self.textMessage = Text(self.window, height=4, width=100) self.textMessage.grid(row=12, column=1, rowspan=2, columnspan=6) self.loadTags() self.addMessage(self.textMessage, self.taggedDataList.readFile()) self.entryDir.focus_get() self.window.mainloop() if self.fileTagChanged: self.fileTagChanged = False self.taggedDataList.writeFile() def openDirectory(self): if not checkIfPath(self.dirVar.get()): self.addMessage( self.textMessage, "# Error 00: Directory \"" + self.dirVar.get() + "\" doesn't exist.") return self.listPath.delete(0, END) try: for roots, dirs, files in os.walk(self.dirVar.get()): for file in files: if file.find('.zip') > -1: self.listPath.insert(END, os.path.join(roots, file)) self.currentDir = self.dirVar.get() self.addMessage(self.textCurDir, self.currentDir, True) except: self.addMessage( self.textMessage, "# Error 01: Unable to open directory: \"" + self.dirVar.get() + '".') def openFile(self): path = self.listPath.get(ANCHOR) if path == '': self.addMessage( self.textMessage, "# Error 02: Please select a file from the list of tags.") return if not checkIfZipPath(path): self.addMessage( self.textMessage, "# Error 03: Zip file \"" + self.dirVar.get() + "\" doesn't exist.") return self.listFileTag.delete(0, END) if self.fileTagChanged: self.fileTagChanged = False self.taggedDataList.writeFile() if not self.taggedDataList.isEmpty(): self.loadFileTags(path) self.currentFile = path self.addMessage(self.textCurFile, path, True) self.imageViewer.close() self.imageViewer.open(path) def searchFileWithKeyword(self): if self.searchVar.get() == '': return j = 0 for i in range(self.listPath.size()): path = self.listPath.get(i - j) name = pathToName(path) if name.find(self.searchVar.get()) < 0: self.listPath.delete(i - j) j += 1 def searchFileWithTag(self): tag = self.listTag.get(ANCHOR) if tag == "": return '# Error 23: Please select a tag from the list of tags.' self.listPath.delete(0, END) for path in self.taggedDataList.getPathWithTags(tag): self.listPath.insert(END, path) return 'All files with tag "' + tag + '" are listed.' def loadTags(self): msg = self.tagSetting.readFile() if msg.find('# Error') < 0: for tag in self.tagSetting.get(): self.listTag.insert(END, tag) self.addMessage(self.textMessage, msg) def loadFileTags(self, zipfile): for tag in self.taggedDataList.getZipTags(zipfile): self.listFileTag.insert(END, tag) def addTag(self): if self.tagVar.get() != '': self.listTag.insert(END, self.tagVar.get()) msg = self.tagSetting.addTag(self.tagVar.get()) else: msg = "# Error 20: Empty Input in entryTag." self.addMessage(self.textMessage, msg) def deleteTag(self): msg = self.tagSetting.deleteTag(self.listTag.get(ANCHOR)) self.listTag.delete(ANCHOR) self.addMessage(self.textMessage, msg) def addFileTag(self): if self.currentFile == '': msg = "# Error 22: Please start with opening a file." self.addMessage(self.textMessage, msg) return if self.listTag.get(ANCHOR) != '': self.listFileTag.insert(END, self.listTag.get(ANCHOR)) msg = self.taggedDataList.addTag(self.currentFile, self.listTag.get(ANCHOR)) self.fileTagChanged = True else: msg = "# Error 21: Please select a tag from the taglist at left side." self.addMessage(self.textMessage, msg) def deleteFileTag(self): if self.currentFile == '': msg = "# Error 22: Please start with opening a file." self.addMessage(self.textMessage, msg) return if self.listFileTag.get(ANCHOR) != '': msg = self.taggedDataList.deleteTag(self.currentFile, self.listFileTag.get(ANCHOR)) self.listFileTag.delete(ANCHOR) self.fileTagChanged = True else: msg = "# Error 23: Please select a tag from the taglist above." self.addMessage(self.textMessage, msg) def addMessage(self, msgbox, msg, single_line=False): msgbox.config(state=NORMAL) try: if single_line: msgbox.insert(END, msg) msgbox.delete(0.0, END) msgbox.insert(END, msg) else: msgbox.insert(END, msg + '\n') except: msgbox.config(state=DISABLED) msgbox.config(state=DISABLED) msgbox.see(END)
def launch_image_viewer(self, evt=None): imviewer = ImageViewer(parent=self) imviewer.Show(True)
def initUI(self): #Menu self.menubar = self.menuBar() self.fileMenu = self.menubar.addMenu('&File') self.openAct = QAction('&Open', self) self.openAct.setShortcut('Ctrl+O') self.openAct.setIcon(QIcon("./image/open.ico")) self.saveAct = QAction('&Save', self) self.saveAct.setShortcut('Ctrl+S') self.saveAct.setIcon(QIcon("./image/save.ico")) self.importAct = QAction('&Import Data', self) self.importAct.setShortcut('Ctrl+I') self.importAct.setIcon(QIcon("./image/import.ico")) self.exportAct = QAction('&Export Data', self) self.exportAct.setShortcut('Ctrl+E') self.exportAct.setIcon(QIcon("./image/export.ico")) self.exportAct.setEnabled(False) self.exitAct = QAction('&Exit', self) self.exitAct.setIcon(QIcon("./image/exit.ico")) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.importAct) self.fileMenu.addAction(self.exportAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAct) self.importAct.triggered.connect(self.OnImport) self.exportAct.triggered.connect(self.OnExport) self.exitAct.triggered.connect(self.close) #Data Browser self.DataBrowser = DataBrowser(self) #Process Region Expansion button self.PRButton = QPushButton(">") self.PRButton.setFixedSize(20, 80) self.PRButton.setCheckable(True) self.PRButton.toggled.connect(self.showDataProcessor) #Data Processor self.DataProcessor = DataProcessor(self) #Image Viewer self.ImageViewer = ImageViewer(self) #Mayavi Region Expansion button self.MYButton = QPushButton(">") self.MYButton.setFixedSize(20, 80) self.MYButton.setCheckable(True) #self.MYButton.setEnabled(False) self.MYButton.toggled.connect(self.show3D) #Mayavi scene self.MYWidget = MYWidget(self) #Layout self.panel, self.splitter, self.Databox, self.DataWidget = self.WinLayout() QTimer.singleShot(10, lambda: self.splitter.moveSplitter(self.DataBrowser.minimumWidth(), 1)) self.splitter.splitterMoved.connect(self.splitterMovedEvent) #center panel self.centralPanel = QWidget(self) self.centralPanel.setLayout(self.panel) self.setCentralWidget(self.centralPanel) self.setWindowTitle('ARPES Data Viewer -- By Wei Yao -- Ver 1.0') self.show() self.initCompleteFlag = True
import cv2 import numpy as np from detecter import Detecter from vfcamera import VideoFileCamera from imageviewer import ImageViewer from queue import Queue queue = Queue(1) filePath = 'TrafficLight.mp4' viewer = ImageViewer('Video', queue) viewer.start() camera = VideoFileCamera(filePath, queue) camera.start() # class FileCamera: # def __init__(self, file_path): # self.cap = cv2.VideoCapture(file_path) # self.THRESHOLD = 0.25 # def detect(self): # queue = Queue(10) # # while(self.cap.isOpened()): # # _, frame = self.cap.read() # # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # # detecter = Detecter() # # detecter.setup('./frozen_inference_graph.pb', './mscoco_label_map.pbtxt') # # image_ex = np.expand_dims(frame, axis=0)
def __init__(self): super().__init__() self.working_directory = os.path.dirname(os.path.realpath(__file__)) self.current_directory = None self.left_camera_is_free = True self.right_camera_is_free = True self.setWindowTitle('BookScanner') self.setWindowIcon( QtGui.QIcon('{0}/icon.png'.format(self.working_directory))) self.setMinimumSize(1190, 710) fg = self.frameGeometry() fg.moveCenter(QtGui.QDesktopWidget().availableGeometry().center()) self.move(fg.topLeft()) self.background = QtGui.QLabel(self) self.background.setGeometry(0, 0, 9999, 9999) self.background.setStyleSheet( 'background-image: url({0}/background.png)'.format(self.working_directory)) self.layout = QtGui.QVBoxLayout() self.layout.setContentsMargins(30, 30, 30, 30) self.header = QtGui.QWidget() self.header.setFixedSize(1130, 100) self.logo = QtGui.QLabel(self.header) self.logo.setGeometry(0, 0, 170, 100) self.logo.setStyleSheet( 'background-image: url({0}/logo.png)'.format(self.working_directory)) self.directory_btn = QtGui.QPushButton('Select directory', self.header) self.directory_btn.setGeometry(200, 0, 200, 30) self.directory_btn.clicked.connect(self.select_directory) self.directory_lbl = QtGui.QLabel(self.header) self.directory_lbl.setGeometry(430, 0, 400, 30) self.automatic_numeration_box = QtGui.QCheckBox(self.header) self.automatic_numeration_box.setGeometry(860, 0, 30, 30) self.automatic_numeration_lbl = QtGui.QLabel( 'Automatic numbering', self.header) self.automatic_numeration_lbl.setGeometry(890, 0, 200, 30) self.filename_left_lbl = QtGui.QLabel( 'Filename for left camera:', self.header) self.filename_left_lbl.setGeometry(200, 60, 220, 30) self.filename_left_field = QtGui.QLineEdit(self.header) self.filename_left_field.setGeometry(420, 60, 220, 30) self.filename_right_lbl = QtGui.QLabel( 'Filename for right camera:', self.header) self.filename_right_lbl.setGeometry(670, 60, 220, 30) self.filename_right_field = QtGui.QLineEdit(self.header) self.filename_right_field.setGeometry(890, 60, 220, 30) self.layout.addWidget(self.header) self.body = QtGui.QWidget() self.body_layout = QtGui.QHBoxLayout() self.image_left = ImageViewer(self) self.body_layout.addWidget(self.image_left) self.image_right = ImageViewer(self) self.body_layout.addWidget(self.image_right) self.body.setLayout(self.body_layout) self.layout.addWidget(self.body) self.footer = QtGui.QWidget() self.footer.setFixedSize(660, 30) self.shoot_btn = QtGui.QPushButton('Make photos', self.footer) self.shoot_btn.setGeometry(0, 0, 200, 30) self.shoot_btn.clicked.connect(self.shoot) QtGui.QShortcut(QtGui.QKeySequence('Space'), self, self.shoot) self.shoot_left_btn = QtGui.QPushButton('Make left photo', self.footer) self.shoot_left_btn.setGeometry(230, 0, 200, 30) self.shoot_left_btn.clicked.connect(self.shoot_left) self.shoot_right_btn = QtGui.QPushButton( 'Make right photo', self.footer) self.shoot_right_btn.setGeometry(460, 0, 200, 30) self.shoot_right_btn.clicked.connect(self.shoot_right) self.layout.addWidget(self.footer) self.setLayout(self.layout)
class Viewer(QDockWidget): pix_is_ready = Signal() def __init__(self, parent=None): super().__init__('Viewer', parent) self.setAllowedAreas(Qt.NoDockWidgetArea) self.image_list = [] self.index = 0 self.filters = [] self.init_filters() self.image_viewer = ImageViewer() self.image_viewer.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.scroll_area = QScrollArea() self.scroll_area.setWidget(self.image_viewer) self.setWidget(self.scroll_area) self.hide() self.setFloating(True) self.context_menu = QMenu(self) self.pix_is_ready.connect(self.image_viewer.show) def init_filters(self): formats = QImageReader.supportedImageFormats() for f in formats: self.filters.append('*.' + f.data().decode('utf-8')) def contextMenuEvent(self, event): self.context_menu.exec(event.globalPos()) def setup(self): if not self.image_list: self.open_directory() if not self.image_viewer.pixmap(): self.set_image(0) self.show() def open_directory(self, path=''): if not path: path = QFileDialog.getExistingDirectory(self, 'Open Directory', '.', QFileDialog.ShowDirsOnly) if not path: return directory = QDir(path) directory.setNameFilters(self.filters) self.image_list = [] image_files = directory.entryList() for file in image_files: self.add_item(directory.absolutePath() + '/' + file) def set_reference(self): self.open_directory() self.set_image(0) def add_item(self, path=''): if path: self.image_list.append(path) def set_image(self, index): if index < 0 or index >= len(self.image_list): return None self.image_viewer.set_image(self.image_list[index]) self.index = index self.pix_is_ready.emit() self.setWindowTitle(self.image_list[index]) self.parent().update_actions() def next(self): self.set_image(self.index + 1) self.image_viewer.scale_image_by_factor(self.image_viewer.factor) def previous(self): self.set_image(self.index - 1) self.image_viewer.scale_image_by_factor(self.image_viewer.factor) def zoom_in(self): self.image_viewer.scale_image_by_rate(1.25) def zoom_out(self): self.image_viewer.scale_image_by_rate(0.8) def normal_size(self): self.image_viewer.adjustSize() self.image_viewer.factor = 1.0 def fit_to_window(self): if self.parent().viewer_act[6].isChecked(): self.image_viewer.aspect_fit(self.scroll_area.size()) def show(self): if not self.image_viewer.pixmap(): if self.image_list: self.set_image(0) else: self.open_directory() self.set_image(0) self.image_viewer.adjustSize() self.setVisible(True) def close(self): self.hide() def resizeEvent(self, event): self.fit_to_window() super().resizeEvent(event) def isReady(self): if self.image_viewer.pixmap(): return True else: return False
from vfcamera import VideoFileCamera from imageviewer import ImageViewer from queue import Queue queue = Queue(10) viewer = ImageViewer("image", queue) viewer.start() camera = VideoFileCamera('../video1.avi', queue) camera.start()
def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_Widget() self.ui.setupUi(self) # 成员变量 self.__menuBtnGroup = QButtonGroup(self) # 菜单按钮组 self.__checkedMenuBtn = None # 当前选中的菜单按钮 self.__openImgBtn = QPushButton( '打开图片', self.ui.widget_center_container) # 打开图片按钮 self.__imgFileName = '' # 图片文件名称 self.__viewer = ImageViewer() # 图片查看器 self.__contrastViewer = ImageViewer() # 原图查看器 self.__settings = QSettings() self.__imgList = [] # 图片列表 self.__redoList = [] # 重做列表 self.__previewImgDict = { # 预览图片字典 'brightnessAdjust': np.ndarray([0]), 'contrastRatioAdjust': np.ndarray([0]), 'smoothing': np.ndarray([0]), 'fixedThreshold': np.ndarray([0]), 'adaptiveThreshold': np.ndarray([0]), 'morphology': np.ndarray([0]), 'cannyEdge': np.ndarray([0]) } self.__mousePressFlag = False # 鼠标左键是否被按下 self.__mousePressPos = QPoint() # 鼠标按下位置 self.__fixedThresholdActiveFlag = False # 固定阈值分割激活标志 self.__cannyEdgeActiveFlag = False # Canny边缘检测激活标志 # 程序名称和图标 self.setWindowTitle("图像实践") self.setWindowIcon(QIcon(':/image/app_icon.png')) # 自动填充背景色 self.ui.widget_bg.setAutoFillBackground(True) # 无边框窗口 self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) # widget_center阴影 self.shadow = QGraphicsDropShadowEffect(self) self.shadow.setOffset(0, 0) self.shadow.setColor(QColor('#666666')) self.shadow.setBlurRadius(30) self.ui.widget_center_container.setGraphicsEffect(self.shadow) self.shadow.setEnabled(False) # 禁用阴影 # 禁止widget_center将鼠标事件传到父级 self.ui.widget_center.setAttribute(Qt.WA_NoMousePropagation) # 设置widget_imageviewer_container布局 imageviewerContainerLayout = QHBoxLayout( self.ui.widget_imageviewer_container) imageviewerContainerLayout.addWidget(self.__contrastViewer) imageviewerContainerLayout.addWidget(self.__viewer) # 设置控件样式 self.ui.widget_center.setProperty('form', 'widget_center') self.ui.widget_tool.setProperty('form', 'widget_tool') self.ui.btn_min.setProperty('form', 'btn_min') # 窗口操作按钮 self.ui.btn_max.setProperty('form', 'btn_max') self.ui.btn_close.setProperty('form', 'btn_close') self.__openImgBtn.setProperty('form', 'btn_open_img') # 打开图片按钮 self.__openImgBtn.setFixedSize(350, 50) self.ui.btn_save.setProperty('form', 'btn_save') # 保存按钮 self.ui.btn_brightness_ok.setProperty('form', 'btn_dark') # 工具栏按钮 self.ui.btn_undo.setProperty('form', 'btn_undo') # 撤销重做按钮 self.ui.btn_redo.setProperty('form', 'btn_redo') self.ui.btn_zoomin.setProperty('form', 'btn_zoomin') # 图片操作按钮 self.ui.btn_zoomout.setProperty('form', 'btn_zoomout') self.ui.btn_fit_display.setProperty('form', 'btn_fit_display') self.ui.btn_rotate.setProperty('form', 'btn_rotate') self.ui.btn_contrast.setProperty('form', 'btn_contrast') self.ui.btn_brightness_cancel.setProperty('form', 'btn_light') # 基本页面 self.ui.btn_contrast_ratio_ok.setProperty('form', 'btn_dark') self.ui.btn_contrast_ratio_cancel.setProperty('form', 'btn_light') self.ui.btn_color_reversal.setProperty('form', 'btn_dark_big') self.ui.btn_to_gray_image.setProperty('form', 'btn_dark_big') self.ui.btn_histogram_equalization.setProperty('form', 'btn_dark_big') # self.ui.btn_show_histogram.setProperty('form', 'btn_light_big') self.ui.btn_smoothing_cancel.setProperty('form', 'btn_light') # 平滑页面 self.ui.btn_smoothing_ok.setProperty('form', 'btn_dark') self.ui.lbl_fixed_threshold_title.setProperty( 'form', 'lbl_title_small') # 分割页面 self.ui.btn_fixed_threshold_cancel.setProperty('form', 'btn_light') self.ui.btn_fixed_threshold_ok.setProperty('form', 'btn_dark') self.ui.lbl_adaptive_threshold_title.setProperty( 'form', 'lbl_title_small') self.ui.btn_adaptive_threshold_cancel.setProperty('form', 'btn_light') self.ui.btn_adaptive_threshold_ok.setProperty('form', 'btn_dark') self.ui.lbl_otsu_threshold_title.setProperty('form', 'lbl_title_small') self.ui.btn_otsu_threshold.setProperty('form', 'btn_dark_big') self.ui.btn_morphology_cancel.setProperty('form', 'btn_light') self.ui.btn_morphology_ok.setProperty('form', 'btn_dark') self.ui.lbl_sobel_edge_detection.setProperty( 'form', 'lbl_title_small') # 边缘检测页面 self.ui.btn_sobel_edge_detection.setProperty('form', 'btn_dark_big') self.ui.lbl_laplacian_edge_detection.setProperty( 'form', 'lbl_title_small') self.ui.btn_laplacian_edge_detection.setProperty( 'form', 'btn_dark_big') self.ui.lbl_canny_edge_detection.setProperty('form', 'lbl_title_small') self.ui.btn_canny_edge_detection_cancel.setProperty( 'form', 'btn_light') self.ui.btn_canny_edge_detection_ok.setProperty('form', 'btn_dark') self.ui.lbl_contour_feature_title.setProperty( 'form', 'lbl_title_small') # 特征提取页面 self.ui.btn_contour_tracing.setProperty('form', 'btn_dark_big') self.ui.lbl_shape_feature_title.setProperty('form', 'lbl_title_small') self.ui.btn_hough_line_transform.setProperty('form', 'btn_dark_big') self.ui.lbl_point_feature_title.setProperty('form', 'lbl_title_small') self.ui.btn_harris_feature.setProperty('form', 'btn_dark_big') self.ui.btn_fast_feature.setProperty('form', 'btn_dark_big') self.ui.btn_orb_feature.setProperty('form', 'btn_dark_big') # 菜单按钮组 self.__menuBtnGroup.setExclusive(False) for btn in self.ui.widget_menu.findChildren(QPushButton): btn.setProperty('form', 'btn_menu') self.__menuBtnGroup.addButton(btn) btn.clicked.connect(self.__menuBtnSlot) # 设置鼠标悬停提示 self.ui.btn_save.setToolTip('Ctrl+S') self.ui.btn_undo.setToolTip('Ctrl+Z') self.ui.btn_redo.setToolTip('Ctrl+R') # 初始化窗口状态 self.__openImgBtn.lower() self.ui.widget_center.move(0, 10e4) self.ui.widget_tool.hide() self.ui.widget_menu.setDisabled(True) self.ui.btn_save.setDisabled(True) self.__contrastViewer.hide() self.ui.btn_undo.setDisabled(True) self.ui.btn_redo.setDisabled(True) # 最小化按钮槽函数 self.ui.btn_min.clicked.connect(self.__minBtnSlot) # 最大化按钮槽函数 self.ui.btn_max.clicked.connect(self.__maxBtnSlot) # 关闭按钮槽函数 self.ui.btn_close.clicked.connect(self.__closeBtnSlot) # 打开图片按钮槽函数 self.__openImgBtn.clicked.connect(self.__openImgBtnSlot) # 保存按钮槽函数 self.ui.btn_save.clicked.connect(self.__saveBtnSlot) # 撤销按钮槽函数 self.ui.btn_undo.clicked.connect(self.__undoBtnSlot) # 重做按钮槽函数 self.ui.btn_redo.clicked.connect(self.__redoBtnSlot) # 缩小按钮槽函数 self.ui.btn_zoomout.clicked.connect( lambda: self.__viewer.animatedZoom(self.__viewer.propScale / 2)) # 放大按钮槽函数 self.ui.btn_zoomin.clicked.connect( lambda: self.__viewer.animatedZoom(self.__viewer.propScale * 2)) # 适应显示按钮槽函数 self.ui.btn_fit_display.clicked.connect(self.__fitDisplayBtnSlot) # 旋转按钮槽函数 self.ui.btn_rotate.clicked.connect(self.__rotateBtnSlot) # 对比按钮槽函数 self.ui.btn_contrast.clicked.connect(self.__contrastBtnSlot) # 亮度调节槽函数 bindSpinboxAndSlider(self.ui.sb_brightness, self.ui.hs_brightness, self.__brightnessAdjustPreviewSlot) self.ui.btn_brightness_ok.clicked.connect( self.__brightnessAdjustOkBtnSlot) self.ui.btn_brightness_cancel.clicked.connect( self.__brightnessAdjustCancelBtnSlot) # 对比度调节槽函数 bindSpinboxAndSlider(self.ui.sb_contrast_ratio, self.ui.hs_contrast_ratio, self.__contrastRatioAdjustPreviewSlot) self.ui.btn_contrast_ratio_ok.clicked.connect( self.__contrastRatioAdjustOkBtnSlot) self.ui.btn_contrast_ratio_cancel.clicked.connect( self.__contrastRatioAdjustCancelBtnSlot) # 反相按钮槽函数 self.ui.btn_color_reversal.clicked.connect(self.__colorReversalBtnSlot) # 灰度化按钮槽函数 self.ui.btn_to_gray_image.clicked.connect(self.__toGrayImageBtnSlot) # 直方图均衡化槽函数 self.ui.btn_histogram_equalization.clicked.connect( self.__histogramEqualizationBtnSlot) # 显示直方图按钮槽函数 # self.ui.btn_show_histogram.clicked.connect(self.__showHistogramBtnSlot) # 平滑操作槽函数 bindSpinboxAndSlider(self.ui.sb_smoothing_radius, self.ui.hs_smoothing_radius, self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_average.clicked.connect( self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_gaussian.clicked.connect( self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_median.clicked.connect( self.__smoothingPreviewSlot) self.ui.btn_smoothing_ok.clicked.connect(self.__smoothingOkBtnSlot) self.ui.btn_smoothing_cancel.clicked.connect( self.__smoothingCancelBtnSlot) # 固定阈值分割槽函数 bindSpinboxAndSlider(self.ui.sb_fixed_threshold, self.ui.hs_fixed_threshold, self.__fixedThresholdPreviewSlot) self.ui.btn_fixed_threshold_ok.clicked.connect( self.__fixedThresholdOkBtnSlot) self.ui.btn_fixed_threshold_cancel.clicked.connect( self.__fixedThresholdCancelBtnSlot) # 自适应阈值分割槽函数 bindSpinboxAndSlider(self.ui.sb_adaptive_threshold_radius, self.ui.hs_adaptive_threshold_radius, self.__adaptiveThresholdPreviewSlot) bindSpinboxAndSlider(self.ui.sb_adaptive_threshold_offset, self.ui.hs_adaptive_threshold_offset, self.__adaptiveThresholdPreviewSlot) self.ui.radio_adaptive_threshold_type_average.clicked.connect( self.__adaptiveThresholdPreviewSlot) self.ui.radio_adaptive_threshold_type_gaussian.clicked.connect( self.__adaptiveThresholdPreviewSlot) self.ui.btn_adaptive_threshold_ok.clicked.connect( self.__adaptiveThresholdOkBtnSlot) self.ui.btn_adaptive_threshold_cancel.clicked.connect( self.__adaptiveThresholdCancelBtnSlot) # OTSU阈值分割槽函数 self.ui.btn_otsu_threshold.clicked.connect(self.__otsuThresholdBtnSlot) # 形态学操作槽函数 bindSpinboxAndSlider(self.ui.sb_morphology_radius, self.ui.hs_morphology_radius, self.__morphologyPreviewSlot) self.ui.radio_morphology_type_dilate.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_erode.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_open.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_close.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_shape_rect.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_shape_circle.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_cross.clicked.connect( self.__morphologyPreviewSlot) self.ui.btn_morphology_ok.clicked.connect(self.__morphologyOkBtnSlot) self.ui.btn_morphology_cancel.clicked.connect( self.__morphologyCancelBtnSlot) # 索伯边缘检测槽函数 self.ui.btn_sobel_edge_detection.clicked.connect( self.__sobelEdgeDetectionBtnSlot) # 拉普拉斯边缘检测 self.ui.btn_laplacian_edge_detection.clicked.connect( self.__laplacianEdgeDetectionBtnSlot) # Canny边缘检测槽函数 bindSpinboxAndSlider(self.ui.sb_canny_low_threshold, self.ui.hs_canny_low_threshold, self.__cannyEdgePreviewSlot) bindSpinboxAndSlider(self.ui.sb_canny_high_threshold, self.ui.hs_canny_high_threshold, self.__cannyEdgePreviewSlot) self.ui.btn_canny_edge_detection_ok.clicked.connect( self.__cannyEdgeOkBtnSlot) self.ui.btn_canny_edge_detection_cancel.clicked.connect( self.__cannyEdgeCancelBtnSlot) # 轮廓跟踪槽函数 self.ui.btn_contour_tracing.clicked.connect(self.__contourTracingSlot) # 霍夫直线变换槽函数 self.ui.btn_hough_line_transform.clicked.connect( self.__houghLineTransformSlot) # Harris特征检测槽函数 self.ui.btn_harris_feature.clicked.connect( self.__harrisFeatureDetectionSlot) # FAST特征检测槽函数 self.ui.btn_fast_feature.clicked.connect( self.__fastFeatureDetectionSlot) # ORB特征检测槽函数 self.ui.btn_orb_feature.clicked.connect(self.__orbFeatureDetectionSlot)
class Widget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_Widget() self.ui.setupUi(self) # 成员变量 self.__menuBtnGroup = QButtonGroup(self) # 菜单按钮组 self.__checkedMenuBtn = None # 当前选中的菜单按钮 self.__openImgBtn = QPushButton( '打开图片', self.ui.widget_center_container) # 打开图片按钮 self.__imgFileName = '' # 图片文件名称 self.__viewer = ImageViewer() # 图片查看器 self.__contrastViewer = ImageViewer() # 原图查看器 self.__settings = QSettings() self.__imgList = [] # 图片列表 self.__redoList = [] # 重做列表 self.__previewImgDict = { # 预览图片字典 'brightnessAdjust': np.ndarray([0]), 'contrastRatioAdjust': np.ndarray([0]), 'smoothing': np.ndarray([0]), 'fixedThreshold': np.ndarray([0]), 'adaptiveThreshold': np.ndarray([0]), 'morphology': np.ndarray([0]), 'cannyEdge': np.ndarray([0]) } self.__mousePressFlag = False # 鼠标左键是否被按下 self.__mousePressPos = QPoint() # 鼠标按下位置 self.__fixedThresholdActiveFlag = False # 固定阈值分割激活标志 self.__cannyEdgeActiveFlag = False # Canny边缘检测激活标志 # 程序名称和图标 self.setWindowTitle("图像实践") self.setWindowIcon(QIcon(':/image/app_icon.png')) # 自动填充背景色 self.ui.widget_bg.setAutoFillBackground(True) # 无边框窗口 self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) # widget_center阴影 self.shadow = QGraphicsDropShadowEffect(self) self.shadow.setOffset(0, 0) self.shadow.setColor(QColor('#666666')) self.shadow.setBlurRadius(30) self.ui.widget_center_container.setGraphicsEffect(self.shadow) self.shadow.setEnabled(False) # 禁用阴影 # 禁止widget_center将鼠标事件传到父级 self.ui.widget_center.setAttribute(Qt.WA_NoMousePropagation) # 设置widget_imageviewer_container布局 imageviewerContainerLayout = QHBoxLayout( self.ui.widget_imageviewer_container) imageviewerContainerLayout.addWidget(self.__contrastViewer) imageviewerContainerLayout.addWidget(self.__viewer) # 设置控件样式 self.ui.widget_center.setProperty('form', 'widget_center') self.ui.widget_tool.setProperty('form', 'widget_tool') self.ui.btn_min.setProperty('form', 'btn_min') # 窗口操作按钮 self.ui.btn_max.setProperty('form', 'btn_max') self.ui.btn_close.setProperty('form', 'btn_close') self.__openImgBtn.setProperty('form', 'btn_open_img') # 打开图片按钮 self.__openImgBtn.setFixedSize(350, 50) self.ui.btn_save.setProperty('form', 'btn_save') # 保存按钮 self.ui.btn_brightness_ok.setProperty('form', 'btn_dark') # 工具栏按钮 self.ui.btn_undo.setProperty('form', 'btn_undo') # 撤销重做按钮 self.ui.btn_redo.setProperty('form', 'btn_redo') self.ui.btn_zoomin.setProperty('form', 'btn_zoomin') # 图片操作按钮 self.ui.btn_zoomout.setProperty('form', 'btn_zoomout') self.ui.btn_fit_display.setProperty('form', 'btn_fit_display') self.ui.btn_rotate.setProperty('form', 'btn_rotate') self.ui.btn_contrast.setProperty('form', 'btn_contrast') self.ui.btn_brightness_cancel.setProperty('form', 'btn_light') # 基本页面 self.ui.btn_contrast_ratio_ok.setProperty('form', 'btn_dark') self.ui.btn_contrast_ratio_cancel.setProperty('form', 'btn_light') self.ui.btn_color_reversal.setProperty('form', 'btn_dark_big') self.ui.btn_to_gray_image.setProperty('form', 'btn_dark_big') self.ui.btn_histogram_equalization.setProperty('form', 'btn_dark_big') # self.ui.btn_show_histogram.setProperty('form', 'btn_light_big') self.ui.btn_smoothing_cancel.setProperty('form', 'btn_light') # 平滑页面 self.ui.btn_smoothing_ok.setProperty('form', 'btn_dark') self.ui.lbl_fixed_threshold_title.setProperty( 'form', 'lbl_title_small') # 分割页面 self.ui.btn_fixed_threshold_cancel.setProperty('form', 'btn_light') self.ui.btn_fixed_threshold_ok.setProperty('form', 'btn_dark') self.ui.lbl_adaptive_threshold_title.setProperty( 'form', 'lbl_title_small') self.ui.btn_adaptive_threshold_cancel.setProperty('form', 'btn_light') self.ui.btn_adaptive_threshold_ok.setProperty('form', 'btn_dark') self.ui.lbl_otsu_threshold_title.setProperty('form', 'lbl_title_small') self.ui.btn_otsu_threshold.setProperty('form', 'btn_dark_big') self.ui.btn_morphology_cancel.setProperty('form', 'btn_light') self.ui.btn_morphology_ok.setProperty('form', 'btn_dark') self.ui.lbl_sobel_edge_detection.setProperty( 'form', 'lbl_title_small') # 边缘检测页面 self.ui.btn_sobel_edge_detection.setProperty('form', 'btn_dark_big') self.ui.lbl_laplacian_edge_detection.setProperty( 'form', 'lbl_title_small') self.ui.btn_laplacian_edge_detection.setProperty( 'form', 'btn_dark_big') self.ui.lbl_canny_edge_detection.setProperty('form', 'lbl_title_small') self.ui.btn_canny_edge_detection_cancel.setProperty( 'form', 'btn_light') self.ui.btn_canny_edge_detection_ok.setProperty('form', 'btn_dark') self.ui.lbl_contour_feature_title.setProperty( 'form', 'lbl_title_small') # 特征提取页面 self.ui.btn_contour_tracing.setProperty('form', 'btn_dark_big') self.ui.lbl_shape_feature_title.setProperty('form', 'lbl_title_small') self.ui.btn_hough_line_transform.setProperty('form', 'btn_dark_big') self.ui.lbl_point_feature_title.setProperty('form', 'lbl_title_small') self.ui.btn_harris_feature.setProperty('form', 'btn_dark_big') self.ui.btn_fast_feature.setProperty('form', 'btn_dark_big') self.ui.btn_orb_feature.setProperty('form', 'btn_dark_big') # 菜单按钮组 self.__menuBtnGroup.setExclusive(False) for btn in self.ui.widget_menu.findChildren(QPushButton): btn.setProperty('form', 'btn_menu') self.__menuBtnGroup.addButton(btn) btn.clicked.connect(self.__menuBtnSlot) # 设置鼠标悬停提示 self.ui.btn_save.setToolTip('Ctrl+S') self.ui.btn_undo.setToolTip('Ctrl+Z') self.ui.btn_redo.setToolTip('Ctrl+R') # 初始化窗口状态 self.__openImgBtn.lower() self.ui.widget_center.move(0, 10e4) self.ui.widget_tool.hide() self.ui.widget_menu.setDisabled(True) self.ui.btn_save.setDisabled(True) self.__contrastViewer.hide() self.ui.btn_undo.setDisabled(True) self.ui.btn_redo.setDisabled(True) # 最小化按钮槽函数 self.ui.btn_min.clicked.connect(self.__minBtnSlot) # 最大化按钮槽函数 self.ui.btn_max.clicked.connect(self.__maxBtnSlot) # 关闭按钮槽函数 self.ui.btn_close.clicked.connect(self.__closeBtnSlot) # 打开图片按钮槽函数 self.__openImgBtn.clicked.connect(self.__openImgBtnSlot) # 保存按钮槽函数 self.ui.btn_save.clicked.connect(self.__saveBtnSlot) # 撤销按钮槽函数 self.ui.btn_undo.clicked.connect(self.__undoBtnSlot) # 重做按钮槽函数 self.ui.btn_redo.clicked.connect(self.__redoBtnSlot) # 缩小按钮槽函数 self.ui.btn_zoomout.clicked.connect( lambda: self.__viewer.animatedZoom(self.__viewer.propScale / 2)) # 放大按钮槽函数 self.ui.btn_zoomin.clicked.connect( lambda: self.__viewer.animatedZoom(self.__viewer.propScale * 2)) # 适应显示按钮槽函数 self.ui.btn_fit_display.clicked.connect(self.__fitDisplayBtnSlot) # 旋转按钮槽函数 self.ui.btn_rotate.clicked.connect(self.__rotateBtnSlot) # 对比按钮槽函数 self.ui.btn_contrast.clicked.connect(self.__contrastBtnSlot) # 亮度调节槽函数 bindSpinboxAndSlider(self.ui.sb_brightness, self.ui.hs_brightness, self.__brightnessAdjustPreviewSlot) self.ui.btn_brightness_ok.clicked.connect( self.__brightnessAdjustOkBtnSlot) self.ui.btn_brightness_cancel.clicked.connect( self.__brightnessAdjustCancelBtnSlot) # 对比度调节槽函数 bindSpinboxAndSlider(self.ui.sb_contrast_ratio, self.ui.hs_contrast_ratio, self.__contrastRatioAdjustPreviewSlot) self.ui.btn_contrast_ratio_ok.clicked.connect( self.__contrastRatioAdjustOkBtnSlot) self.ui.btn_contrast_ratio_cancel.clicked.connect( self.__contrastRatioAdjustCancelBtnSlot) # 反相按钮槽函数 self.ui.btn_color_reversal.clicked.connect(self.__colorReversalBtnSlot) # 灰度化按钮槽函数 self.ui.btn_to_gray_image.clicked.connect(self.__toGrayImageBtnSlot) # 直方图均衡化槽函数 self.ui.btn_histogram_equalization.clicked.connect( self.__histogramEqualizationBtnSlot) # 显示直方图按钮槽函数 # self.ui.btn_show_histogram.clicked.connect(self.__showHistogramBtnSlot) # 平滑操作槽函数 bindSpinboxAndSlider(self.ui.sb_smoothing_radius, self.ui.hs_smoothing_radius, self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_average.clicked.connect( self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_gaussian.clicked.connect( self.__smoothingPreviewSlot) self.ui.radio_smoothing_type_median.clicked.connect( self.__smoothingPreviewSlot) self.ui.btn_smoothing_ok.clicked.connect(self.__smoothingOkBtnSlot) self.ui.btn_smoothing_cancel.clicked.connect( self.__smoothingCancelBtnSlot) # 固定阈值分割槽函数 bindSpinboxAndSlider(self.ui.sb_fixed_threshold, self.ui.hs_fixed_threshold, self.__fixedThresholdPreviewSlot) self.ui.btn_fixed_threshold_ok.clicked.connect( self.__fixedThresholdOkBtnSlot) self.ui.btn_fixed_threshold_cancel.clicked.connect( self.__fixedThresholdCancelBtnSlot) # 自适应阈值分割槽函数 bindSpinboxAndSlider(self.ui.sb_adaptive_threshold_radius, self.ui.hs_adaptive_threshold_radius, self.__adaptiveThresholdPreviewSlot) bindSpinboxAndSlider(self.ui.sb_adaptive_threshold_offset, self.ui.hs_adaptive_threshold_offset, self.__adaptiveThresholdPreviewSlot) self.ui.radio_adaptive_threshold_type_average.clicked.connect( self.__adaptiveThresholdPreviewSlot) self.ui.radio_adaptive_threshold_type_gaussian.clicked.connect( self.__adaptiveThresholdPreviewSlot) self.ui.btn_adaptive_threshold_ok.clicked.connect( self.__adaptiveThresholdOkBtnSlot) self.ui.btn_adaptive_threshold_cancel.clicked.connect( self.__adaptiveThresholdCancelBtnSlot) # OTSU阈值分割槽函数 self.ui.btn_otsu_threshold.clicked.connect(self.__otsuThresholdBtnSlot) # 形态学操作槽函数 bindSpinboxAndSlider(self.ui.sb_morphology_radius, self.ui.hs_morphology_radius, self.__morphologyPreviewSlot) self.ui.radio_morphology_type_dilate.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_erode.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_open.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_close.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_shape_rect.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_shape_circle.clicked.connect( self.__morphologyPreviewSlot) self.ui.radio_morphology_type_cross.clicked.connect( self.__morphologyPreviewSlot) self.ui.btn_morphology_ok.clicked.connect(self.__morphologyOkBtnSlot) self.ui.btn_morphology_cancel.clicked.connect( self.__morphologyCancelBtnSlot) # 索伯边缘检测槽函数 self.ui.btn_sobel_edge_detection.clicked.connect( self.__sobelEdgeDetectionBtnSlot) # 拉普拉斯边缘检测 self.ui.btn_laplacian_edge_detection.clicked.connect( self.__laplacianEdgeDetectionBtnSlot) # Canny边缘检测槽函数 bindSpinboxAndSlider(self.ui.sb_canny_low_threshold, self.ui.hs_canny_low_threshold, self.__cannyEdgePreviewSlot) bindSpinboxAndSlider(self.ui.sb_canny_high_threshold, self.ui.hs_canny_high_threshold, self.__cannyEdgePreviewSlot) self.ui.btn_canny_edge_detection_ok.clicked.connect( self.__cannyEdgeOkBtnSlot) self.ui.btn_canny_edge_detection_cancel.clicked.connect( self.__cannyEdgeCancelBtnSlot) # 轮廓跟踪槽函数 self.ui.btn_contour_tracing.clicked.connect(self.__contourTracingSlot) # 霍夫直线变换槽函数 self.ui.btn_hough_line_transform.clicked.connect( self.__houghLineTransformSlot) # Harris特征检测槽函数 self.ui.btn_harris_feature.clicked.connect( self.__harrisFeatureDetectionSlot) # FAST特征检测槽函数 self.ui.btn_fast_feature.clicked.connect( self.__fastFeatureDetectionSlot) # ORB特征检测槽函数 self.ui.btn_orb_feature.clicked.connect(self.__orbFeatureDetectionSlot) # 重写绘图函数 def paintEvent(self, event): # 绘制窗体阴影 shadow_margin = 15 shadow_pixmap = QPixmap('://image/mainWnd_shadow.png') painter = QPainter(self) pngTop = QRect(shadow_margin, 0, shadow_pixmap.width() - shadow_margin * 2, shadow_margin) winTop = QRect(shadow_margin, 0, self.width() - shadow_margin * 2, shadow_margin) painter.drawPixmap(winTop, shadow_pixmap, pngTop) pngBottom = QRect(shadow_margin, shadow_pixmap.height() - shadow_margin, shadow_pixmap.width() - shadow_margin * 2, shadow_margin) winBottom = QRect(shadow_margin, self.height() - shadow_margin, self.width() - shadow_margin * 2, shadow_margin) painter.drawPixmap(winBottom, shadow_pixmap, pngBottom) pngLeft = QRect(0, shadow_margin, shadow_margin, shadow_pixmap.height() - shadow_margin * 2) winLeft = QRect(0, shadow_margin, shadow_margin, self.height() - shadow_margin * 2) painter.drawPixmap(winLeft, shadow_pixmap, pngLeft) pngRight = QRect(shadow_pixmap.width() - shadow_margin, shadow_margin, shadow_margin, shadow_pixmap.height() - shadow_margin * 2) winRight = QRect(self.width() - shadow_margin, shadow_margin, shadow_margin, self.height() - shadow_margin * 2) painter.drawPixmap(winRight, shadow_pixmap, pngRight) pngLeftTop = QRect(0, 0, shadow_margin, shadow_margin) winLeftTop = QRect(0, 0, shadow_margin, shadow_margin) painter.drawPixmap(winLeftTop, shadow_pixmap, pngLeftTop) pngRightTop = QRect(shadow_pixmap.width() - shadow_margin, 0, shadow_margin, shadow_margin) winRightTop = QRect(self.width() - shadow_margin, 0, shadow_margin, shadow_margin) painter.drawPixmap(winRightTop, shadow_pixmap, pngRightTop) pngLeftBottom = QRect(0, shadow_pixmap.height() - shadow_margin, shadow_margin, shadow_margin) winLeftBottom = QRect(0, self.height() - shadow_margin, shadow_margin, shadow_margin) painter.drawPixmap(winLeftBottom, shadow_pixmap, pngLeftBottom) pngRightBottom = QRect(shadow_pixmap.width() - shadow_margin, shadow_pixmap.height() - shadow_margin, shadow_margin, shadow_margin) winRightBottom = QRect(self.width() - shadow_margin, self.height() - shadow_margin, shadow_margin, shadow_margin) painter.drawPixmap(winRightBottom, shadow_pixmap, pngRightBottom) # 重写改变窗口大小事件 def resizeEvent(self, event): # 使用背景图片 # palette = QPalette() # bgImg = QPixmap(':/image/background.jpg').scaled(self.ui.widget_bg.width(), self.ui.widget_bg.height()) # palette.setBrush(self.backgroundRole(), QBrush(bgImg)) # self.ui.widget_bg.setPalette(palette) # 使用纯色背景 self.ui.widget_bg.setPalette(QPalette(QColor(43, 87, 154))) # 计算widget_center_container大小 ownerSize = QSize( self.ui.widget_bg.width(), self.ui.widget_bg.height() - self.ui.widget_title.minimumHeight()) # 调整打开图片按钮位置 self.__openImgBtn.move( (ownerSize.width() - self.__openImgBtn.width()) / 2, (ownerSize.height() - self.__openImgBtn.height()) / 2) # 调整CenterWidget的大小 self.ui.widget_center.setFixedSize(ownerSize) # 重写鼠标按下事件 def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.__mousePressFlag = True self.__mousePressPos = event.pos() # 重写鼠标释放事件 def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.__mousePressFlag = False # 重写鼠标移动事件 def mouseMoveEvent(self, event): if self.__mousePressFlag and not self.isMaximized(): self.move(self.pos() + event.pos() - self.__mousePressPos) # 重写鼠标双击事件 def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: self.ui.btn_max.clicked.emit() # 重写键盘按下事件 def keyPressEvent(self, event): if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_S and not self.__openImgBtn.isEnabled(): self.__saveBtnSlot() if event.key() == Qt.Key_Z and self.ui.btn_undo.isEnabled(): self.__undoBtnSlot() if event.key() == Qt.Key_R and self.ui.btn_redo.isEnabled(): self.__redoBtnSlot() # 最小化按钮槽函数 def __minBtnSlot(self): self.showMinimized() # 最大化按钮槽函数 def __maxBtnSlot(self): if not self.isMaximized(): # 切换至全屏状态 self.layout().setContentsMargins(0, 0, 0, 0) self.showMaximized() self.ui.btn_max.setProperty('form', 'btn_max_back') else: # 切换为窗口状态 self.layout().setContentsMargins(15, 15, 15, 15) self.showNormal() self.ui.btn_max.setProperty('form', 'btn_max') self.style().polish(self.ui.btn_max) # 关闭按钮槽函数 def __closeBtnSlot(self): self.close() # 菜单按钮槽函数 def __menuBtnSlot(self): menu = self.sender().objectName() if self.__checkedMenuBtn is not None and menu == self.__checkedMenuBtn.objectName( ): self.ui.widget_tool.hide() self.__checkedMenuBtn = None else: # 切换page if menu == 'btn_menu_basic': self.ui.widget_tool.setCurrentWidget(self.ui.page_basic) elif menu == 'btn_menu_smoothing': self.ui.widget_tool.setCurrentWidget(self.ui.page_smoothing) elif menu == 'btn_menu_segmentation': self.ui.widget_tool.setCurrentWidget(self.ui.page_segmentation) elif menu == 'btn_menu_morphology': self.ui.widget_tool.setCurrentWidget(self.ui.page_morphology) elif menu == 'btn_menu_edge_detection': self.ui.widget_tool.setCurrentWidget( self.ui.page_edge_detection) elif menu == 'btn_menu_feature_extraction': self.ui.widget_tool.setCurrentWidget(self.ui.page_feature) if self.__checkedMenuBtn is None: self.ui.widget_tool.show() else: self.__checkedMenuBtn.setChecked(False) self.__checkedMenuBtn = self.sender() # 打开图片按钮槽函数 def __openImgBtnSlot(self): # 防止多次点击 self.__openImgBtn.setDisabled(True) # 获取桌面路径 openDir = QStandardPaths.writableLocation( QStandardPaths.DesktopLocation) # 从QSetting中读取路径 if self.__settings.contains('openDir'): openDir = self.__settings.value('openDir') # 打开文件选择对话框 path, _ = QFileDialog.getOpenFileName( self, '选择图片', openDir, '图片 (*.jpg *.png *.bmp *.jpe *.jpeg *.webp *.tif *.tiff)') if path == '': return # 路径保存至QSetting 文件名保存至__imgFileName self.__settings.setValue('openDir', path[:str.rindex(path, '/')]) self.__imgFileName = path[str.rindex(path, '/') + 1:] # 读取图片并添加到图片列表 self.__imgList.append(cv.imdecode(np.fromfile(path, dtype=np.uint8), 1)) # 显示图片 self.__viewer.loadImage(self.__imgList[0]) self.__contrastViewer.loadImage(self.__imgList[0]) # 弹出中心窗体 self.__popCenterWidget() # 保存按钮槽函数 def __saveBtnSlot(self): # 获取保存路径 openDir = QStandardPaths.writableLocation( QStandardPaths.DesktopLocation) if self.__settings.contains('openDir'): openDir = self.__settings.value( 'openDir') + '/' + self.__imgFileName path, _ = QFileDialog.getSaveFileName( self, '保存', openDir, '图片 (*.jpg *.png *.bmp *.jpe *.jpeg *.webp *.tif *.tiff)') if path == '': return # 将本次打开目录存入settings self.__settings.setValue('openDir', path[:str.rindex(path, '/')]) # 保存图片 cv.imwrite(path, self.__imgList[-1]) # 撤销按钮槽函数 def __undoBtnSlot(self): self.__redoList.append(self.__imgList.pop()) self.__viewer.changeImage(self.__imgList[-1]) if len(self.__imgList) == 1: self.ui.btn_undo.setDisabled(True) self.ui.btn_redo.setEnabled(True) # 重做按钮槽函数 def __redoBtnSlot(self): self.__imgList.append(self.__redoList.pop()) self.__viewer.changeImage(self.__imgList[-1]) if len(self.__redoList) == 0: self.ui.btn_redo.setDisabled(True) self.ui.btn_undo.setEnabled(True) # 适应显示按钮槽函数 def __fitDisplayBtnSlot(self): self.__viewer.fitDisplay() self.__contrastViewer.fitDisplay() # 旋转按钮槽函数 def __rotateBtnSlot(self): self.__viewer.rotateImage() self.__contrastViewer.rotateImage() # 对比按钮槽函数 def __contrastBtnSlot(self, b): if b: self.__contrastViewer.show() else: self.__contrastViewer.hide() # 亮度调节预览槽函数 def __brightnessAdjustPreviewSlot(self, value): self.__previewImgDict['brightnessAdjust'] = adjustBrightness( self.__imgList[-1], value) self.__viewer.changeImage(self.__previewImgDict['brightnessAdjust']) # 亮度调节确定按钮槽函数 # 说明:"确定"按钮的作用是将该功能的预览图片追加到图片列表中, 如果该功能当前的参数控件为默认值则不追加 def __brightnessAdjustOkBtnSlot(self): if not self.ui.hs_brightness.value() == 0: self.__appendImg(self.__previewImgDict['brightnessAdjust']) # 恢复参数控件的值 self.ui.hs_brightness.setValue(0) # 亮度调节取消按钮槽函数 def __brightnessAdjustCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['brightnessAdjust'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_brightness.setValue(0) # 对比度调节预览槽函数 def __contrastRatioAdjustPreviewSlot(self, value): self.__previewImgDict['contrastRatioAdjust'] = adjustContrastRatio( self.__imgList[-1], np.tan(value * np.pi / 180)) # value为映射函数倾斜角 self.__viewer.changeImage(self.__previewImgDict['contrastRatioAdjust']) # 对比度调节确定按钮槽函数 def __contrastRatioAdjustOkBtnSlot(self): if not self.ui.hs_contrast_ratio.value() == 45: self.__appendImg(self.__previewImgDict['contrastRatioAdjust']) # 恢复参数控件的值 self.ui.hs_contrast_ratio.setValue(45) # 对比度调节取消按钮槽函数 def __contrastRatioAdjustCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['contrastRatioAdjust'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_contrast_ratio.setValue(45) # 反相按钮槽函数 def __colorReversalBtnSlot(self): self.__appendImg(colorReversal(self.__imgList[-1])) # 灰度化按钮槽函数 def __toGrayImageBtnSlot(self): # 如果已经是灰度图 则直接返回 if self.__imgList[-1].ndim == 2: MessageDialog(self, '当前已经是灰度图像') return self.__appendImg(cv.cvtColor(self.__imgList[-1], cv.COLOR_BGR2GRAY)) # 直方图均衡化槽函数 def __histogramEqualizationBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先进行灰度化') return self.__appendImg(histogramEqualization(self.__imgList[-1])) # 显示直方图按钮槽函数 # def __showHistogramBtnSlot(self): # print('待写') # 平滑预览槽函数 def __smoothingPreviewSlot(self): radius = self.ui.hs_smoothing_radius.value() if self.ui.radio_smoothing_type_average.isChecked(): self.__previewImgDict['smoothing'] = averageSmoothing( self.__imgList[-1], radius) elif self.ui.radio_smoothing_type_gaussian.isChecked(): self.__previewImgDict['smoothing'] = gaussianSmoothing( self.__imgList[-1], radius) else: self.__previewImgDict['smoothing'] = medianSmoothing( self.__imgList[-1], radius) self.__viewer.changeImage(self.__previewImgDict['smoothing']) # 平滑确定按钮槽函数 def __smoothingOkBtnSlot(self): if not self.ui.hs_smoothing_radius.value() == 0: self.__appendImg(self.__previewImgDict['smoothing']) # 恢复参数控件的值 self.ui.hs_smoothing_radius.setValue(0) # 平滑取消按钮槽函数 def __smoothingCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['smoothing'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_smoothing_radius.setValue(0) # 固定阈值分割预览函数 def __fixedThresholdPreviewSlot(self, threshold): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_fixed_threshold.setValue(127) return self.__previewImgDict['fixedThreshold'] = fixedThresholdSegmentation( self.__imgList[-1], threshold) self.__viewer.changeImage(self.__previewImgDict['fixedThreshold']) self.__fixedThresholdActiveFlag = True # 置为激活状态 # 固定阈值分割确定按钮槽函数 def __fixedThresholdOkBtnSlot(self): if self.__fixedThresholdActiveFlag: self.__appendImg(self.__previewImgDict['fixedThreshold']) # 恢复参数控件的值 self.ui.hs_fixed_threshold.setValue(127) # 置为非激活状态 self.__fixedThresholdActiveFlag = False # 固定阈值分割取消按钮槽函数 def __fixedThresholdCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['fixedThreshold'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_fixed_threshold.setValue(127) # 置为非激活状态 self.__fixedThresholdActiveFlag = False # 再次主动刷新显示 self.__viewer.changeImage(self.__imgList[-1]) # 自适应阈值分割预览函数 def __adaptiveThresholdPreviewSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_adaptive_threshold_radius.setValue(0) self.ui.hs_adaptive_threshold_offset.setValue(0) return if self.ui.radio_adaptive_threshold_type_average.isChecked(): method = 'mean' else: method = 'gaussian' radius = self.ui.hs_adaptive_threshold_radius.value() offset = self.ui.hs_adaptive_threshold_offset.value() if radius == 0: self.__previewImgDict['adaptiveThreshold'] = self.__imgList[-1] else: self.__previewImgDict[ 'adaptiveThreshold'] = adaptiveThresholdSegmentation( self.__imgList[-1], method, radius, offset) self.__viewer.changeImage(self.__previewImgDict['adaptiveThreshold']) # 自适应阈值分割确定按钮槽函数 def __adaptiveThresholdOkBtnSlot(self): if not self.ui.hs_adaptive_threshold_radius.value() == 0: self.__appendImg(self.__previewImgDict['adaptiveThreshold']) # 恢复参数控件的值 self.ui.hs_adaptive_threshold_radius.setValue(0) self.ui.hs_adaptive_threshold_offset.setValue(0) # 自适应阈值分割取消按钮槽函数 def __adaptiveThresholdCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['adaptiveThreshold'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_adaptive_threshold_radius.setValue(0) self.ui.hs_adaptive_threshold_offset.setValue(0) # OTSU阈值分割槽函数 def __otsuThresholdBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(otsuThresholdSegmentation(self.__imgList[-1])) # 形态学预览按钮槽函数 def __morphologyPreviewSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') self.ui.hs_morphology_radius.setValue(0) return if self.ui.radio_morphology_shape_rect.isChecked(): shape = 'rect' elif self.ui.radio_morphology_shape_circle.isChecked(): shape = 'circle' else: shape = 'cross' radius = self.ui.hs_morphology_radius.value() if self.ui.radio_morphology_type_dilate.isChecked(): self.__previewImgDict['morphology'] = dilateOperation( self.__imgList[-1], shape, radius) elif self.ui.radio_morphology_type_erode.isChecked(): self.__previewImgDict['morphology'] = erodeOperation( self.__imgList[-1], shape, radius) elif self.ui.radio_morphology_type_open.isChecked(): self.__previewImgDict['morphology'] = openOperation( self.__imgList[-1], shape, radius) else: self.__previewImgDict['morphology'] = closeOperation( self.__imgList[-1], shape, radius) self.__viewer.changeImage(self.__previewImgDict['morphology']) # 形态学确定按钮槽函数 def __morphologyOkBtnSlot(self): if not self.ui.hs_morphology_radius.value() == 0: self.__appendImg(self.__previewImgDict['morphology']) # 恢复参数控件的值 self.ui.hs_morphology_radius.setValue(0) # 形态学取消按钮槽函数 def __morphologyCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['morphology'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_morphology_radius.setValue(0) # 索伯边缘检测槽函数 def __sobelEdgeDetectionBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return if self.ui.radio_sobel_edge_detection_axis_x.isChecked(): axis = 'x' else: axis = 'y' if self.ui.radio_sobel_edge_detection_radius_1.isChecked(): radius = 1 elif self.ui.radio_sobel_edge_detection_radius_2.isChecked(): radius = 2 else: radius = 3 self.__appendImg(sobelEdgeDetection(self.__imgList[-1], axis, radius)) # 拉普拉斯边缘检测槽函数 def __laplacianEdgeDetectionBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return if self.ui.radio_laplacian_edge_detection_type_4.isChecked(): neighbourhood = 4 else: neighbourhood = 8 self.__appendImg( laplacianEdgeDetection(self.__imgList[-1], neighbourhood)) # Canny边缘检测预览按钮槽函数 def __cannyEdgePreviewSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_canny_low_threshold.setValue(100) self.ui.hs_canny_high_threshold.setValue(200) return lowThreshold = self.ui.hs_canny_low_threshold.value() highThreshold = self.ui.hs_canny_high_threshold.value() self.__previewImgDict['cannyEdge'] = cannyEdgeDetection( self.__imgList[-1], lowThreshold, highThreshold) self.__viewer.changeImage(self.__previewImgDict['cannyEdge']) self.__cannyEdgeActiveFlag = True # 置为激活状态 # Canny边缘检测确定按钮槽函数 def __cannyEdgeOkBtnSlot(self): if self.__cannyEdgeActiveFlag: self.__appendImg(self.__previewImgDict['cannyEdge']) # 恢复参数控件的值 self.ui.hs_canny_low_threshold.setValue(100) self.ui.hs_canny_high_threshold.setValue(200) # 置为非激活状态 self.__cannyEdgeActiveFlag = False # 再次主动刷新显示(因为滑杆恢复导致预览函数再次对图像进行Canny边缘检测) self.__viewer.changeImage(self.__imgList[-1]) # Canny边缘检测取消按钮槽函数 def __cannyEdgeCancelBtnSlot(self): # 重置预览图片 self.__previewImgDict['cannyEdge'] = self.__imgList[-1] # 恢复参数控件的值(预览图片会刷新显示) self.ui.hs_canny_low_threshold.setValue(100) self.ui.hs_canny_high_threshold.setValue(200) # 置为非激活状态 self.__cannyEdgeActiveFlag = False # 再次主动刷新显示 self.__viewer.changeImage(self.__imgList[-1]) # 轮廓跟踪槽函数 def __contourTracingSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') return self.__appendImg(contourTracing(self.__imgList[-1])) # 霍夫直线变换槽函数 def __houghLineTransformSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') return newImg, flag = houghLineTransform(self.__imgList[-1]) if flag: self.__appendImg(newImg) else: MessageDialog(self, '未检测到直线') # Harris特征检测槽函数 def __harrisFeatureDetectionSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(harrisFeatureDetection(self.__imgList[-1])) # FAST特征检测槽函数 def __fastFeatureDetectionSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(fastFeatureDetection(self.__imgList[-1])) # ORB特征检测槽函数 def __orbFeatureDetectionSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(orbFeatureDetection(self.__imgList[-1])) # 弹出中心窗体 def __popCenterWidget(self): animation = QPropertyAnimation(self.ui.widget_center, b'pos', self) animation.setDuration(800) animation.setStartValue( QPoint(0, self.ui.widget_center_container.height())) animation.setEndValue(QPoint(0, 0)) animation.setEasingCurve(QEasingCurve.InOutCubic) animation.finished.connect( lambda: self.shadow.setEnabled(True)) # 动画结束后启用阴影 animation.start() self.ui.widget_menu.setEnabled(True) self.ui.btn_save.setEnabled(True) # 往图片列表中追加图片 def __appendImg(self, img): # 追加图片 并刷新显示 self.__imgList.append(img) self.__viewer.changeImage(self.__imgList[-1]) # 启用撤销按钮 self.ui.btn_undo.setEnabled(True) # 清空重做列表 并禁用重做按钮 self.__redoList.clear() self.ui.btn_redo.setDisabled(True)
####################### FOR TESTING ######################### if __name__ == "__main__": import wx from datamodel import DataModel from dbconnect import DBConnect from imageviewer import ImageViewer import sys app = wx.PySimpleApp() p = Properties.getInstance() dm = DataModel.getInstance() db = DBConnect.getInstance() ir = ImageReader() # Load a properties file if passed in args if len(sys.argv) > 1: propsFile = sys.argv[1] p.LoadFile(propsFile) else: if not p.show_load_dialog(): wx.GetApp().Exit() obkey = dm.GetRandomObject() fds = db.GetFullChannelPathsForImage(obkey[:-1]) images = ir.ReadImages(fds) ImageViewer(images, img_key=obkey[:-1]).Show() app.MainLoop()
class BookScanner(QtGui.QWidget): @staticmethod def _exec(cmd): return subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].decode('utf-8') def __init__(self): super().__init__() self.working_directory = os.path.dirname(os.path.realpath(__file__)) self.current_directory = None self.left_camera_is_free = True self.right_camera_is_free = True self.setWindowTitle('BookScanner') self.setWindowIcon( QtGui.QIcon('{0}/icon.png'.format(self.working_directory))) self.setMinimumSize(1190, 710) fg = self.frameGeometry() fg.moveCenter(QtGui.QDesktopWidget().availableGeometry().center()) self.move(fg.topLeft()) self.background = QtGui.QLabel(self) self.background.setGeometry(0, 0, 9999, 9999) self.background.setStyleSheet( 'background-image: url({0}/background.png)'.format(self.working_directory)) self.layout = QtGui.QVBoxLayout() self.layout.setContentsMargins(30, 30, 30, 30) self.header = QtGui.QWidget() self.header.setFixedSize(1130, 100) self.logo = QtGui.QLabel(self.header) self.logo.setGeometry(0, 0, 170, 100) self.logo.setStyleSheet( 'background-image: url({0}/logo.png)'.format(self.working_directory)) self.directory_btn = QtGui.QPushButton('Select directory', self.header) self.directory_btn.setGeometry(200, 0, 200, 30) self.directory_btn.clicked.connect(self.select_directory) self.directory_lbl = QtGui.QLabel(self.header) self.directory_lbl.setGeometry(430, 0, 400, 30) self.automatic_numeration_box = QtGui.QCheckBox(self.header) self.automatic_numeration_box.setGeometry(860, 0, 30, 30) self.automatic_numeration_lbl = QtGui.QLabel( 'Automatic numbering', self.header) self.automatic_numeration_lbl.setGeometry(890, 0, 200, 30) self.filename_left_lbl = QtGui.QLabel( 'Filename for left camera:', self.header) self.filename_left_lbl.setGeometry(200, 60, 220, 30) self.filename_left_field = QtGui.QLineEdit(self.header) self.filename_left_field.setGeometry(420, 60, 220, 30) self.filename_right_lbl = QtGui.QLabel( 'Filename for right camera:', self.header) self.filename_right_lbl.setGeometry(670, 60, 220, 30) self.filename_right_field = QtGui.QLineEdit(self.header) self.filename_right_field.setGeometry(890, 60, 220, 30) self.layout.addWidget(self.header) self.body = QtGui.QWidget() self.body_layout = QtGui.QHBoxLayout() self.image_left = ImageViewer(self) self.body_layout.addWidget(self.image_left) self.image_right = ImageViewer(self) self.body_layout.addWidget(self.image_right) self.body.setLayout(self.body_layout) self.layout.addWidget(self.body) self.footer = QtGui.QWidget() self.footer.setFixedSize(660, 30) self.shoot_btn = QtGui.QPushButton('Make photos', self.footer) self.shoot_btn.setGeometry(0, 0, 200, 30) self.shoot_btn.clicked.connect(self.shoot) QtGui.QShortcut(QtGui.QKeySequence('Space'), self, self.shoot) self.shoot_left_btn = QtGui.QPushButton('Make left photo', self.footer) self.shoot_left_btn.setGeometry(230, 0, 200, 30) self.shoot_left_btn.clicked.connect(self.shoot_left) self.shoot_right_btn = QtGui.QPushButton( 'Make right photo', self.footer) self.shoot_right_btn.setGeometry(460, 0, 200, 30) self.shoot_right_btn.clicked.connect(self.shoot_right) self.layout.addWidget(self.footer) self.setLayout(self.layout) def select_directory(self): self.current_directory = QtGui.QFileDialog.getExistingDirectory( self, 'Select directory') self.directory_lbl.setText( 'Current directory: ' + self.current_directory) def shoot(self): if self.validate_cameras(2) and self.validate_directory(): self.shoot_left() self.shoot_right() self.left_step = 2 self.right_step = 2 def shoot_left(self): if self.validate_cameras(1) and self.validate_directory() and self.validate_filename_left(): self.left_camera_is_free = False self.update_controls_states() self.thread_left = Thread(self.load_left) self.thread_left.finished.connect(self.render_left) self.left_step = 1 def shoot_right(self): if self.validate_cameras(2) and self.validate_directory() and self.validate_filename_right(): self.right_camera_is_free = False self.update_controls_states() self.thread_right = Thread(self.load_right) self.thread_right.finished.connect(self.render_right) self.right_step = 1 def validate_cameras(self, n): if len(gphoto2.devices()) < n: QtGui.QMessageBox.warning( self, 'Warning', 'The number of connected cameras should be at least {0}.'.format(n)) return False return True def validate_directory(self): if not self.current_directory: QtGui.QMessageBox.warning( self, 'Warning', 'Current directory is not set.') return False return True def validate_filename_left(self): if not self.filename_left_field.text(): QtGui.QMessageBox.warning( self, 'Warning', 'Filename for left camera is not set.') return False if os.path.isfile(self.path_left('jpg')): reply = QtGui.QMessageBox.question( self, 'Question', 'File with filename for left camera already exists. Rewrite?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply != QtGui.QMessageBox.Yes: return False return True def validate_filename_right(self): if not self.filename_right_field.text(): QtGui.QMessageBox.warning( self, 'Warning', 'Filename for right camera is not set.') return False if os.path.isfile(self.path_right('jpg')): reply = QtGui.QMessageBox.question( self, 'Question', 'File with filename for right camera already exists. Rewrite?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply != QtGui.QMessageBox.Yes: return False return True def update_controls_states(self): self.shoot_btn.setEnabled( self.left_camera_is_free and self.right_camera_is_free) self.filename_left_field.setEnabled(self.left_camera_is_free) self.shoot_left_btn.setEnabled(self.left_camera_is_free) self.filename_right_field.setEnabled(self.right_camera_is_free) self.shoot_right_btn.setEnabled(self.right_camera_is_free) def load_left(self): gphoto2.devices()[0].capture(self.path_left('%C')) self._exec( 'convert "{0}" -rotate 270 "{0}"'.format(self.path_left('jpg'))) def render_left(self): self.left_camera_is_free = True self.update_controls_states() self.image_left.configure(self.path_left('jpg'), 0.25) if self.automatic_numeration_box.isChecked(): self.filename_left_field.setText( self.next_filename( self.filename_left_field.text(), self.left_step)) def load_right(self): gphoto2.devices()[1].capture(self.path_right('%C')) self._exec( 'convert "{0}" -rotate 90 "{0}"'.format(self.path_right('jpg'))) def render_right(self): self.right_camera_is_free = True self.update_controls_states() self.image_right.configure(self.path_right('jpg'), 0.25) if self.automatic_numeration_box.isChecked(): self.filename_right_field.setText( self.next_filename( self.filename_right_field.text(), self.right_step)) def path_left(self, format): return '{0}/{1}.{2}'.format(self.current_directory, self.filename_left_field.text(), format) def path_right(self, format): return '{0}/{1}.{2}'.format(self.current_directory, self.filename_right_field.text(), format) def next_filename(self, filename, step): match = re.search('^\d+', filename) if match: return str(int(match.group(0)) + step).rjust(len(match.group(0)), '0') else: return filename
class MainWindow(QMainWindow): ''' MainWindow class ''' def __init__(self, App): super(MainWindow, self).__init__() self.App = App self.splitterPos = 0 self.initCompleteFlag = False self.signal = resizeSignal() self.setGeometry(300, 100, 900, 900) self.setWindowIcon(QIcon("./image/mainwin.ico")) self.initUI() self.signal.resize.connect(self.ImageViewer.resizeEvent_wrapper) def initUI(self): #Menu self.menubar = self.menuBar() self.fileMenu = self.menubar.addMenu('&File') self.openAct = QAction('&Open', self) self.openAct.setShortcut('Ctrl+O') self.openAct.setIcon(QIcon("./image/open.ico")) self.saveAct = QAction('&Save', self) self.saveAct.setShortcut('Ctrl+S') self.saveAct.setIcon(QIcon("./image/save.ico")) self.importAct = QAction('&Import Data', self) self.importAct.setShortcut('Ctrl+I') self.importAct.setIcon(QIcon("./image/import.ico")) self.exportAct = QAction('&Export Data', self) self.exportAct.setShortcut('Ctrl+E') self.exportAct.setIcon(QIcon("./image/export.ico")) self.exportAct.setEnabled(False) self.exitAct = QAction('&Exit', self) self.exitAct.setIcon(QIcon("./image/exit.ico")) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.importAct) self.fileMenu.addAction(self.exportAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAct) self.importAct.triggered.connect(self.OnImport) self.exportAct.triggered.connect(self.OnExport) self.exitAct.triggered.connect(self.close) #Data Browser self.DataBrowser = DataBrowser(self) #Process Region Expansion button self.PRButton = QPushButton(">") self.PRButton.setFixedSize(20, 80) self.PRButton.setCheckable(True) self.PRButton.toggled.connect(self.showDataProcessor) #Data Processor self.DataProcessor = DataProcessor(self) #Image Viewer self.ImageViewer = ImageViewer(self) #Mayavi Region Expansion button self.MYButton = QPushButton(">") self.MYButton.setFixedSize(20, 80) self.MYButton.setCheckable(True) #self.MYButton.setEnabled(False) self.MYButton.toggled.connect(self.show3D) #Mayavi scene self.MYWidget = MYWidget(self) #Layout self.panel, self.splitter, self.Databox, self.DataWidget = self.WinLayout() QTimer.singleShot(10, lambda: self.splitter.moveSplitter(self.DataBrowser.minimumWidth(), 1)) self.splitter.splitterMoved.connect(self.splitterMovedEvent) #center panel self.centralPanel = QWidget(self) self.centralPanel.setLayout(self.panel) self.setCentralWidget(self.centralPanel) self.setWindowTitle('ARPES Data Viewer -- By Wei Yao -- Ver 1.0') self.show() self.initCompleteFlag = True def WinLayout(self): panel = QHBoxLayout() splitter = QSplitter() splitter.addWidget(self.DataBrowser) Databox = QHBoxLayout() Databox.addWidget(self.PRButton) Databox.addWidget(self.ImageViewer) Databox.addWidget(self.MYButton) DataWidget = QWidget() DataWidget.setLayout(Databox) splitter.addWidget(DataWidget) splitter.setStretchFactor(0,0) splitter.setStretchFactor(1,1) panel.addWidget(splitter) return panel, splitter, Databox, DataWidget def showDataProcessor(self, state): if state: self.Databox.insertWidget(0, self.DataProcessor) self.DataProcessor.show() self.resizeDataWidget(self.DataProcessor.width()+7, 'DP') #7 is for additional space due to using layout self.PRButton.setText("<") else: self.Databox.takeAt(0) self.DataProcessor.hide() #wait for some events are processed in the event loop. #https://stackoverflow.com/questions/28660960/resize-qmainwindow-to-minimal-size-after-content-of-layout-changes #This QTimer is used to wait for correct process of layout QTimer.singleShot(10, lambda: self.resizeDataWidget(-self.DataProcessor.width()-7, 'DP')) self.PRButton.setText(">") def show3D(self, state): if self.DataProcessor.isVisible(): pos = 3 else: pos = 2 if state: self.Databox.insertWidget(pos, self.MYWidget) self.MYWidget.show() self.resizeDataWidget(self.MYWidget.width()+7, 'MY') self.MYButton.setText("<") self.MYWidget.setData(self.DataBrowser.DataList.currentItem().Data) else: self.Databox.takeAt(pos) self.MYWidget.hide() QTimer.singleShot(10, lambda: self.resizeDataWidget(-self.MYWidget.width()-7, 'MY')) self.MYButton.setText(">") def resizeDataWidget(self, dwidth, flag): if dwidth > 0: screen_width = QApplication.desktop().screenGeometry().width() if self.width()+dwidth+1 < screen_width: self.resize(self.width()+dwidth+1, self.height()) self.resize(self.width()-1, self.height()) # to redraw the gui else: self.resize(screen_width, self.height()) elif dwidth < 0: #This QTimer is used to wait for correct minimum width of datawidget if flag == 'DP': delay = 10 elif flag == 'MY': delay = 30 QTimer.singleShot(delay, lambda: self.resize(self.width()+dwidth, self.height())) def OnImport(self): filelist = QFileDialog.getOpenFileNames(self, "Import Data", ".", "Igor Packed Files(*.pxt; *.pxp);;ArPy Files(*.arpy);;Ig2Py Files(*.Ig2Py)")[0] if len(filelist) > 0: self.ImportData(filelist) def OnExport(self): name = self.DataBrowser.DataList.currentItem().Data.name savedfile = QFileDialog.getSaveFileName(self, "Export Data", name, "Igor Packed Files(*.pxt);;ArPy Files(*.arpy)")[0] if len(savedfile) > 0: self.ExportData(savedfile) def ImportData(self, filelist): for filepath in filelist: base = os.path.basename(filepath) ext = os.path.splitext(base)[1] if ext.lower() == ".ig2py": spec = Data.Ig2Py2Data(filepath) if spec != None: self.NewData(spec) elif ext.lower() == ".arpy": spec = Data.ArPy2Data(filepath) if spec != None: self.NewData(spec) elif ext.lower() == ".pxt": specList = IgorPackedFile2Data(filepath, True) for spec in specList: self.NewData(spec) elif ext.lower() == ".pxp": specList = IgorPackedFile2Data(filepath, False) for spec in specList: self.NewData(spec) def ExportData(self, savedfile): base = os.path.basename(savedfile) ext = os.path.splitext(base)[1] if ext == ".arpy": data = self.DataBrowser.DataList.currentItem().Data Data.Data2ArPy(data, savedfile) elif ext == ".pxt": #data = self.DataBrowser.DataList.currentItem().Data datalist = [item.Data for item in self.DataBrowser.DataList.selectedItems()] Data2IgorPackedFile(datalist, savedfile) def setData(self, data): self.DataProcessor.updateUI(data) self.ImageViewer.setData(data) if self.MYButton.isChecked(): self.MYWidget.setData(data) def NewData(self, data): self.DataBrowser.DataList.addDataItem(data) def resizeEvent(self, event): if self.DataBrowser.width() < self.splitterPos: self.splitter.moveSplitter(self.splitterPos, 1) elif self.initCompleteFlag: self.signal.resize.emit() def splitterMovedEvent(self, pos, idx): self.splitterPos = pos if pos > self.DataBrowser.minimumWidth(): if self.initCompleteFlag: self.signal.resize.emit() else: self.resize(self.width()+1, self.height()) # to redraw the gui self.resize(self.width()-1, self.height()) def closeEvent(self, event): self.DataBrowser.DataList.noteWin.close()
from vfcamera import VideoFileCamera from imageviewer import ImageViewer from queue import Queue queue = Queue(10) viewer = ImageViewer('image', queue) viewer.start() camera = VideoFileCamera('video1.avi', queue) camera.start()
from vfcamera import VideoFileCamera from imageviewer import ImageViewer from imagesender import ImageSender url = 'http://localhost:8080/start/camera/1' viewer = ImageViewer('image') sender = ImageSender(url) camera = VideoFileCamera('OpenCV/video1.avi') camera.addObserver(viewer) camera.addObserver(sender) camera.start() viewer.start() sender.start()