Skip to content

[Python] 使用 PIL 套件將兩張圖片合併

Last Updated on 2021-05-17 by Clay

最近一直把時間浪費在開發自己有興趣的工具(浪費得非常開心),導致不斷遇到一些關於影像處理的奇怪問題。

今天想要紀錄的,便是如何將兩張圖片合併 —— 我發現若是簡單地使用 PIL 當中的 composite() 函式會將第二張圖片蓋掉第一張圖片,無奈之下,我開始嘗試將第二張圖片的背景轉為透明。轉換成透明背景後圖片,合併的結果我覺得相當自然。

若是對 Python 當中 PIL 模組感興趣的話,可以參考他們的官方教學:

那麼,以下我便開始介紹怎麼將兩張圖片合併。


前置準備

如果是第一次使用 PIL,需要以以下指令安裝:

pip3 install pillow

由於我是使用自己寫的『繪圖板』來進行圖片的合併,相關的心得筆記可以參考我之前寫過的紀錄:


合併圖片的流程

首先,我有一張這樣的狗狗圖片:

然後,我有我的繪圖板程式:

我將程式碼儲存成 PaintLabel.py,為重寫 PyQt5 當中 QLabel 的程式。

# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *


class PLabel(QLabel):
    def __init__(self, centralwidget):
        super(PLabel, self).__init__(centralwidget)
        self.tracing_xy = []
        self.lineHistory = []
        self.pen = QPen(Qt.black, 10, Qt.SolidLine)


    def paintEvent(self, QPaintEvent):
        self.painter = QPainter()
        self.painter.begin(self)
        self.painter.setPen(self.pen)

        start_x_temp = 0
        start_y_temp = 0

        if self.lineHistory:
            for line_n in range(len(self.lineHistory)):
                for point_n in range(1, len(self.lineHistory[line_n])):
                    start_x, start_y = self.lineHistory[line_n][point_n-1][0], self.lineHistory[line_n][point_n-1][1]
                    end_x, end_y = self.lineHistory[line_n][point_n][0], self.lineHistory[line_n][point_n][1]
                    self.painter.drawLine(start_x, start_y, end_x, end_y)

        for x, y in self.tracing_xy:
            if start_x_temp == 0 and start_y_temp == 0:
                self.painter.drawLine(self.start_xy[0][0], self.start_xy[0][1], x, y)
            else:
                self.painter.drawLine(start_x_temp, start_y_temp, x, y)

            start_x_temp = x
            start_y_temp = y

        self.painter.end()

    def mousePressEvent(self, QMouseEvent):
        self.start_xy = [(QMouseEvent.pos().x(), QMouseEvent.pos().y())]

    def mouseMoveEvent(self, QMouseEvent):
        self.tracing_xy.append((QMouseEvent.pos().x(), QMouseEvent.pos().y()))
        self.update()

    def mouseReleaseEvent(self, QMouseEvent):
        self.lineHistory.append(self.start_xy+self.tracing_xy)
        self.tracing_xy = []



相關的筆記可以在我之前寫的《Python 中使用 PyQt5 製作簡易繪圖板》當中看到。

然後我將兩張圖片疊在一起(上方的是繪圖板):

我可以任意畫畫。

然後,我們寫一個程式將兩張圖片儲存起來,程式碼也不長,加上剛才寫重寫 QLabel 的程式也才一百行左右而已。

# -*- coding: utf-8 -*-
import sys

from PyQt5 import QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from label_test import Ui_MainWindow
from PIL import ImageQt, Image
from PaintLabel import PLabel


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.statusbar.setVisible(False)

        self.paintBoard = PLabel(self.ui.centralwidget)
        self.paintBoard.setGeometry(QRect(self.ui.label.pos(), self.ui.label.size()))

        self.add = QShortcut(QKeySequence("Ctrl+S"), self)
        self.add.activated.connect(self.compositeEvent)

    def compositeEvent(self):
        image1 = ImageQt.fromqpixmap(self.ui.label.pixmap())
        image2 = ImageQt.fromqimage(self.paintBoard.grab())
        image1 = image1.convert('RGBA')
        image2 = image2.resize(image1.size)

        # Transparency
        newImage2 = []
        for item in image2.getdata():
            if item[:3] == (240, 240, 240):
                newImage2.append((240, 240, 240, 0))
            else:
                newImage2.append(item)

        image2.putdata(newImage2)

        print(image1.mode, image1.size)
        print(image2.mode, image2.size)

        newImage = Image.alpha_composite(image1, image2)
        newImage.save('test.png')


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())



最重要的其實就是這一行:

newImage = Image.alpha_composite(image1, image2)



使用 alpha_composite() 函式就可以將我的兩張圖片合併在一起了。儲存檔案,就是我最上方的那張狗狗海盜圖。

Leave a Reply