GTK3 UI for coloring plugin

GIMP3-ML
DESKTOP-F04AGRR\Kritik Soman 3 years ago
parent 03407d9564
commit 1c20fb0995

@ -1,3 +1,5 @@
include gimpml/plugins/colorpalette/color_palette.png
include gimpml/plugins/images/plugin_logo.png
include gimpml/plugins/images/error_icon.png
include gimpml/tools/model_info.csv

@ -1,4 +1,4 @@
This branch is under development. Dedicated for GIMP 3 and Python 3. :star: :star: :star: :star: are welcome. <br>
;This branch is under development. Dedicated for GIMP 3 and Python 3. :star: :star: :star: :star: are welcome. <br>
<img src="https://github.com/kritiksoman/tmp/blob/master/cover.png" width="1280" height="180"> <br>
# Objectives

@ -13,3 +13,4 @@ from .tools.complete_install import setup_python_weights
from .tools.semseg import get_seg as semseg
from .tools.superresolution import get_super as super
from .tools.inpainting import get_inpaint as inpaint
# from .plugins.plugin_utils import show_error_dialog

@ -27,23 +27,47 @@ from gi.repository import Gio
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import gettext
import subprocess
import pickle
import os
_ = gettext.gettext
image_paths = {"colorpalette": os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'colorpalette',
'color_palette.png'),
"logo": os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'images',
'plugin_logo.png'),
"error": os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'images',
'error_icon.png')}
def N_(message): return message
import subprocess
import pickle
import os
def show_dialog(message, title, icon="logo"):
use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header")
dialog = GimpUi.Dialog(use_header_bar=use_header_bar, title=_(title))
# Add buttons
dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL)
dialog.add_button("_OK", Gtk.ResponseType.APPLY)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=10)
dialog.get_content_area().add(vbox)
vbox.show()
# Show Logo
logo = Gtk.Image.new_from_file(image_paths[icon])
vbox.pack_start(logo, False, False, 1)
logo.show()
# Show message
label = Gtk.Label(label=_(message))
vbox.pack_start(label, False, False, 1)
label.show()
dialog.show()
dialog.run()
return
def coloring(procedure, image, n_drawables, drawables, force_cpu, progress_bar):
# layers = Gimp.Image.get_selected_layers(image)
# Gimp.get_pdb().run_procedure('gimp-message', [GObject.Value(GObject.TYPE_STRING, "Error")])
# Save inference parameters and layers
config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "tools")
with open(os.path.join(config_path, 'gimp_ml_config.pkl'), 'rb') as file:
data_output = pickle.load(file)
@ -73,10 +97,10 @@ def coloring(procedure, image, n_drawables, drawables, force_cpu, progress_bar):
])
with open(os.path.join(weight_path, '..', 'gimp_ml_run.pkl'), 'wb') as file:
pickle.dump({"force_cpu": bool(force_cpu)}, file)
pickle.dump({"force_cpu": bool(force_cpu), "n_drawables": n_drawables}, file)
# Run inference
subprocess.call([python_path, plugin_path])
result = Gimp.file_load(Gimp.RunMode.NONINTERACTIVE,
Gio.file_new_for_path(os.path.join(weight_path, '..', 'cache.png')))
result_layer = result.get_active_layer()
@ -84,42 +108,45 @@ def coloring(procedure, image, n_drawables, drawables, force_cpu, progress_bar):
copy.set_name("Coloring")
copy.set_mode(Gimp.LayerMode.NORMAL_LEGACY) # DIFFERENCE_LEGACY
image.insert_layer(copy, None, -1)
image.undo_group_end()
Gimp.context_pop()
# Remove temporary layers that were saved
my_dir = os.path.join(weight_path, '..')
for f_name in os.listdir(my_dir):
if f_name.startswith("cache"):
os.remove(os.path.join(my_dir, f_name))
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
def run(procedure, run_mode, image, n_drawables, layer, args, data):
# gio_file = args.index(0)
# bucket_size = args.index(0)
force_cpu = args.index(1)
# output_format = args.index(2)
progress_bar = None
config = None
if run_mode == Gimp.RunMode.INTERACTIVE:
config = procedure.create_config()
# Set properties from arguments. These properties will be changed by the UI.
# config.set_property("file", gio_file)
# config.set_property("bucket_size", bucket_size)
config.set_property("force_cpu", force_cpu)
# config.set_property("output_format", output_format)
config.begin_run(image, run_mode, args)
GimpUi.init("coloring.py")
use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header")
dialog = GimpUi.Dialog(use_header_bar=use_header_bar,
title=_("Coloring..."))
# Check number of selected layers
if n_drawables > 2:
show_dialog("Please select only greyscale image and color mask layer.", "Error !", "error")
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
elif n_drawables == 1:
n_drawables_text = _("| No Color Mask Selected")
elif n_drawables == 2:
n_drawables_text = _("| Color Mask Selected")
dialog = GimpUi.Dialog(use_header_bar=use_header_bar, title=_("Coloring..."))
dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL)
dialog.add_button("_OK", Gtk.ResponseType.OK)
dialog.add_button("_Help", Gtk.ResponseType.APPLY)
dialog.add_button("_Color Palette", Gtk.ResponseType.YES)
dialog.add_button("_Run Inference", Gtk.ResponseType.OK)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
homogeneous=False, spacing=10)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=10)
dialog.get_content_area().add(vbox)
vbox.show()
@ -132,84 +159,78 @@ def run(procedure, run_mode, image, n_drawables, layer, args, data):
vbox.add(grid)
grid.show()
# # Bucket size parameter
# label = Gtk.Label.new_with_mnemonic(_("_Bucket Size"))
# grid.attach(label, 0, 1, 1, 1)
# label.show()
# spin = GimpUi.prop_spin_button_new(config, "bucket_size", step_increment=0.001, page_increment=0.1, digits=3)
# grid.attach(spin, 1, 1, 1, 1)
# spin.show()
# Show Logo
logo = Gtk.Image.new_from_file(image_paths["logo"])
# grid.attach(logo, 0, 0, 1, 1)
vbox.pack_start(logo, False, False, 1)
logo.show()
# Show License
license_text = _("PLUGIN LICENSE : MIT")
label = Gtk.Label(label=license_text)
# grid.attach(label, 1, 1, 1, 1)
vbox.pack_start(label, False, False, 1)
label.show()
# Force CPU parameter
spin = GimpUi.prop_check_button_new(config, "force_cpu", _("Force _CPU"))
spin.set_tooltip_text(_("If checked, CPU is used for model inference."
" Otherwise, GPU will be used if available."))
grid.attach(spin, 1, 2, 1, 1)
grid.attach(spin, 0, 0, 1, 1)
spin.show()
# # Output format parameter
# label = Gtk.Label.new_with_mnemonic(_("_Output Format"))
# grid.attach(label, 0, 3, 1, 1)
# label.show()
# combo = GimpUi.prop_string_combo_box_new(config, "output_format", output_format_enum.get_tree_model(), 0, 1)
# grid.attach(combo, 1, 3, 1, 1)
# combo.show()
# Show n_drawables text
label = Gtk.Label(label=n_drawables_text)
grid.attach(label, 1, 0, 1, 1)
# vbox.pack_start(label, False, False, 1)
label.show()
progress_bar = Gtk.ProgressBar()
vbox.add(progress_bar)
progress_bar.show()
dialog.show()
if dialog.run() != Gtk.ResponseType.OK:
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL,
GLib.Error())
result = coloring(procedure, image, n_drawables, layer, force_cpu, progress_bar)
# If the execution was successful, save parameters so they will be restored next time we show dialog.
if result.index(0) == Gimp.PDBStatusType.SUCCESS and config is not None:
config.end_run(Gimp.PDBStatusType.SUCCESS)
return result
while True:
response = dialog.run()
if response == Gtk.ResponseType.OK:
result = coloring(procedure, image, n_drawables, layer, force_cpu, progress_bar)
# If the execution was successful, save parameters so they will be restored next time we show dialog.
if result.index(0) == Gimp.PDBStatusType.SUCCESS and config is not None:
config.end_run(Gimp.PDBStatusType.SUCCESS)
return result
elif response == Gtk.ResponseType.APPLY:
url = "https://github.com/kritiksoman/GIMP-ML/blob/GIMP3-ML/docs/MANUAL.md"
Gio.app_info_launch_default_for_uri(url, None)
continue
elif response == Gtk.ResponseType.YES:
image_new = Gimp.Image.new(1200, 675, 0) # 0 for RGB
display = Gimp.Display.new(image_new)
result = Gimp.file_load(Gimp.RunMode.NONINTERACTIVE, Gio.file_new_for_path(image_paths["colorpalette"]))
result_layer = result.get_active_layer()
copy = Gimp.Layer.new_from_drawable(result_layer, image_new)
copy.set_name("Color Palette")
copy.set_mode(Gimp.LayerMode.NORMAL_LEGACY) # DIFFERENCE_LEGACY
image_new.insert_layer(copy, None, -1)
Gimp.displays_flush()
continue
else:
dialog.destroy()
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
class Coloring(Gimp.PlugIn):
## Parameters ##
# Parameters #
__gproperties__ = {
# "filename": (str,
# # TODO: I wanted this property to be a path (and not just str) , so I could use
# # prop_file_chooser_button_new to open a file dialog. However, it fails without an error message.
# # Gimp.ConfigPath,
# _("Histogram _File"),
# _("Histogram _File"),
# "coloring.csv",
# # Gimp.ConfigPathType.FILE,
# GObject.ParamFlags.READWRITE),
# "file": (Gio.File,
# _("Histogram _File"),
# "Histogram export file",
# GObject.ParamFlags.READWRITE),
# "bucket_size": (float,
# _("_Bucket Size"),
# "Bucket Size",
# 0.001, 1.0, 0.01,
# GObject.ParamFlags.READWRITE),
"force_cpu": (bool,
_("Force _CPU"),
"Force CPU",
False,
GObject.ParamFlags.READWRITE),
# "output_format": (str,
# _("Output format"),
# "Output format: 'pixel count', 'normalized', 'percent'",
# "pixel count",
# GObject.ParamFlags.READWRITE),
}
## GimpPlugIn virtual methods ##
# GimpPlugIn virtual methods #
def do_query_procedures(self):
self.set_translation_domain("gimp30-python",
Gio.file_new_for_path(Gimp.locale_directory()))
self.set_translation_domain("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory()))
return ['coloring']
def do_create_procedure(self, name):
@ -228,12 +249,7 @@ class Coloring(Gimp.PlugIn):
"GIMP-ML",
"2021")
procedure.add_menu_path("<Image>/Layer/GIMP-ML/")
# procedure.add_argument_from_property(self, "file")
# procedure.add_argument_from_property(self, "bucket_size")
procedure.add_argument_from_property(self, "force_cpu")
# procedure.add_argument_from_property(self, "output_format")
return procedure

@ -1,5 +1,5 @@
#!/usr/bin/env python3
#coding: utf-8
# coding: utf-8
"""
.d8888b. 8888888 888b d888 8888888b. 888b d888 888
d88P Y88b 888 8888b d8888 888 Y88b 8888b d8888 888
@ -14,91 +14,48 @@ Y88b d88P 888 888 " 888 888 888 " 888 888
Opens the color palette as a new image file in GIMP.
"""
import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
import time
import sys
import os
import gettext
_ = gettext.gettext
def N_(message): return message
def colorpalette(procedure, run_mode, image, n_drawables, drawable, args, data):
image_new = Gimp.Image.new(1200, 675, 0) # 0 for RGB
display = Gimp.Display.new(image_new)
result = Gimp.file_load(Gimp.RunMode.NONINTERACTIVE, Gio.file_new_for_path(
os.path.join(os.path.dirname(os.path.realpath(__file__)), 'color_palette.png')))
result_layer = result.get_active_layer()
copy = Gimp.Layer.new_from_drawable(result_layer, image_new)
copy.set_name("Color Palette")
copy.set_mode(Gimp.LayerMode.NORMAL_LEGACY)# DIFFERENCE_LEGACY
copy.set_mode(Gimp.LayerMode.NORMAL_LEGACY) # DIFFERENCE_LEGACY
image_new.insert_layer(copy, None, -1)
Gimp.displays_flush()
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
class ColorPalette(Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
# "name": (str,
# _("Layer name"),
# _("Layer name"),
# _("Clouds"),
# GObject.ParamFlags.READWRITE),
# "color": (Gimp.RGB,
# _("Fog color"),
# _("Fog color"),
# GObject.ParamFlags.READWRITE),
# "turbulence": (float,
# _("Turbulence"),
# _("Turbulence"),
# 0.0, 10.0, 1.0,
# GObject.ParamFlags.READWRITE),
# "opacity": (float,
# _("Opacity"),
# _("Opacity"),
# 0.0, 100.0, 100.0,
# GObject.ParamFlags.READWRITE),
}
__gproperties__ = {}
## GimpPlugIn virtual methods ##
def do_query_procedures(self):
self.set_translation_domain("gimp30-python",
Gio.file_new_for_path(Gimp.locale_directory()))
self.set_translation_domain("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory()))
return ['colorpalette']
def do_create_procedure(self, name):
procedure = Gimp.ImageProcedure.new(self, name,
Gimp.PDBProcType.PLUGIN,
colorpalette, None)
procedure.set_image_types("RGB*, GRAY*");
procedure.set_documentation(N_("Add a layer of fog"),
"Adds a layer of fog to the image.",
procedure.set_image_types("*")
procedure.set_documentation("Opens color palette.",
"Opens color palette.",
name)
procedure.set_menu_label(N_("_Color Palette..."))
procedure.set_menu_label("_Color Palette...")
procedure.set_attribution("Kritik Soman",
"GIMP-ML",
"2021")
procedure.add_menu_path("<Image>/Layer/GIMP-ML/")
# procedure.add_argument_from_property(self, "name")
# TODO: add support for GBoxed values.
# procedure.add_argument_from_property(self, "color")
# procedure.add_argument_from_property(self, "turbulence")
# procedure.add_argument_from_property(self, "opacity")
return procedure

@ -0,0 +1,44 @@
import pickle
import os
import sys
import cv2
plugin_loc = os.path.dirname(os.path.realpath(__file__)) + '/'
base_loc = os.path.expanduser("~") + '/GIMP-ML/'
# base_loc = "D:/PycharmProjects/"
sys.path.extend([plugin_loc + 'MiDaS'])
# data_path = "D:/PycharmProjects/GIMP3-ML-pip/gimpml/"
from mono_run import run_depth
from monodepth_net import MonoDepthNet
import MiDaS_utils as MiDaS_utils
import numpy as np
import cv2
import torch
def get_mono_depth(input_image, cFlag = False):
image = input_image / 255.0
out = run_depth(image, base_loc + 'weights/MiDaS/model.pt', MonoDepthNet, MiDaS_utils, target_w=640, f=cFlag)
out = np.repeat(out[:, :, np.newaxis], 3, axis=2)
d1, d2 = input_image.shape[:2]
out = cv2.resize(out, (d2, d1))
# cv2.imwrite("/Users/kritiksoman/PycharmProjects/new/out.png", out)
return out
if __name__ == "__main__":
# # This will run when script is run as sub-process
# dbfile = open(data_path + "data_input", 'rb')
# data_input = pickle.load(dbfile)
# dbfile.close()
# # print(data)
# data_output = {'args_input': {'processed': 1}, 'image_output': get_mono_depth(data_input['image'])}
#
# dbfile = open(data_path + "data_output", 'ab')
# pickle.dump(data_output, dbfile) # source, destination
# dbfile.close()
image = cv2.imread(os.path.join(base_loc, "cache.png"))[:, :, ::-1]
output = get_mono_depth(image)
cv2.imwrite(os.path.join(base_loc, 'cache.png'), output[:, :, ::-1])

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@ -217,8 +217,7 @@ class InPainting(Gimp.PlugIn):
if name == 'inpainting':
procedure = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None)
procedure.set_image_types("*")
procedure.set_sensitivity_mask(
Gimp.ProcedureSensitivityMask.DRAWABLE | Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_documentation(
N_("Extracts the monocular depth of the current layer."),
globals()["__doc__"], # This includes the docstring, on the top of the file

@ -250,8 +250,7 @@ class Interpolation(Gimp.PlugIn):
if name == 'interpolation':
procedure = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None)
procedure.set_image_types("*")
procedure.set_sensitivity_mask(
Gimp.ProcedureSensitivityMask.DRAWABLE | Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_documentation(
N_("Extracts the monocular depth of the current layer."),
globals()["__doc__"], # This includes the docstring, on the top of the file

@ -217,8 +217,7 @@ class Matting(Gimp.PlugIn):
if name == 'matting':
procedure = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None)
procedure.set_image_types("*")
procedure.set_sensitivity_mask(
Gimp.ProcedureSensitivityMask.DRAWABLE | Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLES)
procedure.set_documentation(
N_("Extracts the monocular depth of the current layer."),
globals()["__doc__"], # This includes the docstring, on the top of the file

@ -49,12 +49,17 @@ def get_deepcolor(layerimg, layerc=None, cpu_flag=False, weight_path=None):
if __name__ == "__main__":
weight_path = get_weight_path()
image1 = cv2.imread(os.path.join(weight_path, '..', "cache0.png"), cv2.IMREAD_UNCHANGED)
image2 = cv2.imread(os.path.join(weight_path, '..', "cache1.png"), cv2.IMREAD_UNCHANGED)
with open(os.path.join(weight_path, '..', 'gimp_ml_run.pkl'), 'rb') as file:
data_output = pickle.load(file)
n_drawables = data_output["n_drawables"]
image1 = cv2.imread(os.path.join(weight_path, '..', "cache0.png"), cv2.IMREAD_UNCHANGED)
image2 = None
if n_drawables == 2:
image2 = cv2.imread(os.path.join(weight_path, '..', "cache1.png"), cv2.IMREAD_UNCHANGED)
force_cpu = data_output["force_cpu"]
if image1.shape[2] == 4 and (np.sum(image1 == [0, 0, 0, 0])) / (
if n_drawables == 1:
output = get_deepcolor(image1, cpu_flag=force_cpu, weight_path=weight_path)
elif image1.shape[2] == 4 and (np.sum(image1 == [0, 0, 0, 0])) / (
image1.shape[0] * image1.shape[1] * 4) > 0.8:
image2 = image2[:, :, [2, 1, 0]]
image1 = image1[:, :, [2, 1, 0, 3]]

@ -1,5 +1,5 @@
"""
Script will download weights and create gimp_ml_config.pkl, and print path to be added to GIMP
Script will download weights and create gimp_ml_config.pkl, and print path to be added to GIMP Preferences.
"""
import os
import sys
@ -18,17 +18,14 @@ def setup_python_weights(install_location=None):
if os.name == 'nt': # windows
python_string += ".exe"
python_path = os.path.join(os.path.dirname(sys.executable), python_string)
# with open(os.path.join(install_location, 'gimp_ml_config.pkl'), 'ab') as file:
# pickle.dump(python_path, file)
# print(r"\n\n*******************", python_path)
weight_path = os.path.join(install_location, "weights")
if not os.path.isdir(weight_path):
os.mkdir(weight_path)
step = 1
print("\n##########")
if os.name == 'nt': # windows
print("\n##########\n{}>> Automatic downloading of weights not supported on Windows.".format(step))
print("{}>> Automatic downloading of weights not supported on Windows.".format(step))
step += 1
print("{}>> Please downloads weights folder from: \n"
"https://drive.google.com/drive/folders/10IiBO4fuMiGQ-spBStnObbk9R-pGp6u8?usp=sharing".format(step))
@ -39,27 +36,24 @@ def setup_python_weights(install_location=None):
with open(os.path.join(file_path, 'model_info.csv')) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
headings = next(csv_reader)
line_count = 0
for row in csv_reader:
model = os.path.join(*row[0].split("/"))
file_id = row[1]
fileSize = float(row[2]) # in MB
mFName = row[3]
md5sum = row[4]
if not os.path.isdir(os.path.join(weight_path, model)):
os.makedirs(os.path.join(weight_path, model))
destination = os.path.join(os.path.join(weight_path, model), mFName)
model_path, file_id = os.path.join(*row[0].split("/")), row[1]
file_size, model_file_name, md5sum = float(row[2]), row[3], row[4]
if not os.path.isdir(os.path.join(weight_path, model_path)):
os.makedirs(os.path.join(weight_path, model_path))
destination = os.path.join(os.path.join(weight_path, model_path), model_file_name)
if os.path.isfile(destination):
md5_hash = hashlib.md5()
a_file = open(destination, "rb")
content = a_file.read()
md5_hash.update(content)
digest = md5_hash.hexdigest()
a_file.close()
if not os.path.isfile(destination) or (digest and digest != md5sum):
try:
gimp.progress_init("Downloading " + model + "(~" + str(fileSize) + "MB)...")
gimp.progress_init("Downloading " + model_path + "(~" + str(file_size) + "MB)...")
except:
print("\nDownloading " + model + "(~" + str(fileSize) + "MB)...")
print("\nDownloading " + model_path + "(~" + str(file_size) + "MB)...")
url = 'https://drive.google.com/uc?id={0}'.format(file_id)
gdown.cached_download(url, destination, md5=md5sum)
plugin_loc = os.path.dirname(os.path.realpath(__file__))
@ -70,5 +64,6 @@ def setup_python_weights(install_location=None):
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "plugins"))
print("##########\n")
if __name__ == "__main__":
setup_python_weights()

Loading…
Cancel
Save