基于 DWT 和 DCT 域的盲水印算法
程序简介
基于 DWT 和 DCT 域的盲水印算法。加水印和提水印阶段涉及小波变换(DWT),离散余弦变换(DCT),Zig-Zag 扫描。攻击阶段涉及亮度,对比度,直方图均衡化,椒盐噪声,剪切。
原作有四个阶段,实验图像 512512256,水印 64642。
阶段一、水印置乱。使用 Arnold 变换。
阶段二、水印嵌入。先小波变换(DWT);对 LL 子图(256256)以 88 的大小进行分块(共 3232 块);计算方差;对 3232 个块进行 22 分组(共 1616 组);记录每组最大方差的块(共 1616 块);对 1616 个块进行离散余弦变换(DCT);对 1616 个块进行 Zig-Zag 扫描,从每块 88 的 DCT 系数中提取中高频段共 16 个系数(共 161616 个系数,对应水印 64*64 个系数);水印嵌入(改系数);离散余弦反变换(IDCT);小波反变换(IDWT)。
阶段三、水印攻击。
阶段四、水印提取。执行嵌入步骤到提取中高频系数;水印提取(根据系数判断)。
本程序未翻译阶段一。Arnold 变换相比水印算法,更接近图像加密算法,将放到图像加密的部分中。
阶段二中,DWT 后,也可以不只用 LL 子图,用 HL,LH,HH 子图都可以尝试一下嵌入水印,不同域对不同的噪声敏感度肯定不一致。程序上,需要同步改 DWT 后,IDWT 前,中间所有函数的操作范围。
阶段二中,DCT 后生成的图像像素值和 DCT 系数不一致,那只是方便展示,得知 DCT 阶段已结束的,不能保存图像,在读取进行反变换。因为 DCT 系数不是整数,也会有负数,也可能上千。
阶段二中,Zig-Zag 扫描的本质是一种系数的位置顺序,我们的目的是从 8*8 的系数中选 16 个进行修改,而本实验的 DCT 系数的性质是左上低频,右下高频,故我们标记远离左上的 16 个点即可,至于这些点是连续的还是跳跃的,不是影响结果的关键。关键是嵌入和提取时的 16 个点位置是一致的即可。
阶段二中,原作水印嵌入时,生成了一组随机序列来辅助嵌入,并充当密钥,使用了一个水印强度系数来适应不同的载体图像。由于原作使用二值水印图像,我根据这个性质修改了这一部分,使用加密白/黑点赋值参数,这两个参数直接嵌入,并使用他们来适应不同的载体图像,灵活度更高,不需要传递密钥(但使图像不同区域的篡改程度变地不一致了)。
阶段四中,原作水印提取时,是用密钥算相关系数,对比平均值进行提取。我根据嵌入的修改,设置了一个解密阈值参数来提取,这样就能调整它来适应不同的载体图像,甚至根据攻击方式来调整它以达到提高鲁棒性的目的(这个效果聊胜于无)。
由于程序步骤较多,不适合太自由,就集成为加密阶段,攻击阶段,解密阶段了。这样可以不进行攻击,或进行多次组合攻击。载体和水印可以使用同一张图像。
在完整进程函数 completeprocessing() 中可以设置四个参数来适应不同载体图像,没有单独做按钮了。
本程序也没有再用代码进行水印图像质量评价分析了,采用肉眼进行分析。
程序运行展示
完整源代码
////////////////////////////////////////
// 程序:基于 DWT 和 DCT 域的盲水印算法
// 作者:Gary
// 编译环境:Visual C++ 2010,EasyX_20211109
// 编写日期:2022-5-11
#include <graphics.h>
#include <string>
#include <ShlObj.h>
#include <math.h>
#include <time.h>
static HWND hOut; // 画布
static double pi; // 圆周率
// 图像处理
double img_m[513][513]; // 大图图像
double img_b[513][513]; // 大图图像
double img_k[64][64]; // 小图水印
// 定义一个结构体,按钮
struct Node1
{
int posx1, posy1, posx2, posy2; // 坐标
LPCTSTR text; // 文字
};
// 定义一个类
class Gary
{
public:
void carry(); // 主进程
void initialization(); // 初始化
void move(); // 窗口主视角
void draw(); // 绘制函数
void completeprocessing(); // 完整过程
int variance_block(); // 最大公差块寻找函数
int DCT(IMAGE* img_input); // 离散余弦变换 512*512
int watermark_reading(IMAGE* img_input); // 水印读取函数 64*64
int encryption_image(); // 加密函数 512*512 + 64*64
IMAGE* DWT(IMAGE* img_input); // 小波变换 512*512 -> 512*512
IMAGE* IDCT(IMAGE* img_input); // 离散余弦反变换 512*512 -> 512*512
IMAGE* IDWT(IMAGE* img_input); // 小波反变换 512*512 -> 512*512
IMAGE* watermark_attack(IMAGE* img_input, int num_attack_mode); // 攻击函数 512*512 -> 512*512
IMAGE* decrypt_image(); // 解密函数 512*512 -> 64*64
IMAGE* encryption_process(IMAGE* img_input_image, IMAGE* img_input_watermark); // 加密过程 512*512 + 64*64 -> 512*512
IMAGE* attack_process(IMAGE* img_input, int num_attack_mode); // 攻击过程 512*512 -> 512*512
IMAGE* decryption_process(IMAGE* img_input); // 解密过程 512*512 -> 64*64
int num_button; // 按钮数量
int exit_carry; // 进程控制
int exit_move; // 进程控制
int num_variance_block[256]; // 最大公差块坐标编号
int num_encryption_white; // 加密白点赋值参数
int num_encryption_black; // 加密黑点赋值参数
int num_encryption_threshold; // 加密阈值
int num_decrypt_threshold; // 解密阈值
IMAGE* img_output; // 输出图像
IMAGE* img_temporary; // 大图图像 512*512
IMAGE* img_watermark; // 水印图像 64*64
TCHAR address_load_image[MAX_PATH]; // 图像地址
TCHAR address_load_watermark[MAX_PATH]; // 水印地址
TCHAR address_save_image[MAX_PATH]; // 保存大图
TCHAR address_save_watermark[MAX_PATH]; // 保存水印
Node1 boxm[30]; // 按钮
};
// 完整进程函数
void Gary::completeprocessing()
{
// 加密白点赋值参数
num_encryption_white = -10;
// 加密黑点赋值参数
num_encryption_black = 10;
// 加密阈值
num_encryption_threshold = 128;
// 解密阈值
num_decrypt_threshold = 1;
// 加密过程
img_temporary = encryption_process(img_temporary, img_watermark);
// 攻击过程
img_temporary = attack_process(img_temporary, 0);
// 解密过程
img_watermark = decryption_process(img_temporary);
}
// 加密过程函数
IMAGE* Gary::encryption_process(IMAGE* img_input_image, IMAGE* img_input_watermark)
{
int i;
// 小波变换
img_temporary = DWT(img_input_image);
// 最大公差块寻找函数
i = variance_block();
// 离散余弦变换
i = DCT(img_temporary);
// 水印读取函数
i = watermark_reading(img_input_watermark);
// 加密函数
i = encryption_image();
// 离散余弦反变换
img_temporary = IDCT(img_temporary);
// 小波反变换
img_temporary = IDWT(img_temporary);
// 返回嵌入还原后的大图
return img_temporary;
}
// 解密过程函数
IMAGE* Gary::decryption_process(IMAGE* img_input)
{
int i;
// 小波变换
img_temporary = DWT(img_input);
// 最大公差块寻找函数
i = variance_block();
// 离散余弦变换
i = DCT(img_temporary);
// 解密函数
img_watermark = decrypt_image();
// 返回解密后的水印小图
return img_watermark;
}
// 攻击过程函数
IMAGE* Gary::attack_process(IMAGE* img_input, int num_attack_mode)
{
// 攻击函数
img_temporary = watermark_attack(img_input, num_attack_mode);
// 返回受攻击后的大图
return img_temporary;
}
// 攻击函数
IMAGE* Gary::watermark_attack(IMAGE* img_input, int num_attack_mode)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
int i, j, k;
// 存储
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
// 根据攻击模式参数进行攻击
switch(num_attack_mode)
{
// 图像变亮
case 0:
{
img_b[i][j] = 127.0 + 128.0 * img_b[i][j] / 255.0;
break;
}
// 图像变暗
case 1:
{
img_b[i][j] = 0.0 + 128.0 * img_b[i][j] / 255.0;
break;
}
// 降低对比度
case 2:
{
img_b[i][j] = ( img_b[i][j] <= 128 ? 78.0 + 50.0 * img_b[i][j] / 128.0 : 128.0 + 50.0 * ( img_b[i][j] - 128.0 ) / 128.0 );
break;
}
// 提高对比度
case 3:
{
img_b[i][j] = ( img_b[i][j] <= 128 ? 0.0 + 50.0 * img_b[i][j] / 128.0 : 205.0 + 50.0 * ( img_b[i][j] - 128.0 ) / 128.0 );
break;
}
// 椒盐噪声
case 4:
{
k = rand() % 50;
if(k == 0)
{
img_b[i][j] = 255;
}
else if(k == 25)
{
img_b[i][j] = 0;
}
}
default:break;
}
}
}
// 直方图均衡化
if(num_attack_mode == 5)
{
double g[256];
double g1[256];
// 初始化灰度值
for(i = 0; i < 256; i++)
{
g[i] = 0;
}
// 统计
for(i = 0; i < 512 * 512; i++)
{
g[int(img_b[i % 512][i / 512])]++;
}
// 累计概率
g1[0] = double(g[0]) / 512.0 / 512.0;
for(i = 1; i < 256; i++)
{
g1[i] = double(g[i]) / 512.0 / 512.0 + g1[i - 1];
}
// 均衡化
j = 0;
for(i = 0; i < 256; i++)
{
if(g1[i] >= double(j) / 256.0 && g1[i]<double(j + 1) / 256.0)
{
g1[i] = j;
}
else
{
i--;
j++;
}
}
// 重新赋值
for(i = 0; i < 512 * 512; i++)
{
img_b[i % 512][i / 512] = g1[int(img_b[i % 512][i / 512])];
}
}
// 剪切攻击-中间
else if(num_attack_mode == 6)
{
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
if(i > 128 && j > 128 && i < 384 && j < 384)
{
img_b[i][j] = 0;
}
}
}
}
// 剪切攻击-角
else if(num_attack_mode == 7)
{
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
if(i + j > 500)
{
img_b[i][j] = 0;
}
}
}
}
// 剪切攻击-对半
else if(num_attack_mode == 8)
{
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
if(j < 256)
{
img_b[i][j] = 0;
}
}
}
}
// 新建输出图像
img_output = new IMAGE(512, 512);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制受攻击后的图像
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
putpixel(i, j, RGB(img_b[i][j], img_b[i][j], img_b[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 解密函数
IMAGE* Gary::decrypt_image()
{
int i, j, k;
// zig_zag 扫描算法
int zig_zag[16];
// 选频段
zig_zag[0] = 55; zig_zag[1] = 62; zig_zag[2] = 61; zig_zag[3] = 54;
zig_zag[4] = 47; zig_zag[5] = 39; zig_zag[6] = 46; zig_zag[7] = 53;
zig_zag[8] = 60; zig_zag[9] = 59; zig_zag[10] = 52; zig_zag[11] = 45;
zig_zag[12] = 38; zig_zag[13] = 31; zig_zag[14] = 23; zig_zag[15] = 58;
// 解密,通过 DCT 后中低频段的值判断水印点的值
// 256 个最大方差块
for(k = 0; k < 256; k++)
{
// 在每块中的中高频段选 16 个点
for(i = 0; i < 16; i++)
{
// 根据 img_m 的值附 img_k 的值
// 这个阈值应当根据需要进行更改
if(img_m[num_variance_block[k] % 32 * 8 + zig_zag[i] % 8][num_variance_block[k] / 32 * 8 + zig_zag[i] / 8] > num_decrypt_threshold)
{
img_k[k / 4][k % 4 * 16 + i] = 250;
}
else
{
img_k[k / 4][k % 4 * 16 + i] = 0;
}
}
}
// 清空绘图区
setfillcolor(WHITE);
fillrectangle(0, 0, 550, 512);
// 水印绘制
// 新建输出图像
img_output = new IMAGE(64, 64);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制提取出的水印
for(i = 0; i < 64; i++)
{
for(j = 0; j < 64; j++)
{
putpixel(i, j, RGB(img_k[i][j], img_k[i][j], img_k[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(275 - 32, 256 - 32, img_output);
// 返回输出图像
return img_output;
}
// 离散余弦反变换
IMAGE* Gary::IDCT(IMAGE* img_input)
{
// 处理
int i, j;
int flag;
int posx, posy;
int m, n;
// 根据 DCT 存储的值,即根据 img_m 的值进行反变换
for(posx = 0; posx < 512 / 2; posx += 8)
{
for(posy = 0; posy < 512 / 2; posy += 8)
{
// 仅对组内最大方差块进行 IDCT 变换
// 判断当前块是否是组内最大方差块
for(flag = 0; flag < 512 / 2; flag++)
{
if(num_variance_block[flag] % 32 * 8 == posx && num_variance_block[flag] / 32 * 8 == posy)
{
break;
}
}
// 是
if(flag < 256)
{
for(m = posx; m < posx + 8; m++)
{
for(n = posy; n < posy + 8; n++)
{
// 初始化像素值
img_b[m][n] = 0;
// 反变换
for(i = 0; i < 8; i++)
{
for(j = 0; j < 8; j++)
{
img_b[m][n] += img_m[posx + i][posy + j] * cos(double(( 2 * ( m - posx ) + 1 ) * i) * pi / 16.0) * cos(double(( 2 * ( n - posy ) + 1 ) * j) * pi / 16.0) / 8.0 * ( i != 0 ? sqrt(2.0) : 1.0 ) * ( j != 0 ? sqrt(2.0) : 1.0 );
}
}
}
}
}
}
}
// 新建输出图像
img_output = new IMAGE(512, 512);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 覆盖一层大图
putimage(0, 0, img_input);
// 绘制 LL 区
for(i = 0; i < 256; i++)
{
for(j = 0; j < 256; j++)
{
putpixel(i, j, RGB(img_b[i][j], img_b[i][j], img_b[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 小波反变换
IMAGE* Gary::IDWT(IMAGE* img_input)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
int i, j, k;
// 存储
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 变换
k = 512;
// 纵向延展
for(i = 0; i < k; i++)
{
for(j = 0; j < k / 2; j++)
{
img_m[i][j * 2] = img_b[i][j] + img_b[i][j + k / 2 + 1];
img_m[i][j * 2 + 1] = img_b[i][j] - img_b[i][j + k / 2 + 1];
}
for(j = 0; j < k; j++)
{
img_b[i][j] = img_m[i][j];
}
}
// 横向延展
for(j = 0; j < k; j++)
{
for(i = 0; i < k / 2; i++)
{
img_m[i * 2][j] = img_b[i][j] + img_b[i + k / 2 + 1][j];
img_m[i * 2 + 1][j] = img_b[i][j] - img_b[i + k / 2 + 1][j];
}
for(i = 0; i < k; i++)
{
img_b[i][j] = img_m[i][j];
}
}
// 新建输出图像
img_output = new IMAGE(512, 512);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
putpixel(i, j, RGB(img_b[i][j], img_b[i][j], img_b[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 加密函数
int Gary::encryption_image()
{
// zig_zag 扫描算法
int zig_zag[16];
// 选频段
zig_zag[0] = 55; zig_zag[1] = 62; zig_zag[2] = 61; zig_zag[3] = 54;
zig_zag[4] = 47; zig_zag[5] = 39; zig_zag[6] = 46; zig_zag[7] = 53;
zig_zag[8] = 60; zig_zag[9] = 59; zig_zag[10] = 52; zig_zag[11] = 45;
zig_zag[12] = 38; zig_zag[13] = 31; zig_zag[14] = 23; zig_zag[15] = 58;
// 该函数根据水印值改变 DCT 的值,即根据 img_k 改变 img_m
int i, k;
// 256 个最大方差块
for(k = 0; k < 256; k++)
{
// 在每块中的中高频段选 16 个点
for(i = 0; i < 16; i++)
{
// 根据 img_k 改变 img_m
if(img_k[k / 4][k % 4 * 16 + i] > num_encryption_threshold)
{
// 白点改为 一个值
img_m[num_variance_block[k] % 32 * 8 + zig_zag[i] % 8][num_variance_block[k] / 32 * 8 + zig_zag[i] / 8] = num_encryption_white;
}
else
{
// 黑点改为 一个值
img_m[num_variance_block[k] % 32 * 8 + zig_zag[i] % 8][num_variance_block[k] / 32 * 8 + zig_zag[i] / 8] = num_encryption_black;
}
}
}
// 改这个值有一个基本原则,即反变换后尽量所有像素值在 0 ~ 255 的正常范围,不需要额外归一化了
// 针对不同图像应适合不同值
return 0;
}
// 水印读取函数
int Gary::watermark_reading(IMAGE* img_input)
{
int i, j;
// 加载输入图像
putimage(0, 0, img_input);
// 存储
for(i = 0; i < 64; i++)
{
for(j = 0; j < 64; j++)
{
img_k[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 该函数负责记录水印信息,不输出图像
// 结果存在于 img_k[][] 之中
return 0;
}
// 离散余弦变换
int Gary::DCT(IMAGE* img_input)
{
int i, j;
int posx, posy;
int m, n;
int flag;
// 加载输入图像
putimage(0, 0, img_input);
// 存储
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 变换,以块为单位进行变换
for(posx = 0; posx < 512 / 2; posx += 8)
{
for(posy = 0; posy < 512 / 2; posy += 8)
{
// 仅对组内最大方差块进行 DCT 变换
// 判断当前块是否是组内最大方差块
for(flag = 0; flag < 256; flag++)
{
if(num_variance_block[flag] % 32 * 8 == posx && num_variance_block[flag] / 32 * 8 == posy)
{
break;
}
}
// 是
if(flag < 256)
{
// 二维 DCT 变换,形式上类似于二维 FFT
for(m = posx; m < posx + 8; m++)
{
for(n = posy; n < posy + 8; n++)
{
// 初始化
img_m[m][n] = 0;
// 公式变换
for(i = 0; i < 8; i++)
{
for(j = 0; j < 8; j++)
{
img_m[m][n] += img_b[posx + i][posy + j] * cos(double(( 2 * i + 1 ) * ( m - posx )) * pi / 16.0) * cos(double(( 2 * j + 1 ) * ( n - posy )) * pi / 16.0) / 8.0 * ( ( m - posx ) != 0 ? sqrt(2.0) : 1.0 ) * ( ( n - posy ) != 0 ? sqrt(2.0) : 1.0 );
}
}
// 绘制,需要强调的是,DWT 的结果不是在 0 ~ 255 中的,两端都超过
// 该次绘制只是视觉效果,无法输出图像
putpixel(m, n, RGB(img_m[m][n], img_m[m][n], img_m[m][n]));
}
}
}
}
}
// 该函数负责记录 DCT 的结果
// 结果存在于 img_m[][] 之中
return 0;
}
// 最大公差块寻找函数
int Gary::variance_block()
{
// 对 LL 部分 8*8 分块算方差,对 32*32 再分 2*2,取 4 个中最大值的块标记
int i, j;
double k;
int posx, posy;
// 记录方差的结构体
struct Node
{
double fc, avg;
};
Node box[32][32];
// 存储
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 分块算方差
for(posx = 0; posx < 512 / 2; posx += 8)
{
for(posy = 0; posy < 512 / 2; posy += 8)
{
// 初始化方差,均值
box[posx / 8][posy / 8].fc = 0;
box[posx / 8][posy / 8].avg = 0;
// 算均值
for(i = posx; i < posx + 8; i++)
{
for(j = posy; j < posy + 8; j++)
{
box[posx / 8][posy / 8].avg += img_b[i][j];
}
}
box[posx / 8][posy / 8].avg /= 64.0;
// 算方差
for(i = posx; i < posx + 8; i++)
{
for(j = posy; j < posy + 8; j++)
{
box[posx / 8][posy / 8].fc += pow(img_b[i][j] - box[posx / 8][posy / 8].avg, 2);
}
}
box[posx / 8][posy / 8].fc /= 64.0;
}
}
// 再分组,四选一
int flag = 0;
for(posx = 0; posx < 512 / 2; posx += 16)
{
for(posy = 0; posy < 512 / 2; posy += 16)
{
// 找最大方差块
k = 0;
for(i = posx / 8; i < posx / 8 + 2; i++)
{
for(j = posy / 8; j < posy / 8 + 2; j++)
{
// 记录
if(k < box[i][j].fc)
{
k = box[i][j].fc;
num_variance_block[flag] = i + j * 32;
}
}
}
// 绘制,显示组内最大方差块,不输出图像
// 可见方差大的块集中在图像梯度变化大的边缘,改变这些块的频域值肉眼不易捕捉,符合 HVS 的要求
setfillcolor(RED);
setlinecolor(BLACK);
line(posx + 16, posy, posx + 16, posy + 16);
line(posx, posy + 16, posx + 16, posy + 16);
fillcircle(num_variance_block[flag] % 32 * 8 + 4, num_variance_block[flag] / 32 * 8 + 4, 4);
// 标记数加一,共 16*16 个块,块内有 8*8 个像素
flag++;
}
}
// 延迟显示时间
Sleep(1000);
// 该函数负责记录块编号,不输出图像
// 结果存在于 num_variance_block[] 之中
return 0;
}
// 小波变换
IMAGE* Gary::DWT(IMAGE* img_input)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
int i, j, k;
// 存储
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
k = 512;
// 横向折叠
for(j = 0; j < k; j++)
{
for(i = 0; i < k / 2; i++)
{
img_m[i][j] = ( img_b[i * 2][j] + img_b[i * 2 + 1][j] ) / 2.0;
}
for(i = k / 2; i < k; i++)
{
img_m[i][j] = ( img_b[( i - ( k / 2 + 1 ) ) * 2][j] - img_b[( i - ( k / 2 + 1 ) ) * 2 + 1][j] ) / 2.0;
}
for(i = 0; i < k; i++)
{
img_b[i][j] = img_m[i][j];
}
}
// 纵向折叠
for(i = 0; i < k; i++)
{
for(j = 0; j < k / 2; j++)
{
img_m[i][j] = ( img_b[i][j * 2] + img_b[i][j * 2 + 1] ) / 2.0;
}
for(j = k / 2; j < k; j++)
{
img_m[i][j] = ( img_b[i][( j - ( k / 2 + 1 ) ) * 2] - img_b[i][( j - ( k / 2 + 1 ) ) * 2 + 1] ) / 2.0;
}
for(j = 0; j < k; j++)
{
img_b[i][j] = img_m[i][j];
}
}
// 新建输出图像
img_output = new IMAGE(512, 512);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制
for(i = 0; i < 512; i++)
{
for(j = 0; j < 512; j++)
{
putpixel(i, j, RGB(img_b[i][j], img_b[i][j], img_b[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 绘制函数,主要绘制按钮
void Gary::draw()
{
int i, j;
// 主界面
setlinecolor(BLACK);
setfillcolor(WHITE);
setlinestyle(PS_SOLID, 1);
fillrectangle(550, 0, 800, 512);
// 根据按钮数量绘制
settextcolor(BLACK);
for(i = 0; i < num_button; i++)
{
// 边框
fillrectangle(boxm[i].posx1, boxm[i].posy1, boxm[i].posx2, boxm[i].posy2);
// 文字
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + 10, boxm[i].text);
}
// 设置参数
setbkcolor(WHITE);
settextcolor(BLACK);
setlinecolor(BLACK);
// 变量绘制
j = 30;
// 图像地址
i = 4;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_load_image);
// 水印地址
i = 5;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_load_watermark);
// 保存大图
i = 6;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_save_image);
// 保存水印
i = 7;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_save_watermark);
}
// 初始化函数
void Gary::initialization()
{
int i;
// 参数初始化
pi = acos(-1.0);
num_encryption_white = 10; // 加密白点赋值参数
num_encryption_black = -10; // 加密黑点赋值参数
num_encryption_threshold = 128; // 加密阈值
num_decrypt_threshold = 0; // 解密阈值
// 随机种子初始化
srand((unsigned)time(NULL));
// 地址初始化
TCHAR s1[30] = _T("t1.png");
for(i = 0; i < 30; i++) { address_load_image[i] = s1[i]; }
TCHAR s2[30] = _T("sy.png");
for(i = 0; i < 30; i++) { address_load_watermark[i] = s2[i]; }
TCHAR s3[30] = _T("image.png");
for(i = 0; i < 30; i++) { address_save_image[i] = s3[i]; }
TCHAR s4[30] = _T("sy_new.png");
for(i = 0; i < 30; i++) { address_save_watermark[i] = s4[i]; }
// 按钮的初始化
num_button = 10;
// 坐标
for(i = 0; i < num_button; i++)
{
boxm[i].posx1 = 560 + 120 * ( i % 2 );
boxm[i].posy1 = 15 + 100 * ( i / 2 );
boxm[i].posx2 = 670 + 120 * ( i % 2 );
boxm[i].posy2 = 85 + 100 * ( i / 2 );
}
// 内容
boxm[0].text = _T("加密过程"); boxm[1].text = _T("攻击过程");
boxm[2].text = _T("解密过程"); boxm[3].text = _T("完整过程");
boxm[4].text = _T("加载载体地址"); boxm[5].text = _T("加载水印地址");
boxm[6].text = _T("保存载体地址"); boxm[7].text = _T("保存水印地址");
boxm[8].text = _T("重置"); boxm[9].text = _T("退出");
// 第一次加载
img_temporary = new IMAGE(512, 512);
img_watermark = new IMAGE(64, 64);
// 第一次加载
loadimage(img_temporary, address_load_image, 512, 512);
loadimage(img_watermark, address_load_watermark, 64, 64);
// 图片显示
putimage(0, 0, img_temporary);
// 第一次绘制
draw();
}
// 主进程函数
void Gary::carry()
{
// 窗口定义
hOut = initgraph(801, 513);
SetWindowText(hOut, _T("基于 DWT 和 DCT 域的盲水印算法"));
// 背景绘制
setbkcolor(WHITE);
cleardevice();
// 进程控制
exit_carry = 0;
while(exit_carry == 0)
{
initialization();
move();
}
closegraph();
}
// 窗口主视角函数,获取用户操作
void Gary::move()
{
TCHAR ss[15];
// 鼠标定义
ExMessage m;
int i;
exit_move = 0;
while(exit_move == 0)
{
// 鼠标信息
if(peekmessage(&m, EM_MOUSE | EM_KEY))
{
// 左键单击判断
if(m.message == WM_LBUTTONDOWN)
{
// 判断是否点击了有效按钮
for(i = 0; i < num_button; i++)
{
if(m.x > boxm[i].posx1 && m.y > boxm[i].posy1 && m.x < boxm[i].posx2 && m.y < boxm[i].posy2)
{
break;
}
}
// 点击矩形按钮
switch(i)
{
// 加密过程
case 0:
{
img_temporary = encryption_process(img_temporary, img_watermark);
break;
}
// 攻击过程
case 1:
{
// 输入
InputBox(ss, 10, _T("输入攻击模式 0.变亮 1. 变暗 2.对比度变低 3.对比度变高 4.椒盐噪声 5.直方图均衡化 6 ~ 8.剪切"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 0 && i <= 8)
{
img_temporary = attack_process(img_temporary, i);
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 解密过程
case 2:
{
img_watermark = decryption_process(img_temporary);
break;
}
// 完整过程
case 3:
{
completeprocessing();
break;
}
// 加载图片地址
case 4:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
// 打开文件对话框
OPENFILENAME ofn;
// 保存文件完整路径
TCHAR szFileName[MAX_PATH] = { 0 };
// 设置过滤条件
TCHAR szFilter[] = TEXT("PNG Files (*.png)\0*.png\0") \
TEXT("All Files (*.*)\0*.*\0\0");
ZeroMemory(&ofn, sizeof(ofn));
// 保存文件完整路径
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
// 保存文件名
ofn.lpstrFileTitle = address_load_image;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrFilter = szFilter;
// 默认扩展名
ofn.lpstrDefExt = _T("png");
ofn.lpstrTitle = NULL;
ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ofn.lStructSize = sizeof(OPENFILENAME);
// 拥有该对话框的窗口句柄
ofn.hwndOwner = hOut;
GetOpenFileName(&ofn);
}
// 重置,初始化
img_temporary = new IMAGE(512, 512);
img_watermark = new IMAGE(64, 64);
// 加载
loadimage(img_temporary, address_load_image, 512, 512);
loadimage(img_watermark, address_load_watermark, 64, 64);
// 图片显示
putimage(0, 0, img_temporary);
break;
}
// 加载水印地址
case 5:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
// 打开文件对话框
OPENFILENAME ofn;
// 保存文件完整路径
TCHAR szFileName[MAX_PATH] = { 0 };
// 设置过滤条件
TCHAR szFilter[] = TEXT("PNG Files (*.png)\0*.png\0") \
TEXT("All Files (*.*)\0*.*\0\0");
ZeroMemory(&ofn, sizeof(ofn));
// 保存文件完整路径
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
// 保存文件名
ofn.lpstrFileTitle = address_load_watermark;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrFilter = szFilter;
// 默认扩展名
ofn.lpstrDefExt = _T("png");
ofn.lpstrTitle = NULL;
ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ofn.lStructSize = sizeof(OPENFILENAME);
// 拥有该对话框的窗口句柄
ofn.hwndOwner = hOut;
GetOpenFileName(&ofn);
}
img_watermark = new IMAGE(64, 64);
// 加载
loadimage(img_watermark, address_load_watermark, 64, 64);
break;
}
// 保存图片
case 6:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
InputBox(address_save_image, 10, _T("输入图片相对位置及名称"));
}
saveimage(address_save_image, img_temporary);
break;
}
// 保存图片
case 7:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
InputBox(address_save_watermark, 10, _T("输入图片相对位置及名称"));
}
saveimage(address_save_watermark, img_watermark);
break;
}
// 重置
case 8:
{
exit_move = 1;
break;
}
// 退出
case 9:
{
exit_move = 1;
exit_carry = 1;
break;
}
default:break;
}
draw();
}
}
}
}
// 主函数
int main(void)
{
Gary G;
G.carry();
return 0;
}
添加评论
取消回复