309 lines
10 KiB
Python
309 lines
10 KiB
Python
import cv2
|
||
import numpy as np
|
||
import torch
|
||
from PIL import Image
|
||
import torchvision
|
||
import functools
|
||
import inspect
|
||
|
||
# 套用裝飾器到現有函數
|
||
def unsharp_mask(image, kernel_size=(5, 5), sigma=1.0, amount=1.0, threshold=0):
|
||
"""使用OpenCV實現的Unsharp Mask銳化處理
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
kernel_size: 高斯模糊的核大小,必須是奇數
|
||
sigma: 高斯模糊的標準差
|
||
amount: 銳化程度,值越大效果越強
|
||
threshold: 邊緣檢測閾值,僅在邊緣處進行銳化
|
||
返回:
|
||
銳化後的PIL.Image對象
|
||
"""
|
||
# 轉換PIL圖像為numpy數組
|
||
numpy_img = np.array(image, dtype=np.uint8)
|
||
|
||
# 對原圖進行高斯模糊
|
||
blurred = cv2.GaussianBlur(numpy_img, kernel_size, sigma)
|
||
|
||
# 計算銳化後的圖像
|
||
sharpened = cv2.addWeighted(numpy_img, 1 + amount, blurred, -amount, 0)
|
||
|
||
# 如果設置了threshold,只在邊緣處應用銳化
|
||
if threshold > 0:
|
||
low_contrast_mask = np.absolute(numpy_img - blurred) < threshold
|
||
np.copyto(sharpened, numpy_img, where=low_contrast_mask)
|
||
|
||
# 確保像素值在有效範圍內
|
||
sharpened = np.clip(sharpened, 0, 255).astype(np.uint8)
|
||
|
||
# 轉回PIL圖像
|
||
return Image.fromarray(sharpened)
|
||
|
||
def histogram_equalization(image):
|
||
"""GPU加速的一般直方圖等化
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
返回:
|
||
直方圖等化後的PIL.Image對象
|
||
"""
|
||
# 轉換為numpy數組並轉為PyTorch張量
|
||
numpy_img = np.array(image)
|
||
tensor_img = torch.from_numpy(numpy_img).float().to('cuda')
|
||
|
||
# 分離通道並進行直方圖等化
|
||
result = torch.zeros_like(tensor_img)
|
||
for i in range(3): # 對RGB三個通道分別處理
|
||
channel = tensor_img[..., i]
|
||
|
||
# 計算直方圖
|
||
hist = torch.histc(channel, bins=256, min=0, max=255)
|
||
|
||
# 計算累積分布函數(CDF)
|
||
cdf = torch.cumsum(hist, dim=0)
|
||
cdf_normalized = ((cdf - cdf.min()) * 255) / (cdf.max() - cdf.min())
|
||
|
||
# 應用直方圖等化
|
||
result[..., i] = cdf_normalized[channel.long()]
|
||
|
||
# 轉回CPU和numpy數組
|
||
result = torch.clamp(result, 0, 255).byte()
|
||
result_np = result.cpu().numpy()
|
||
return Image.fromarray(result_np)
|
||
|
||
def Contrast_Limited_Adaptive_Histogram_Equalization(image, clip_limit=3.0, tile_size=(8, 8)):
|
||
"""使用OpenCV實現的對比度限制自適應直方圖均衡化(CLAHE)
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
clip_limit: 剪切限制,用於限制對比度增強的程度,較大的值會產生更強的對比度
|
||
tile_size: 圖像分塊大小的元組(height, width),較小的值會產生更局部的增強效果
|
||
|
||
返回:
|
||
CLAHE處理後的PIL.Image對象
|
||
"""
|
||
# 將PIL圖像轉換為OpenCV格式(BGR)
|
||
numpy_img = np.array(image)
|
||
bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
|
||
|
||
# 轉換到LAB色彩空間
|
||
lab_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2LAB)
|
||
|
||
# 創建CLAHE對象
|
||
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_size)
|
||
|
||
# 分離LAB通道
|
||
l, a, b = cv2.split(lab_img)
|
||
|
||
# 對L通道應用CLAHE
|
||
l_clahe = clahe.apply(l)
|
||
|
||
# 合併處理後的L通道與原始的a和b通道
|
||
lab_output = cv2.merge([l_clahe, a, b])
|
||
|
||
# 將LAB轉回BGR,然後轉換為RGB
|
||
bgr_output = cv2.cvtColor(lab_output, cv2.COLOR_LAB2BGR)
|
||
rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
|
||
|
||
# 轉換為PIL圖像並返回
|
||
return Image.fromarray(rgb_output)
|
||
|
||
def adaptive_histogram_equalization_without_limit(image, tile_size=(8, 8)):
|
||
"""使用OpenCV實現的自適應直方圖均衡化(AHE)
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
tile_size: 圖像分塊大小的元組(height, width),較小的值會產生更局部的增強效果
|
||
|
||
返回:
|
||
AHE處理後的PIL.Image對象
|
||
"""
|
||
# 將PIL圖像轉換為OpenCV格式(BGR)
|
||
numpy_img = np.array(image)
|
||
bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
|
||
|
||
# 轉換到LAB色彩空間
|
||
lab_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2LAB)
|
||
|
||
# 分離LAB通道
|
||
l, a, b = cv2.split(lab_img)
|
||
|
||
# 創建AHE對象(不設置clip limit)
|
||
clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=tile_size)
|
||
|
||
# 對L通道應用AHE
|
||
l_ahe = clahe.apply(l)
|
||
|
||
# 合併處理後的L通道與原始的a和b通道
|
||
lab_output = cv2.merge([l_ahe, a, b])
|
||
|
||
# 將LAB轉回BGR,然後轉換為RGB
|
||
bgr_output = cv2.cvtColor(lab_output, cv2.COLOR_LAB2BGR)
|
||
rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
|
||
|
||
# 轉換為PIL圖像並返回
|
||
return Image.fromarray(rgb_output)
|
||
|
||
def laplacian_sharpen(image):
|
||
"""
|
||
GPU加速的拉普拉斯銳化處理函數
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
返回:
|
||
銳化後的PIL.Image對象
|
||
"""
|
||
# 轉換為numpy數組並轉為PyTorch張量
|
||
numpy_img = np.array(image)
|
||
tensor_img = torch.from_numpy(numpy_img).float().to('cuda')
|
||
|
||
# 創建拉普拉斯算子
|
||
laplacian_kernel = torch.tensor([
|
||
[0, 1, 0],
|
||
[1, -4, 1],
|
||
[0, 1, 0]
|
||
], dtype=torch.float32, device='cuda').unsqueeze(0).unsqueeze(0)
|
||
|
||
# 對每個通道進行處理
|
||
result = torch.zeros_like(tensor_img)
|
||
for i in range(3): # RGB三個通道
|
||
channel = tensor_img[..., i]
|
||
# 添加批次和通道維度
|
||
channel = channel.unsqueeze(0).unsqueeze(0)
|
||
# 應用拉普拉斯算子
|
||
laplacian = torch.nn.functional.conv2d(channel, laplacian_kernel, padding=1)
|
||
# 移除批次和通道維度
|
||
laplacian = laplacian.squeeze()
|
||
# 銳化處理:原圖 - 拉普拉斯
|
||
result[..., i] = channel.squeeze() - laplacian
|
||
|
||
# 確保像素值在合理範圍內
|
||
result = torch.clamp(result, 0, 255).byte()
|
||
|
||
# 轉回CPU和numpy數組
|
||
result_np = result.cpu().numpy()
|
||
return Image.fromarray(result_np)
|
||
|
||
def adjust_hsv(image, v_adjustment=0):
|
||
"""調整圖像的HSV色彩空間中的H和V通道
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
v_adjustment: V通道的調整值,範圍建議在[-255, 255]之間
|
||
|
||
返回:
|
||
HSV調整後的PIL.Image對象
|
||
"""
|
||
# 將PIL圖像轉換為OpenCV格式(BGR)
|
||
numpy_img = np.array(image)
|
||
bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
|
||
|
||
# 轉換到HSV色彩空間
|
||
hsv_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV)
|
||
|
||
# 調整V通道
|
||
hsv_img[..., 2] = np.clip(hsv_img[..., 2] + v_adjustment, 0, 255)
|
||
|
||
# 將HSV轉回BGR,然後轉換為RGB
|
||
bgr_output = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
|
||
rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
|
||
|
||
# 轉換為PIL圖像並返回
|
||
return Image.fromarray(rgb_output)
|
||
|
||
def gamma_correction(image, gamma=1.0):
|
||
"""對圖像進行伽馬校正
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
gamma: 伽馬值,gamma > 1 時圖像變暗,gamma < 1 時圖像變亮,gamma = 1 時保持不變
|
||
|
||
返回:
|
||
伽馬校正後的PIL.Image對象
|
||
"""
|
||
# 將PIL圖像轉換為numpy數組
|
||
numpy_img = np.array(image)
|
||
|
||
# 將像素值歸一化到[0, 1]範圍
|
||
normalized = numpy_img.astype(float) / 255.0
|
||
|
||
# 應用伽馬校正
|
||
corrected = np.power(normalized, gamma)
|
||
|
||
# 將值縮放回[0, 255]範圍
|
||
output = np.clip(corrected * 255.0, 0, 255).astype(np.uint8)
|
||
|
||
# 轉換回PIL圖像並返回
|
||
return Image.fromarray(output)
|
||
|
||
def Hight_Light(image, Threshold):
|
||
image = np.array(image)
|
||
|
||
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||
# 使用閾值檢測高光點(白色液滴)
|
||
_, thresh = cv2.threshold(gray, Threshold, 255, cv2.THRESH_BINARY)
|
||
# 使用形態學操作(膨脹)來擴大遮罩區域
|
||
kernel = np.ones((5, 5), np.uint8)
|
||
dilated = cv2.dilate(thresh, kernel, iterations=1)
|
||
# 使用 inpaint 修復高光點
|
||
image_inpaint = cv2.inpaint(image, dilated, 3, cv2.INPAINT_TELEA)
|
||
|
||
return Image.fromarray(image_inpaint)
|
||
|
||
def median_filter(image: Image.Image, kernel_size: int = 3):
|
||
"""
|
||
中值濾波(Median Filter)實現
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
kernel_size: 濾波核大小,必須是奇數
|
||
|
||
返回:
|
||
濾波後的PIL.Image對象
|
||
"""
|
||
# 確保kernel_size是奇數
|
||
if kernel_size % 2 == 0:
|
||
kernel_size += 1
|
||
|
||
# 轉換PIL圖像為numpy數組
|
||
numpy_img = np.array(image, dtype=np.uint8)
|
||
|
||
# 對每個通道應用中值濾波
|
||
result = np.zeros_like(numpy_img)
|
||
for i in range(3): # 對RGB三個通道分別處理
|
||
result[:, :, i] = cv2.medianBlur(numpy_img[:, :, i], kernel_size)
|
||
|
||
# 確保像素值在有效範圍內
|
||
result = np.clip(result, 0, 255).astype(np.uint8)
|
||
|
||
# 轉回PIL圖像
|
||
return Image.fromarray(result)
|
||
|
||
def mean_filter(image: Image.Image, kernel_size: int = 3):
|
||
"""
|
||
均質濾波(Mean Filter)實現
|
||
|
||
參數:
|
||
image: PIL.Image對象(RGB格式)
|
||
kernel_size: 濾波核大小,必須是奇數
|
||
|
||
返回:
|
||
濾波後的PIL.Image對象
|
||
"""
|
||
# 確保kernel_size是奇數
|
||
if kernel_size % 2 == 0:
|
||
kernel_size += 1
|
||
|
||
# 轉換PIL圖像為numpy數組
|
||
numpy_img = np.array(image, dtype=np.uint8)
|
||
|
||
# 創建均質濾波核(所有元素都是1/(kernel_size*kernel_size))
|
||
kernel = np.ones((kernel_size, kernel_size), np.float32) / (kernel_size * kernel_size)
|
||
|
||
# 對每個通道應用均質濾波
|
||
result = np.zeros_like(numpy_img)
|
||
for i in range(3): # 對RGB三個通道分別處理
|
||
result[:, :, i] = cv2.filter2D(numpy_img[:, :, i], -1, kernel)
|
||
|
||
# 確保像素值在有效範圍內
|
||
result = np.clip(result, 0, 255).astype(np.uint8)
|
||
|
||
# 轉回PIL圖像
|
||
return Image.fromarray(result) |