Spaces:
Runtime error
Runtime error
import gradio as gr | |
from PIL import Image, ImageFilter, ImageOps | |
import cv2 | |
import numpy as np | |
import os | |
from collections import defaultdict | |
from skimage.color import deltaE_ciede2000, rgb2lab | |
def DoG_filter(image, kernel_size=0, sigma=1.0, k_sigma=2.0, gamma=1.5): | |
g1 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma) | |
g2 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma * k_sigma) | |
return g1 - gamma * g2 | |
def XDoG_filter(image, kernel_size=0, sigma=1.4, k_sigma=1.6, epsilon=0, phi=10, gamma=0.98): | |
epsilon /= 255 | |
dog = DoG_filter(image, kernel_size, sigma, k_sigma, gamma) | |
dog /= dog.max() | |
e = 1 + np.tanh(phi * (dog - epsilon)) | |
e[e >= 1] = 1 | |
return (e * 255).astype('uint8') | |
def binarize_image(image): | |
_, binarized = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | |
return binarized | |
def process_XDoG(image_path): | |
kernel_size=0 | |
sigma=1.4 | |
k_sigma=1.6 | |
epsilon=0 | |
phi=10 | |
gamma=0.98 | |
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) | |
xdog_image = XDoG_filter(image, kernel_size, sigma, k_sigma, epsilon, phi, gamma) | |
binarized_image = binarize_image(xdog_image) | |
final_image = Image.fromarray(binarized_image) | |
return final_image | |
def replace_color(image, color_1, blur_radius=2): | |
data = np.array(image) | |
original_shape = data.shape | |
data = data.reshape(-1, 4) | |
color_1 = np.array(color_1) | |
matches = np.all(data[:, :3] == color_1, axis=1) | |
nochange_count = 0 | |
mask = np.zeros(data.shape[0], dtype=bool) | |
while np.any(matches): | |
new_matches = np.zeros_like(matches) | |
match_num = np.sum(matches) | |
for i in tqdm(range(len(data))): | |
if matches[i]: | |
x, y = divmod(i, original_shape[1]) | |
neighbors = [ | |
(x, y-1), (x, y+1), (x-1, y), (x+1, y) | |
] | |
valid_neighbors = [] | |
for nx, ny in neighbors: | |
if 0 <= nx < original_shape[0] and 0 <= ny < original_shape[1]: | |
ni = nx * original_shape[1] + ny | |
if not np.all(data[ni, :3] == color_1, axis=0): | |
valid_neighbors.append(data[ni, :3]) | |
if valid_neighbors: | |
new_color = np.mean(valid_neighbors, axis=0).astype(np.uint8) | |
data[i, :3] = new_color | |
data[i, 3] = 255 | |
mask[i] = True | |
else: | |
new_matches[i] = True | |
matches = new_matches | |
if match_num == np.sum(matches): | |
nochange_count += 1 | |
if nochange_count > 5: | |
break | |
data = data.reshape(original_shape) | |
mask = mask.reshape(original_shape[:2]) | |
result_image = Image.fromarray(data, 'RGBA') | |
blurred_image = result_image.filter(ImageFilter.GaussianBlur(radius=blur_radius)) | |
blurred_data = np.array(blurred_image) | |
np.copyto(data, blurred_data, where=mask[..., None]) | |
return Image.fromarray(data, 'RGBA') | |
def generate_distant_colors(consolidated_colors, distance_threshold): | |
consolidated_lab = [rgb2lab(np.array([color], dtype=np.float32) / 255.0).reshape(3) for color, _ in consolidated_colors] | |
max_attempts = 10000 | |
for _ in range(max_attempts): | |
random_rgb = np.random.randint(0, 256, size=3) | |
random_lab = rgb2lab(np.array([random_rgb], dtype=np.float32) / 255.0).reshape(3) | |
if all(deltaE_ciede2000(base_color_lab, random_lab) > distance_threshold for base_color_lab in consolidated_lab): | |
return tuple(random_rgb) | |
return (128, 128, 128) | |
def consolidate_colors(major_colors, threshold): | |
colors_lab = [rgb2lab(np.array([[color]], dtype=np.float32)/255.0).reshape(3) for color, _ in major_colors] | |
i = 0 | |
while i < len(colors_lab): | |
j = i + 1 | |
while j < len(colors_lab): | |
if deltaE_ciede2000(colors_lab[i], colors_lab[j]) < threshold: | |
if major_colors[i][1] >= major_colors[j][1]: | |
major_colors[i] = (major_colors[i][0], major_colors[i][1] + major_colors[j][1]) | |
major_colors.pop(j) | |
colors_lab.pop(j) | |
else: | |
major_colors[j] = (major_colors[j][0], major_colors[j][1] + major_colors[i][1]) | |
major_colors.pop(i) | |
colors_lab.pop(i) | |
continue | |
j += 1 | |
i += 1 | |
return major_colors | |
def get_major_colors(image, threshold_percentage=0.01): | |
if image.mode != 'RGB': | |
image = image.convert('RGB') | |
color_count = defaultdict(int) | |
for pixel in image.getdata(): | |
color_count[pixel] += 1 | |
total_pixels = image.width * image.height | |
major_colors = [(color, count) for color, count in color_count.items() if (count / total_pixels) >= threshold_percentage] | |
return major_colors | |
def line_color(image, mask, new_color): | |
data = np.array(image) | |
data[mask, :3] = new_color | |
return Image.fromarray(data, 'RGBA') | |
def main(image, lineart): | |
lineart = lineart.point(lambda x: 0 if x < 200 else 255) | |
lineart = ImageOps.invert(lineart) | |
kernel = np.ones((3, 3), np.uint8) | |
lineart = cv2.dilate(np.array(lineart), kernel, iterations=1) | |
lineart = Image.fromarray(lineart) | |
mask = np.array(lineart) == 255 | |
major_colors = get_major_colors(image, threshold_percentage=0.05) | |
major_colors = consolidate_colors(major_colors, 10) | |
new_color_1 = generate_distant_colors(major_colors, 100) | |
filled_image = line_color(image, mask, new_color_1) | |
replace_color_image = replace_color(filled_image, new_color_1, 2).convert('RGB') | |
return replace_color_image | |
# Gradioインターフェース用のメイン関数 | |
def gradio_interface(image): | |
image_path = 'temp_input_image.jpg' | |
image.save(image_path) | |
image = Image.open(image_path).convert('RGBA') | |
lineart = process_XDoG(image_path).convert('L') | |
replace_color_image = main(image, lineart) | |
return replace_color_image | |
# Gradioアプリを設定し、起動する | |
iface = gr.Interface( | |
fn=gradio_interface, | |
inputs=gr.Image(type='pil', label="Original Image"), | |
outputs=gr.Image(type='pil', label="Processed Image"), | |
title="Line Art Removal", | |
description="画像をアップロードして線画を除去します。" | |
) | |
iface.launch() | |