评估图片清晰度
import numpy as np
import torch
import cv2
import matplotlib.pyplot as plt
import os
from pathlib import Path
def read_raw_gt(file_path, height, width, bl):
"""读取原始RAW文件并进行预处理"""
raw_image = np.fromfile(file_path, dtype=np.int16)
raw_image = raw_image.reshape((height, width)).astype(np.float32)
raw_image = np.maximum(raw_image - bl, 0) / (4095 - bl) # 归一化到[0,1]
raw_image = np.expand_dims(raw_image, axis=2) # 增加通道维度
# 分割为RGGB四通道
raw_out = np.concatenate((
raw_image[0:height:2, 0:width:2, :], # R
raw_image[0:height:2, 1:width:2, :], # G
raw_image[1:height:2, 0:width:2, :], # G
raw_image[1:height:2, 1:width:2, :]), axis=2)
return raw_out # 形状 (H//2, W//2, 4)
def RGGB_t_2img(raw_tensor):
"""将四通道RGGB tensor转换为RGB图像"""
raw_image = raw_tensor.numpy() if torch.is_tensor(raw_tensor) else raw_tensor
channels, height, width = raw_image.shape
assert channels == 4, "Tensor must have 4 channels"
# 构建拜耳阵列
bayer = np.zeros((height * 2, width * 2), dtype=np.uint8)
bayer[0::2, 0::2] = (raw_image[0] * 255).astype(np.uint8) # R
bayer[0::2, 1::2] = (raw_image[1] * 255).astype(np.uint8) # G
bayer[1::2, 0::2] = (raw_image[2] * 255).astype(np.uint8) # G
bayer[1::2, 1::2] = (raw_image[3] * 255).astype(np.uint8) # B
# 解拜耳转换为RGB
rgb = cv2.cvtColor(bayer, cv2.COLOR_BayerRGGB2RGB)
return rgb
def calculate_laplacian_variance(rgb_image):
"""计算拉普拉斯方差清晰度指标"""
gray = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2GRAY)
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
return laplacian.var()
def generate_blur_heatmap(rgb_image, window_size=64):
"""生成局部模糊热力图"""
gray = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2GRAY)
h, w = gray.shape
heatmap = np.zeros((h // window_size, w // window_size))
for i in range(0, h // window_size):
for j in range(0, w // window_size):
y = i * window_size
x = j * window_size
patch = gray[y:y + window_size, x:x + window_size]
lap_var = cv2.Laplacian(patch, cv2.CV_64F).var()
heatmap[i, j] = lap_var
# 归一化处理
heatmap = (heatmap - heatmap.min()) / (heatmap.max() - heatmap.min() + 1e-8)
return cv2.resize(heatmap, (w, h), interpolation=cv2.INTER_NEAREST)
def analyze_gt_blur(file_path, height, width, bl, output_dir='./results'):
"""增强版分析函数,支持结果分文件保存"""
# 创建输出目录
Path(output_dir).mkdir(parents=True, exist_ok=True)
# 生成基础文件名
base_name = os.path.splitext(os.path.basename(file_path))[0]
safe_name = base_name.replace(' ', '_') # 处理特殊字符
# 数据处理流程(保持原逻辑)
raw_out = read_raw_gt(file_path, height, width, bl)
raw_tensor = torch.from_numpy(raw_out).permute(2, 0, 1)
rgb_image = RGGB_t_2img(raw_tensor)
global_var = calculate_laplacian_variance(rgb_image)
heatmap = generate_blur_heatmap(rgb_image)
# 独立保存RGB图像
rgb_path = os.path.join(output_dir, f"{safe_name}_rgb.png")
plt.imsave(rgb_path, rgb_image)
# 独立保存热力图(含colorbar)
plt.figure(figsize=(8, 6))
plt.imshow(heatmap, cmap='jet')
plt.colorbar()
heatmap_path = os.path.join(output_dir, f"{safe_name}_heatmap.png")
plt.savefig(heatmap_path, bbox_inches='tight')
plt.close()
# 保存组合分析图
analysis_path = os.path.join(output_dir, f"{safe_name}_analysis.png")
fig = plt.figure(figsize=(15, 6))
# RGB图像子图
ax1 = fig.add_subplot(1, 3, 1)
ax1.imshow(rgb_image)
ax1.set_title("RGB Image")
# 热力图层
ax2 = fig.add_subplot(1, 3, 2)
im = ax2.imshow(heatmap, cmap='jet')
fig.colorbar(im, ax=ax2)
ax2.set_title("Blur Heatmap")
# 直方图
ax3 = fig.add_subplot(1, 3, 3)
ax3.hist(heatmap.flatten(), bins=50)
ax3.set_title("Blur Distribution")
# 主标题
fig.suptitle(f"File: {base_name}\nGlobal Laplacian Variance: {global_var:.2f}")
plt.savefig(analysis_path, dpi=300, bbox_inches='tight')
plt.close()
return {
"rgb_path": rgb_path,
"heatmap_path": heatmap_path,
"analysis_path": analysis_path,
"global_var": global_var
}
import cv2
import numpy as np
from matplotlib import cm
def create_overlay(rgb_image, heatmap, alpha=0.5):
"""创建热力图叠加可视化"""
# 标准化热力图数据
norm_heat = (heatmap - np.min(heatmap)) / (np.max(heatmap) - np.min(heatmap) + 1e-8)
# 生成伪彩色热力图
heatmap_color = cm.jet(norm_heat)[:, :, :3] # 转换为RGB
heatmap_color = (heatmap_color * 255).astype(np.uint8)
# 调整原始图像尺寸(如果需要)
if rgb_image.shape[:2] != heatmap.shape[:2]:
rgb_image = cv2.resize(rgb_image, (heatmap.shape[1], heatmap.shape[0]))
# 创建叠加效果
overlay = cv2.addWeighted(rgb_image, 1 - alpha, heatmap_color, alpha, 0)
return overlay
def enhanced_analysis(file_path, height, width, bl, output_dir='./results'):
"""增强版分析函数,包含叠加图生成"""
# 基础处理(保持原有流程)
raw_out = read_raw_gt(file_path, height, width, bl)
raw_tensor = torch.from_numpy(raw_out).permute(2, 0, 1)
rgb_image = RGGB_t_2img(raw_tensor)
heatmap = generate_blur_heatmap(rgb_image)
# 创建输出路径
base_name = Path(file_path).stem
safe_name = base_name.replace(' ', '_')
Path(output_dir).mkdir(exist_ok=True)
# 保存原始RGB
rgb_path = f"{output_dir}/{safe_name}_original.png"
cv2.imwrite(rgb_path, cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR))
# 保存独立热力图
plt.figure(figsize=(8, 6))
plt.imshow(heatmap, cmap='jet')
plt.colorbar()
heatmap_path = f"{output_dir}/{safe_name}_heatmap.png"
plt.savefig(heatmap_path, bbox_inches='tight')
plt.close()
# 生成并保存叠加图
overlay = create_overlay(rgb_image, heatmap)
overlay_path = f"{output_dir}/{safe_name}_overlay.png"
cv2.imwrite(overlay_path, cv2.cvtColor(overlay, cv2.COLOR_RGB2BGR))
return {
'original': rgb_path,
'heatmap': heatmap_path,
'overlay': overlay_path
}
# 测试配置
height = 1440 # RAW文件原始高度
width = 2570 # RAW文件原始宽度
height = 1080 # RAW文件原始高度
width = 1920 # RAW文件原始宽度
bl = 0 # 黑电平值
# 测试文件列表
raw_files = [
'h-29x_200.raw',
'h-218x_200.raw',
'h-259x_200.raw'
]
import os
gt_dir = 'gt'
# 检查目录是否存在
if os.path.exists(gt_dir) and os.path.isdir(gt_dir):
raw_files = [os.path.join(gt_dir, f) for f in os.listdir(gt_dir) if os.path.isfile(os.path.join(gt_dir, f))]
print(raw_files)
# 执行分析
for raw_file in raw_files:
print(f"Analyzing {raw_file}...")
# results = analyze_gt_blur(raw_file, height, width, bl)
# print(f"Global sharpness variance: {results['global_var']:.2f}\n")
results = enhanced_analysis(raw_file,
height=height,
width=width,
bl=bl,
output_dir='./enhanced_results')
全局清晰度评估
- 拉普拉斯方差指标(Global Laplacian Variance: 97.50)
-
- 质量评级:★★★☆☆(中等偏上)(典型值域:10-200)
- 数值对比参考:
-
- 优质图像:>100
- 可接受范围:50-100
- 需要优化:<50
二、模糊热图特征分析
- 空间分布特征:
区域坐标 | 模糊值 | 对应物体 | 成因推测 |
(620, 880) | 0.82 | 桌面反光区域 | 镜面反射过曝 |
(1120, 480) | 0.76 | 移动的椅子 | 拍摄时物体位移 |
(280, 1040) | 0.68 | 墙面装饰画 | 镜头畸变导致 |