傅里叶变换——幅度谱和相位谱

1.傅里叶转换的基础概念

时域与频域的处理:

时域:信号随时间变化的样子(比如心跳波形、声音的振动)。

频域:信号由哪些频率的波组成(比如低音、中音、高音的比例)

核心思想:

任何信号(比如声音、图片)都可以拆解成一堆不同频率的“波”的组合。

数学本质: 傅里叶变换的公式本质是用 正弦波和余弦波的叠加 来拟合原始信号。 (公式可以简单理解为:信号 = 波1 + 波2 + 波3 + …)

2.什么是幅度谱和相位谱

首先我们要了解图片,图片由像素组成,与幅度谱和相位谱有关的傅里叶变换,就是把“直接看图”转成“分析波”,而:

  1. 幅度谱:这些波的“强弱”(比如低频波强=图片整体偏亮);

低频:图像平滑区域(如天空、人脸)。

高频:图像边缘和细节(如文字、纹理)

  1. 相位谱:这些波的“怎么拼成图”(决定形状和结构)。

相位决定图像的轮廓和形状,即使幅度谱相同,相位不同,图像也会完全不同。

3.例题

SQCTF:FFT IFFT

首先查看题目代码分析加密过程

import os
import cv2
import struct
import numpy as np


def mapping(data, down=0, up=255, tp=np.uint8):
    data_max = data.max()
    data_min = data.min()
    interval = data_max - data_min
    new_interval = up - down
    new_data = (data - data_min) * new_interval / interval + down
    new_data = new_data.astype(tp)
    return new_data


def fft(img):
    fft = np.fft.fft2(img)
    fft = np.fft.fftshift(fft)
    m = np.log(np.abs(fft))
    p = np.angle(fft)
    return m, p


if __name__ == '__main__':
    os.mkdir('m')
    os.mkdir('p')
    os.mkdir('frame')
    os.system('ffmpeg -i secret.mp4 frame/%03d.png')    #视频拆分多张照片

    files = os.listdir('frame')
    r_file = open('r', 'wb')

    for file in files:
        img = cv2.imread(f'frame/{file}', cv2.IMREAD_GRAYSCALE)     #读取所有帧

        m, p = fft(img)
        r_file.write(struct.pack('!ff', m.min(), m.max()))

        new_img1 = mapping(m)               #绘制
        new_img2 = mapping(p)               #绘制

        cv2.imwrite(f'm/{file}', new_img1)
        cv2.imwrite(f'p/{file}', new_img2)

    r_file.close()
    os.system('ffmpeg -i m/%03d.png -r 25 -vcodec png 1.mkv')
    os.system('ffmpeg -i p/%03d.png -r 25 -vcodec png 2.mkv')

这段代码的主要功能是对视频文件进行傅里叶变换处理,并生成相关的图像和视频文件,具体功能如下:

  1. 视频帧提取:
    1.   使用ffmpeg将输入的secret.mp4视频文件拆分成一系列帧图像,保存到frame文件夹中
  2. 傅里叶变换处理:
    1. 对每一帧图像进行灰度处理
      1.   执行二维傅里叶变换,并将频谱移到中心
      1.   计算傅里叶变换的幅度谱(m)和相位谱(p
      1.   幅度谱通过取对数进行处理(np.log(np.abs(fft))
  3. 数据映射与保存:
    1. 将幅度谱和相位谱的值映射到 0-255 范围(便于可视化)
      1.   将映射后的幅度谱保存到m文件夹,相位谱保存到p文件夹
  4. 生成统计数据:
    1.   创建r文件,以二进制形式存储每帧幅度谱的最小值和最大值
  5. 生成输出视频:
    1. 使用ffmpegm文件夹中的幅度谱图像合成为1.mkv视频
      1.   将p文件夹中的相位谱图像合成为2.mkv视频

fft = np.fft.fft2(img) # 对图像做二维傅里叶变换

fft = np.fft.fftshift(fft) # 将零频(低频)移到中心

m = np.log(np.abs(fft)) # 提取幅度谱,用对数压缩动态范围

p = np.angle(fft) # 提取相位谱

那么我们可以用脚本恢复一下

import os
import cv2
import struct
import numpy as np
import shutil


def inverse_mapping(data, orig_min, orig_max, tp=np.float64):
    """将0-255范围的图像映射回原始数据范围"""data = data.astype(np.float64)
    new_data = data * (orig_max - orig_min) / 255 + orig_min
    return new_data.astype(tp)


def ifft(m, p):
    """从幅度谱和相位谱进行逆傅里叶变换"""# 组合幅度和相位为复数
    fft_complex = np.exp(m) * np.exp(1j * p)  # 幅度谱需要指数还原(原处理用了log)
    # 逆移位和逆傅里叶变换
    fft_shift = np.fft.ifftshift(fft_complex)
    img = np.fft.ifft2(fft_shift)
    # 取实部并转换为灰度值范围
    img = np.abs(img)
    return img


def decrypt():
    # 创建输出目录
    os.makedirs('recovered_frame', exist_ok=True)

    # 读取幅度谱的min/max数据
    with open('r', 'rb') as f:
        data = f.read()
    # 解析二进制数据(每个帧对应两个float值:min和max)
    frame_count = len(data) // 8  # 每个float占4字节,每个帧2个float
    min_max_list = [struct.unpack('!ff', data[i * 8:(i + 1) * 8]) for i in range(frame_count)]

    # 获取并排序所有帧文件
    m_files = sorted(os.listdir('m'), key=lambda x: int(x.split('.')[0]))
    p_files = sorted(os.listdir('p'), key=lambda x: int(x.split('.')[0]))

    # 逐帧恢复
    for i, (m_file, p_file) in enumerate(zip(m_files, p_files)):
        # 读取幅度谱和相位谱图像
        m_img = cv2.imread(f'm/{m_file}', cv2.IMREAD_GRAYSCALE)
        p_img = cv2.imread(f'p/{p_file}', cv2.IMREAD_GRAYSCALE)

        # 获取当前帧的幅度谱原始min和max
        m_min, m_max = min_max_list[i]

        # 将图像映射回原始傅里叶变换的幅度和相位
        m = inverse_mapping(m_img, m_min, m_max)
        p = inverse_mapping(p_img, -np.pi, np.pi)  # 相位范围是[-π, π]

        # 逆傅里叶变换恢复图像
        recovered_img = ifft(m, p)

        # 映射到0-255灰度范围并保存
        recovered_img = ((recovered_img - recovered_img.min()) /
                         (recovered_img.max() - recovered_img.min()) * 255).astype(np.uint8)
        cv2.imwrite(f'recovered_frame/{m_file}', recovered_img)
        print(f'已恢复帧 {i + 1}/{len(m_files)}')

    # 用ffmpeg将恢复的帧合成为视频
    os.system('ffmpeg -i recovered_frame/%03d.png -r 25 -vcodec libx264 recovered.mp4')
    print('解密完成,输出视频:recovered.mp4')


if __name__ == '__main__':
    # 清理之前的恢复结果(可选)
    if os.path.exists('recovered_frame'):
        shutil.rmtree('recovered_frame')
    decrypt()

解密成功:

SQCTF{HELLO}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇