前言
Windows10 在 UWP 應用中支持亞克力畫刷,可以在部件的底部繪制亞克力效果的背景圖。下面我們使用 QLabel 來模擬這個磨砂過程。
實現方法
MSDN 文檔中介紹了亞克力材料的配方,包括:高斯模糊、亮度混合、色調混合和噪聲紋理。
高斯模糊
我們先來實現高斯模糊的效果,使用 scipy 可以很輕松的實現這個過程:
# coding:utf-8
import numpy as np
from PIL import Image
from PyQt5.QtGui import QPixmap
from scipy.ndimage.filters import gaussian_filter
def gaussianBlur(imagePath: str, blurRadius=18, brightFactor=1, blurPicSize: tuple = None) -> np.ndarray:
""" 對圖片進行高斯模糊處理
Parameters
----------
imagePath: str
圖片路徑
blurRadius: int
模糊半徑
brightFactor:float
亮度縮放因子
blurPicSize: tuple
高斯模糊前將圖片縮放到指定大小,可以加快模糊速度
Returns
-------
image: `~np.ndarray` of shape `(w, h, c)`
高斯模糊后的圖像
"""
if not imagePath.startswith(':'):
image = Image.open(imagePath)
else:
image = Image.fromqpixmap(QPixmap(imagePath))
if blurPicSize:
# 調整圖片尺寸,減小計算量,還能增加額外的模糊
w, h = image.size
ratio = min(blurPicSize[0] / w, blurPicSize[1] / h)
w_, h_ = w * ratio, h * ratio
if w_ < w:
image = image.resize((int(w_), int(h_)), Image.ANTIALIAS)
image = np.array(image)
# 處理圖像是灰度圖的情況
if len(image.shape) == 2:
image = np.stack([image, image, image], axis=-1)
# 對每一個顏色通道分別磨砂
for i in range(3):
image[:, :, i] = gaussian_filter(
image[:, :, i], blurRadius) * brightFactor
return image
亞克力紋理
接下來在 QLabel
上面繪制出亮度混合、色調混合和噪聲紋理,一般色調混合使用的顏色是圖像的主題色,可以用 colorthief
庫提取,具體使用方法參見《如何在 python 中提取圖片主題色》:
class AcrylicTextureLabel(QLabel):
""" 亞克力紋理標簽 """
def __init__(self, tintColor: QColor, luminosityColor: QColor, noiseOpacity=0.03, parent=None):
"""
Parameters
----------
tintColor: QColor
RGB 主色調
luminosityColor: QColor
亮度層顏色
noiseOpacity: float
噪聲層透明度
parent:
父級窗口
"""
super().__init__(parent=parent)
self.tintColor = QColor(tintColor)
self.luminosityColor = QColor(luminosityColor)
self.noiseOpacity = noiseOpacity
self.noiseImage = QImage('resource/noise.png')
self.setAttribute(Qt.WA_TranslucentBackground)
def setTintColor(self, color: QColor):
""" 設置主色調 """
self.tintColor = color
self.update()
def paintEvent(self, e):
""" 繪制亞克力紋理 """
acrylicTexture = QImage(64, 64, QImage.Format_ARGB32_Premultiplied)
# 繪制亮度層
acrylicTexture.fill(self.luminosityColor)
# 繪制主色調
painter = QPainter(acrylicTexture)
painter.fillRect(acrylicTexture.rect(), self.tintColor)
# 繪制噪聲
painter.setOpacity(self.noiseOpacity)
painter.drawImage(acrylicTexture.rect(), self.noiseImage)
acrylicBrush = QBrush(acrylicTexture)
painter = QPainter(self)
painter.fillRect(self.rect(), acrylicBrush)
用到的噪聲圖像如下圖所示:
亞克力標簽
最后在 QLabel
上疊加磨砂圖像和亞克力紋理,可以通過 Image.toqpixmap()
將 Image
轉換為 QPixmap
:
class AcrylicLabel(QLabel):
""" 亞克力標簽 """
def __init__(self, blurRadius: int, tintColor: QColor, luminosityColor=QColor(255, 255, 255, 0),
maxBlurSize: tuple = None, parent=None):
"""
Parameters
----------
blurRadius: int
磨砂半徑
tintColor: QColor
主色調
luminosityColor: QColor
亮度層顏色
maxBlurSize: tuple
最大磨砂尺寸,越小磨砂速度越快
parent:
父級窗口
"""
super().__init__(parent=parent)
self.imagePath = ''
self.blurPixmap = QPixmap()
self.blurRadius = blurRadius
self.maxBlurSize = maxBlurSize
self.acrylicTextureLabel = AcrylicTextureLabel(
tintColor, luminosityColor, parent=self)
def setImage(self, imagePath: str):
""" 設置圖片 """
if imagePath == self.imagePath:
return
self.imagePath = imagePath
image = Image.fromarray(gaussianBlur(
imagePath, self.blurRadius, 0.85, self.maxBlurSize))
self.blurPixmap = image.toqpixmap() # type:QPixmap
self.setPixmap(self.blurPixmap)
self.adjustSize()
def setTintColor(self, color: QColor):
""" 設置主色調 """
self.acrylicTextureLabel.setTintColor(color)
def resizeEvent(self, e):
super().resizeEvent(e)
self.acrylicTextureLabel.resize(self.size())
self.setPixmap(self.blurPixmap.scaled(
self.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation))
測試
下面是測試用的埃羅芒阿老師:
代碼如下:
# coding:utf-8
import sys
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication
from acrylic import AcrylicLabel
app = QApplication(sys.argv)
w = AcrylicLabel(20, QColor(105, 114, 168, 102))
w.setImage('resource/ClariS_ヒトリゴト (アニメ盤).jpg')
w.show()
app.exec_()
結果如下: