我有下面的图片。
我用 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);
}
这给了我下面的图片。
现在我想使用
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())
这给了我这个。
我尝试这个代码
self.masking = self.image.createMaskFromColor(0, Qt.MaskMode.MaskInColor)
painter.drawImage(rect, self.masking)
但它把一切都变成黑白并给了我这个。
实现此类效果的方法有多种,这取决于多个方面,包括源图像的实际构成方式。
在您的具体情况下,您有一个完全透明的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”);__init__
中创建遮罩(将QPixmap转换为QImage有点昂贵);QPainter.fillRect()
代替;QRectF(0, 0, self.image.width(), self.image.height())
也毫无意义,因为你可以只使用 self.image.rect()
;super().paintEvent(event)
);返回它也是毫无意义的,因为它是隐式的 None
;end()
调用,该调用在函数返回后立即隐式调用(因为画家将被垃圾收集);self.show()
也是一个好习惯,即使它的目的是作为一个窗口;