2024-11-08 21:07:34 +01:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2025-06-22 16:21:18 +02:00
# (C) 2024-2026 ---
2025-02-03 17:42:38 +01:00
# This file is part of gimp-python-fu-removebg .
# gimp-python-fu-removebg is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
# gimp-python-fu-removebg is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with Xfce-nameday-plugin. If not, see <https://www.gnu.org/licenses/>.
2024-11-08 21:07:34 +01:00
import gi
gi . require_version ( 'Gimp' , '3.0' )
from gi.repository import Gimp
gi . require_version ( 'GimpUi' , '3.0' )
from gi.repository import GimpUi
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
gi . require_version ( 'Gtk' , '3.0' )
from gi.repository import Gtk
2025-05-25 21:14:16 +02:00
from pathlib import Path
2025-05-28 20:35:12 +02:00
from typing import Any , Callable , List , Optional , Tuple , Union
2024-11-08 21:07:34 +01:00
import os , sys , string , tempfile , platform
2024-11-09 15:55:37 +01:00
import subprocess
2024-11-08 21:07:34 +01:00
def N_ ( message ): return message
def _ ( message ): return GLib . dgettext ( None , message )
tupleModel = ( "u2net" , "u2net_human_seg" , "u2net_cloth_seg" , "u2netp" , "silueta" , "isnet-general-use" , "isnet-anime" , "sam" , "birefnet-general" , "birefnet-general-lite" , "birefnet-portrait" , "birefnet-dis" , "birefnet-hrsod" , "birefnet-cod" , "birefnet-massive" )
2025-05-28 20:35:12 +02:00
REMBG_BASE_PATH = ""
def find_rembg_install () -> Optional [ Path ]:
home = sys . exec_prefix
possible_paths = []
# Common installation paths
if sys . platform == "win32" :
possible_paths = [
2025-05-28 21:08:46 +02:00
Path ( "C:/Program Files (x86)/Rembg/" ),
Path ( "C:/Program Files/Rembg/" ),
2025-05-28 20:35:12 +02:00
]
for path in possible_paths :
if path . is_dir ():
return path
# Fallback to user-configured path if specified
if REMBG_BASE_PATH and ( nik_path := Path ( REMBG_BASE_PATH )) . is_dir ():
return nik_path
show_alert (
text = "rembg installation path not found" ,
message = "Please specify the correct installation path 'REMBG_BASE_PATH' in the script." ,
)
return None
2025-06-03 13:17:01 +02:00
def show_alert ( text : str , message : str , parent = None ) -> None :
"""Popup a message dialog with the given text and message"""
dialog = Gtk . MessageDialog (
transient_for = parent ,
flags = 0 ,
message_type = Gtk . MessageType . ERROR ,
buttons = Gtk . ButtonsType . CLOSE ,
text = text ,
)
dialog . format_secondary_text ( message )
2025-06-03 13:26:21 +02:00
dialog . set_title ( f "REMBG" )
2025-06-03 13:17:01 +02:00
dialog . run ()
dialog . destroy ()
2024-11-08 21:07:34 +01:00
class PythonRemoveBG ( Gimp . PlugIn ):
## GimpPlugIn virtual methods ##
def do_set_i18n ( self , procname ):
return True , 'gimp30-python' , None
def do_query_procedures ( self ):
return [ 'python-fu-removebg' ]
def do_create_procedure ( self , name ):
procedure = Gimp . ImageProcedure . new ( self , name ,
Gimp . PDBProcType . PLUGIN ,
self . removebg , None )
procedure . set_sensitivity_mask ( Gimp . ProcedureSensitivityMask . ALWAYS )
procedure . set_documentation ( _ ( "Remove Background AI" ),
_ ( "Remove Background AI" ),
name )
2025-05-28 19:38:16 +02:00
procedure . set_menu_label ( _ ( "RemoveBG" ))
2025-06-22 16:21:18 +02:00
procedure . set_attribution ( "M" ,
2024-11-08 21:07:34 +01:00
"(c) GPL V3.0 or later" ,
2025-03-12 16:11:27 +01:00
"2024-2025" )
2024-11-08 21:07:34 +01:00
procedure . add_menu_path ( '<Image>/Filters/RemoveBG/' )
procedure . add_boolean_argument ( "asMask" , "as Mask" , "As mask" , False , GObject . ParamFlags . READWRITE )
procedure . add_boolean_argument ( "AlphaMatting" , "AlphaMatting" , "AlphaMatting" , False , GObject . ParamFlags . READWRITE )
procedure . add_double_argument ( "aeValue" , "aeValue" , "aeValue" , 0 , 100 , 15 , GObject . ParamFlags . READWRITE )
2025-04-15 14:24:15 +02:00
model_choice = Gimp . Choice . new ()
model_choice . add ( "u2net" , 0 , "u2net" , "" )
model_choice . add ( "u2net_human_seg" , 1 , "u2net_human_seg" , "" )
model_choice . add ( "u2net_cloth_seg" , 2 , "u2net_cloth_seg" , "" )
model_choice . add ( "u2netp" , 3 , "u2netp" , "" )
model_choice . add ( "silueta" , 4 , "silueta" , "" )
model_choice . add ( "isnet-general-use" , 5 , "isnet-general-use" , "" )
model_choice . add ( "isnet-anime" , 6 , "isnet-anime" , "" )
model_choice . add ( "sam" , 7 , "sam" , "" )
model_choice . add ( "birefnet-general" , 8 , "birefnet-general" , "" )
model_choice . add ( "birefnet-cod" , 9 , "birefnet-cod" , "" )
model_choice . add ( "birefnet-massive" , 10 , "birefnet-massive" , "" )
model_choice . add ( "birefnet-portrait" , 11 , "birefnet-portrait" , "" )
model_choice . add ( "birefnet-general-lite" , 12 , "birefnet-general-lite" , "" )
model_choice . add ( "birefnet-hrsod" , 13 , "birefnet-hrsod" , "" )
model_choice . add ( "birefnet-dis" , 14 , "birefnet-dis" , "" )
2024-11-08 21:07:34 +01:00
procedure . add_choice_argument ( "Model" , _ ( "Model" ),
2025-05-25 20:51:18 +02:00
_ ( "Model" ), model_choice ,
2024-11-08 21:07:34 +01:00
"u2net" , GObject . ParamFlags . READWRITE )
return procedure
def removebg ( self , procedure , run_mode , image , drawables , config , run_data ) :
if run_mode == Gimp . RunMode . INTERACTIVE :
GimpUi . init ( 'python-fu-removebg.py' )
dialog = GimpUi . ProcedureDialog ( procedure = procedure , config = config )
dialog . fill ( None )
if not dialog . run ():
dialog . destroy ()
return procedure . new_return_values ( Gimp . PDBStatusType . CANCEL , GLib . Error ())
else :
dialog . destroy ()
AlphaMatting = config . get_property ( 'AlphaMatting' )
asMask = config . get_property ( 'asMask' )
2024-11-09 15:55:37 +01:00
selModel = config . get_choice_id ( "Model" )
aeValue = config . get_property ( 'aeValue' )
2024-11-08 21:07:34 +01:00
removeTmpFile = True
tdir = tempfile . gettempdir ()
print ( tdir )
2024-11-09 15:55:37 +01:00
jpgFileF = "Temp-gimp-0000.jpg"
jpgFile = os . path . join ( tdir , jpgFileF )
pngFileF = "Temp-gimp-0000.png"
pngFile = os . path . join ( tdir , pngFileF )
2024-11-08 21:07:34 +01:00
x1 = 0
y1 = 0
2024-11-09 15:55:37 +01:00
2024-11-08 21:07:34 +01:00
option = ""
Gimp . context_pop ()
image . undo_group_start ()
curLayer = image . get_selected_layers ()
fileOut = Gio . File . new_for_path ( pngFile )
file = Gio . File . new_for_path ( jpgFile )
f = image . get_file ()
if curLayer [ 0 ]:
if not file . query_exists ():
file . create ( Gio . FileCreateFlags . REPLACE_DESTINATION , None )
else :
pdb . gimp_edit_copy ( drawable )
non_empty , x1 , y1 , x2 , y2 = pdb . gimp_selection_bounds ( image )
tmpImage = gimp . Image ( x2 - x1 , y2 - y1 , 0 )
tmpDrawable = gimp . Layer ( tmpImage , "Temp" , tmpImage . width , tmpImage . height , RGB_IMAGE , 100 , NORMAL_MODE )
pdb . gimp_image_add_layer ( tmpImage , tmpDrawable , 0 )
pat = pdb . gimp_context_get_pattern ()
pdb . gimp_context_set_pattern ( "Leopard" )
pdb . gimp_drawable_fill ( tmpDrawable , 4 )
pdb . gimp_context_set_pattern ( pat )
pdb . gimp_floating_sel_anchor ( pdb . gimp_edit_paste ( tmpDrawable , TRUE ))
pdb . file_jpeg_save ( tmpImage , tmpDrawable , jpgFile , jpgFile , 0.95 , 0 , 1 , 0 , "" , 0 , 1 , 0 , 0 )
pdb . gimp_image_delete ( tmpImage )
2025-05-28 20:35:12 +02:00
# aiExe = Path("C://Program Files (x86)/Rembg/rembg.exe")
aiExe = str ( find_rembg_install ())
aiExe += "/rembg.exe"
2024-11-09 15:55:37 +01:00
2024-11-08 21:07:34 +01:00
if AlphaMatting :
option = "-a -ae %d " % ( aeValue )
2024-11-09 15:55:37 +01:00
cmd = ' %s i -m %s %s " %s " " %s "' % ( aiExe , tupleModel [ selModel ], option , f . get_path (), pngFile )
print ( cmd )
subprocess . run ( cmd )
2024-11-08 21:07:34 +01:00
file_exists = os . path . exists ( pngFile )
if file_exists :
newlayer = Gimp . file_load_layer ( run_mode , image , fileOut )
image . insert_layer ( newlayer , None , - 1 )
if asMask :
2024-11-08 21:31:18 +01:00
image . select_item ( Gimp . ChannelOps . REPLACE , newlayer )
2025-05-25 21:50:49 +02:00
copyLayer = newlayer . copy ()
2024-11-08 21:07:34 +01:00
image . remove_layer ( newlayer )
2024-11-08 21:31:18 +01:00
image . insert_layer ( copyLayer , None , - 1 )
2025-05-25 21:50:49 +02:00
mask = copyLayer . create_mask ( Gimp . AddMaskType . SELECTION )
2024-11-08 21:07:34 +01:00
copyLayer . add_mask ( mask )
2025-05-25 21:50:49 +02:00
image . get_selection () . none ( image )
2024-11-08 21:07:34 +01:00
image . undo_group_end ()
Gimp . displays_flush ()
if removeTmpFile :
if file . query_exists ():
file . delete ()
if fileOut . query_exists ():
fileOut . delete ()
return procedure . new_return_values ( Gimp . PDBStatusType . SUCCESS , GLib . Error ())
Gimp . main ( PythonRemoveBG . __gtype__ , sys . argv )