本文将介绍一种基于量化表分析和 DCT 系数分析的图片 PS 检测方法,帮助你判断图片是否经过处理。
在 JPEG 图片的压缩过程中,量化表起着关键作用。不同的软件或处理操作可能会改变量化表的数值分布。我们通过计算量化表中最大值与最小值的差异,以及标准差等指标,来判断图片是否可能经过 PS 处理。如果量化表差异过大,就意味着图片可能在后期被修改过。
DCT(离散余弦变换)是 JPEG 压缩的核心步骤之一。对图片进行 DCT 变换后,低频系数包含了图片的主要信息,高频系数则反映了细节和噪声。经过 PS 处理的图片,其 DCT 系数的分布往往会发生变化。我们通过计算低频系数的方差,以及高频系数的分布等特征,来判断图片是否存在处理痕迹。
以下是将该检测方法封装成通用类的 Python 代码:
from skimage.io import imread
from skimage.color import rgb2ycbcr
from scipy.fftpack import dct
import numpy as np
from PIL import Image, JpegImagePlugin
import cv2
class PSImageAnalyzer:
def __init__(self, quant_max_diff_threshold=100, dct_low_freq_var_threshold=1000, block_size=8, save_marked_image=False, save_path=None):
"""
初始化 PS 图片分析器
:param quant_max_diff_threshold: 量化表最大差异阈值
:param dct_low_freq_var_threshold: DCT 低频系数方差阈值
:param block_size: DCT 分块大小
:param save_marked_image: 是否保存标注图片
:param save_path: 标注图片保存路径
"""
self.quant_max_diff_threshold = quant_max_diff_threshold
self.dct_low_freq_var_threshold = dct_low_freq_var_threshold
self.block_size = block_size
self.save_marked_image = save_marked_image
self.save_path = save_path
def analyze_quantization_table(self, image_path):
"""
分析 JPEG 图片的量化表,判断图片是否可能经过 PS 处理。
:param image_path: 图片文件的路径
:return: 一个元组,包含布尔值和相应的提示信息
"""
try:
img = Image.open(image_path)
if isinstance(img, JpegImagePlugin.JpegImageFile):
q_table = img.quantization
if q_table:
q_table = np.array(list(q_table.values()))
max_diff = np.max(q_table) - np.min(q_table)
if max_diff > self.quant_max_diff_threshold:
return True, "量化表差异大,可能被处理过"
return False, "量化表正常"
return False, "未找到量化表"
return False, "图片不是 JPEG 格式"
except Exception as e:
return False, f"错误: {e}"
def analyze_dct_coefficients(self, image_path):
"""
分析图片的 DCT 系数,判断图片是否可能经过 PS 处理。
同时标记出可能存在问题的 DCT 块。
:param image_path: 图片文件的路径
:return: 一个元组,包含布尔值、相应的提示信息和标记后的图片
"""
try:
image = imread(image_path)
marked_image = image.copy()
ycbcr = rgb2ycbcr(image)
y_channel = ycbcr[:, :, 0]
dct_blocks = []
suspicious_blocks = []
for i in range(0, y_channel.shape[0], self.block_size):
for j in range(0, y_channel.shape[1], self.block_size):
block = y_channel[i:i + self.block_size, j:j + self.block_size]
if block.shape == (self.block_size, self.block_size):
dct_block = dct(dct(block, axis=0, norm='ortho'), axis=1, norm='ortho')
dct_blocks.append(dct_block)
low_freq_var = np.var(dct_block[:2, :2])
if low_freq_var > self.dct_low_freq_var_threshold:
suspicious_blocks.append((i, j))
dct_blocks = np.array(dct_blocks)
mean_dct = np.mean(dct_blocks, axis=0)
low_freq_var = np.var(mean_dct[:2, :2])
if low_freq_var > self.dct_low_freq_var_threshold:
for i, j in suspicious_blocks:
cv2.rectangle(marked_image, (j, i), (j + self.block_size, i + self.block_size), (0, 0, 255), 1)
return True, "DCT 低频系数方差大,可能被处理过", marked_image
return False, "DCT 系数正常", marked_image
except Exception as e:
return False, f"错误: {e}", image
def is_ps_combined_check(self, image_path):
"""
综合量化表分析和 DCT 系数分析,判断图片是否可能经过 PS 处理。
:param image_path: 图片文件的路径
:return: 一个元组,包含布尔值、相应的提示信息和标记后的图片
"""
quant_is_ps, quant_message = self.analyze_quantization_table(image_path)
dct_is_ps, dct_message, marked_image = self.analyze_dct_coefficients(image_path)
final_is_ps = quant_is_ps or dct_is_ps
final_messages = []
if quant_is_ps:
final_messages.append(quant_message)
if dct_is_ps:
final_messages.append(dct_message)
if final_is_ps:
final_message = "检测到可能的 PS 痕迹:" + ", ".join(final_messages)
if self.save_marked_image and self.save_path:
cv2.imwrite(self.save_path, cv2.cvtColor(marked_image, cv2.COLOR_RGB2BGR))
else:
final_message = "未检测到 PS 痕迹,图片正常。"
return final_is_ps, final_message, marked_image
# 使用示例
if __name__ == "__main__":
analyzer = PSImageAnalyzer(
quant_max_diff_threshold=100,
dct_low_freq_var_threshold=1000,
block_size=8,
save_marked_image=True,
save_path='marked_image.jpg'
)
is_ps, message, marked_image = analyzer.is_ps_combined_check('2.jpg')
print(f"是否 PS: {is_ps}, 信息: {message}")
# 显示标记后的图片
cv2.imshow('Marked Image', cv2.cvtColor(marked_image, cv2.COLOR_RGB2BGR))
cv2.waitKey(0)
cv2.destroyAllWindows()
量化表最大差异阈值(quant_max_diff_threshold)
此阈值如同一个“松紧调节器”。当数值增大时,判断标准变得宽松,这可能会让一些经过 PS 处理但量化表差异相对较小的图片“蒙混过关”,降低检测的敏感度;而当数值减小时,判断标准更为严格,虽能提高对处理图片的捕捉能力,但也可能因过于敏感而将正常图片误判为经过 PS 处理。
DCT 低频系数方差阈值(dct_low_freq_var_threshold)
该阈值的调整直接影响着对图片 DCT 系数变化的敏感度。增大阈值,意味着需要更大的低频系数方差才会判定图片可能被处理,这可能会遗漏一些细微的处理痕迹;减小阈值,则能更敏锐地捕捉到图片的变化,但也容易将正常的图像波动误判为处理痕迹。
DCT 分块大小(block_size)
分块大小决定了对图片分析的细致程度。较大的分块就像用广角镜头观察图片,更侧重于整体特征,可能会忽略一些局部的 PS 处理痕迹;较小的分块则如同使用显微镜,能更细致地观察局部细节,但同时也会增加计算量,并且可能受到图片噪声等因素的干扰,导致误判率上升。
是否保存标注图片(save_marked_image)与保存路径(save_path)
这两个参数为开发者提供了灵活的结果管理方式。根据实际需求,选择是否保存标注图片,能够方便后续对检测结果进行深入分析和复查,同时合理设置保存路径有助于对检测结果进行有序的组织和管理。
通过量化表分析和 DCT 系数分析,我们可以在一定程度上判断图片是否经过 PS 处理。开发人员可以根据实际情况调整参数,平衡检测敏感度和误判率。这种方法虽然不能 100% 准确判断图片是否经过处理,但为图片真实性鉴定提供了一种有效的思路和工具。