我正在尝试将各种来源的基于PyQt5的图像查看器Python代码拼凑在一起,并将该功能扩展到加载图像中的裁剪感兴趣区域(ROI)。问题是,在确定像素位置时,映射坐标和鼠标单击会考虑滚动条和菜单栏。下面是加载图像并提供边界框功能的代码,但是由于偏移,我似乎无法准确地绘制/裁剪框。在
from PyQt5.QtCore import QDir, Qt
from PyQt5.QtGui import QImage, QPainter, QPalette, QPixmap
from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QLabel,
QMainWindow, QMenu, QMessageBox, QScrollArea, QSizePolicy)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
class ImageViewer(QMainWindow):
def __init__(self):
super(ImageViewer, self).__init__()
self.printer = QPrinter()
self.scaleFactor = 0.0
self.imageLabel = QLabel()
self.imageLabel.setBackgroundRole(QPalette.Base)
self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.imageLabel.setScaledContents(True)
self.scrollArea = QScrollArea()
self.scrollArea.setBackgroundRole(QPalette.Dark)
self.scrollArea.setWidget(self.imageLabel)
self.setCentralWidget(self.scrollArea)
self.createActions()
self.createMenus()
self.setWindowTitle("Image Viewer")
self.resize(500, 400)
def open(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
QDir.currentPath())
if fileName:
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer",
"Cannot load %s." % fileName)
return
self.imageLabel.setPixmap(QPixmap.fromImage(image))
self.scaleFactor = 1.0
self.printAct.setEnabled(True)
self.fitToWindowAct.setEnabled(True)
self.updateActions()
if not self.fitToWindowAct.isChecked():
self.imageLabel.adjustSize()
def print_(self):
dialog = QPrintDialog(self.printer, self)
if dialog.exec_():
painter = QPainter(self.printer)
rect = painter.viewport()
size = self.imageLabel.pixmap().size()
size.scale(rect.size(), Qt.KeepAspectRatio)
painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
painter.setWindow(self.imageLabel.pixmap().rect())
painter.drawPixmap(0, 0, self.imageLabel.pixmap())
def zoomIn(self):
self.scaleImage(1.25)
def zoomOut(self):
self.scaleImage(0.8)
def normalSize(self):
self.imageLabel.adjustSize()
self.scaleFactor = 1.0
def fitToWindow(self):
fitToWindow = self.fitToWindowAct.isChecked()
self.scrollArea.setWidgetResizable(fitToWindow)
if not fitToWindow:
self.normalSize()
self.updateActions()
def about(self):
QMessageBox.about(self, "About Image Viewer",
"<p>The <b>Image Viewer</b> example shows how to combine "
"QLabel and QScrollArea to display an image. QLabel is "
"typically used for displaying text, but it can also display "
"an image. QScrollArea provides a scrolling view around "
"another widget. If the child widget exceeds the size of the "
"frame, QScrollArea automatically provides scroll bars.</p>"
"<p>The example demonstrates how QLabel's ability to scale "
"its contents (QLabel.scaledContents), and QScrollArea's "
"ability to automatically resize its contents "
"(QScrollArea.widgetResizable), can be used to implement "
"zooming and scaling features.</p>"
"<p>In addition the example shows how to use QPainter to "
"print an image.</p>")
def createActions(self):
self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
triggered=self.open)
self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
enabled=False, triggered=self.print_)
self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
triggered=self.close)
self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
enabled=False, triggered=self.zoomIn)
self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
enabled=False, triggered=self.zoomOut)
self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
enabled=False, triggered=self.normalSize)
self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)
self.aboutAct = QAction("&About", self, triggered=self.about)
self.aboutQtAct = QAction("About &Qt", self,
triggered=QApplication.instance().aboutQt)
def createMenus(self):
self.fileMenu = QMenu("&File", self)
self.fileMenu.addAction(self.openAct)
self.fileMenu.addAction(self.printAct)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.exitAct)
self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.zoomInAct)
self.viewMenu.addAction(self.zoomOutAct)
self.viewMenu.addAction(self.normalSizeAct)
self.viewMenu.addSeparator()
self.viewMenu.addAction(self.fitToWindowAct)
self.helpMenu = QMenu("&Help", self)
self.helpMenu.addAction(self.aboutAct)
self.helpMenu.addAction(self.aboutQtAct)
self.menuBar().addMenu(self.fileMenu)
self.menuBar().addMenu(self.viewMenu)
self.menuBar().addMenu(self.helpMenu)
def updateActions(self):
self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())
def scaleImage(self, factor):
self.scaleFactor *= factor
self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())
self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)
self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)
def adjustScrollBar(self, scrollBar, factor):
scrollBar.setValue(int(factor * scrollBar.value()
+ ((factor - 1) * scrollBar.pageStep()/2)))
def mousePressEvent (self, eventQMouseEvent):
self.originQPoint = self.scrollArea.mapFrom(self, eventQMouseEvent.pos())
#self.originQPoint = eventQMouseEvent.pos()
self.currentQRubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, QtCore.QSize()))
self.currentQRubberBand.show()
def mouseMoveEvent (self, eventQMouseEvent):
self.x = int(eventQMouseEvent.x())
self.y = int(eventQMouseEvent.y())
text1 = str(self.x)
text2 = str(self.y)
#print(self.x,self.y)
QtWidgets.QToolTip.showText(eventQMouseEvent.pos() , "X: "+text1+" "+"Y: "+text2,self)
if self.currentQRubberBand.isVisible():
self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, eventQMouseEvent.pos()).normalized() & self.imageLabel.pixmap().rect())
def mouseReleaseEvent (self, eventQMouseEvent):
self.currentQRubberBand.hide()
currentQRect = self.currentQRubberBand.geometry()
self.currentQRubberBand.deleteLater()
cropQPixmap = self.imageLabel.pixmap().copy(currentQRect)
cropQPixmap.save('output.png')
if __name__ == '__main__':
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
app = QApplication(sys.argv)
imageViewer = ImageViewer()
imageViewer.show()
sys.exit(app.exec_())
在这些情况下,QRubberBand最好是QLabel的子元素,因此不需要进行多次转换。在
另一方面,事件的坐标与窗口相关,因此我们必须将其转换为QLabel的坐标。为此,一种简单的方法是将相对于窗口的局部坐标转换为全局坐标,然后将全局坐标转换为相对于QLabel的局部坐标。在
最后,当您缩放图像时,会影响坐标,因为currentQRect相对于缩放的QLabel,但内部QPixmap没有缩放。在
相关问题 更多 >
编程相关推荐