(一)PS识别: Python 图像分析PS识别之道-CSDN博客
(三)PS识别:基于噪声分析PS识别的技术实现-CSDN博客
(五)PS识别:压缩痕迹挖掘-压缩量化表与 DCT 系数分析
在这个图像编辑技术日益发达的时代,PS(Adobe Photoshop)处理过的图片随处可见。无论是社交媒体上的精美自拍,还是广告宣传中的产品图片,都可能经过了精心的 PS 处理。这也引发了人们对于图片真实性的关注,PS 特征识别技术应运而生。本文将深入探讨 PS 特征识别的原理,并通过 Python 代码进行实战演示。
一 直方图分析
直方图是图像中像素强度分布的图形表示,它展示了不同亮度级别的像素数量。经过 PS 处理的图片,其直方图可能会出现一些异常特征,例如:
- 尖锐峰值:在高像素值区域出现尖锐峰值,可能意味着图片经过了过度曝光调整。
- 截断现象:直方图两端的像素数量突然增多,可能是图片经过了曝光调整,导致部分像素值被截断。
- 多个显著峰值和谷值:如果直方图中出现多个显著的峰值和谷值,图片可能是拼接而成的。
二 实现逻辑分析
逻辑图解释
- 开始与图片读取:流程从开始节点出发,尝试读取指定路径的图片,判断图片是否成功读取。若读取失败,输出错误信息并结束流程。
- 灰度转换与直方图计算:图片读取成功后,将其转换为灰度图并计算直方图。
- 直方图分析:对计算得到的直方图进行分析,判断图片是否经过 PS 处理。如果判断为处理过,进入编辑区域检测与标注步骤。
- 边缘分析:若直方图分析未发现异常,进行边缘分析,判断边缘变化是否过小。若边缘变化过小,同样进入编辑区域检测与标注步骤。
- 结果输出与可视化:根据判断结果输出相应信息,绘制灰度直方图,并显示原始图和标注图的对比,最后流程结束。
通过这个逻辑图,能清晰地看到图片识别过程中各个步骤的先后顺序和判断逻辑。
三、Python 实战:PS 特征识别
1. 安装依赖库
在开始之前,我们需要安装一些必要的 Python 库:
pip install opencv-python numpy matplotlib scipy scikit-image
2.代码实现
import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
from skimage.filters import sobel
from skimage.segmentation import find_boundaries
from scipy.stats import skew, kurtosis
from typing import Tuple
class ImagePSAnalyzer:
def __init__(
self,
peak_height_threshold_multiplier: float = 2.0,
peak_distance: int = 30,
significant_peak_ratio: float = 0.3,
valley_height_threshold_multiplier: float = 1.0,
valley_distance: int = 20,
smooth_diff_threshold: float = 0.3,
left_right_range: int = 10,
left_right_pixel_ratio_threshold: float = 0.05,
middle_pixel_ratio_threshold: float = 0.8,
truncation_threshold_multiplier: float = 1.5,
high_pixel_peak_threshold_multiplier: float = 5.0,
skewness_left_threshold: float = -1.0,
skewness_right_threshold: float = 1.0,
kurtosis_threshold: float = 3.0
):
"""
初始化图像 PS 分析器,设置各种阈值参数
:param peak_height_threshold_multiplier: 峰值检测高度阈值的标准差倍数
:param peak_distance: 峰值检测时相邻峰值的最小距离
:param significant_peak_ratio: 显著峰值高度与最大峰值高度的比例阈值
:param valley_height_threshold_multiplier: 谷值检测高度阈值的标准差倍数
:param valley_distance: 谷值检测时相邻谷值的最小距离
:param smooth_diff_threshold: 直方图平滑度差异阈值
:param left_right_range: 直方图左右截断检测范围
:param left_right_pixel_ratio_threshold: 直方图左右两端像素比例阈值
:param middle_pixel_ratio_threshold: 直方图中间部分像素比例阈值
:param truncation_threshold_multiplier: 截断检测阈值的标准差倍数
:param high_pixel_peak_threshold_multiplier: 高像素值区域尖锐峰值阈值的均值倍数
:param skewness_left_threshold: 左截断时偏度阈值
:param skewness_right_threshold: 右截断时偏度阈值
:param kurtosis_threshold: 峰度阈值
"""
self.peak_height_threshold_multiplier = peak_height_threshold_multiplier
self.peak_distance = peak_distance
self.significant_peak_ratio = significant_peak_ratio
self.valley_height_threshold_multiplier = valley_height_threshold_multiplier
self.valley_distance = valley_distance
self.smooth_diff_threshold = smooth_diff_threshold
self.left_right_range = left_right_range
self.left_right_pixel_ratio_threshold = left_right_pixel_ratio_threshold
self.middle_pixel_ratio_threshold = middle_pixel_ratio_threshold
self.truncation_threshold_multiplier = truncation_threshold_multiplier
self.high_pixel_peak_threshold_multiplier = high_pixel_peak_threshold_multiplier
self.skewness_left_threshold = skewness_left_threshold
self.skewness_right_threshold = skewness_right_threshold
self.kurtosis_threshold = kurtosis_threshold
def analyze_histogram(self, hist: np.ndarray) -> Tuple[bool, str]:
"""
分析直方图,判断图片是否有经过 PS 处理的迹象
:param hist: 图像的直方图
:return: 是否有 PS 处理迹象,对应的提示信息
"""
# 计算直方图的统计信息
mean_value = np.mean(hist)
std_dev = np.std(hist)
total_pixels = np.sum(hist)
# 计算偏度和峰度
skewness = skew(hist.flatten())
kurt = kurtosis(hist.flatten())
# 改进的峰值检测
peaks, peak_properties = find_peaks(
hist.flatten(),
height=mean_value + self.peak_height_threshold_multiplier * std_dev,
distance=self.peak_distance
)
peak_heights = peak_properties['peak_heights']
significant_peaks = []
# 检查 peak_heights 是否为空
if peak_heights.size > 0:
max_peak_height = np.max(peak_heights)
for i in range(len(peaks)):
if peak_heights[i] / max_peak_height > self.significant_peak_ratio:
significant_peaks.append(peaks[i])
else:
max_peak_height = 0
# 谷值检测
valleys, _ = find_peaks(
-hist.flatten(),
height=-(mean_value + self.valley_height_threshold_multiplier * std_dev),
distance=self.valley_distance
)
# 直方图平滑度分析
smoothed_hist = np.convolve(hist.flatten(), np.ones(5) / 5, mode='same')
diff = np.sum(np.abs(hist.flatten() - smoothed_hist)) / total_pixels
is_rough = diff > self.smooth_diff_threshold
# 优化截断检测
# 扩大截断检测范围
left_range = self.left_right_range
right_range = self.left_right_range
# 计算不同区间的像素比例
left_pixel_ratio = np.sum(hist[:left_range]) / total_pixels
right_pixel_ratio = np.sum(hist[-right_range:]) / total_pixels
# 计算中间部分像素比例
middle_pixel_ratio = np.sum(hist[left_range:-right_range]) / total_pixels
# 动态调整截断阈值
left_threshold = mean_value + self.truncation_threshold_multiplier * std_dev
right_threshold = mean_value + self.truncation_threshold_multiplier * std_dev
# 检查连续多个像素点是否超过阈值
left_truncated = np.any(hist[:left_range] > left_threshold) and \
left_pixel_ratio > self.left_right_pixel_ratio_threshold and \
middle_pixel_ratio < self.middle_pixel_ratio_threshold
right_truncated = np.any(hist[-right_range:] > right_threshold) and \
right_pixel_ratio > self.left_right_pixel_ratio_threshold and \
middle_pixel_ratio < self.middle_pixel_ratio_threshold
# 结合偏度和峰度判断
if left_truncated:
left_truncated = left_truncated and (
skewness < self.skewness_left_threshold or kurt > self.kurtosis_threshold)
if right_truncated:
right_truncated = right_truncated and (
skewness > self.skewness_right_threshold or kurt > self.kurtosis_threshold)
# 判断是否异常
if len(significant_peaks) > 2 and len(valleys) > 1: # 多个显著峰值和谷值,可能是拼接图片
return True, f"直方图出现 {len(significant_peaks)} 个显著峰值和 {len(valleys)} 个谷值,图片可能是拼接图片"
elif left_truncated or right_truncated: # 直方图截断
return True, "直方图出现截断,结合像素比例、偏度和峰度判断,图片可能经过曝光调整"
elif any(hist[i] > self.high_pixel_peak_threshold_multiplier * mean_value for i in
range(200, 256)): # 高像素值区域尖锐峰值,可能过度曝光
return True, "直方图在高像素值区域出现尖锐峰值,图片可能经过过度曝光调整"
elif is_rough: # 直方图不平滑
return True, "直方图分布粗糙,图片可能经过处理"
else:
return False, "直方图分布正常,未发现 PS 处理迹象"
def detect_edited_regions(self, image: np.ndarray) -> np.ndarray:
"""
检测图片中可能被编辑的区域
:param image: 输入图像
:return: 标注了可能编辑区域的图像
"""
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
# 边缘检测
edges = sobel(gray)
edge_variation = np.std(edges)
# 查找可能的边界
boundaries = find_boundaries(edges > 0.1, mode='thick')
# 复制原图用于标注
marked_image = image.copy()
# 标注边界
marked_image[boundaries] = [0, 0, 255] # 用红色标注可能的编辑区域
return marked_image
def analyze_image(self, image: np.ndarray) -> Tuple[bool, str, np.ndarray]:
"""
综合分析图像,包括直方图和边缘信息
:param image: 输入图像
:return: 是否有 PS 处理迹象,对应的提示信息,标注后的图像
"""
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
# 计算直方图
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
# 分析直方图
is_processed_hist, message_hist = self.analyze_histogram(hist)
if is_processed_hist:
marked_image = self.detect_edited_regions(image)
return True, message_hist, marked_image
# 边缘分析
edges = sobel(gray)
edge_variation = np.std(edges)
if edge_variation < 0.05:
marked_image = self.detect_edited_regions(image)
return True, "图像边缘变化过小,可能经过处理", marked_image
return False, "图像正常,未发现 PS 处理迹象", image
if __name__ == "__main__":
# 初始化图像 PS 分析器
analyzer = ImagePSAnalyzer()
# 读取图片
image = cv2.imread('2008-07-07_2022-09-21_JFZDPT-BJ-20230111-00197_2.jpg')
if image is None:
print("无法读取图片,请检查图片路径。")
else:
# 分析图像
is_processed, message, marked_image = analyzer.analyze_image(image)
# 计算并绘制灰度直方图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
plt.plot(hist)
plt.title('Image Histogram')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.show()
print(message)
print(f"is_processed 结果: {is_processed}, 信息: {message}")
# 确保原图和标注后的图片尺寸一致
if image.shape != marked_image.shape:
marked_image = cv2.resize(marked_image, (image.shape[1], image.shape[0]))
# 水平拼接原图和标注后的图片
comparison_image = np.hstack((image, marked_image))
# 创建一个可调整大小的窗口
cv2.namedWindow('Original vs Marked Image', cv2.WINDOW_NORMAL)
# 显示对比图像
cv2.imshow('Original vs Marked Image', comparison_image)
# 调整窗口初始大小(可选)
cv2.resizeWindow('Original vs Marked Image', 1200, 800)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 代码解释
ImagePSAnalyzer 类:封装了 PS 特征识别的主要功能,包括直方图分析、边缘检测和图像分析。
analyze_histogram 方法:通过分析直方图的峰值、谷值、平滑度和截断现象,判断图片是否经过了 PS 处理。
detect_edited_regions 方法:使用边缘检测算法,检测图片中可能被编辑的区域,并标注出来。
analyze_image 方法:综合分析图像的直方图和边缘信息,判断图片是否经过了 PS 处理,并返回判断结果、提示信息和标注后的图像。
四、总结
PS 特征识别技术可以帮助我们判断图片是否经过了处理,从而提高图片的真实性和可信度。通过直方图分析、元数据检查和边缘检测等方法,我们可以有效地识别出 PS 处理的痕迹。本文通过 Python 代码实现了一个简单的 PS 特征识别程序,你可以根据实际需求调整阈值参数,以提高识别的准确性。
需要注意的是,PS 特征识别技术并不是 100% 准确的,一些高级的 PS 处理技术可能会掩盖这些特征。因此,在实际应用中,我们需要结合多种方法进行综合判断。