MATLAB 图像处理大作业

news2024/11/17 7:31:36

1、基础知识

利用 MATLAB 提供的 Image file/IO 函数完成以下处理:

(a)以测试图像中心为圆心,图像长宽中较小值一半为半径画一个红颜色的圆;

(b)将测试图像涂成国际象棋状的‘黑白格’样子;

用一种看图软件浏览上述两个图,看是否达到目标。

第一题通过 rectangle 函数在图像上画圆,设置‘Curvature’为 1,即对应为圆;

load('hall.mat');
temp1 = hall_color;

imshow(temp1);
hold on;
[h,w,d] = size(temp1);              
r = min(h,w)/2;                 %取半径
rectangle('Position',[w/2-r,h/2-r,2*r,2*r],'Curvature',1,'LineWidth',1,'EdgeColor','r');
%画圆
saveas(gca,'temp1.png');

效果如图:

将图片涂成黑白棋盘形状,可以通过将图片分块,根据不同块的坐标决定是否进行涂黑操作。代码与效果如下:

block = 10;                 %单个格子的长度
for i = 0:floor(h/block)
    for j = 0:floor(w/block)
        if(mod(i+j,2)==1)
            i_end = (i+1)*block;
            j_end = (j+1)*block;
            if(h<(i+1)*block)
                i_end = h;
            end
            if(w<(j+1)*block)
                j_end = w;
            end 
            temp2(block*i+1:i_end,block*j+1:j_end,1) = 0;          
            temp2(block*i+1:i_end,block*j+1:j_end,2) = 0;
            temp2(block*i+1:i_end,block*j+1:j_end,3) = 0;
            %对三个通道进行涂黑
        end
    end
end
imwrite(temp2,'黑白.png','PNG');%写入文件

2、图像压缩编码


1)图像的预处理是将每个灰度值减去 128,这个步骤是否可以在变换域中进行?请选择一块进行验证

可以在变换域执行。由于对每个像素点减 128,相当于减去一个直流分量,也就相当于零频点强度减 128*x(x 未定)。

从数学上验证,设 A 为 N*N 的待处理矩阵,D 为 DCT 算子,O 为全 1 矩阵,则减去 128 后得到的 C 为:

从而,即将 C~0,0~ 减去 128*N 即可;

代码验证如下:

load('hall.mat');%载入数据A=double(hall_gray(1:8,1:8));%转换类型,便于处理D_1=0:7;D_1=D_1';D_2=1:2:15;D=D_1*D_2;D=cos(D*pi/2/8);D(1,:)=D(1,:)/sqrt(2);DD=D/2;%生成8*8的DCT算子C=D*(A-128*ones(8,8))*D';%所有元素减去128后的DCT变换C2=D*A*D';C2(1,1)=C2(1,1)-128*8;%直接在变换域做处理p=my_equal(C2,C);%判断是否相等all(p)

结果显示 C 与 C2 相等,得证。

2)

请编程实现二维 dct,并与 MATLAB 自带的库函数 dct2 比较是否一致

由指导书中知识,只需构造 DCT 算子 D,随后对矩阵 A 进行运算,变换域矩阵 C=DAD^T^。若 A 不为方阵,由构造原理,先对 A 的每列进行 DCT 变换,在对变换后的矩阵的每行进行 DCT 变换,即

$$ C=D_{M*M}A_{M*N}D_{N*N}^T; $$

故而在代码中,通过自定义的 DCT_operator 函数可以方便地生成 N 维 DCT 算子,随后进行如上计算即可。

代码如下:

function B = my_dct2(A)
%对A做dct变换[M,N]=size(A);D1=DCT_operator(M);D2=DCT_operator(N);B=D1*A*D2';

DCT_operator 函数定义如下:

function D = DCT_operator(N)
%返回DCT算子D[r,c]=size(N);if(r~=1&&c~=1)error('Input must be a single number');endD=zeros(N);D1=0:N-1;D2=1:2:2*N-1;D=D1'*D2;D=cos(D*pi/2/N);D(1,:)=D(1,:)/sqrt(2);D=D*sqrt(2/N);

随意构造一个随机矩阵 A,计算 my_dct2 与 dct2 的结果,可以发现结果一致;

3)如果将 DCT 系数矩阵中右侧四列的系数全部置零,逆变换后的图像会发生什么变化?验证你的结论。如果左侧四列变为 0 呢?

选取 hall_gray 的 120 列、120 行作为测试图像,做 DCT 变换后得到系数矩阵 C。将 C 的右 4 列、左 4 列分别置为 0,随后做逆变换,显示图像结果如下:

可以看出,将 DCT 系数矩阵右 4 列变为 0 后,图像没有明显变换,但是将左 4 列变为 0,图像明显变黑。从中可以看出人眼对于低频分量的变化较为敏感。且将左 4 列变为 0,相当于去掉了直流分量及低频分量,整体图像变暗。当然,N 越大,则右边 4 列变为 0 的影响越小。

代码如下:

N=120;A=double(hall_gray(1:N,1:N))-128;%预处理B=my_dct2(A);C=dct2(A);C2=C;C2(:,N-4:N)=0;%右边4列为0D=DCT_operator(N);%N阶DCT算子A2=D'*C2*D;%DCT逆变换A2=uint8(A2);%数据类型变换subplot(1,3,1);%绘图imshow(A2);title('右4列变0');subplot(1,3,2);C3=C;C3(:,1:4)=0;%左4列变0A3=D'*C3*D;%DCT逆变换imshow(uint8(A3));title('左四列变0');subplot(1,3,3);imshow(uint8(A));title('原图');

4)若对 DCT 系数分别做转置、选择 90 度和旋转 180 度操作,逆变换后恢复的图像有何变化?请选择一块图验证你的结论。

对 DCT 系数做转置,相当于对原图片进行转置。证明如下:

$$ A'=D^TC^TD=D^T*(D*A^T*D^T)*D=A^T $$

对 DCT 矩阵旋转 90 度,180 度,猜测逆变换后图像也有旋转,但不好从数学上说明;

效果图、代码如下:

A=double(hall_gray(1:N,1:N))-128;D=DCT_operator(N);C=dct2(A);%转置C1=C';A1=D'*C1*D+128;%旋转90度C2=rot90(C);A2=D'*C2*D+128;%旋转180度C3=rot90(C,2);A3=D'*C3*D+128;

可以看出,旋转后逆变换的图像,除了旋转,相较于原图变化很大,但还是可以大致看出大礼堂的形状。

5)如果认为差分编码是一个系统,请绘出这个系统的频率响应,说明它是一个滤波器。DC 系数先进行差分编码再进行熵编码,说明 DC 系数的频率分量更多。

差分系统表达式可写为:

$$ y(n)= \begin{cases} x(n-1)-x(n),&\text{n!=1;}\

x(1),&\text{n=1;}\

\end{cases} $$

故而

$$ H(z)=\frac{1}{z^{-1}-1} $$

代码如下:

b = [-1 1];
a = 1;
freqz(b,a,2001);

图像如下:

由此可见,差分编码系统为高通滤波器。DC 系数先进行差分编码,说明高频率分量更多。

6)DC 预测误差的取值和 Category 值有何关系?如何利用预测误差算出其 Category?

观察 Category 的计算表,可以得知,每个 Category 的值对应于区间:

$$ [-2^n-1,-2^{n-1}], [2^{n-1},2^n-1]\

n为category,n>0; $$

由此 Category 的计算公式为:

$$ Category = \begin{cases} floor(log_2|x|)+1, &x!=0;\

0,&x=0; \end{cases} \x为预测误差 $$

7)你知道哪些 zigzag 扫描的方法?请利用 MATLAB 的强大功能设计一种最佳方法。

要实现 zig_zag,有以下两种思路:

1.打表法,通过写出 zigzag 扫描得到的列向量对应的 8*8 矩阵转为的列向量中的下标,即可方便地进行 zigzag 扫描。然而该方法只适用于固定大小矩阵。

2.扫描法:通过程序直接模拟 zigzag 扫描,从而可以进行任意大小矩阵的 zigzag 扫描。具体方法为,定义一个方向指示变量 dir,每次循环都会为列向量 y 添加元素。定义 i,j 代表矩阵元素下标,每次判断该位置元素是否为边界终止点,从而确定下一次扫描的点的下标所在。

综上,定义了 zigzag()函数来进行 zigzag 扫描,扫描方法作为参数传入,详见 zigzag.m 文件;

%模拟zigzag扫描
    dir = 1;        %方向变量,1代表向上扫描,0代表向下扫描
    i = 1;
    j = 1;
    num = 1;
    normal_move = 0;
    for index = 1:r*c
        i,j
        y(index) = A(i,j);
        if(i==1 | j==1 | i==r | j==c) %到达边界
            if( dir & j==c ) %于右边界到达终点
                i = i+1;
                dir = ~dir; %改变方向
            elseif( dir & i==1 )%于上边界到达终点
                j = j+1;
                dir = ~dir;
            elseif( ~dir & i==r )%于下边界达到终点
                j = j+1;
                dir = ~dir;
            elseif( ~dir & j==1 )%于左边界达到终点
                i = i+1;
                dir = ~dir;
                
            else    %说明是起点
                if(dir==1)  %正常移动
                    i=i-1; j=j+1;
                else 
                    i=i+1; j=j-1;
                end
            end
            
        else
            if(dir==1)  %正常移动
                i=i-1; j=j+1;
            else 
                i=i+1; j=j-1;
            end
        end
    end

打表方法如下:

if(r~=8 | c~= 8)
    error('A must be an 8*8 matrix,otherwise you should use method 1');
end
    
index = [1,9,2,3,10,17,25,18,11,4,5,12,19,26,33,41,...
        34,27,20,13,6,7,14,21,28,35,42,49,57,50,43,36,...      	29,22,15,8,16,23,30,37,44,51,58,59,52,45,38,31,24,32,39,46,53,...
        60,61,54,47,40,48,55,62,63,56,64];
x = A(:);
for i = 1:64
    y(i) = x(index(i));
end

通过验证可知程序正确性:

8)对测试图像分块、DCT 和量化,将量化后的系数写成矩阵形式,其中每一列为一个块的 DCT 系数经过 zigzag 扫描后形成的列矢量,第一行为各个块的 DCT 系数。

将测试图像分块,DCT,量化后结果写入矩阵中,每列为一块 DCT 系数结果 zigzag 扫描后的列矢量。代码思路大致分为如下步骤:

  1. 对图像进行补全,使得行、列数正好为 8 的倍数;

  1. 利用 for 循环进行分块,每次选取一个块作为变量 block,用于后续处理;

  1. 对 block 做减去 128 的预处理;

  1. 对 block 进行 DCT 变换; . 对 DCT 系数进行 zigzag 扫描后,结果写入对应的结果矩阵中。

具体代码如下(详见 ex2_8.m),结果存于 ex2_8_result 中:

A=double(hall_gray);%先对测试图像进行补全[rc]=size(A);if(mod(r,8)~=0)A(r+1:(floor(r/8)+1)*8,:)=0;endif(mod(c,8)~=0)A(:,c+1:(floor(c/8)+1)*8)=0;end%随后开始分块[rc]=size(A);result=zeros(64,r*c/64);fori=1:r/8forj=1:c/8block=A(8*(i-1)+1:8*i,8*(j-1)+1:8*j);%分块完成block=block-128;%预处理D=dct2(block);%DCT变换D=round(D./QTAB);%量化result(:,(i-1)*c/8+j)=zigzag(D,2);%zigzag扫描endend

9)请事先本章介绍的 JPEG 编码(不包括写 JFIF 文件),输出为 DC 系数的码流、AC 系数码流、图像高度和宽度,将四个变量写入 jpegcodes.mat 中。

为了实现 JPEG 编码,首先定义两个函数 DC_coeff()与 AC_coeff(),分别用于求出每个块的 DC 码与 AC 码;

函数代码如下:

function DC_code = DC_coeff(DC_vector)
%输入直流分量的向量DC_vector%输出DC为其对应的二进制码流D=zeros(length(DC_vector),1);D(2:length(DC_vector))=-diff(DC_vector);D(1)=DC_vector(1);%差分编码DC_code='';load('JpegCoeff.mat');fori=1:length(D)DC_code=strcat(DC_code,DC_translate(D(i),DCTAB));endend

其中调用到自定义的子函数 DC_translate(),用于将预测误差通过已知的 DCTAB 表进行翻译。其代码如下:

function y = DC_translate(c,DCTAB)
%y为DC系数翻译的二进制字符串%c为预测误差%DCTAB即为对应的码表y='';cor=0;%cor为Categoryif(c~=0)cor=floor(log2(abs(c)))+1;ends_length=DCTAB(cor+1,1);fori=1:s_lengthy=strcat(y,DCTAB(cor+1,1+i)+'0');end%查表,Huffman编码s=dec2bin(abs(c));if(c<0)fori=1:length(s)if(s(i)=='1')s(i)='0';elseif(s(i)=='0')s(i)='1';endendend%预测误差的二进制码       y=strcat(y,s);end

随后用课件中的例子进行简单验证,大致没有问题。

AC_coeff()函数同理,代码以及验证(同课件上的例子)如下:

function y = AC_coeff(AC_vector)
%输入AC_vector为经过量化的待处理的AC系数%输出y为对应的二进制码流load('JpegCoeff.mat');run=0;y='';ZRL='11111111001';%16连0EOB='1010';%块结束符fori=1:length(AC_vector)if(AC_vector(i)==0)run=run+1;elseif(run<16)y=strcat(y,AC_translate(run,AC_vector(i),ACTAB));%添加该非0系数的二进制码run=0;elsewhile(run>=16)y=strcat(y,ZRL);run=run-16;endy=strcat(y,AC_translate(run,AC_vector(i),ACTAB));endendendy=strcat(y,EOB);%在结尾增加EOBendfunction y = AC_translate(run,c,ACTAB)
%该函数为子函数%run为游程数%c为非零AC系数%ACTAB为Huffman对照表%返回值y对应AC系数二进制码size=0;if(c~=0)size=floor(log2(abs(c)))+1;end%确定该系数的sizeamplitude=dec2bin(abs(c));if(c<0)fori=1:length(amplitude)if(c(i)=='0')c(i)='1';elseif(c(i)=='1')c(i)='0';endendend%确定amplitudehuffman='';row=run*10+size;l=ACTAB(row,3);fori=1:lhuffman=strcat(huffman,ACTAB(row,3+i)+'0');end%确定run/size的huffman编码y=strcat(huffman,amplitude)%返回值end

随后需要对整幅图像进行 jpeg 编码,由于在之前的题目中已经得到了 DCT 系数的结果矩阵 result,该矩阵的第一行即为 DC 系数,该矩阵每列第二道末尾为 AC 系数。故只需进行循环即可得到 DC 与 AC 的二进制码。于是在结构上采用上一题的程序,增加以下语句完成整幅图像的编码:

DC=result(1,:);%第一行即为DC系数H=r;%高W=c;%图像宽度DC_code=DC_coeff(DC);%DC码AC_code='';%AC码fori=1:r*c/64%逐块翻译AC码AC_code=strcat(AC_code,AC_coeff(result(2:end,i)));end%将结果写入结构体中jpegcodes=struct('DC_code',{DC_code},'AC_code',AC_code,'H',H,'W',W);save'jpegcodes.mat'jpegcodes

结果通过一个结构体 jpegcodes 存入 jpegcodes.mat 中。

10)计算压缩比(输入文件长度/输出码流长度),注意转换为相同进制。

在灰度图中,一个像素占一字节,由测试图像的长宽可计算出图像文件大小为 20160B。

DC 码流与 AC 码流均为二进制码,其长度即为其 bit 数。

代码如下:

[r,c]=size(hall_gray);pic_size=r*c;%计算原始图像字节数code_length=length(jpegcodes.DC_code)+length(jpegcodes.AC_code);%计算码流长度ratio=pic_size*8/code_length%字节数乘8后除于码流长度即为压缩比

结果如下:

若考虑上写入文件中的图像长度、宽度数据,每个数据假设为 int8 型,则相当于码流长度再加上 16bits,计算出的压缩比为 6.4107。由此可见,JPEG 编码方式可以节省许多内存空间。

11)请实现本章介绍的 JPEG 解码,输入是你生成的 jpegcodes.mat 文件,分别用客观和主观方式评价。

对生成的压缩信息做解压,主要分为以下几个步骤:

  1. 对 DC、AC 码进行熵解码,虽然 MATLAB 自带 Huffman 编码,但是由于 DC、AC 码流中除了 Huffman 码外还有二进制数,故不能直接将码进行处理;若想采用逐个输入解码的话,MATLAB 会报错,综上考虑,决定自己根据已有的编码表构造出 Huffman 二叉树,从而进行解码的工作。每个 Huffman 码解码后再将二进制数还原,最终将(8)问中的 result 矩阵还原出来。构建 Huffman 树的工作写于函数文件 build_huffmantree.m 中,关键代码如下:

fori=1:r%共有r个编码row=encode_table(i,:);value_number=row(1);%值的个数code_number=row(2+value_number);%编码长度index=1;code=row(3+value_number:3+value_number+code_number-1);%获取codefork=1:length(code)%对tree进行构造if(code(k)==0)if(tree(index).left~=0)%若存在left节点index=tree(index).left;%读取下一个节点indexelsetree(length(tree)+1)=struct('left',0,'right',0,'value',-1);%创建新节点tree(index).left=length(tree);%对left进行赋值index=length(tree);%disp(strcat('create a new node, index:',num2str(index)));endelseif(code(k)==1)if(tree(index).right~=0)%若存在right节点index=tree(index).right;%读取下一个节点indexelsetree(length(tree)+1)=struct('left',0,'right',0,'value',-1);%创建新节点tree(index).right=length(tree);%创建新节点index=length(tree);endelseerror('code should not contain numbers otherwise 1,0');endendtree(index).value=row(2:value_number+1);%定义节点的value即为解码结果end

由以上代码即可构造出一课 Huffman 编码树,每个叶子节点的 value 值对应于该 Huffman 码的数值。故而每次从根节点开始遍历,直达节点到达叶子节点,即可找到这段码对应的数值。

随后对整段 DC 码流进行解码,每次解码完后,根据 Category 解出后面几位二进制码代表的预测误差,随后继续循环 Huffman 解码。如此便可解出 DC 码流。

index=1;tree_index=1;%用于指示树中节点位置find_end=0;%用于指示是否完成一段的解码while(index<length(DC_code))if(DC_code(index)==0)tree_index=tree(tree_index).left;if(tree(tree_index).value~=-1)find_end=1;%该段解码完成endelseif(DC_code(index)==1)tree_index=tree(tree_index).right;if(tree(tree_index).value~=-1)find_end=1;endelseerror('DC_code error!');endindex=index+1;%找到结尾的处理if(find_end)category=tree(tree_index).value;tree_index=1;%重回根节点find_end=0;%更新number=0;%number为预测误差二进制码if(category~=0)number=DC_code(index:index+category-1);index=index+category;elseindex=index+1;%更新indexendpre_error=0;%预测误差is_neg=0;%是否为负数if(number(1)==0&category~=0)%说明该预测误差为负数number=double(~number);%按位取反is_neg=1;endfori=1:length(number)number(i)=number(i)*(2^(length(number)-i));%各位乘对应的系数endpre_error=sum(number);if(is_neg)pre_error=-pre_error;end%得到预测误差y(length(y)+1)=pre_error;%添加新元素endend%最后反差分编码y(2:end)=-y(2:end);fori=2:length(y)y(i)=y(i)+y(i-1);end

对于 AC 码同理,不再赘述。但是在 AC 码的还原中出现了许多 bug,由此发现前面进行 AC 码编码函数就存在着些许 bug,经过修改后整个部分终于得以成功运转。综上得到原有的 result 矩阵。将该 result 矩阵存在 ex2_11_result.mat 下,通过与第 8 问中的 result 矩阵进行对比发现,这两个矩阵相等。即解码正确。

  1. 对该 result 矩阵的每列进行反 zigzag 还原,还原成 8*8 矩阵。

为了简单起见,采用打表法,由于之前已经存在下标对照表,只需将赋值的顺序发过来即可,关键代码如下:

index=[1,9,2,3,10,17,25,18,11,4,5,12,19,26,33,41,...34,27,20,13,6,7,14,21,28,35,42,49,57,50,43,36,...29,22,15,8,16,23,30,37,44,51,58,59,52,45,38,31,24,32,39,46,53,...60,61,54,47,40,48,55,62,63,56,64];%zigzag的对应索引y=zeros(64,1);fori=1:64y(index(i))=s(i);			%反过来对应endy=reshape(y,8,8);%变为矩阵
  1. 随后对每块矩阵进行反量化,随后进行 DCT 逆变换;

代码如下:

column=result2(:,i);%取一列block=anti_zigzag(column);%还原图像块block=block.*QTAB;%反量化pic_block=idct2(block);%逆DCT2
  1. 将各块进行拼接;

r_index=ceil(i/c);c_index=mod(i,c);if(c_index==0)c_index=c;end%确定图像块所处的位置pic(8*r_index-7:8*r_index,8*c_index-7:8*c_index)=pic_block;%拼接

综上,将各步骤合成为单个函数 picture_recover(),从而可以方便地调用函数进行解码。解码结果以及 PSNR 计算结果如下:

PSNR = 34.8975,可以看出在 30——40dB 间,查询维基百科可知,一般的图像压缩 PSNR 值就在 30——40dB 间,可以看出压缩效果较好。

从主观上来看,这两幅图看不出明显差别,但是解压后的图像显得更加顺畅平缓(图中大礼堂门口的柱子处)由此可见,Jpeg 的确是一种优秀的图像压缩方式。

12)将量化步长减小为原来的一半,重做编解码。同标准量化步长的情况比较压缩比与图像质量。

此步只需将 JpegCoeff.mat 中的 QTAB 矩阵改为原来的一半后计算压缩比与 PSNR 即可。故在每步使用到 QTAB 时将 QTAB 减半即可。

更改后结果如下:

QTAB

压缩比

PSNR

主观感受

原版

6.4188

34.8975

看不出明显变化

一半

4.4081

37.3897

看不出明显变化

由此可见,将 QTAB 减半后,压缩比减小到 4.4,PSNR 则有所增大。但是由于本来的图像质量就较好,故而外面倾向于选择压缩比更大的量化矩阵。

13)看电视时偶尔可以看到美丽的雪花图像(见 snow.mat),请对其进行编码,和测试图像压缩比与图像质量进行比较,并解释比较结果。

将图像压缩的过程组合成函数 JPEG_encoder,输出结构体 jpegcodes,压缩比、PSNR 以及解压图像结果如下:

可以看出,与之前的测试图像结果进行对比,压缩比减小了许多,同时,PSNR 也下降了许多,说明对于这种雪花图像的压缩效果并不好。原因在于雪花图像是随机图像,与我们日常看到的图像存在不同,图像上并不连续,而 jpeg 是根据人眼对连续亮度的东西较为敏感而设计的,故而压缩效果很差。

3、信息隐藏


1)实现本章介绍的空域隐藏方法和提取方法。验证其抗 JPEG 编码能力

首先,需要得到待隐藏信息。在这里为了简单起见,将字符串设置为待隐藏信息,并且转化为其对应的 ascii 码(二进制),从而得到一个二进制的数组,数组结尾带 8 个 0,代表到达字符结尾。待隐藏信息存于'msg.mat'中。

%generate messagemessage='Tsinghua University';bin_msg=[];fori=1:length(message)msg=binstr2array(dec2bin(abs(message(i))))';%转为二进制数组msg=[zeros(1,8-length(msg)),msg];%每个字符对应8位bin_msg(end+1:end+8)=msg;endbin_msg(end+1:end+8)=zeros(1,8);savemsg.matbin_msg

要实现空域隐藏方法,只需将图像每个像素中最低位换为信息即可。由于每个像素最低位代表其是否为奇数,故只需对待接收信息的像素先减去最低位,再加上信息大小即可。相关代码如下:

[r,c]=size(hall_gray);hall=double(hall_gray(:));%将信息载体先转为列向量l=length(bin_msg);if(r*c<length(bin_msg))%防止信息长度大于载体l=r*c;enda=mod(hall(1:l),2);hall(1:l)=hall(1:l)-a;%将前l位最低位全部变为0hall(1:l)=hall(1:l)+bin_msg(1:l)';%将信息写入最低位hall2=reshape(hall,r,c);%reshape成原来的图像

由上图可见,修改最低位对于图像基本没有影响。完全无法看出图片中隐藏着信息。若要对该图进行信息提取,只需将该图每个像素的最低位提取出来,随后根据之前的约定,当某一位为全 0 时,即找到了结尾位,由此可以得到每个字符的信息,从而还原出字符串。

%信息提取code=mod(hall2(:),2);%取最低位recover_msg=[];%字符数组fori=1:floor(length(code)/8)zifu=code(8*i-7:8*i);%取连续8位zifu=zifu.*(2.^(7:-1:0)');%乘对应的幂if(sum(zifu)~=0)recover_msg(end+1)=sum(zifu);elsebreak;%说明到达结尾endendrecover_msg=char(recover_msg)%转为字符串

结果如图,可见还原成功。

随后对隐藏信息的图像进行 JPEG 编码以及解码,对解码后的图像进行提取信息操作,结果如下:

由于 JPEG 编码为有损编码,故提取信息为乱码。可见空域信息隐藏非常不抗 JPEG 编码。

2)依次实现本章介绍的三种变换域信息隐藏方法与提取方法,分析嵌密方法的隐蔽性以及嵌密后图像的质量变化和压缩比变化

a.同空域方法,用信息为逐一替换掉每个量化后的 DCT 系数的最低位,在进行熵编码

要将每一位 DCT 系数都进行替换,但是要隐藏的信息可能没有那么长,在此不妨做如下规定:对于要隐藏的文本信息,最后面全部补为 0,从而使得信息长度与图像 DCT 系数(像素个数)一致,从而对每个 DCT 系数都作出修改。

但是还有一个问题需要考虑,DCT 系数不同于像素,其值存在负值。考虑到负数的二进制 2 补码表示中,最低位为 1 时为奇数。故而可以采用判定其是否为奇数来推断最低位。

综上所述,将以上功能封装成函数 msg_hide()与 msg_take(),详细定义参见该两个函数文件。其中该两个函数均有参数 method 代表采用的是第几种 DCT 域信息隐藏方法。同时为了方便地得到图片的全部块的 DCT 系数矩阵,自定义函数 DCT_result();

关键代码如下:

result=result(:);%result变为列向量result=result-mod(result,2);%使每个DCT系数的最后一位都为0if(length(msg_array)<length(result))msg_array(end+1:end+length(result)-length(msg_array))=0;%使msg_array与result等长endresult=result+msg_array(1:length(result));%修改每一位DCT系数的最低位result=reshape(result,r,c);

随后对得到的 result 矩阵进行熵编码,代码与之前一致,在此略去。

图像质量与压缩比评价如下:

可以看出加密后压缩比变小,且相比于正常图片 JPEG 编码后的结果,加密后图像质量下降较多。

b.同方法 1,用信息位逐一替换掉每个量化后的 DCT 系数的最低位,在进行熵解码。注意不是每个 DCT 系数都嵌入了信息。

此种方法减少了对 DCT 系数的修改。要实现只对部分 DCT 系数做修改,且能提取信息,需要做一些规定。在此处我进行的规定是当读取到停止位(即 8 个 0)时,说明信息已经提取完毕。当然也可以采取在开头写入信息的长度之类的方式。做此修改后即可实现方法 2。

关键代码如下:

result=result(:);%result变为列向量l=length(msg_array);if(l>length(result))l=length(result);endresult(1:l)=result(1:l)-mod(result(1:l),2);%使每个DCT系数的最后一位都为0result(1:l)=msg_array(1:l)+result(1:l);%修改部分result=reshape(result,r,c);

图像质量与压缩比评价如下:

可以看出,图像质量较于第一种方法有了明显的上升。但是由于此时要隐藏的信息大小较小,如果信息量很大的话,那就需要修改更多的 DCT 系数甚至修改全部的 DCT 系数,此时与第一种方法无异。故此种算法会使得图像质量随着信息量增大而变差。在信息量较小时的确不失为一种好方法。

c.先将待隐藏信息用 1,-1 的序列表示,再逐一将信息为追加在每个快 zigzag 顺序的最后一个非零 DCT 系数之后,若原本该图像块的最后一个系数就不为零,那就用信息为替换该系数。

用此方法只能实现一个块隐藏 1bit 信息,相对之前的方法来说可隐藏信息量大幅减少。实现上较为简单,关键代码如下:

l=size(result,2);%图像的块数if(l>length(msg_array))l=length(msg_array);endfori=1:l
	column=result(:,i);index=length(column);%最后一个非0系数的下标find=0;
  	while(~find)if(column(index)~=0)
    	find=1;
  	elseindex=index-1;endend%由此找到最后一个非零系数indexif(index==length(column))
	column(index)=msg_array(i);elsecolumn(index+1)=msg_array(i);end%将信息写入result(:,i)=column;

图像质量与压缩比信息如下:

可以看出此种方法对图像质量的影响较小,但是与第二种方法相比较并没有明显优势,甚至稍微劣势于第二种方法。且能隐藏的信息量

取决于图像块的数量,与前两种方法相比,差距还是较大的。

但是我们不妨将得到的图片展示出来,从主观上判断:

从图像上来看,前两种方法获得的图像的左上角都有很明显的噪声块,且第一种方法导致的图像失真较明显。而第三种方法从肉眼上看并没有明显的噪声块存在。由此可以判定,方法 3 的隐蔽性远远高于前两种方法。原因在于方法 3 只是选取高频的 DCT 系数进行改变,而人眼对于高频分量并不敏感,所以并不能看出明显的差别。而前两种方法对于低频 DCT 系数都存在修改,故而较为容易发现其隐藏了信息。综上列出如下评价表:

method

压缩比

图像质量

隐蔽性

1

变小

变差

2

稍微变小(信息量较少时)

稍微变差(信息量较小时)

3

稍微变小

稍微变差

良好

3)(选做)请设计实现新的隐藏算法并分析其优缺点。

没想好,暂时空着。

4、人脸识别


1)所给资料 Faces 目录下包含从网图中截取的 28 张人脸,试以此作为样本训练人脸标准 v

a)、样本人脸大小不一,是否需要首先将图像调整为相同大小?

不需要。因为训练方法只是对图像中的所有像素点的颜色,计算其中各个颜色出现的频率,得到特征 u(R),与图像大小没有关系。

b)、假设 L 分别取 3、4、5,所得的三个 v 之间有何关系?

当 L 选取 3/4/5 时,意味着对于每个 uint8 只选取其前 3/4/5 位作为特征。故而 L 越大,v 中每个元素包含的信息量越大。而 L 越小,v 中每个点对应的颜色数目就越多。例如:L 为 3 的 v 中每个元素(即该颜色的概率密度)对应于 L 为 4 的 v 中,RGB 前 3 位相同的颜色的概率密度之和。对于 L 为 5 同理。数学表达如下:

2)设计一种从任意大小的图片中检测任意多张人脸的算法并编程实现(输出图像在判定为人脸的位置加上红色的方框)。随意选取一张多人照片(比如支部活动或者足球比赛),对程序进行调试。尝试 L 分别取不同的值,评价检测结果有何区别。

设计算法各个步骤如下:

  1. 根据训练集训练出特征标准 v;

  1. 输入图像并对图像做分块处理(类似于 JPEG 编码);

  1. 对于每块图像块 R,计算其特征 u(R);

  1. 计算 u(R)与 v 的度量系数系数,与阈值比较,判定该块是否为人脸;

  1. 将相邻的人脸块统一并进行标识(用方框围住);

以下进行该算法的程序实现与结果测试:

(1)训练特征标准

观察 Faces 中的训练样本,每张图片基本都是人脸,仅有少数的其它干扰,故我们可以认为训练集中的图片仅由人脸构成。故而我们定义函数 feature_extract()来对单张图片进行特征提取,编写 train.m 脚本实现训练,并将得到的标准特征 v 存入文件 face_standard.mat 中。

feature_extract()的关键代码如下:

pic=double(reshape(pic,size(pic,1)*size(pic,2),1,3));v=zeros(2^(3*L),1);basic=2^(8-L);%初始化fori=1:size(pic,1)a=[pic(i,1,1),pic(i,1,2),pic(i,1,3)];%取出像素index=sum(floor(a/basic).*(2.^(2*L:-L:0)))+1;%计算该颜色对应的下标v(index)=v(index)+1;%次数+1endv=v/size(pic,1);%循环统计每个颜色出现的次数并求出频率

train.m 关键代码如下:

fori=1:numv=v+feature_extract('Faces/',strcat(num2str(i),'.bmp'),L);endv=v/num;

得到的结果 v 如图(L=5):

(2) 输入图像并对其分块处理

不妨尝试取 block_l 长的正方形大小的图像块进行处理(16<=block_l<=50,依图像不同而有变化),取图像块顺序为逐行取(同 JPEG 编码),但有一点不同,此处若每行最后一块大小不足 block_l*block_l 大小,并不进行补全,而是直接取剩下的即可。此块代码较简单,且类似于 JPEG 编码中的分块,不在此赘述。详见 face_detect()函数。

(3) 计算图像块特征

与训练特征标准中的对单张图片求特征的方法一致。不再赘述。

(4) 计算度量系数,从而进行检测

u=feature_extract(block,L);		%提取图像块特征cor=sqrt(u).*sqrt(v);				%计算系数ifcor>=threshold					%大于阈值identify(i,j)=1;				%对应图像块判定为人脸end

但是在此处阈值需要设定的较好,方可有较好的识别效果。为此写了一个脚本来对不同阈值的效果进行对比(即效果图全部显示),随后用手工的方法挑选最佳阈值。效果图大致如下:

(5) 将相邻的方块统一并进行标识

此处只需将相邻的方块进行合并,忽略单独的小方块即可。合并代码可以见文 face_detect.m 修改后效果图如下(好看多了):

(6) 测试

此处我们选择一张多人照片进行测试:

可以看出,虽然检测出人脸,但是方框的范围过大,总的来说 L=6 的情况似乎较好一些。此处问题的原因在于,合并各块结果并画方框时,我采用的是只要相邻就判定为同一张人脸,故而对于多人图来说,极有可能造成几张人脸最终被合成一个方框的惨剧。故而我们在测试多人图时将画方框的代码注释后再进行测试。

由上面结果可以看出,其实算法的确将人脸部分检测出(还包含一些皮肤部分),但是由于这些方块相邻,故而合并方框的代码并不能很好地执行。所以在接下来的演示中依旧采用不合并方框的代码。对于不同 L,只要阈值设置好,结果基本相近。但个人更喜欢 L=4/5 。

3.对上述图像分别进行如下处理后

(a)顺时针旋转 90 度

(b)保持高度不变,宽度拉伸为原来的 2 倍(imresize)

(c)适当改变颜色(imadjust)

再试试你的算法检测结果如何?并分析所得结果。

代码如下:

L=4;threshold=0.27;subplot(2,2,1);img=imread('test.jpg');[y,num,identify]=face_detect(img,L,thresholds(L-2));title('原图');subplot(2,2,2);img2=rot90(img);[y,num,identify]=face_detect(img2,L,thresholds(L-2));title('旋转90度');subplot(2,2,3);img3=imresize(img,[size(img,1)2*size(img,2)],'nearest');[y,num,identify]=face_detect(img3,L,thresholds(L-2));title('调整图像宽度');subplot(2,2,4);img4=imadjust(img,[.2.30;.6.71],[]);[y,num,identify]=face_detect(img4,L,thresholds(L-2));title('调整图像颜色');

结果如下:

可以看出,对图像旋转、更改大小后,对于检测没有什么影响。但是对于颜色做出修改后,无法检测出人脸。原因就在于算法是基于训练出的颜色的,若将颜色改变较多,则无法有效地进行识别。而颜色与旋转、改变大小等操作无关,故而结果基本一致。

(4)如果可以重新选择人脸样本训练标准,你觉得该如何选取?

个人认为可以增加人脸样本数量。并且可以针对不同人种设定不同的人脸训练集合(例如黄种人、白种人、黑人训练集分开),从而得到不同的人脸肤色标准,从而可以对一张图中不同肤色的人脸有较好的识别能力。

(5) 人脸识别部分感想:

感觉人脸识别部分通过肤色来进行人脸识别,虽然可以识别到人脸,但是皮肤等部分也会被包括进去,故而存在一些局限性。 同时,在检测后,对识别出的人脸进行方框标识的算法也较简单,比较适用于单人图或者多人但是人脸隔得较远的图。 总的来说,通过本章部分,对人脸识别有了个初步的认识。

5、文件列表


文件

说明

pic

报告中使用到的图片

Faces

人脸识别训练集

temp1.png

练习 1_2 结果图

黑白.png

练习 1_2 结果图

ex2_8_result.mat

存储 DCT 系数矩阵(每列为一个图像块经 zigzag 扫描后的一列)

jpegcodes.mat

存储经过 JPEG 编码信息的结构体(有 4 个变量域:DC_code,AC_code,H,W)

ex2_11_result.mat

经过解码后得到的 DCT 系数矩阵,用于与 2_8 中的矩阵进行对比来验证解码函数的正确性

msg.mat

用于给 ex3_1 提供待隐藏信息

face_standard.mat

储存 L=3 到 6 所训练出的人脸判别标准矢量

test.jpg/test2.jpg/test3.jpg

人脸识别用的测试图像

AC_coeff.m

对一列向量进行 AC 编码

AC_decoder.m

对 AC 码进行解码

anti_zigzag.m

对数组进行反 zigzag 还原(限 8*8 矩阵)

binstr2array.m

将 0,1 组成的字符数组转化为数组

build_huffmantree.m

由编码表建立 Huffman 树

DC_coeff.m

用于 DC 编码

DC_decoder.m

用于 DC 解码

DCT_operator.m

构建 DCT 算子

DCT_result.m

将图像转为 DCT 系数矩阵(量化后的)

face_detect.m

检测图像中的人脸并标注

feature_extract.m

从图像块中提取颜色特征

JPEG_decoder.m

对 JPEG 文件进行解码

JPEG_encoder.m

对图片进行 JPEG 编码

msg_generate.m

产生 msg.mat

msg_hide.m

将信息隐藏进图片

msg_take.m

提取信息

my_dct2.m

实现 DCT 变换

my_equal.m

在误差范围内判断是否相等

picture_recover.m

从 jpegcodes 恢复图像

result_recover.m

从 jpegcodes 恢复 DCT 系数矩阵

train.m

训练人脸识别标准

zigzag.m

对图像块进行 zigzag 扫描

exx_x.m

对应练习题 x_x

MATLAB 图像处理大作业.md

Markdown 版实验报告(推荐用 Typora 阅览)

完整代码:

https://mp.csdn.net/mp_download/manage/download/UpDetailed

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/164314.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华芯片特微 M33内核 KEIL5环境配置不上问题

1 JFLASH连接不上问题 官方手册有说解决这个问题 2 JFLASH能连接上KEIL提示no found sw-dp 在替换keil下载算法后还是提示no found sw-dp 1 怀疑是keil 527版本太高了, 就换了518 还是不行 2 怀疑是keil检测到盗版了就不让下, 替换Jlink为以前老版本还是不行 解决方案: 下…

聊天气泡图片的动态拉伸、适配与镜像

聊天气泡图片的动态拉伸、适配与镜像前情提要创建.9.png格式的图片从资源文件夹加载.9.png图片从本地文件加载“.9.png”图片项目痛点进阶探索iOS中的方式Android中的探索构造chunk数据构造padding数据镜像翻转功能屏幕的适配简单封装演示示例一条线段控制的拉伸两条线段控制的…

Pandas 安装与教程

前言Pandas 是 Python 语言的一个扩展程序库&#xff0c;用于数据分析。Pandas 是一个开放源码、BSD 许可的库&#xff0c;提供高性能、易于使用的数据结构和数据分析工具。Pandas 名字衍生自术语 "panel data"&#xff08;面板数据&#xff09;和 "Python data…

[apidoc]Apidoc-文档生成工具

Apidoc主要是用于生成API文档的工具&#xff0c;可以用于多种语言&#xff0c;包括java、javascript、php等 这里主要是为了写前端的APIDOC&#xff0c;方便交互是双方的使用; 工具的安装 工具包的安装 npm i apidoc [-g|-D]可以-g全局安装&#xff0c;或者-D局部安装,因为…

网盘系统|基于SpringBoot的网盘系统的设计与实现

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

【无功优化】考虑泄流效应的光伏并网点电压系统侧无功优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

软考中级,【软件评测师】经验分享

&#xff0c;以下是我的考试成绩&#xff0c;一次通过很是幸运&#xff0c;希望把我的好运传递给大家&#xff0c;大家都能一次通过谈经验之前&#xff0c;先和大家说说考试的题型以及考试的内容&#xff0c;根据往年的考试题目我们可以很容易得知&#xff0c;软件评测师考试分…

Cisco(62)——PBR策略路由案例

场景1-单下一跳: 拓扑: 需求: R1和R2均连接100.100.100.100,R4看做一台PC,当PC访问100.100.100.100的时候优先走左边,当左边down掉之后切换到右边链路,使用PBR操作。 实现: 1.IP地址等基本配置 R4: R4(config)#no ip routingR4(config)#int e0/0 R4(config-if)#ip add…

Typora自动上传文章图片太难折腾?十三行JavaScript代码足矣

前言 Typora是我用过最爽的markdown文本编辑器了。但是有一点很让人难受&#xff0c;就是在写文章的时候&#xff0c;粘贴上的图片是本地路径。这就导致在复制文章到各大博客平台时发表&#xff0c;图片无法显示。然后需要各种办法去处理文章中的图片&#xff0c;不仅要手动上传…

【学习笔记】【Pytorch】十、线性层

【学习笔记】【Pytorch】九、线性层学习地址主要内容一、前言二、Pytorch的线性层三、Linear类的使用1.使用说明2.代码实现学习地址 PyTorch深度学习快速入门教程【小土堆】. 主要内容 一、前言 在神经网络中&#xff0c;我们通常用线性层来完成两层神经元间的线性变换。 …

【C++】面向对象---继承(万字详解)

目录前言一、继承的定义及概念二、继承方式三、基类和派生类之间的转换四、切片五、继承中的作用域六、派生类中的默认成员函数七、继承中的友元与静态成员继承与友元继承中的静态成员八、棱形继承和虚继承棱形继承虚继承总结前言 继承是面向对象的一个重点&#xff0c;而继承…

活动星投票医疗保障案例推介网络评选微信的投票方式线上免费投票

“医疗保障案例推介”网络评选投票_线上免费投票系统_功能齐全的微信投票_在线免费投票用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票…

图形编辑器:标尺功能的实现

大家好&#xff0c;我是前端西瓜哥。今天我们来实现图形编辑器的标尺功能。 项目地址&#xff1a; https://github.com/F-star/suika 线上体验&#xff1a; https://blog.fstars.wang/app/suika/ 标尺指的是画布上边和左边的两个有刻度的尺子&#xff0c;作用让用户知道他正在编…

java 探花交友day2 项目简介,环境搭建 登录验证码

技术方案&#xff1a; 项目结构&#xff1a; 项目概述 通过接口文档&#xff08;API文档&#xff09;定义规范 开发工具安装与配置 Linux虚拟机 YAPI 账号 tanhuaitcast.cn 密码123456 安装个安卓模拟器&#xff0c;然后安装APK 开发环境说明 初始工程搭建 阿里云短…

Leetcode:235. 二叉搜索树的最近公共祖先(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 精简版&#xff1a; 迭代&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先…

1589_AURIX_TC275_PMU_Flash的基本特性以及操作

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 关于这部分&#xff0c;感觉能够看到的比较有实践指导价值的信息不多。这里关于是否支持cache的信息&#xff0c;之前在内核手册等地方其实也看过了。 DFlash不支持buffer命中的功能&#…

21.Isaac教程--GEMS 导航堆栈简介

Isaac GEMS 导航堆栈简介 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 导航堆栈必须执行以下高级功能&#xff1a; Mapping 映射用于自动创建操作环境的地图。 该地图既用于定位&#xff0c;又用于路径规划。 它可以由具有附加功能的人进行注…

deap遗传算法 tirads代码解读

deap遗传算法 tirads代码解读写在最前面Overview 程序概览参考deap框架介绍creator模块创建适应度类Types定义适应度策略创建个体类Toolbox类创建种群&#xff08;个体、策略以及粒子&#xff09;Initialization1. 创建 attr_int 运算符2. 创建 individual_guess() 运算3.创建新…

学会python后:收集每天热点内容信息,并发送到自己的邮箱

嗨害大家好鸭&#xff01;我是小熊猫~ 实现目的 本篇文章内容主要为如何用代码&#xff0c;把你想要的内容&#xff0c;以邮件的形式发送出去 内容可以自己完善&#xff0c;还可以设置一个定时发送&#xff0c;或者开机启动自动运行代码 代理注册与使用 注册账号并登录 生成ap…

使用TDengine时序数据库的介绍以及系统整合

目录 一、 如何使用 安装目录介绍 数据文件查看和备份 客户端连接 sql使用 二、 系统整合 Java连接配置 Demo示例 三、 对采集点、超级表、子表和设备等关系进行维护 一、 如何使用 安装目录介绍 目录/文件 说明 /usr/local/taos/bin TDengine 可执行文件目录…