DIP作业一:用MATLAB实现LSB算法

数字图像处理课内作业,算法很简单,后面我设计了一个GUI把它打包成了APP。虽然之前也使用过matlab的这个功能,但是忘记了,所以花了一天的时间才把它弄完整,因此为了避免日后再去到处找资料,在这儿写篇博客记录下来。

一、LSB算法的介绍

这里简单说一下LSB算法,包括它的基本原理和一些改进。

1、基本原理

对空域的LSB做替换,用来替换LSB的序列就是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。由于水印信息嵌入的位置是LSB,为了满足水印的不可见性,允许嵌入的水印强度不可能太高。然而针对空域的各种处理,如游程编码前的预处理,会对不显著分量进行一定的压缩,所以LSB算法对这些操作很敏感。因此LSB算法最初是用于脆弱性水印的。(这是百度的说明,看的都难搜)
从本质上说,LSB算法就是将秘密信息嵌入到载体图像像素值的最低有效位,也称最不显著位,改变这一位置对载体图像的品质影响最小。 其实不一定是图像像素的最低有效位,次低,次次低也可以,只要不影响图片的显示效果就行。

2、基本步骤

1.将得到的隐藏有秘密信息的十进制像素值转换为二进制数据; 2.用二进制秘密信息中的每一比特信息替换与之相对应的载体数据的最低有效位; 3.将得到的含秘密信息的二进制数据转换为十进制像素值,从而获得含秘密信息的图像。

3、一些改进的LSB

因为LSB是一种很普通的算法,网上大部分论文写的也大体上有两种改进方式,要么打乱它嵌入图像时的顺序,也就是设计一个秘钥,秘钥的设计是重点;要么把数据嵌入到图像每一个像素的不同位置上(当然只是针对不显著位)。我设计时用了第一种,也就是设计一个秘钥然后根据秘钥来嵌入和读取图片,防止破解。

4、缺陷

LSB虽说能够隐藏信息,但是由于图像的大小限制,所以没办法嵌入太多的信息,而且如果图片和所需嵌入信息量过大,那么运算起来也是十分耗时的。相对而言,其他的加密算法要比这个效率高,但是其优点就是容易实现,如果设计一个比较难以破解的秘钥,其安全性也较好。

二、MATLAB基本程序

我嵌入的是一段语音信号,图片随便从网上找了几张,先粗略的把程序写完跑了一遍,发现效果还行就是耗时较长,后面又设计了一个GUI,还是用了一些时间的。这里用第一版,没有加GUI的程序说明

1、嵌入信息

1)文件读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
clc,clear
path1 = 'G:\work\LSB\source\timg.jpg';
path2 = 'G:\work\LSB\source\sour.mp3';
keypath = 'G:\work\LSB\source\key.mat';
load(keypath);%读取秘钥
message = fopen(path2,'r');%需要隐藏的文件
[msgg,msg_len]=fread(message,'ubit1');%按位以二进制形式读取音频内容与长度
image = imread(path1);%图片文件
image1 = image(:,:,1);
image2 = image(:,:,2);
image3 = image(:,:,3);
[m,n] = size(image1);
image_r = [image1;image2;image3];

读取秘钥,音频文件读取为二进制,图像先读取为uint8数据类型,后面替换的时候再做转换

2)信息处理与隐藏

1
2
3
4
5
6
7
msg_bin = num2str(msgg);%转换为字符
msg = strjoin(cellstr(msg_bin)','');%字符拼接
for i = 1:msg_len
hog = dec2bin(image_r(key(i)),8);%图像每一位转换成8位二进制
hog(8) = msg(i);
image_r(key(i)) = bin2dec(hog);%替换
end

因为图像像素转换成二进制时格式是字符串形式,所以为了方便替换,音频信息也转换成了字符串形式。这里用到的四个函数:第一个num2str,功能是把读取为矩阵的二进制音频文件转换成元胞形式;第二个是strjoin,把元胞矩阵合并成一个字符数组;第三个是dec2bin,十进制转换成二进制;第四个dec2bin,二进制转十进制

3)信息合并与保存

1
2
3
4
image_result(:,:,1) = image_r(1:m,1:n);
image_result(:,:,2) = image_r(m+1:m*2,1:n);
image_result(:,:,3) = image_r(m*2+1:m*3,1:n);
imwrite(image_result, 'G:\work\LSB\source\result3.png');

这里没什么好说的,唯一注意一点就是图像保存为jpg格式时会被自动压缩,再次读取时数据会有很大的改变。写程序时在这里卡了半天,最后才发现是这个原因。

2、读取信息

这儿读取信息就是根据上面的秘钥来一位一位的读就成,直接把代码贴这儿:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
clc,clear
%% 文件读取
keypath = 'G:\work\LSB\source\key.mat';
load(keypath);%读取秘钥
image = imread('G:\work\LSB\source\result3.png');
image1 = image(:,:,1);
image2 = image(:,:,2);
image3 = image(:,:,3);
image_r = [image1;image2;image3];
n = length(key);
%% 信息提取
for i = 1:n
hog = dec2bin(image_r(key(i)),8);
msg_r(i,1) = str2num(hog(8));
end
%% 信息保存
a = fopen('G:\work\LSB\source\result2.mp3','w');
fwrite(a,msg_r,'ubit1');
%% 播放音乐
[input_file, Fs] = audioread('G:\work\LSB\source\result2.mp3');
x = input_file(:, 1);
sound(x, Fs); %%播放
clear sound %%关闭
% audiowrite('target_file.WAV', input_file, Fs);

这里要说的是audioread函数和sound函数,就是一自动播放音乐的函数,能够把保存后的音频文件以double类型读取,然后再由sound函数播放,在语音信号处理上用这个函数。

3、GUI界面设计与程序

GUI设置了三个面板,一个菜单,能够对不同的图片嵌入不同的音频信息,读取并播放隐藏在图片中的音频。先放几张图:

1

2

3

然后是几种组件的程序设计,我用到的组件有:面板、按钮、弹出式列表框、坐标框和菜单;

1)菜单的设计

设计菜单的作用是让使用者能够在同一个GUI下实现不同功能的自由切换,也就是三个面板之前的不同切换。首先在gui_OpeningFcn函数中设置初始化状态:

1
2
3
4
5
6
set(handles.uipanel1,'parent',gcf);
set(handles.uipanel3,'parent',gcf);
set(handles.uipanel5,'parent',gcf);
set(handles.uipanel5,'visible','on');
set(handles.uipanel1,'visible','off');
set(handles.uipanel3,'visible','off');

这里前面三句的意思是让三个面板呈现并列的关系,关于这一点,有一个值得注意的地方。在设计GUI时,不要把一个面板拖动到另一个面板里面,或者是让它们的上边重合。这样的话,面积小的面板会成为另一个面积较大面板的子类,再显示时就会出现bug,只有通过前三句程序才会让它们回到并列关系,但是这样面板在显示时会出现位置的偏移,所以在使用多面板时,首先要在gui_OpeningFcn先写上上面三条程序,然后调整它们的位置不要用鼠标拖动,利用Ctrl+方向键 来调整它们的位置。后面三句就是设置面板的显示与隐藏。

菜单的整体设计如下:

3

程序作用是调整面板的显示与隐藏,主要是以下两个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function writeInfo_Callback(hObject, eventdata, handles)
% hObject handle to writeInfo (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
set(handles.uipanel1,'visible','on');
set(handles.uipanel3,'visible','off');
set(handles.uipanel5,'visible','off');
function readInfo_Callback(hObject, eventdata, handles)
% hObject handle to readInfo (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
set(handles.uipanel1,'visible','off');
set(handles.uipanel3,'visible','on');
set(handles.uipanel5,'visible','off');

2)写入文件

首先是选择图片文件,然后再选择音频文件,点击加密后,会出现result.png的文件,这里可以选择不同的图片文件和不同的音频文件生成不同的result.png文件。因为加密过程比较缓慢,所以之前加了一个进度条来显示,结果发现加了进度条之后更加慢了,因此就没有加载。先展示一下效果:

3

代码方面,第一段是图片文件选择,加上把指定文件夹下的.mp3文件全部显示到弹出式列表框下面,具体写在gui_OpeningFcn函数中:

1
2
3
4
fileFolder=fullfile('G:\work\LSB\source\init\');
dirOutput=dir(fullfile(fileFolder,'*.mp3'));
fileNames={dirOutput.name};
set(handles.popupmenu1,'string',fileNames);

第二段是图片的显示,显示原始图片,具体写在pushbutton1_Callback函数中:

1
2
3
4
5
6
7
8
9
10
axes(handles.axes3);
global path1
[filename, pathname] = uigetfile('*.jpg', '读取图片文件'); %选择图片文件
if isequal(filename,0) %判断是否选择
msgbox('没有选择任何图片');
else
path1=fullfile(pathname, filename); %获得图片路径
image=imread(path1); %将图片读入矩阵
imshow(image);
end

第三段是选择加密文件,popupmenu1_Callback函数:

1
2
3
4
5
6
7
8
global path2
global selected_string
global fileFolder
val = get(handles.popupmenu1, 'value');
string_list = get(handles.popupmenu1,'string');
selected_string=string_list{val};
path2 = [fileFolder,selected_string];
guidata(hObject, handles);

最后展示一下读取文件的界面,文件读取完成会自动播放歌曲:

3

剩下是加密函数和解密函数,和前面说的一样,所以这儿就不贴代码了,全部代码已经上传到github。