Spaces:
Running
Running
Huxxshadow
commited on
Upload 4 files
Browse files- app_zh_private.py +130 -0
- color_quantization.py +61 -0
- pixelation.py +123 -0
- requirements.txt +4 -0
app_zh_private.py
ADDED
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from color_quantization import (
|
3 |
+
kmeans_quantization,
|
4 |
+
median_cut_quantization,
|
5 |
+
floyd_steinberg_dithering,
|
6 |
+
median_cut_perceptual_weighting
|
7 |
+
)
|
8 |
+
from pixelation import pixelate_image, mosaic_pixelation, oil_paint_pixelation, hierarchical_pixelation
|
9 |
+
import math
|
10 |
+
|
11 |
+
|
12 |
+
# 定义像素化和颜色量化处理函数
|
13 |
+
def pixelate_and_quantize(image, pixel_size, n_colors, methods, interpolation, pixelation_types):
|
14 |
+
results = []
|
15 |
+
|
16 |
+
# 根据用户选择的像素化方法进行像素化处理
|
17 |
+
for pixelation_type in pixelation_types:
|
18 |
+
if pixelation_type == "Classic Nearest Neighbor":
|
19 |
+
pixelated_img = pixelate_image(image, pixel_size, interpolation)
|
20 |
+
elif pixelation_type == "Mosaic":
|
21 |
+
pixelated_img = mosaic_pixelation(image, pixel_size)
|
22 |
+
elif pixelation_type == "Oil Painting":
|
23 |
+
pixelated_img = oil_paint_pixelation(image, pixel_size)
|
24 |
+
elif pixelation_type == "Hierarchical":
|
25 |
+
pixelated_img = hierarchical_pixelation(image, pixel_size, pixel_size * 2)
|
26 |
+
else:
|
27 |
+
raise ValueError(f"未知的像素化方法: {pixelation_type}")
|
28 |
+
|
29 |
+
# 根据用户选择的量化方法进行颜色量化,并将量化后的图像添加到列表中
|
30 |
+
for method in methods:
|
31 |
+
if method == "K-Means":
|
32 |
+
quantized_img = kmeans_quantization(pixelated_img, n_colors)
|
33 |
+
elif method == "Median Cut":
|
34 |
+
quantized_img = median_cut_quantization(pixelated_img, n_colors)
|
35 |
+
elif method == "Floyd-Steinberg Dithering":
|
36 |
+
quantized_img = floyd_steinberg_dithering(pixelated_img, n_colors)
|
37 |
+
elif method == "Median Cut (Perceptual Weighting)":
|
38 |
+
quantized_img = median_cut_perceptual_weighting(pixelated_img, n_colors)
|
39 |
+
else:
|
40 |
+
raise ValueError(f"未知的量化方法: {method}")
|
41 |
+
|
42 |
+
# 只返回量化后的图像
|
43 |
+
results.append(quantized_img)
|
44 |
+
|
45 |
+
return results
|
46 |
+
|
47 |
+
|
48 |
+
# Gradio 用户界面
|
49 |
+
def gradio_interface():
|
50 |
+
with gr.Blocks(title="Image Pixelation Tool") as demo:
|
51 |
+
gr.Markdown("## 像素化图片工具 (AI像素修补)")
|
52 |
+
with gr.Row():
|
53 |
+
with gr.Column(scale=4):
|
54 |
+
gr.Markdown("""
|
55 |
+
此工具提供了多种图像像素化效果和颜色量化算法,包括经典的邻近像素化、马赛克效果、油画效果以及层次像素化。
|
56 |
+
用户可以根据需要选择不同的像素化方法并对图像进行颜色量化处理。
|
57 |
+
|
58 |
+
### 许可协议(License)
|
59 |
+
本工具基于 **MPL 2.0(Mozilla Public License 2.0)** 协议发布。该协议允许修改和分发源代码,且允许商业使用,前提是对源代码的修改部分仍需保持开源。
|
60 |
+
|
61 |
+
- **允许**:修改、分发、用于商业用途。
|
62 |
+
- **限制**:修改过的源代码必须继续保持开源。
|
63 |
+
|
64 |
+
[查看详细的 MPL 2.0 协议](https://www.mozilla.org/en-US/MPL/2.0/)
|
65 |
+
|
66 |
+
**注意:如需将本工具用于商业用途,请联系作者以获得进一步的授权。**
|
67 |
+
|
68 |
+
联系方式:[email protected]
|
69 |
+
|
70 |
+
版权所有 © 2024
|
71 |
+
""")
|
72 |
+
with gr.Column(scale=1):
|
73 |
+
gr.Image(value="https://gravatar.com/avatar/483611663515c5a569c10664f3abf78c8d01498febd864a3413ecc8fe01dd740.jpg?s=256", show_download_button=False,interactive=False,show_fullscreen_button=False)
|
74 |
+
|
75 |
+
with gr.Row():
|
76 |
+
with gr.Column():
|
77 |
+
image_input = gr.Image(type="pil", label="上传图片")
|
78 |
+
|
79 |
+
with gr.Column():
|
80 |
+
pixel_size_slider = gr.Slider(minimum=2, maximum=100, value=10, step=1, label="选择像素块大小")
|
81 |
+
color_slider = gr.Slider(minimum=2, maximum=128, value=16, step=1, label="选择颜色数量")
|
82 |
+
|
83 |
+
method_checkboxes = gr.CheckboxGroup(
|
84 |
+
choices=["K-Means", "Median Cut", "Floyd-Steinberg Dithering", "MedianCut(PerceptualWeighting)"],
|
85 |
+
value=["K-Means"],
|
86 |
+
label="选择颜色量化方法"
|
87 |
+
)
|
88 |
+
pixelation_checkboxes = gr.CheckboxGroup(
|
89 |
+
choices=["Classic Nearest Neighbor", "Mosaic", "Oil Painting", "Hierarchical"],
|
90 |
+
value=["Classic Nearest Neighbor"],
|
91 |
+
label="选择像素化方法"
|
92 |
+
)
|
93 |
+
interpolation_radio = gr.Radio(
|
94 |
+
choices=["Nearest", "Bilinear", "Bicubic", "Lanczos"],
|
95 |
+
value="Nearest",
|
96 |
+
label="选择插值方法(仅对Classic Nearest Neighbour有效)"
|
97 |
+
)
|
98 |
+
|
99 |
+
btn = gr.Button("生成像素化图片")
|
100 |
+
|
101 |
+
@gr.render(inputs=[image_input, pixel_size_slider, color_slider, method_checkboxes, interpolation_radio,
|
102 |
+
pixelation_checkboxes], triggers=[btn.click])
|
103 |
+
def show_pictures(img_input, pixel_size, n_colors, methods, interpolation, pixelation_types):
|
104 |
+
num_outputs = len(methods) * len(pixelation_types)
|
105 |
+
num_outputs = min(num_outputs, 16)
|
106 |
+
images = pixelate_and_quantize(img_input, pixel_size, n_colors, methods, interpolation, pixelation_types)
|
107 |
+
cols = math.ceil(math.sqrt(num_outputs))
|
108 |
+
rows = math.ceil(num_outputs / cols)
|
109 |
+
for i in range(rows):
|
110 |
+
single_row = gr.Row()
|
111 |
+
with single_row:
|
112 |
+
for j in range(cols):
|
113 |
+
single_col = gr.Column()
|
114 |
+
idx = i * cols + j
|
115 |
+
with single_col:
|
116 |
+
if idx < num_outputs:
|
117 |
+
# 计算当前的 pixelation 和 quantization 方法
|
118 |
+
current_pixelation = pixelation_types[idx // len(methods)]
|
119 |
+
current_method = methods[idx % len(methods)]
|
120 |
+
# 更新图片的标签,包含像素化方法和颜色量化方法
|
121 |
+
label = f"像素化: {current_pixelation}, 颜色量化: {current_method}"
|
122 |
+
gr.Image(images[idx], label=label, format="png")
|
123 |
+
idx += 1
|
124 |
+
return demo
|
125 |
+
|
126 |
+
|
127 |
+
# 启动 Gradio 界面
|
128 |
+
if __name__ == "__main__":
|
129 |
+
demo = gradio_interface()
|
130 |
+
demo.launch()
|
color_quantization.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PIL import Image
|
2 |
+
from sklearn.cluster import KMeans
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
|
6 |
+
# K-Means 颜色量化
|
7 |
+
def kmeans_quantization(image, n_colors):
|
8 |
+
img = image.convert("RGB")
|
9 |
+
img_np = np.array(img)
|
10 |
+
w, h, d = img_np.shape
|
11 |
+
img_flat = img_np.reshape((w * h, d))
|
12 |
+
kmeans = KMeans(n_clusters=n_colors, random_state=0).fit(img_flat)
|
13 |
+
centroids = kmeans.cluster_centers_.astype(int)
|
14 |
+
labels = kmeans.labels_
|
15 |
+
quantized_img_flat = centroids[labels]
|
16 |
+
quantized_img = quantized_img_flat.reshape((w, h, d))
|
17 |
+
return Image.fromarray(np.uint8(quantized_img))
|
18 |
+
|
19 |
+
|
20 |
+
# Median Cut 颜色量化
|
21 |
+
def median_cut_quantization(image, n_colors):
|
22 |
+
quantized_img = image.convert("P", palette=Image.ADAPTIVE, colors=n_colors)
|
23 |
+
return quantized_img.convert("RGB")
|
24 |
+
|
25 |
+
|
26 |
+
# Floyd-Steinberg Dithering
|
27 |
+
def floyd_steinberg_dithering(image, n_colors):
|
28 |
+
quantized_img = image.convert("P", palette=Image.ADAPTIVE, colors=n_colors)
|
29 |
+
return quantized_img.convert("RGB")
|
30 |
+
|
31 |
+
|
32 |
+
# Median Cut with Perceptual Weighting 颜色量化
|
33 |
+
def median_cut_perceptual_weighting(image, n_colors):
|
34 |
+
"""
|
35 |
+
使用感知加权的 Median Cut 颜色量化。
|
36 |
+
|
37 |
+
参数:
|
38 |
+
- image: 输入的 PIL 图像对象
|
39 |
+
- n_colors: 目标颜色数量
|
40 |
+
|
41 |
+
返回:
|
42 |
+
- 量化后的 PIL 图像对象
|
43 |
+
"""
|
44 |
+
img = image.convert("RGB")
|
45 |
+
|
46 |
+
# 定义 RGB 通道的感知加权
|
47 |
+
perceptual_weights = np.array([0.299, 0.587, 0.114])
|
48 |
+
|
49 |
+
# 将图像转为 numpy 数组
|
50 |
+
img_np = np.array(img)
|
51 |
+
|
52 |
+
# 计算加权后的颜色距离
|
53 |
+
weighted_img_np = img_np.astype(float)
|
54 |
+
for i in range(3): # 对 R, G, B 通道分别加权
|
55 |
+
weighted_img_np[:, :, i] *= perceptual_weights[i]
|
56 |
+
|
57 |
+
# 使用 Pillow 的 Median Cut 算法进行基于加权的颜色量化
|
58 |
+
weighted_image = Image.fromarray(np.uint8(img_np)) # 不对图像像素值进行改变
|
59 |
+
quantized_img = weighted_image.convert("P", palette=Image.ADAPTIVE, colors=n_colors)
|
60 |
+
|
61 |
+
return quantized_img.convert("RGB")
|
pixelation.py
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PIL import Image
|
2 |
+
import numpy as np
|
3 |
+
from collections import Counter
|
4 |
+
|
5 |
+
def pixelate_image(image, pixel_size, interpolation):
|
6 |
+
"""
|
7 |
+
对图像进行像素化。
|
8 |
+
|
9 |
+
参数:
|
10 |
+
- image: 输入的 PIL 图像对象
|
11 |
+
- pixel_size: 像素块大小
|
12 |
+
- interpolation: 插值方法 ("Nearest", "Bilinear", "Bicubic", "Lanczos")
|
13 |
+
|
14 |
+
返回:
|
15 |
+
- 像素化后的 PIL 图像对象
|
16 |
+
"""
|
17 |
+
# 将输入图像转为 RGB 模式
|
18 |
+
img = image.convert("RGB")
|
19 |
+
|
20 |
+
# 获取原图像的尺寸
|
21 |
+
width, height = img.size
|
22 |
+
|
23 |
+
# 选择插值方式
|
24 |
+
if interpolation == "Nearest":
|
25 |
+
resample_method = Image.NEAREST
|
26 |
+
elif interpolation == "Bilinear":
|
27 |
+
resample_method = Image.BILINEAR
|
28 |
+
elif interpolation == "Bicubic":
|
29 |
+
resample_method = Image.BICUBIC
|
30 |
+
elif interpolation == "Lanczos":
|
31 |
+
resample_method = Image.LANCZOS
|
32 |
+
else:
|
33 |
+
raise ValueError(f"未知的插值方法: {interpolation}")
|
34 |
+
|
35 |
+
# 第一步:缩小图像,使用邻近插值保持像素块的正方形效果
|
36 |
+
small_img = img.resize(
|
37 |
+
(width // pixel_size, height // pixel_size),
|
38 |
+
resample=resample_method
|
39 |
+
)
|
40 |
+
|
41 |
+
# 第二步:放大图像,使用用户选择的插值方法
|
42 |
+
pixelated_img = small_img.resize(
|
43 |
+
(width, height),
|
44 |
+
resample=resample_method
|
45 |
+
)
|
46 |
+
|
47 |
+
return pixelated_img
|
48 |
+
|
49 |
+
|
50 |
+
def mosaic_pixelation(image, pixel_size):
|
51 |
+
"""
|
52 |
+
使用马赛克方法对图像进行像素化。
|
53 |
+
|
54 |
+
参数:
|
55 |
+
- image: 输入的 PIL 图像对象
|
56 |
+
- pixel_size: 像素块大小
|
57 |
+
|
58 |
+
返回:
|
59 |
+
- 马赛克效果的 PIL 图像对象
|
60 |
+
"""
|
61 |
+
img = image.convert("RGB")
|
62 |
+
img_np = np.array(img)
|
63 |
+
h, w, _ = img_np.shape
|
64 |
+
|
65 |
+
for y in range(0, h, pixel_size):
|
66 |
+
for x in range(0, w, pixel_size):
|
67 |
+
block = img_np[y:y + pixel_size, x:x + pixel_size]
|
68 |
+
mean_color = block.mean(axis=(0, 1)).astype(int)
|
69 |
+
img_np[y:y + pixel_size, x:x + pixel_size] = mean_color
|
70 |
+
|
71 |
+
return Image.fromarray(img_np)
|
72 |
+
|
73 |
+
|
74 |
+
def oil_paint_pixelation(image, pixel_size):
|
75 |
+
"""
|
76 |
+
使用油画滤镜方法对图像进行像素化。
|
77 |
+
|
78 |
+
参数:
|
79 |
+
- image: 输入的 PIL 图像对象
|
80 |
+
- pixel_size: 像素块大小
|
81 |
+
|
82 |
+
返回:
|
83 |
+
- 油画滤镜效果的 PIL 图像对象
|
84 |
+
"""
|
85 |
+
img = image.convert("RGB")
|
86 |
+
img_np = np.array(img)
|
87 |
+
h, w, _ = img_np.shape
|
88 |
+
|
89 |
+
for y in range(0, h, pixel_size):
|
90 |
+
for x in range(0, w, pixel_size):
|
91 |
+
block = img_np[y:y + pixel_size, x:x + pixel_size]
|
92 |
+
block_colors = [tuple(color) for color in block.reshape(-1, 3)]
|
93 |
+
most_common_color = Counter(block_colors).most_common(1)[0][0]
|
94 |
+
img_np[y:y + pixel_size, x:x + pixel_size] = most_common_color
|
95 |
+
|
96 |
+
return Image.fromarray(img_np)
|
97 |
+
|
98 |
+
|
99 |
+
def hierarchical_pixelation(image, min_pixel_size, max_pixel_size):
|
100 |
+
"""
|
101 |
+
使用层次像素化方法对图像进行像素化。
|
102 |
+
|
103 |
+
参数:
|
104 |
+
- image: 输入的 PIL 图像对象
|
105 |
+
- min_pixel_size: 最小像素块大小
|
106 |
+
- max_pixel_size: 最大像素块大小
|
107 |
+
|
108 |
+
返回:
|
109 |
+
- 层次像素化效果的 PIL 图像对象
|
110 |
+
"""
|
111 |
+
img = image.convert("RGB")
|
112 |
+
img_np = np.array(img)
|
113 |
+
h, w, _ = img_np.shape
|
114 |
+
step = max((max_pixel_size - min_pixel_size) // (w // min_pixel_size), 1)
|
115 |
+
|
116 |
+
for pixel_size in range(min_pixel_size, max_pixel_size + 1, step):
|
117 |
+
for y in range(0, h, pixel_size):
|
118 |
+
for x in range(0, w, pixel_size):
|
119 |
+
block = img_np[y:y + pixel_size, x:x + pixel_size]
|
120 |
+
mean_color = block.mean(axis=(0, 1)).astype(int)
|
121 |
+
img_np[y:y + pixel_size, x:x + pixel_size] = mean_color
|
122 |
+
|
123 |
+
return Image.fromarray(img_np)
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio==4.44.0
|
2 |
+
numpy==2.0.2
|
3 |
+
pillow==10.4.0
|
4 |
+
scikit-learn==1.5.2
|