tori29umai commited on
Commit
ec4bc2b
1 Parent(s): 3c53cb7

Add application file

Browse files
Files changed (1) hide show
  1. app.py +131 -10
app.py CHANGED
@@ -6,36 +6,157 @@ import os
6
  from collections import defaultdict
7
  from skimage.color import deltaE_ciede2000, rgb2lab
8
 
9
- # XDoGフィルターを適用する関数
10
- def XDoG_filter(image, kernel_size=0, sigma=1.4, k_sigma=1.6, epsilon=0, phi=10, gamma=0.98):
11
- epsilon /= 255
12
  g1 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma)
13
  g2 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma * k_sigma)
14
- dog = g1 - gamma * g2
 
 
 
 
15
  dog /= dog.max()
16
  e = 1 + np.tanh(phi * (dog - epsilon))
17
  e[e >= 1] = 1
18
  return (e * 255).astype('uint8')
19
 
20
- # 画像を二値化する関数
21
  def binarize_image(image):
22
  _, binarized = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
23
  return binarized
24
 
25
- # XDoGフィルターを画像に適用し、その後二値化する関数
26
  def process_XDoG(image_path):
 
 
 
 
 
 
 
27
  image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
28
- xdog_image = XDoG_filter(image)
29
  binarized_image = binarize_image(xdog_image)
30
- return Image.fromarray(binarized_image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  # Gradioインターフェース用のメイン関数
33
  def gradio_interface(image):
34
  image_path = 'temp_input_image.jpg'
35
  image.save(image_path)
 
36
  lineart = process_XDoG(image_path).convert('L')
37
- processed_image = ImageOps.invert(lineart)
38
- return processed_image
39
 
40
  # Gradioアプリを設定し、起動する
41
  iface = gr.Interface(
 
6
  from collections import defaultdict
7
  from skimage.color import deltaE_ciede2000, rgb2lab
8
 
9
+ def DoG_filter(image, kernel_size=0, sigma=1.0, k_sigma=2.0, gamma=1.5):
 
 
10
  g1 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma)
11
  g2 = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma * k_sigma)
12
+ return g1 - gamma * g2
13
+
14
+ def XDoG_filter(image, kernel_size=0, sigma=1.4, k_sigma=1.6, epsilon=0, phi=10, gamma=0.98):
15
+ epsilon /= 255
16
+ dog = DoG_filter(image, kernel_size, sigma, k_sigma, gamma)
17
  dog /= dog.max()
18
  e = 1 + np.tanh(phi * (dog - epsilon))
19
  e[e >= 1] = 1
20
  return (e * 255).astype('uint8')
21
 
 
22
  def binarize_image(image):
23
  _, binarized = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
24
  return binarized
25
 
26
+
27
  def process_XDoG(image_path):
28
+ kernel_size=0
29
+ sigma=1.4
30
+ k_sigma=1.6
31
+ epsilon=0
32
+ phi=10
33
+ gamma=0.98
34
+
35
  image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
36
+ xdog_image = XDoG_filter(image, kernel_size, sigma, k_sigma, epsilon, phi, gamma)
37
  binarized_image = binarize_image(xdog_image)
38
+ final_image = Image.fromarray(binarized_image)
39
+ return final_image
40
+
41
+
42
+
43
+ def replace_color(image, color_1, blur_radius=2):
44
+ data = np.array(image)
45
+ original_shape = data.shape
46
+ data = data.reshape(-1, 4)
47
+ color_1 = np.array(color_1)
48
+ matches = np.all(data[:, :3] == color_1, axis=1)
49
+ nochange_count = 0
50
+ mask = np.zeros(data.shape[0], dtype=bool)
51
+
52
+ while np.any(matches):
53
+ new_matches = np.zeros_like(matches)
54
+ match_num = np.sum(matches)
55
+ for i in tqdm(range(len(data))):
56
+ if matches[i]:
57
+ x, y = divmod(i, original_shape[1])
58
+ neighbors = [
59
+ (x, y-1), (x, y+1), (x-1, y), (x+1, y)
60
+ ]
61
+ valid_neighbors = []
62
+ for nx, ny in neighbors:
63
+ if 0 <= nx < original_shape[0] and 0 <= ny < original_shape[1]:
64
+ ni = nx * original_shape[1] + ny
65
+ if not np.all(data[ni, :3] == color_1, axis=0):
66
+ valid_neighbors.append(data[ni, :3])
67
+ if valid_neighbors:
68
+ new_color = np.mean(valid_neighbors, axis=0).astype(np.uint8)
69
+ data[i, :3] = new_color
70
+ data[i, 3] = 255
71
+ mask[i] = True
72
+ else:
73
+ new_matches[i] = True
74
+ matches = new_matches
75
+ if match_num == np.sum(matches):
76
+ nochange_count += 1
77
+ if nochange_count > 5:
78
+ break
79
+
80
+ data = data.reshape(original_shape)
81
+ mask = mask.reshape(original_shape[:2])
82
+
83
+ result_image = Image.fromarray(data, 'RGBA')
84
+ blurred_image = result_image.filter(ImageFilter.GaussianBlur(radius=blur_radius))
85
+ blurred_data = np.array(blurred_image)
86
+
87
+ np.copyto(data, blurred_data, where=mask[..., None])
88
+
89
+ return Image.fromarray(data, 'RGBA')
90
+
91
+ def generate_distant_colors(consolidated_colors, distance_threshold):
92
+ consolidated_lab = [rgb2lab(np.array([color], dtype=np.float32) / 255.0).reshape(3) for color, _ in consolidated_colors]
93
+ max_attempts = 10000
94
+ for _ in range(max_attempts):
95
+ random_rgb = np.random.randint(0, 256, size=3)
96
+ random_lab = rgb2lab(np.array([random_rgb], dtype=np.float32) / 255.0).reshape(3)
97
+ if all(deltaE_ciede2000(base_color_lab, random_lab) > distance_threshold for base_color_lab in consolidated_lab):
98
+ return tuple(random_rgb)
99
+ return (128, 128, 128)
100
+
101
+ def consolidate_colors(major_colors, threshold):
102
+ colors_lab = [rgb2lab(np.array([[color]], dtype=np.float32)/255.0).reshape(3) for color, _ in major_colors]
103
+ i = 0
104
+ while i < len(colors_lab):
105
+ j = i + 1
106
+ while j < len(colors_lab):
107
+ if deltaE_ciede2000(colors_lab[i], colors_lab[j]) < threshold:
108
+ if major_colors[i][1] >= major_colors[j][1]:
109
+ major_colors[i] = (major_colors[i][0], major_colors[i][1] + major_colors[j][1])
110
+ major_colors.pop(j)
111
+ colors_lab.pop(j)
112
+ else:
113
+ major_colors[j] = (major_colors[j][0], major_colors[j][1] + major_colors[i][1])
114
+ major_colors.pop(i)
115
+ colors_lab.pop(i)
116
+ continue
117
+ j += 1
118
+ i += 1
119
+ return major_colors
120
+
121
+ def get_major_colors(image, threshold_percentage=0.01):
122
+ if image.mode != 'RGB':
123
+ image = image.convert('RGB')
124
+ color_count = defaultdict(int)
125
+ for pixel in image.getdata():
126
+ color_count[pixel] += 1
127
+ total_pixels = image.width * image.height
128
+ major_colors = [(color, count) for color, count in color_count.items() if (count / total_pixels) >= threshold_percentage]
129
+ return major_colors
130
+
131
+ def line_color(image, mask, new_color):
132
+ data = np.array(image)
133
+ data[mask, :3] = new_color
134
+ return Image.fromarray(data, 'RGBA')
135
+
136
+
137
+ def main(image, lineart):
138
+ lineart = lineart.point(lambda x: 0 if x < 200 else 255)
139
+ lineart = ImageOps.invert(lineart)
140
+ kernel = np.ones((3, 3), np.uint8)
141
+ lineart = cv2.dilate(np.array(lineart), kernel, iterations=1)
142
+ lineart = Image.fromarray(lineart)
143
+ mask = np.array(lineart) == 255
144
+ major_colors = get_major_colors(image, threshold_percentage=0.05)
145
+ major_colors = consolidate_colors(major_colors, 10)
146
+ new_color_1 = generate_distant_colors(major_colors, 100)
147
+ filled_image = line_color(image, mask, new_color_1)
148
+ replace_color_image = replace_color(filled_image, new_color_1, 2).convert('RGB')
149
+ return replace_color_image
150
+
151
 
152
  # Gradioインターフェース用のメイン関数
153
  def gradio_interface(image):
154
  image_path = 'temp_input_image.jpg'
155
  image.save(image_path)
156
+ image = Image.open(image_path).convert('RGBA')
157
  lineart = process_XDoG(image_path).convert('L')
158
+ replace_color_image = main(image, lineart)
159
+ return replace_color_image
160
 
161
  # Gradioアプリを設定し、起動する
162
  iface = gr.Interface(