mirror of
https://github.com/HChaZZY/Any2MIF.git
synced 2025-12-06 10:33:49 +08:00
init
This commit is contained in:
239
core/image_processor.py
Normal file
239
core/image_processor.py
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2025 Any2MIF Project
|
||||
# All rights reserved.
|
||||
|
||||
"""
|
||||
Any2MIF - 图像处理器
|
||||
负责图像的灰度化、二值化等操作
|
||||
"""
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
from PIL import Image, ImageFilter
|
||||
|
||||
class ImageProcessor:
|
||||
"""图像处理器"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化图像处理器"""
|
||||
# 支持的图像格式
|
||||
self.supported_formats = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff']
|
||||
|
||||
def is_image_file(self, file_path):
|
||||
"""
|
||||
检查文件是否为图像文件
|
||||
|
||||
参数:
|
||||
file_path (str): 文件路径
|
||||
|
||||
返回:
|
||||
bool: 是否为图像文件
|
||||
"""
|
||||
_, ext = os.path.splitext(file_path.lower())
|
||||
return ext in self.supported_formats
|
||||
|
||||
def grayscale(self, image, method='weighted'):
|
||||
"""
|
||||
将图像转换为灰度图
|
||||
|
||||
参数:
|
||||
image (PIL.Image): 输入图像
|
||||
method (str): 灰度化方法
|
||||
'average': 平均值法
|
||||
'weighted': 加权平均法
|
||||
'max': 最大值法
|
||||
|
||||
返回:
|
||||
PIL.Image: 灰度图像
|
||||
"""
|
||||
# 确保图像是RGB模式
|
||||
if image.mode != 'RGB':
|
||||
image = image.convert('RGB')
|
||||
|
||||
# 转换为numpy数组
|
||||
img_array = np.array(image)
|
||||
|
||||
# 根据方法进行灰度化
|
||||
if method == 'average':
|
||||
# 平均值法: (R + G + B) / 3
|
||||
gray_array = np.mean(img_array, axis=2).astype(np.uint8)
|
||||
elif method == 'weighted':
|
||||
# 加权平均法: 0.299 * R + 0.587 * G + 0.114 * B
|
||||
gray_array = (0.299 * img_array[:, :, 0] +
|
||||
0.587 * img_array[:, :, 1] +
|
||||
0.114 * img_array[:, :, 2]).astype(np.uint8)
|
||||
elif method == 'max':
|
||||
# 最大值法: max(R, G, B)
|
||||
gray_array = np.max(img_array, axis=2).astype(np.uint8)
|
||||
else:
|
||||
# 默认使用加权平均法
|
||||
gray_array = (0.299 * img_array[:, :, 0] +
|
||||
0.587 * img_array[:, :, 1] +
|
||||
0.114 * img_array[:, :, 2]).astype(np.uint8)
|
||||
|
||||
# 转换回PIL图像
|
||||
return Image.fromarray(gray_array, mode='L')
|
||||
|
||||
def threshold(self, image, threshold=128, method='fixed'):
|
||||
"""
|
||||
对图像进行二值化处理
|
||||
|
||||
参数:
|
||||
image (PIL.Image): 输入图像
|
||||
threshold (int): 阈值 (0-255)
|
||||
method (str): 二值化方法
|
||||
'fixed': 固定阈值
|
||||
'adaptive': 自适应阈值
|
||||
'otsu': Otsu阈值
|
||||
|
||||
返回:
|
||||
PIL.Image: 二值化图像
|
||||
"""
|
||||
# 确保图像是灰度模式
|
||||
if image.mode != 'L':
|
||||
image = image.convert('L')
|
||||
|
||||
# 转换为numpy数组
|
||||
img_array = np.array(image)
|
||||
|
||||
# 根据方法进行二值化
|
||||
if method == 'fixed':
|
||||
# 固定阈值
|
||||
binary_array = (img_array > threshold).astype(np.uint8) * 255
|
||||
elif method == 'adaptive':
|
||||
# 自适应阈值 (简化版本)
|
||||
# 使用局部区域的平均值作为阈值
|
||||
kernel_size = 15
|
||||
# 创建一个平均滤波器
|
||||
kernel = np.ones((kernel_size, kernel_size)) / (kernel_size * kernel_size)
|
||||
# 计算局部平均值
|
||||
local_mean = np.zeros_like(img_array, dtype=np.float32)
|
||||
|
||||
# 简单的局部平均实现
|
||||
padded = np.pad(img_array, kernel_size // 2, mode='reflect')
|
||||
for i in range(img_array.shape[0]):
|
||||
for j in range(img_array.shape[1]):
|
||||
local_mean[i, j] = np.mean(padded[i:i+kernel_size, j:j+kernel_size])
|
||||
|
||||
# 二值化
|
||||
binary_array = (img_array > local_mean - threshold // 2).astype(np.uint8) * 255
|
||||
elif method == 'otsu':
|
||||
# Otsu阈值
|
||||
# 计算直方图
|
||||
hist, bins = np.histogram(img_array.flatten(), 256, [0, 256])
|
||||
|
||||
# 计算累积和
|
||||
cum_sum = hist.cumsum()
|
||||
cum_sum_sq = (hist * np.arange(256)).cumsum()
|
||||
|
||||
# 初始化
|
||||
max_var = 0
|
||||
otsu_threshold = 0
|
||||
|
||||
# 遍历所有可能的阈值
|
||||
for t in range(1, 256):
|
||||
# 前景和背景的权重
|
||||
w_bg = cum_sum[t]
|
||||
w_fg = cum_sum[-1] - w_bg
|
||||
|
||||
# 如果前景或背景为空,跳过
|
||||
if w_bg == 0 or w_fg == 0:
|
||||
continue
|
||||
|
||||
# 前景和背景的均值
|
||||
mean_bg = cum_sum_sq[t] / w_bg
|
||||
mean_fg = (cum_sum_sq[-1] - cum_sum_sq[t]) / w_fg
|
||||
|
||||
# 计算类间方差
|
||||
var_between = w_bg * w_fg * (mean_bg - mean_fg) ** 2
|
||||
|
||||
# 更新最大方差和阈值
|
||||
if var_between > max_var:
|
||||
max_var = var_between
|
||||
otsu_threshold = t
|
||||
|
||||
# 使用Otsu阈值进行二值化
|
||||
binary_array = (img_array > otsu_threshold).astype(np.uint8) * 255
|
||||
else:
|
||||
# 默认使用固定阈值
|
||||
binary_array = (img_array > threshold).astype(np.uint8) * 255
|
||||
|
||||
# 转换回PIL图像
|
||||
return Image.fromarray(binary_array, mode='L')
|
||||
|
||||
def denoise(self, image, method='median', strength=3):
|
||||
"""
|
||||
对图像进行降噪处理
|
||||
|
||||
参数:
|
||||
image (PIL.Image): 输入图像
|
||||
method (str): 降噪方法
|
||||
'median': 中值滤波
|
||||
'gaussian': 高斯滤波
|
||||
'bilateral': 双边滤波 (简化版本)
|
||||
strength (int): 降噪强度 (1-10)
|
||||
|
||||
返回:
|
||||
PIL.Image: 降噪后的图像
|
||||
"""
|
||||
# 根据方法进行降噪
|
||||
if method == 'median':
|
||||
# 中值滤波
|
||||
kernel_size = strength * 2 - 1 # 确保是奇数
|
||||
return image.filter(ImageFilter.MedianFilter(size=kernel_size))
|
||||
elif method == 'gaussian':
|
||||
# 高斯滤波
|
||||
radius = strength / 2
|
||||
return image.filter(ImageFilter.GaussianBlur(radius=radius))
|
||||
elif method == 'bilateral':
|
||||
# 双边滤波 (简化版本)
|
||||
# 由于PIL没有内置的双边滤波,我们使用高斯滤波代替
|
||||
# 在实际应用中,可以使用OpenCV的双边滤波
|
||||
radius = strength / 2
|
||||
return image.filter(ImageFilter.GaussianBlur(radius=radius))
|
||||
else:
|
||||
# 默认使用中值滤波
|
||||
kernel_size = strength * 2 - 1 # 确保是奇数
|
||||
return image.filter(ImageFilter.MedianFilter(size=kernel_size))
|
||||
|
||||
def resize(self, image, width, height, keep_aspect=True):
|
||||
"""
|
||||
调整图像大小
|
||||
|
||||
参数:
|
||||
image (PIL.Image): 输入图像
|
||||
width (int): 目标宽度
|
||||
height (int): 目标高度
|
||||
keep_aspect (bool): 是否保持纵横比
|
||||
|
||||
返回:
|
||||
PIL.Image: 调整大小后的图像
|
||||
"""
|
||||
if keep_aspect:
|
||||
# 计算缩放比例
|
||||
width_ratio = width / image.width
|
||||
height_ratio = height / image.height
|
||||
ratio = min(width_ratio, height_ratio)
|
||||
|
||||
# 计算新尺寸
|
||||
new_width = int(image.width * ratio)
|
||||
new_height = int(image.height * ratio)
|
||||
|
||||
# 调整图像大小
|
||||
resized_image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
|
||||
# 创建新图像
|
||||
new_image = Image.new(image.mode, (width, height), (0, 0, 0))
|
||||
|
||||
# 计算粘贴位置
|
||||
paste_x = (width - new_width) // 2
|
||||
paste_y = (height - new_height) // 2
|
||||
|
||||
# 粘贴调整大小后的图像
|
||||
new_image.paste(resized_image, (paste_x, paste_y))
|
||||
|
||||
return new_image
|
||||
else:
|
||||
# 直接调整到指定大小
|
||||
return image.resize((width, height), Image.Resampling.LANCZOS)
|
||||
Reference in New Issue
Block a user