图像处理PyQt5 [关闭]

问题描述 投票:-2回答:1

我是python的初学者,目前正在学习如何使用PyQt5进行图像处理。我收到了此入门代码:

from PyQt5.QtCore import QDir, Qt
from PyQt5.QtGui import QImage, QPainter, QPalette, QPixmap, QColor
from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QLabel,
                         QMainWindow, QMenu, QMessageBox, QScrollArea, QSizePolicy)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter

from ImgEditWindow import ImgEditWindow
from time import time  # time() function from the time module


# -----------------------------------------------------
def getRgbaPixel(col, row, image):
"""
Helper function to get a pixel including an alpha channel from a QImage using a coordinate.
:param col:  Column coordinate of the pixel.
:param row:  Row coordinate of the pixel.
:param image:  A QImage to find a pixel in.
:return:  An RGBA QColor representing the pixel at position (col, row) in image.
"""

return QColor.fromRgba(image.pixel(col, row))


# -----------------------------------------------------

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.imageEditWindow = ImgEditWindow(
        self)  # Pass a reference to the current ImageViewer object as a param of the parent window.

    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.imageEditAct.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)

    self.imageEditAct = QAction("Image Operations", self, enabled=False, triggered=self.imageEditWindow.show)

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)

    # TODO: Move this action to an Edit menu.
    self.viewMenu.addAction(self.imageEditAct)

    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)))

我现在想在我的代码中实现6个图像操作。我设法完成了1st 4,但是我不确定如何进行灰度和亮度处理。

def save(self):
    tmpImg = self.imageLabel.pixmap().toImage()

def invertImage(self):
    """
    Inverts the pixels of the current image displayed.
    Triggered by a QPushButton.clicked signal in ImgEditWindow.
    :return: None.  The current image is edited.
    """

    print("invertImage(): Start.")
    startTime = time()

    # Retrieve the QImage from the QPixmap that actually displays an image in the QLabel, self.imageLabel.
    tmpImg = self.imageLabel.pixmap().toImage()

    for r in range(tmpImg.height()):
        for c in range(tmpImg.width()):
            pixel = getRgbaPixel(c, r, tmpImg)
            newPixel = QColor(255 - pixel.red(), 255 - pixel.green(), 255 - pixel.blue(), 255 - pixel.alpha())
            tmpImg.setPixel(c, r, newPixel.rgba())

    self.imageLabel.setPixmap(QPixmap.fromImage(tmpImg))

    secs = time() - startTime
    print("invertImage(): End. Time taken = " + str(secs) + " seconds.")

def hFlip(self):
    tmpImg = self.imageLabel.pixmap().toImage()
    transformed = tmpImg.mirrored(horizontal=True, vertical=False)
    self.imageLabel.setPixmap(Pixmap.fromImage(transformed)

我应该如何进行灰度亮度调整?

python image-processing pyqt5 grayscale viewer
1个回答
0
投票

对于大多数需求,您可以只使用QImage提供的方法:

[提示:请始终参考official Qt文档,而不是PyQt文档,以了解每个类的功能;即使它们是供C ++开发人员使用的,几乎所有内容都可以以相同的方式在Python中使用,并且您可以使用PyQt文档来了解差异。

虽然您要求的几乎所有转换都已经由fast

QImage函数提供,但是唯一的问题是亮度转换,据我所知,必须使用Qt以像素为单位进行转换,并且这个过程可能很艰巨。我提出的纯Qt解决方案是使用每个像素的convertToFormat(format)值,并使用该像素颜色的lightness分量来计算新颜色。
mirrored(horizontal, vertical)

mirrored(horizontal, vertical)

更新:如果可以使用HSL模块,则可以使用其from PyQt5 import QtCore, QtGui, QtWidgets class ImageConverter(QtWidgets.QWidget): def __init__(self): super().__init__() layout = QtWidgets.QGridLayout(self) openButton = QtWidgets.QPushButton('Open...') layout.addWidget(openButton, 0, 0) openButton.clicked.connect(self.openFile) # a mapping of labels and their functions conversions = [ ('Original', self.asOriginal), ('Negative', self.toNegative), ('Gray scale', self.toGray), ('Monochrome', self.toMono), ('Horizontal Flip', self.hFlip), ('Vertical Flip', self.vFlip), ('Brightness', self.brightness) ] self.convertCombo = QtWidgets.QComboBox() layout.addWidget(self.convertCombo, 0, 1) for label, func in conversions: # set the data of each item to the function that will modify # the image, then we can access that function from the currently # selected item self.convertCombo.addItem(label, func) self.convertCombo.setEnabled(False) # brightness computation is *very* demanding with my implementation, # let's use a timer to call it only if necessary self.brightnessTimer = QtCore.QTimer( singleShot=True, interval=500, timeout=self.brightness) self.brightnessSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal) layout.addWidget(self.brightnessSlider, 0, 2) self.brightnessSlider.setEnabled(False) self.brightnessSlider.setValue(50) self.brightnessSlider.setRange(0, 100) # a simple label showing the brightness value self.brightnessLabel = QtWidgets.QLabel('50%') layout.addWidget(self.brightnessLabel, 0, 3) self.original = QtWidgets.QLabel() layout.addWidget(self.original, 1, 0) self.transformed = QtWidgets.QLabel() layout.addWidget(self.transformed, 1, 1, 1, 3) # each time an item is selected, call the function set as its item # data, so that the conversion is done automagically ;-) self.convertCombo.currentIndexChanged.connect( lambda i: self.convertCombo.itemData(i)()) # enable the slider only if the brightness mode is selected self.convertCombo.currentIndexChanged.connect( lambda i: self.brightnessSlider.setEnabled( i == len(conversions) - 1)) # don't do the brightness computation at each value change, but only # after some time the value is changed self.brightnessSlider.valueChanged.connect( self.brightnessTimer.start) self.brightnessSlider.valueChanged.connect( lambda v: self.brightnessLabel.setText('{}%'.format(v))) def openFile(self): path, filter = QtWidgets.QFileDialog.getOpenFileName( self, 'Select image', '') pixmap = QtGui.QPixmap(path) if not pixmap.isNull(): # the file is a valid pixmap! self.original.setPixmap(pixmap) self.convertCombo.setEnabled(True) # let's get the conversion set in the combobox from the item # data, and then call it self.convertCombo.currentData()() # that's the same as getting the current item data (which is # a reference to a function): # itemFunction = self.convertCombo.currentData() # and call that function # itemFunction() def asOriginal(self): self.transformed.setPixmap(self.original.pixmap()) def toNegative(self): image = self.original.pixmap().toImage() image.invertPixels() self.transformed.setPixmap(QtGui.QPixmap.fromImage(image)) def toGray(self): image = self.original.pixmap().toImage() transformed = image.convertToFormat(QtGui.QImage.Format_Grayscale8) self.transformed.setPixmap(QtGui.QPixmap.fromImage(transformed)) def toMono(self): image = self.original.pixmap().toImage() transformed = image.convertToFormat(QtGui.QImage.Format_Mono) self.transformed.setPixmap(QtGui.QPixmap.fromImage(transformed)) def hFlip(self): image = self.original.pixmap().toImage() transformed = image.mirrored(horizontal=True, vertical=False) self.transformed.setPixmap(QtGui.QPixmap.fromImage(transformed)) def vFlip(self): image = self.original.pixmap().toImage() transformed = image.mirrored(horizontal=False, vertical=True) self.transformed.setPixmap(QtGui.QPixmap.fromImage(transformed)) def brightness(self): image = self.original.pixmap().toImage() brightness = self.brightnessSlider.value() if brightness == 50: # brightness is default, use the original image self.asOriginal() return elif brightness < 50: ratio = brightness / 50 convFunc = lambda l: l * ratio else: ratio = (brightness - 50) / 50 convFunc = lambda l: l + (1 - l) * ratio for r in range(image.height()): for c in range(image.width()): h, s, l, a = QtGui.QColor(image.pixel(c, r)).getHslF() image.setPixel(c, r, QtGui.QColor.fromHslF( h, s, convFunc(l), a).rgba()) self.transformed.setPixmap(QtGui.QPixmap.fromImage(image)) if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) window = ImageConverter() window.show() sys.exit(app.exec()) 子模块的fancy image conversion screenshot!功能,它比我想出的要快得多:

PIL/pillow

就是说,这种转换与我使用的HSL方法有很大不同,因为PIL似乎使用了另一种“亮度”概念,而我并不完全知道其参考参数。不幸的是,我不是计算机成像方面的专家,因此,由您决定使用哪种方法并了解如何使用PIL的亮度功能。

© www.soinside.com 2019 - 2024. All rights reserved.