doanh25032004's picture
Upload folder using huggingface_hub
872b0a0 verified
Raw
History Blame Contribute Delete
2.63 kB
from __future__ import annotations
import torch
import torch.nn.functional as F
from einops import rearrange
class SSIM(torch.nn.Module):
def __init__(self, kernel_size: int = 3, K1: float = 0.01, K2: float = 0.03, L: float = 1.0):
"""
L: dynamic range của dữ liệu ảnh. Ảnh đã chuẩn hóa [0,1] => L=1.
K1, K2: hằng số chuẩn của SSIM (mặc định 0.01, 0.03).
"""
super().__init__()
self.C1 = (K1 * L) ** 2
self.C2 = (K2 * L) ** 2
# 'same' output size: padding = k//2, và không tính phần pad vào trung bình
self.pool = torch.nn.AvgPool2d(kernel_size, stride=1, padding=kernel_size // 2,
count_include_pad=False)
def forward(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
# x, y: [B, 3, H, W] trong [0,1]
eps = 1e-6
mu_x = self.pool(x)
mu_y = self.pool(y)
sigma_x = self.pool(x * x) - mu_x * mu_x
sigma_y = self.pool(y * y) - mu_y * mu_y
sigma_xy = self.pool(x * y) - mu_x * mu_y
# đảm bảo dương tính số nhỏ
sigma_x = torch.clamp(sigma_x, min=0.0)
sigma_y = torch.clamp(sigma_y, min=0.0)
num = (2 * mu_x * mu_y + self.C1) * (2 * sigma_xy + self.C2)
den = (mu_x * mu_x + mu_y * mu_y + self.C1) * (sigma_x + sigma_y + self.C2) + eps
ssim = num / den # kỳ vọng trong [-1, 1]
# trả về "SSIM loss map" trong [0,1], gộp kênh về [B,1,H,W]
ssim_loss_map = (1.0 - ssim) * 0.5
ssim_loss_map = torch.clamp(ssim_loss_map, 0.0, 1.0).mean(dim=1, keepdim=True)
return ssim_loss_map
def _meshgrid_xy(B, H, W, device):
ys, xs = torch.meshgrid(
torch.linspace(-1.0, 1.0, H, device=device),
torch.linspace(-1.0, 1.0, W, device=device),
indexing='ij')
grid = torch.stack([xs, ys], dim=-1).unsqueeze(0).expand(B, -1, -1, -1)
return grid
def flow_to_norm_grid(flow):
B, _, H, W = flow.shape
dx = flow[:, 0:1] * (2.0 / max(W-1, 1))
dy = flow[:, 1:2] * (2.0 / max(H-1, 1))
return rearrange(torch.cat([dx, dy], dim=1), 'b c h w -> b h w c')
def warp(img, flow):
B, C, H, W = img.shape
base = _meshgrid_xy(B, H, W, img.device)
grid = base + flow_to_norm_grid(flow)
return F.grid_sample(img, grid, mode='bilinear', padding_mode='border', align_corners=True)
def image_gradients(img):
dx = img[..., :, 1:] - img[..., :, :-1]
dy = img[..., 1:, :] - img[..., :-1, :]
dx = F.pad(dx, (0,1,0,0))
dy = F.pad(dy, (0,0,0,1))
return dx, dy