Created
June 1, 2024 02:37
-
-
Save tori29umai0123/f4ed0fb1972a73cd42a9e620fa055763 to your computer and use it in GitHub Desktop.
noLine2.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#線画あり画像から、線画を取り除くスクリプト。線画を取り除きたい画像(image_dir)があれば動く。線画入力不要。 | |
from PIL import Image, ImageFilter, ImageOps | |
from collections import defaultdict | |
from skimage import color as sk_color | |
from tqdm import tqdm | |
from skimage.color import deltaE_ciede2000, rgb2lab | |
import cv2 | |
import numpy as np | |
import os | |
from concurrent.futures import ThreadPoolExecutor, as_completed | |
from PIL import Image | |
import os | |
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 process_single_image(args): | |
filename, image_dir, output_dir = args | |
image_path = os.path.join(image_dir, filename) | |
output_path = os.path.join(output_dir, filename) | |
if not os.path.exists(output_path): | |
image = Image.open(image_path).convert('RGBA') | |
lineart = process_XDoG(image_path).convert('L') | |
replace_color_image = main(image, lineart) | |
replace_color_image.save(output_path) | |
return f"{filename} processed." | |
return f"{filename} skipped." | |
def process_images_concurrently(image_dir, output_dir, max_workers): | |
files = [f for f in os.listdir(image_dir) if f.endswith('.webp')] | |
args = [(filename, image_dir, output_dir) for filename in files] | |
with ThreadPoolExecutor(max_workers) as executor: | |
futures = [executor.submit(process_single_image, arg) for arg in args] | |
for future in as_completed(futures): | |
print(future.result()) | |
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') | |
# replace_color_imageをブラー処理 | |
return replace_color_image | |
if __name__ == '__main__': | |
image_dir = 'D:/webp' | |
output_dir = 'D:/not_Line2' | |
max_workers = 12 | |
if not os.path.exists(output_dir): | |
os.makedirs(output_dir) | |
process_images_concurrently(image_dir, output_dir, max_workers) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment