2024-09-30 08:28:33 发布
网友
以这张图片为例:
是否可以从图像构建QPainterPath(路径为黑色形状/轮廓)
我知道您可以使用QPainterPath方法(quadTo、cubicTo、lineTo等-尽管很难准确地重新创建)手动定义此形状,并设置具有适当宽度的QPen。但是我想知道是否有任何方法,通过读取像素数据或其他方式,基于黑色像素定义QPainterPath
quadTo
cubicTo
lineTo
我的目标是填充形状的内部(显示在GUI中),我非常喜欢使用QPaint而不是整体填充图像,因为:
正如在评论和(可能的)duplicate question中所解释的,Qt没有用于光栅图像矢量化的方法
除了手动重新创建路径外,对于具有较大边界的简单图像(如示例中的图像),还可以获得更好的“泛洪”结果
显然,问题在于图像内部(以及外部,如果您想使用不同的颜色)的抗锯齿。 诀窍是执行光栅图像处理程序通常执行的操作,即基于颜色创建遮罩,并扩展遮罩“羽化”它
在Qt术语中,这可以通过小椭圆(2像素宽)实现,该椭圆位于作为遮罩一部分的像素中间。由于无法知道遮罩的边界,我们需要循环通过该区域的所有像素,并在点位于该区域内时绘制平滑像素
如您所见,结果相当不错,但仍远远不够完美(简单遮罩是中间图像,而伪抗锯齿在右侧):
这是一个显示整个过程的示例,包括简单遮罩和“平滑”像素技巧:
from PyQt5 import QtGui, QtCore, QtWidgets class Test(QtWidgets.QWidget): def __init__(self): super().__init__() layout = QtWidgets.QHBoxLayout(self) self.sourceLabel = QtWidgets.QLabel() layout.addWidget(self.sourceLabel) self.maskedLabel = QtWidgets.QLabel() layout.addWidget(self.maskedLabel) self.targetLabel = QtWidgets.QLabel() layout.addWidget(self.targetLabel) self.border = QtGui.QColor(QtCore.Qt.red) self.color = QtGui.QColor(QtCore.Qt.green) self.sourceLabel.installEventFilter(self) self.maskedLabel.installEventFilter(self) self.targetLabel.installEventFilter(self) self.processPixmap('cloud.png') def eventFilter(self, source, event): if event.type() == QtCore.QEvent.MouseButtonPress: self.setColor(event.pos() in self.innerMask) return super().eventFilter(source, event) def setColor(self, inside): dialog = QtWidgets.QColorDialog(self.color, self) if dialog.exec_(): if inside: self.color = dialog.currentColor() else: self.border = dialog.currentColor() self.processPixmap() def processPixmap(self, path=None): if path is None: path = self.path self.path = path self.sourceLabel.setPixmap(QtGui.QPixmap(path)) source = QtGui.QImage(path) fullMask = source.createHeuristicMask() fullMaskRegion = QtGui.QRegion(QtGui.QBitmap(fullMask)) outColor = source.pixel(0, 0) borderMask = source.createMaskFromColor(outColor) borderMaskRegion = QtGui.QRegion(QtGui.QBitmap(borderMask)) self.innerMask = fullMaskRegion - borderMaskRegion outerMask = fullMaskRegion + borderMaskRegion masked = QtGui.QPixmap(source.size()) masked.fill(self.border) qp = QtGui.QPainter(masked) qp.setClipRegion(fullMaskRegion) qp.drawImage(0, 0, source) qp.setClipRegion(self.innerMask) qp.fillRect(source.rect(), self.color) qp.end() self.maskedLabel.setPixmap(masked) t = QtCore.QElapsedTimer() t.start() output = QtGui.QPixmap(source.size()) output.fill(QtCore.Qt.transparent) qp = QtGui.QPainter(output) qp.setRenderHints(qp.Antialiasing) qp.save() qp.setClipRegion(fullMaskRegion) qp.fillRect(source.rect(), self.border) qp.drawImage(0, 0, source) qp.restore() qp.setPen(QtCore.Qt.NoPen) pixel = QtCore.QRectF(-.5, -.5, 2, 2) for rowPixel in range(output.height()): for colPixel in range(output.width()): p = QtCore.QPoint(colPixel, rowPixel) if p in self.innerMask: qp.setBrush(self.color) qp.drawEllipse(pixel.translated(p)) if not p in outerMask: qp.setBrush(self.border) qp.drawEllipse(pixel.translated(p)) qp.end() self.targetLabel.setPixmap(output) print('Antialiasing finished in {}ms'.format(t.elapsed())) import sys app = QtWidgets.QApplication(sys.argv) w = Test() w.show() sys.exit(app.exec_())
您可以单击任何图像的外部以选择外部颜色,单击内部以选择背景颜色
正如在评论和(可能的)duplicate question中所解释的,Qt没有用于光栅图像矢量化的方法
除了手动重新创建路径外,对于具有较大边界的简单图像(如示例中的图像),还可以获得更好的“泛洪”结果
显然,问题在于图像内部(以及外部,如果您想使用不同的颜色)的抗锯齿。
诀窍是执行光栅图像处理程序通常执行的操作,即基于颜色创建遮罩,并扩展遮罩“羽化”它
在Qt术语中,这可以通过小椭圆(2像素宽)实现,该椭圆位于作为遮罩一部分的像素中间。由于无法知道遮罩的边界,我们需要循环通过该区域的所有像素,并在点位于该区域内时绘制平滑像素
如您所见,结果相当不错,但仍远远不够完美(简单遮罩是中间图像,而伪抗锯齿在右侧):
这是一个显示整个过程的示例,包括简单遮罩和“平滑”像素技巧:
您可以单击任何图像的外部以选择外部颜色,单击内部以选择背景颜色
相关问题 更多 >
编程相关推荐