Skip to content

Commit 305feeb

Browse files
committed
feb 13
1 parent 57f43e4 commit 305feeb

5 files changed

Lines changed: 116 additions & 50 deletions

File tree

img/hotori_pyramid.png

593 KB
Loading

img/pixelart/color_reducer.py

Lines changed: 99 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,116 @@
1-
from PIL import Image
1+
import argparse
2+
23
import numpy as np
4+
from PIL import Image, ImageFilter
35
from sklearn.cluster import KMeans
46

5-
def reduce_colors(input_path, output_path, num_colors):
7+
8+
def reduce_colors(input_path, output_path, num_colors, mode="hsv", smooth=0):
69
"""
7-
减少图片颜色数量
8-
10+
减少图片颜色数量并进行平滑处理
11+
912
:param input_path: 输入文件路径
1013
:param output_path: 输出文件路径
1114
:param num_colors: 目标颜色数量
15+
:param mode: 颜色模式 'rgb' 或 'hsv'
16+
:param smooth: 平滑窗口大小 (0表示不平滑,建议3或5)
1217
"""
13-
# 打开图片并保留原始模式(RGB/RGBA)
1418
img = Image.open(input_path)
1519
original_mode = img.mode
16-
has_alpha = 'A' in original_mode
17-
18-
# 转换到RGB/RGBA并提取像素数据
19-
if original_mode not in ('RGB', 'RGBA'):
20-
img = img.convert('RGBA' if has_alpha else 'RGB')
21-
22-
pixels = np.array(img)
23-
height, width = img.size[1], img.size[0]
24-
25-
# 分离颜色通道和透明度通道
26-
if has_alpha:
27-
alpha_channel = pixels[:, :, 3]
28-
color_pixels = pixels[:, :, :3]
20+
21+
if original_mode != "RGBA":
22+
img = img.convert("RGBA")
23+
24+
# 分离通道
25+
r, g, b, alpha = img.split()
26+
rgb_img = Image.merge("RGB", (r, g, b))
27+
28+
# 颜色空间转换
29+
process_mode = mode.upper()
30+
if process_mode == "HSV":
31+
work_img = rgb_img.convert("HSV")
2932
else:
30-
color_pixels = pixels
31-
32-
# 准备K-Means输入数据
33-
color_data = color_pixels.reshape(-1, 3)
34-
35-
# 执行K-Means聚类
33+
work_img = rgb_img
34+
35+
pixels = np.array(work_img)
36+
height, width = pixels.shape[:2]
37+
color_data = pixels.reshape(-1, 3)
38+
39+
# K-Means聚类
3640
kmeans = KMeans(n_clusters=num_colors, random_state=0, n_init=10)
3741
kmeans.fit(color_data)
38-
39-
# 生成新颜色数据
40-
new_colors = kmeans.cluster_centers_[kmeans.labels_]
41-
new_colors = new_colors.reshape(color_pixels.shape).astype(np.uint8)
42-
43-
# 合并透明度通道
44-
if has_alpha:
45-
output_pixels = np.dstack((new_colors, alpha_channel))
42+
43+
# 获取原始标签矩阵
44+
labels = kmeans.labels_.reshape(height, width)
45+
46+
# --- 核心修改:对标签进行平滑处理 ---
47+
if smooth > 0:
48+
# 将标签转为图像以便使用PIL的滤波器
49+
# 标签范围是0 ~ num_colors-1,可以直接存为L模式(8-bit)
50+
label_img = Image.fromarray(labels.astype(np.uint8), mode="L")
51+
52+
# 使用 ModeFilter (众数滤波)
53+
# 它会将像素替换为邻域内出现频率最高的标签,非常适合去除噪点且不引入新颜色
54+
label_img = label_img.filter(ImageFilter.ModeFilter(size=smooth))
55+
56+
# 转回numpy数组
57+
labels = np.array(label_img)
58+
# ---------------------------------
59+
60+
# 根据(可能平滑过的)标签重构图像
61+
# labels.flatten() 将二维标签展平以索引 cluster_centers_
62+
new_colors = kmeans.cluster_centers_[labels.flatten()]
63+
new_colors = new_colors.reshape(pixels.shape).astype(np.uint8)
64+
65+
# 重建图像
66+
output_img_no_alpha = Image.fromarray(new_colors, mode=process_mode)
67+
68+
if process_mode == "HSV":
69+
output_img_no_alpha = output_img_no_alpha.convert("RGB")
70+
71+
# 合并Alpha通道
72+
r_new, g_new, b_new = output_img_no_alpha.split()
73+
if "A" in original_mode:
74+
final_output = Image.merge("RGBA", (r_new, g_new, b_new, alpha))
4675
else:
47-
output_pixels = new_colors
48-
49-
# 创建并保存图片
50-
output_img = Image.fromarray(output_pixels, mode=original_mode)
51-
output_img.save(output_path)
52-
53-
if __name__ == '__main__':
54-
import argparse
55-
56-
parser = argparse.ArgumentParser(description='图片颜色量化工具')
57-
parser.add_argument('input', help='输入图片路径')
58-
parser.add_argument('output', help='输出图片路径')
59-
parser.add_argument('-n', '--num-colors', type=int, required=True,
60-
help='目标颜色数量(2-256)')
61-
76+
final_output = Image.merge("RGB", (r_new, g_new, b_new))
77+
78+
final_output.save(output_path)
79+
80+
81+
if __name__ == "__main__":
82+
parser = argparse.ArgumentParser(description="图片颜色聚类工具")
83+
parser.add_argument("input", help="输入图片路径")
84+
parser.add_argument("-o", "--output", help="输出图片路径")
85+
parser.add_argument(
86+
"-n", "--num-colors", type=int, required=True, help="目标颜色数量(2-256)"
87+
)
88+
parser.add_argument(
89+
"-m",
90+
"--mode",
91+
choices=["rgb", "hsv"],
92+
default="hsv",
93+
help="颜色聚类模式:rgb 或 hsv (默认: hsv)",
94+
)
95+
parser.add_argument('-s', '--smooth', type=int, default=0,
96+
help='平滑窗口大小,推荐3或5,0为不平滑 (默认: 0)')
97+
6298
args = parser.parse_args()
63-
99+
100+
if args.output is None:
101+
from pathlib import Path
102+
103+
smooth_str = ''
104+
if args.smooth is not None:
105+
smooth_str = f"_smooth_{args.smooth}"
106+
107+
108+
input_path = Path(args.input)
109+
# 在文件名末尾添加 color_num_{颜色数量}_{模式}
110+
new_filename = f"{input_path.stem}_color_num_{args.num_colors}_{args.mode}{smooth_str}{input_path.suffix}"
111+
args.output = str(input_path.parent / new_filename)
112+
64113
if not 2 <= args.num_colors <= 256:
65114
raise ValueError("颜色数量必须在2到256之间")
66-
67-
reduce_colors(args.input, args.output, args.num_colors)
115+
116+
reduce_colors(args.input, args.output, args.num_colors, args.mode, args.smooth)

img/pixelart/create_script.js.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#!/bin/bash
22
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3+
rm $DIR/scripts.js
34
uglifyjs $DIR/*.js -c -o $DIR/scripts.js

test-pages/equationtest.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ A strange picture:
5656

5757
![what](/img/Tltamic.jpg)
5858

59+
![hotiri](/img/hotori_pyramid.png)
60+
5961
what?
6062

6163
## Alert Example

webGenerate/webConfig.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ export const gitRepositories = {
3434
* 我希望能从生成的html文件里找到第一个<p>填进去,但目前还没实现)
3535
*/
3636
export const recommend = [
37+
{
38+
date : 20250213,
39+
link : "https://github.com/chenyu76/draftsman.nvim",
40+
title : "Draftsman.nvim",
41+
info :
42+
"(Github链接)Neovim插件。<br>A feature-rich ASCII diagramming tool for Neovim. Draw boxes, arrows, lines, and text, and edit them easily. "
43+
},
44+
{
45+
date : 20250126,
46+
link : "https://github.com/chenyu76/github-latex-readme",
47+
title : "在Github上用LaTeX写README",
48+
info :
49+
"(Github链接)使用 GitHub Actions 将 LaTeX 文件自动编译为 SVG,从而用 LaTeX 编写 Github 上的 README 文件。"
50+
},
3751
{
3852
date : 20251221,
3953
link : "writings/college-reverse-proxy.html",

0 commit comments

Comments
 (0)