分数阶傅里叶变换(FrFT)数字水印:嵌入 + 检测的 自包含 MATLAB 程序

方案:

  • 核心 :快速 FrFT(Ozaktas--Kutay--Bozdagi 1996 chirp-卷积实现),单文件 frft.m
  • 水印 :把扩频序列/量化水印嵌入到 某个阶数 a 的 FrFT 系数子集 中(加性)
  • 检测:盲检测(不需要原图),用相关性 + 阈值;也顺带演示"有水印时可逆提取校验"

1)FrFT 核心函数 frft.m

matlab 复制代码
%% frft.m
% Fast Fractional Fourier Transform (1D)
% Reference: H. M. Ozaktas, M. A. Kutay, G. Bozdagi,
% "Digital computation of the fractional Fourier transform",
% IEEE Trans. Signal Processing, 44(9):2141--2150, 1996.
%
% x = frft(f, a)
%   f : column vector (signal)
%   a : fractional order (physical meaning: rotation by a*pi/2 in TF-plane)
%       a = 0  -> identity
%       a = 1  -> FT
%       a = 2  -> time reversal
%       a = 3  -> IFT (up to scaling)
function Fa = frft(f, a)
    narginchk(2,2);
    f = f(:);
    N = length(f);

    % ---- reduce a to [0,4) and handle easy specials ----
    a = mod(a, 4);
    if abs(a) < 1e-10,        Fa = f; return; end
    if abs(a-2) < 1e-10,      Fa = flipud(f); return; end
    if abs(a-1) < 1e-10
        shift = @(x) circshift(x, floor(N/2));
        Fa = shift( fft( shift(f) ) ) / sqrt(N);
        return;
    end
    if abs(a-3) < 1e-10
        shift = @(x) circshift(x, floor(N/2));
        Fa = shift( ifft( shift(f) ) ) * sqrt(N);
        return;
    end

    % ---- map a to alpha in (-pi/2, pi/2) ----
    alpha = a * pi/2;

    % sign handling (keep |alpha|<=pi/2)
    if alpha > pi/2
        alpha = alpha - pi;
        f = flipud(f);
    elseif alpha < -pi/2
        alpha = alpha + pi;
        f = flipud(f);
    end

    % ---- chirp-convolution method ----
    % sampling period assumed = 1 (you can rescale externally if needed)
    % phi = tan(alpha/2), psi = sin(alpha)
    phi = tan(alpha/2);
    psi = sin(alpha);

    % avoid numerical blow-up near alpha = 0 or pi/2
    if abs(psi) < eps, psi = sign(psi+eps)*eps; end

    n = (0:N-1).';
    % index centering (circular shift trick widely used in this alg.)
    idx = (0:N-1).';
    sh = floor(N/2);
    n0 = idx - sh;

    % 1) chirp premultiply
    f1 = f .* exp(-1i*pi*phi*n0.^2 / N);

    % 2) chirp kernel convolution (via FFT)
    % kernel length chosen appropriately; paper suggests length ~ N
    % simplest robust way for demo: use Chirp-z style via FFT of padded vectors
    % We'll follow the common implementation pattern:
    %   g = f1
    %   h[n] = exp( i*pi*psi*n^2/N )
    %   (g*h)[m] via FFT with enough padding
    padLen = N;
    Fg = fft(f1, N+padLen);
    h  = exp(1i*pi*psi*( (0:N-1).' - sh ).^2 / N );
    Fh = fft(h, N+padLen);
    tmp = ifft(Fg .* Fh);
    F2 = tmp( (sh+1):(sh+N) );  % pick central N outputs

    % 3) chirp postmultiply
    Fa = exp(-1i*pi*phi*n0.^2 / N) .* F2;

    % 4) amplitude scaling (phase term from Ozaktas eq.)
    scale = exp(-1i*(pi*sign(psi)/4 - alpha/2)) / sqrt(abs(psi));
    Fa = Fa * scale / sqrt(N);
end

说明 :上面给出的就是经典论文里的"chirp预乘→卷积→chirp解调→尺度校正"流程。

如果你以后要做到完全严格的能量守恒/可逆 ,要把采样间隔 Δt 显式放进来(变成 phi = Δt^2*tan(alpha/2) 那种形式)。但对"水印+检测"这种工程演示,用 Δt=1 并把强度 δ 控好就够用了。


2)主脚本:嵌入 & 检测(推荐:扩频 + FrFT域加性水印)

这种写法 更安全、更容易做到盲检测

  • 用密钥 seed 生成伪随机位置集合 posSet 与扩频序列 pn
  • 在选定的 FrFT 阶数 a 下把弱水印加进去:
    XposSet += δ × pn
  • 检测时:对同一 a/seed 取出同一批系数,与 pn 算相关系数 → 阈值
matlab 复制代码
%% ============================================================
%  demo_frft_watermark.m
%  Gray-scale image watermarking in FrFT domain (additive spread-spectrum)
%  Works in 2D via separable row-then-col FrFT (many papers use this).
% ============================================================

clear; clc; close all; rng(0);

%% ---------- 0. Read host image ----------
I = imread('lena.tif');                 % <-- 换成你的图路径
if ndims(I)==3, I = rgb2gray(I); end
I = double(I);
[M,N] = size(I);
figure(1); imshow(uint8(I),[]); title('Host image');

%% ---------- 1. Choose FrFT order ----------
a_row = 0.9;   % row-wise FrFT order  (0<a<2)
a_col = 0.9;   % col-wise FrFT order  (可一样,也可不同)

%% ---------- 2. Build separable 2D FrFT domain ----------
% 2D FrFT via separable: FrFT2D = colFrFT ∘ rowFrFT
% For each row:
Frow = zeros(M,N);
for m=1:M
    Frow(m,:) = frft(I(m,:).', a_row).';   % row as column then transpose back
end
% For each column of Frow:
F2d = zeros(M,N);
for n=1:N
    F2d(:,n) = frft(Frow(:,n), a_col);
end

%% ---------- 3. Watermark embedding (spread-spectrum in coefficient magnitudes) ----------
% Key-controlled randomness (acts like your secret key)
seed = 12345;
K = 2000;               % 嵌入系数个数(别太大,否则可见)
delta = 8;              % 嵌入强度(对 uint8 图像 ~0~255,delta=5~15通常够用)

% Select positions in FrFT-domain (excluding DC if you want)
allIdx = (1:M*N).';
% optional: skip very low freq region (near center) if you like
% here we just random-choose
s = RandStream('mt19937ar','Seed',seed);
sel = randperm(s, length(allIdx), K );   % requires R2019b+; 老版本用下面两行:
% tmp = allIdx(randperm(s,length(allIdx))); sel = tmp(1:K);

% Build pseudo-noise (+1/-1)
pn = sign(randn(s,K,1));

% Embed
Fw = F2d;
% You can embed into REAL part (more stable), or magnitude
% -------- choice A: embed into real part (recommended for blind detection) --------
Fw(sel) = Fw(sel) + delta * pn;

%% ---------- 4. Inverse FrFT to get watermarked image ----------
% Inverse FrFT order = -a  (or 4-a, same thing)
IrFrow = zeros(M,N);
for n=1:N
    IrFrow(:,n) = frft(Fw(:,n), -a_col);   % inverse along cols
end
Iw = zeros(M,N);
for m=1:M
    Iw(m,:) = frft(IrFrow(m,:).', -a_row).';
end
Iw = real(Iw);   % discard residual imag (numerical only)

% clip back to [0,255] to simulate storage/display
Iw = max(0, min(255, Iw));

figure(2); imshow(uint8(Iw),[]); title('Watermarked image');
fprintf('PSNR host-wmd = %.2f dB\n', psnr(uint8(I),uint8(Iw)));

%% ---------- 5. Simple attack simulation (optional) ----------
Ia = Iw;
% Ia = imnoise(uint8(Iw),'gaussian',0,0.0005); Ia=double(Ia);  % 高斯噪声
% Ia = imfilter(uint8(Iw),fspecial('average',[3 3]),'replicate'); Ia=double(Ia); % 模糊
% Ia = Ia(10:end-10,10:end-10); Ia=padarray(Ia,[10 10],'replicate'); % 模拟剪裁

%% ---------- 6. Detection (blind: no need original host) ----------
% Recompute FrFT of (possibly attacked) image
Fa_row = zeros(M,N);
for m=1:M
    Fa_row(m,:) = frft(Ia(m,:).', a_row).';
end
Fa_2d = zeros(M,N);
for n=1:N
    Fa_2d(:,n) = frft(Fa_row(:,n), a_col);
end

% Extract test vector
w_extract = Fa_2d(sel);     % corresponding to real-part embedding
rho = abs( dot(w_extract, pn) / (norm(pn)*norm(w_extract) + eps) );

th = 0.05;   % empirical threshold (higher=better robustness, higher=漏检)
fprintf('Correlation rho = %.4f\n', rho);
if rho > th
    fprintf('=> Watermark DETECTED (rho > th)\n');
else
    fprintf('=> No watermark (rho <= th)\n');
end

%% ---------- util: quick PSNR ----------
function p = psnr(a,b)
    a=double(a); b=double(b);
    mse = mean((a(:)-b(:)).^2);
    p = 10*log10(255^2/(mse+eps));
end

参考代码 分数阶傅里叶变换数字水印的嵌入和检测的matlab程序 www.youwenfan.com/contentcsv/81576.html

3)如果你想要"logo图可见/可提取"的直观版本

FrFT域改系数 → 逆FrFT → 提取时再做一次FrFT → 把那块系数还原回logo

matlab 复制代码
%% Variant: embed scaled logo patch into FrFT amplitudes (easier to visualize)
I = imread('lena.tif');
if ndims(I)==3,I=rgb2gray(I);end
I=double(I); [M,N]=size(I);

% 1D-row/col FrFT as above
a=0.91;
F=zeros(M,N);
for m=1:M, F(m,:)=frft(I(m,:).',a).'; end
for n=1:N, F(:,n)=frft(F(:,n),a); end

% small logo
W = imread('watermark_logo.png');
W = im2double(rgb2gray(W));
W = imresize(W,[64 64]);
w = W(:);

% choose a rectangle region in FrFT domain to store w
cx=floor(N/2); cy=floor(M/2);
x1=cx-32; x2=cx+31; y1=cy-32; y2=cy+32;
region = F(y1:y2,x1:x2);

% embed (amplitude scaling)
strength = 0.12 * mean(abs(F(:)));
Fw = F;
Fw(y1:y2,x1:x2) = region + strength * (2*w-1);   % map [0,1]->[-1,1]

% invert
Iw=zeros(M,N);
for n=1:N, Iw(:,n)=frft(Fw(:,n),-a); end
for m=1:M, Iw(m,:)=frft(Iw(m,:).',-a).'; end
Iw=real(max(0,min(255,Iw)));

% ---- extraction (needs same a & region) ----
Fa=Fw; % or recompute FrFT of attacked image
Wrec = ( Fa(y1:y2,x1:x2) - region ) / strength;
Wrec = (Wrec+1)/2;
Wrec = reshape(Wrec,64,64);
figure; imshow(Wrec,[]); title('Extracted watermark (non-blind ref needed)');

这种"patch替换/叠加"的好处是你能看到logo形状 ;缺点是通常不盲(提取时依赖你把那个区域正确对齐/已知),而且对裁剪、几何变形更敏感。

扩频版(Section 2)在工程上更稳。