!/usr/bin/env python

-- coding: utf-8 --

"""
图片极限压缩脚本
功能:扫描当前目录及子目录,极限压缩大于500KB的PNG和JPG图片
警告:此脚本会大幅降低图片质量以换取最小体积!
"""

import os
from pathlib import Path
from PIL import Image
import io

配置参数 - 极限压缩模式

SIZE_THRESHOLD = 100 * 1024 # 500KB
JPEG_QUALITY = 5 # JPEG质量极低 (1-100),越低体积越小
PNG_COLORS = 16 # PNG颜色数量,越少体积越小 (2-256)
MAX_DIMENSION = 1024 # 最大尺寸,超过会缩小

def get_file_size(file_path):

"""获取文件大小(字节)"""
return os.path.getsize(file_path)

def format_size(size_bytes):

"""格式化文件大小显示"""
if size_bytes < 1024:
    return f"{size_bytes}B"
elif size_bytes < 1024 * 1024:
    return f"{size_bytes / 1024:.2f}KB"
else:
    return f"{size_bytes / (1024 * 1024):.2f}MB"

def resize_if_large(img, max_dim):

"""如果图片尺寸过大则缩小"""
width, height = img.size
if width > max_dim or height > max_dim:
    if width > height:
        new_width = max_dim
        new_height = int(height * (max_dim / width))
    else:
        new_height = max_dim
        new_width = int(width * (max_dim / height))
    return img.resize((new_width, new_height), Image.LANCZOS)
return img

def compress_jpeg_extreme(img, file_path, original_size):

"""JPEG极限压缩"""
if img.mode in ('RGBA', 'P'):
    img = img.convert('RGB')

# 缩小尺寸
img = resize_if_large(img, MAX_DIMENSION)

# 使用极低质量
buffer = io.BytesIO()
img.save(buffer, format='JPEG', quality=JPEG_QUALITY, optimize=True, subsampling=2)
new_size = buffer.tell()

if new_size < original_size:
    img.save(file_path, format='JPEG', quality=JPEG_QUALITY, optimize=True, subsampling=2)
    return True, new_size
return False, original_size

def compress_png_extreme(img, file_path, original_size):

"""PNG极限压缩 - 强制量化到极少颜色"""
# 缩小尺寸
img = resize_if_large(img, MAX_DIMENSION)

has_alpha = img.mode == 'RGBA'
best_size = original_size
best_buffer = None

# 尝试不同的颜色数量,找到最小的
for colors in [PNG_COLORS, 32, 64, 128]:
    try:
        buffer = io.BytesIO()
        
        if has_alpha:
            # 有透明通道 - 分离处理
            alpha = img.split()[-1]
            rgb = img.convert('RGB')
            
            # 量化RGB
            rgb_q = rgb.quantize(colors=colors, method=Image.MEDIANCUT)
            rgb_q = rgb_q.convert('RGB')
            
            # 重新合并alpha
            img_final = rgb_q.convert('RGBA')
            img_final.putalpha(alpha)
            img_final.save(buffer, format='PNG', optimize=True, compress_level=9)
        else:
            # 无透明通道 - 直接量化
            if img.mode != 'RGB':
                img_temp = img.convert('RGB')
            else:
                img_temp = img
            
            img_q = img_temp.quantize(colors=colors, method=Image.MEDIANCUT)
            img_q.save(buffer, format='PNG', optimize=True, compress_level=9)
        
        size = buffer.tell()
        if size < best_size:
            best_size = size
            best_buffer = buffer
            
        # 如果已经足够小,就用最少颜色的
        if colors == PNG_COLORS:
            break
            
    except Exception:
        continue

if best_buffer and best_size < original_size:
    best_buffer.seek(0)
    with open(file_path, 'wb') as f:
        f.write(best_buffer.read())
    return True, best_size

return False, original_size

def compress_image(file_path):

"""极限压缩单张图片"""
original_size = get_file_size(file_path)

if original_size <= SIZE_THRESHOLD:
    return False, original_size, original_size

try:
    img = Image.open(file_path)
    file_ext = Path(file_path).suffix.lower()
    
    if file_ext in ['.jpg', '.jpeg']:
        success, new_size = compress_jpeg_extreme(img, file_path, original_size)
    elif file_ext == '.png':
        success, new_size = compress_png_extreme(img, file_path, original_size)
    else:
        return False, original_size, original_size
    
    if success:
        return True, original_size, new_size
    return False, original_size, original_size
    
except Exception as e:
    print(f"  [错误] 处理失败: {e}")
    return False, original_size, original_size

def find_images(root_dir):

"""查找所有PNG和JPG图片"""
images = []
extensions = {'.png', '.jpg', '.jpeg'}

for root, dirs, files in os.walk(root_dir):
    for file in files:
        if Path(file).suffix.lower() in extensions:
            images.append(os.path.join(root, file))

return images

def main():

# 获取脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))

print("=" * 60)
print("图片极限压缩工具 (不考虑画质)")
print("=" * 60)
print(f"扫描目录: {script_dir}")
print(f"压缩阈值: {format_size(SIZE_THRESHOLD)}")
print(f"JPEG质量: {JPEG_QUALITY} (极低)")
print(f"PNG颜色: {PNG_COLORS} 色")
print(f"最大尺寸: {MAX_DIMENSION}px")
print("-" * 60)

# 查找所有图片
images = find_images(script_dir)
print(f"找到 {len(images)} 张图片")
print("-" * 60)

# 统计信息
total_original = 0
total_compressed = 0
compressed_count = 0
skipped_count = 0

# 筛选大于阈值的图片
large_images = [(img, get_file_size(img)) for img in images if get_file_size(img) > SIZE_THRESHOLD]

print(f"大于 {format_size(SIZE_THRESHOLD)} 的图片: {len(large_images)} 张")
print("-" * 60)

for file_path, original_size in large_images:
    relative_path = os.path.relpath(file_path, script_dir)
    print(f"\n处理: {relative_path}")
    print(f"  原始大小: {format_size(original_size)}")
    
    success, orig_size, new_size = compress_image(file_path)
    
    total_original += orig_size
    
    if success:
        total_compressed += new_size
        compressed_count += 1
        saved = orig_size - new_size
        saved_percent = (saved / orig_size) * 100
        print(f"  压缩后: {format_size(new_size)} (节省 {format_size(saved)}, {saved_percent:.1f}%)")
    else:
        total_compressed += orig_size
        skipped_count += 1
        print(f"  [跳过] 无法进一步压缩")

# 输出汇总
print("\n" + "=" * 60)
print("压缩完成!")
print("=" * 60)
print(f"处理图片: {len(large_images)} 张")
print(f"成功压缩: {compressed_count} 张")
print(f"跳过: {skipped_count} 张")

if total_original > 0:
    total_saved = total_original - total_compressed
    print(f"原始总大小: {format_size(total_original)}")
    print(f"压缩后总大小: {format_size(total_compressed)}")
    print(f"总共节省: {format_size(total_saved)} ({(total_saved/total_original)*100:.1f}%)")

print("=" * 60)

if name == "__main__":

main()