Bu rehberde, Pillow ve rembg kütüphanesini kullanarak basit ve etkili bir arka plan kaldırma uygulamasını nasıl oluşturacağımızı öğreneceğiz. Python ile GUI tabanlı bir uygulama tasarlarken PyQt6 kütüphanesini kullanıyorum. Qt Designer ile kolayca tasarlanabiliyor ve geniş özellikler içeriyor. Siz dilerseniz bir konsol uygulaması veya Tkinter gibi alternatif GUI kütüphanelerini tercih edebilirsiniz.
1. Gereksinimler ve Kurulum
1.1. Gerekli Kütüphaneler
Öncelikle, gerekli kütüphaneleri kurmamız gerekiyor. Bu proje için PyQt6, Pillow ve rembg kütüphanelerine ihtiyacımız var:
pip install pyqt6 pillow rembg
1.2. Proje Yapısı
Proje klasörümüzün yapısı bu şekilde olacak:
background_remover/
│
├── main.py
├── img/
│ ├── upload_image.jpg
│ └── output.png
└── app_icon.png
2. PyQt6 ile Arayüzü Oluşturma
2.1. Temel Arayüz
PyQt6 kullanarak arayüzümüzü oluşturacağız. main.py dosyamıza
kütüphaneleri çağırmakla başlayalım:
from PyQt6 import QtCore, QtGui, QtWidgets
from PIL import Image
import rembg
from PyQt6.QtWidgets import QMessageBox
import os
2.2. Arayüz Elemanları
Şimdi, arayüz elemanlarımızı ve temel yapılandırmamızı ekleyelim:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(949, 550)
MainWindow.setFixedSize(949, 550)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(0, 0, 0))
palette.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor(255, 255, 255))
MainWindow.setPalette(palette)
icon = QtGui.QIcon("app_icon.png")
MainWindow.setWindowIcon(icon)
self.label_1 = QtWidgets.QLabel(self.centralwidget)
self.label_1.setGeometry(QtCore.QRect(10, 10, 411, 481))
self.label_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.label_1.setFrameShape(QtWidgets.QFrame.Shape.Box)
self.label_1.setStyleSheet("border: 2px solid black;")
self.label_1.mousePressEvent = self.load_image
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(490, 10, 451, 481))
self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.label_2.setFrameShape(QtWidgets.QFrame.Shape.Box)
self.label_2.setStyleSheet("border: 2px solid black;")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(430, 160, 51, 41))
self.pushButton.setStyleSheet("background-color: #4CAF50; color: white; border: 1px solid #4CAF50; border-radius: 5px;")
self.pushButton.clicked.connect(self.crop_image)
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(430, 210, 51, 41))
self.pushButton_2.setStyleSheet("background-color: #4CAF50; color: white; border: 1px solid #4CAF50; border-radius: 5px;")
self.pushButton_2.clicked.connect(self.save_image)
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QtCore.QRect(430, 260, 51, 41))
self.pushButton_3.setStyleSheet("background-color: #4CAF50; color: white; border: 1px solid #4CAF50; border-radius: 5px;")
self.pushButton_3.clicked.connect(MainWindow.close)
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(10, 500, 931, 23))
self.progressBar.setProperty("value", 0)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.control_key = 0
self.started_image = QtGui.QPixmap('img/upload_image.jpg')
self.label_1.setPixmap(self.started_image)
self.remove_bg_thread = RemoveBackgroundThread('', '')
self.remove_bg_thread.error_signal.connect(self.show_error_dialog)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "le1denfrost Background Remover"))
self.pushButton.setText(_translate("MainWindow", "Kırp"))
self.pushButton_2.setText(_translate("MainWindow", "Kaydet"))
self.pushButton_3.setText(_translate("MainWindow", "Çıkış"))
3. Arka Plan Kaldırma İşlemi
3.1. İş Parçacığı (Thread) Oluşturma
Arka plan kaldırma işlemi uzun sürebileceği için bu işlemi ayrı bir iş
parçacığında gerçekleştireceğiz. İş parçacığını oluşturmak için
RemoveBackgroundThread sınıfını tanımlayalım:
class RemoveBackgroundThread(QtCore.QThread):
progress_changed = QtCore.pyqtSignal(int)
crop_completion_signal = QtCore.pyqtSignal()
error_signal = QtCore.pyqtSignal(str)
def __init__(self, input_path, output_path):
super().__init__()
self.input_path = input_path
self.output_path = os.path.join('img', output_path)
def run(self):
try:
if os.path.exists(self.output_path):
os.remove(self.output_path)
with Image.open(self.input_path) as img:
img = img.convert("RGBA")
output = rembg.remove(img)
output = output.resize(img.size, Image.LANCZOS)
final_output = Image.new("RGBA", img.size, (0, 0, 0, 0))
final_output.paste(output, (0, 0), output)
final_output.save(self.output_path)
progress_value = 0
while progress_value < 100:
progress_value += 1
self.progress_changed.emit(progress_value)
QtCore.QThread.msleep(50)
self.progress_changed.emit(100)
self.crop_completion_signal.emit()
except Exception as e:
self.error_signal.emit(f"Hata oluştu: {str(e)}")
3.2. İşlevleri Bağlama
Şimdi arayüz elemanlarını tanımladığımız ana sınıfın altına iş parçacığını başlatacak ve ilerleme çubuğunu güncelleyecek işlevleri tanımlayalım:
def load_image(self, event):
file_dialog = QtWidgets.QFileDialog()
self.file_path, _ = file_dialog.getOpenFileName(
parent=None, caption="Resim Seç", directory="",
filter="Resim Dosyaları (*.png *.jpg *.bmp *.jpeg)"
)
if self.file_path:
original_image = QtGui.QPixmap(self.file_path)
scaled_image = original_image.scaled(
self.label_1.size(),
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation
)
self.label_1.setPixmap(scaled_image)
if self.control_key == 1:
self.label_2.clear()
if not self.file_path:
self.label_1.setPixmap(self.started_image)
self.control_key = 1
def update_progress(self, value):
self.progressBar.setValue(value)
def show_cropped_image(self):
result_image = QtGui.QPixmap('img/output.png')
scaled_image = result_image.scaled(
self.label_2.size(),
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation
)
self.label_2.setPixmap(scaled_image)
def crop_image(self):
try:
if not hasattr(self, 'file_path') or not self.file_path:
self.show_error_dialog("Lütfen önce bir resim seçin.")
return
self.remove_bg_thread = RemoveBackgroundThread(self.file_path, 'output.png')
self.remove_bg_thread.progress_changed.connect(self.update_progress)
self.remove_bg_thread.crop_completion_signal.connect(self.enable_save_button)
self.remove_bg_thread.finished.connect(self.show_cropped_image)
self.remove_bg_thread.start()
except Exception as e:
self.show_error_dialog(f"Hata oluştu: {str(e)}")
def enable_save_button(self):
self.pushButton_2.setEnabled(True)
4. Resmi Kaydetme ve Hata Yönetimi
4.1. Resmi Kaydetme
Kullanıcının kırpılmış resmi kaydedebilmesi için ana sınıfın altına bir işlev ekleyelim:
def save_image(self):
try:
if not hasattr(self, 'file_path') or not self.file_path or not os.path.exists('img/output.png'):
self.show_error_dialog("Lütfen önce bir resmi kırpın.")
return
file_dialog = QtWidgets.QFileDialog()
file_path, _ = file_dialog.getSaveFileName(
parent=None, caption="Resmi Kaydet", directory="",
filter="PNG Dosyaları (*.png)"
)
if file_path:
result_image = QtGui.QPixmap('img/output.png')
result_image.save(file_path, "PNG")
QMessageBox.information(None, "Başarı", "Kaydetme işlemi tamamlandı.")
self.label_1.setPixmap(self.started_image)
self.label_2.clear()
os.remove('img/output.png')
self.file_path = ""
except Exception as e:
self.show_error_dialog(f"Hata oluştu: {str(e)}")
4.2. Hata Yönetimi
Kullanıcıya hataları göstermek için ana sınıfın altına bir hata diyalogu ekleyelim:
def show_error_dialog(self, message):
QMessageBox.critical(None, "Hata", message)
5. Uygulamayı Çalıştırma
Son olarak, ana uygulamayı çalıştırmak için main.py dosyasının sonuna
aşağıdaki kodu ekleyelim:
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
Sonuç

RemBG ve Pillow kütüphanelerini kullanarak bir arka plan kaldırma uygulaması oluşturmayı öğrenmiş olduk. Uygulamayı geliştirirken birkaç önemli noktaya değindik:
Thread (İş Parçacığı) Kullanımı. Uygulamamızda, arka plan kaldırma işlemi zaman alabileceğinden ve bu işlemin ana iş parçacığını engelleyip uygulamanın donmasına neden olabileceğinden, arka plan kaldırma işlemlerini iş parçacığında gerçekleştirdik. İş parçacıkları, uzun süren işlemleri arka planda çalıştırarak kullanıcı arayüzünün tepkisel kalmasını sağlar.
Sinyallerin Önemi. PyQt6’da sinyaller, iş parçacıkları arasındaki
iletişimi ve arayüzün güncellenmesini sağlamak için kullanılır. Örneğin,
işlem ilerlemesini kullanıcıya göstermek için progress_changed
sinyalini kullandık ve iş parçacığı tamamlandığında
crop_completion_signal sinyalini kullanarak UI’yi güncelledik.
Hata Yönetimi. Uygulamada kullanıcı deneyimini iyileştirmek için hata yönetimine de önem verdik. Herhangi bir hata oluştuğunda kullanıcıya bilgilendirici hata mesajları göstererek, neyin yanlış gittiğini anlamalarını sağladık.