方案:
- 核心 :快速 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)在工程上更稳。