如何在PyQt6中为图像制作遮罩并将其变成不同的颜色?

问题描述 投票:0回答:1

我有下面的图片。

image.png

我用 JavaScript 将其设置为蒙版,这样我可以将图像转换为我想要的每种颜色。这是代码。

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body style="background-color: darkgray;">
    <canvas id="theCanvas" style="background-color: lightgray;"></canvas>
    <script src="main.js"></script>
</body>

</html>

main.js

const theCanvas = document.getElementById("theCanvas");
const ctx = theCanvas.getContext("2d");
theCanvas.width = 150;
theCanvas.height = 150;
const image = new Image();
image.onload = drawImageActualSize;
image.src = "image.png";
color = "red";

function drawImageActualSize() {
    ctx.fillStyle = color;
    ctx.rect(0, 0, theCanvas.width, theCanvas.height);
    ctx.fill();
    ctx.globalCompositeOperation = "destination-atop";
    ctx.drawImage(this, 0, 0, theCanvas.width, theCanvas.height);
    ctx.globalCompositeOperation = "multiply";
    ctx.drawImage(image, 0, 0, theCanvas.width, theCanvas.height);
}

这给了我下面的图片。

image

现在我想使用

Python
PyQt6
中做同样的工作,但是我应该如何在
pyqt6
中制作图像蒙版?

到目前为止,我在下面的代码中做到了这一点。

主.py

from sys import argv
from sys import exit as ex
from pathlib2 import Path
from PyQt6.QtWidgets import QApplication, QWidget, QSizePolicy, QVBoxLayout
from PyQt6.QtCore import Qt, QRectF, QTimer
from PyQt6.QtGui import QPaintEvent, QPainter, QImage, QPen, QColor, QBrush


class PaintWidget(QWidget):
    def __init__(self, parent=None) -> None:
        super().__init__()
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
        )
        self.image = QImage(str(Path(Path(__file__).parent, "image.png")))
        self.image.scaledToWidth(150)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.start(17)

    def paintEvent(self, event: QPaintEvent | None) -> None:
        painter = QPainter()
        painter.begin(self)
        painter.setPen(QPen(QColor(169, 169, 169), 0, Qt.PenStyle.SolidLine))
        painter.setBrush(QBrush(QColor(169, 169, 169), Qt.BrushStyle.SolidPattern))
        painter.drawRect(0, 0, 1920, 1080)
        rect = QRectF(0, 0, self.image.width(), self.image.height())
        painter.drawImage(rect, self.image)
        painter.end()
        return super().paintEvent(event)


class MainWindow(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setup_ui()
        self.show()

    def setup_ui(self) -> None:
        self.showFullScreen()
        self.main_window_layout = QVBoxLayout()
        self.painter_widget = PaintWidget()
        self.main_window_layout.addWidget(self.painter_widget)
        self.setLayout(self.main_window_layout)


if __name__ == "__main__":
    app = QApplication(argv)
    main_window = MainWindow()
    ex(app.exec())

这给了我这个。

image

我尝试这个代码

self.masking = self.image.createMaskFromColor(0, Qt.MaskMode.MaskInColor)

painter.drawImage(rect, self.masking)

但它把一切都变成黑白并给了我这个。

image

javascript python image pyqt pyqt6
1个回答
0
投票

实现此类效果的方法有多种,这取决于多个方面,包括源图像的实际构成方式。

在您的具体情况下,您有一个完全透明的alpha通道,因此您可以使用QPainter的合成模式,该模式在概念上与javascript画布的模式类似,但工作方式略有不同。

您的尝试不起作用,原因很简单:

createMaskFromColor()
仅创建一个基本的单色位图(像素为0或1),并且此类图像通常应用作mask,而不是直接绘制。

一种可能是绘制图像,然后根据图像alpha的掩模设置clip区域,并设置

CompositionMode_Multiply

请注意,这仅在“边框”为全黑时才有效,并且不是 100% 准确,因为您会清楚地看到形状周围有“红色”边缘;巧合的是,这也是你从 javascript 代码中得到的。

class PaintWidget(QWidget):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self.setSizePolicy(
            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
        )
        image = QPixmap(str(Path(Path(__file__).parent, "image.png")))
        self.image = image.scaledToWidth(
            150, Qt.TransformationMode.SmoothTransformation
        )
        self.setMinimumSize(self.image.size())

    def paintEvent(self, event: QPaintEvent) -> None:
        painter = QPainter(self)
        painter.fillRect(self.rect(), QColor(169, 169, 169))

        rect = self.image.rect()
        painter.drawPixmap(rect, self.image)

        mask = self.image.toImage().createAlphaMask()
        painter.setClipRegion(QRegion(QBitmap.fromImage(mask)))
        painter.setCompositionMode(
            painter.CompositionMode.CompositionMode_Multiply)
        painter.fillRect(rect, Qt.GlobalColor.red)

请注意,我对您的代码做了一些更改;最重要的是:

  • scaledToWidth()
    返回图像,调整自身大小(注意它被命名为“scaled”);
  • 在屏幕上绘画时,通常使用 QPixmap 而不是 QImage 更好;为了稍微提高性能,您可以在
    __init__
    中创建遮罩(将QPixmap转换为QImage有点昂贵);
  • 使用与画笔颜色相同的 0 宽度笔来绘制彩色矩形是完全没有意义的:使用
    QPainter.fillRect()
    代替;
  • QRectF(0, 0, self.image.width(), self.image.height())
    也毫无意义,因为你可以只使用
    self.image.rect()
    ;
  • 如果您要重写绘画,则不应调用基本实现(
    super().paintEvent(event)
    );返回它也是毫无意义的,因为它是隐式的
    None
    ;
  • 考虑到上述情况,您还可以通过在其构造函数中使用小部件创建画家来简化代码,并避免
    end()
    调用,该调用在函数返回后立即隐式调用(因为画家将被垃圾收集);
  • 不需要使用计时器来更新小部件,除非内容实际发生变化;
  • 在小部件的构造函数中调用
    self.show()
    也是一个好习惯,即使它的目的是作为一个窗口;
© www.soinside.com 2019 - 2024. All rights reserved.