├── .gitignore ├── LICENSE ├── README.md ├── docs └── 实验报告.md ├── img ├── adjustcolor.png ├── detect_face_1.png ├── doublewidth.png ├── hw2_4_11.png ├── hw2_4_13.png ├── hw2_4_3.png ├── hw2_4_4.png ├── hw2_4_5.png ├── hw3_4_2.png ├── hw3_4_2_details.png ├── hw4_3_1.png ├── hw4_3_2_situ_1.png ├── hw4_3_2_situ_2.png ├── hw4_3_2_situ_3.png ├── hw4_3_2_situ_4.png ├── hw_1_3_2_chessboard.jpg ├── hw_1_3_2_circle.jpg └── rot90.png ├── src ├── .gitignore ├── Compress.m ├── DCT2.m ├── DCT2_D.m ├── Decompress.m ├── DetectFace.m ├── dct_conceal_1.m ├── dct_conceal_2.m ├── dct_conceal_3.m ├── dct_reveal_1.m ├── dct_reveal_2.m ├── dct_reveal_3.m ├── get_face_feat.m ├── hw1_3_2.m ├── hw2_4_1.m ├── hw2_4_10.m ├── hw2_4_11.m ├── hw2_4_12.m ├── hw2_4_13.m ├── hw2_4_2.m ├── hw2_4_3.m ├── hw2_4_4.m ├── hw2_4_5.m ├── hw2_4_7.m ├── hw2_4_8.m ├── hw2_4_9.m ├── hw3_4_1.m ├── hw3_4_2.m ├── hw4_3_1.m ├── hw4_3_2.m ├── hw4_3_3.m ├── inv_zig_zag.m ├── jpegcodes.mat ├── train_face.m └── zig_zag.m └── 图像处理所需资源 ├── .DS_Store ├── Faces ├── .DS_Store ├── 1.bmp ├── 10.bmp ├── 11.bmp ├── 12.bmp ├── 13.bmp ├── 14.bmp ├── 15.bmp ├── 16.bmp ├── 17.bmp ├── 18.bmp ├── 19.bmp ├── 2.bmp ├── 20.bmp ├── 21.bmp ├── 22.bmp ├── 23.bmp ├── 24.bmp ├── 25.bmp ├── 26.bmp ├── 27.bmp ├── 28.bmp ├── 29.bmp ├── 3.bmp ├── 30.bmp ├── 31.bmp ├── 32.bmp ├── 33.bmp ├── 4.bmp ├── 5.bmp ├── 6.bmp ├── 7.bmp ├── 8.bmp └── 9.bmp ├── JpegCoeff.mat ├── hall.mat ├── snow.mat ├── test1.png └── test2.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | *.pdf 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TCL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MATLAB 图像处理 2 | 3 | ## 作业要求 4 | 5 | 参见`docs/图像处理大作业.pdf` 6 | 7 | ## 图像处理思路 8 | 9 | ### 3.1 图像基础知识 10 | 11 | #### (1)在图像中画圆 12 | 13 | 首先确定圆的圆心,为测试图像的中心;然后确定圆的半径,为图像长、宽中的较小值。接下来根据圆的定义画圆:所有与圆心的距离等于半径的点构成一个圆。在这里,为了让画出的圆清晰可见,将所有与圆心距离在 0.98~1.02 倍半径之间的点全部标红,呈现在图像上。取出标红点的关键代码如下: 14 | 15 | ~~~matlab 16 | size_pic = size(hall_gray); 17 | idx = zeros([size_pic, 2]); 18 | for i = 1: 1: height 19 | for j = 1: 1: width 20 | idx(i, j, 1) = i; 21 | idx(i, j, 2) = j; 22 | end 23 | end 24 | circle_idx = ((idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 <= (1.02 * radius) ^ 2 & ... 25 | (idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 >= (0.98 * radius) ^ 2); 26 | ~~~ 27 | 28 | 最后效果如下: 29 | 30 | ![hw_1_3_2_circle](./img/hw_1_3_2_circle.jpg) 31 | 32 | #### (2)将测试图像涂成国际象棋状的“黑白格” 33 | 34 | 将测试图像分成 8*8 的区域,然后对每块区域根据国际象棋“黑白格”的规则染色。在黑色的地方,将原图对应区域置为0;白色的地方则保持不变。结果如下 35 | 36 | ![hw_1_3_2_chessboard](./img/hw_1_3_2_chessboard.jpg) 37 | 38 | 本题代码位于`src/hw1_3_2.m`中。 39 | 40 | ### 3.2 图像压缩编码 41 | 42 | #### (1)在变换域预处理 43 | 44 | 图像的预处理是将每个像素灰度值减128,若要在变换域进行,可以利用DCT变换的线性性来操作。 45 | 46 | 首先对原图像作二维DCT变换,然后对相同大小的、所有元素均为128的矩阵作二维DCT变换。由于全128的矩阵DCT变换结果只有直流分量,即左上角元素,所以只需让原图的DCT变换结果的直流分量减去全128矩阵DCT变换结果的直流分量即可。具体代码如下: 47 | 48 | ~~~matlab 49 | [part_h, part_w] = size(hall_part); 50 | DC_128 = dct2(zeros(part_h, part_w) + 128); 51 | maphall_2 = dct2(hall_part); 52 | maphall_2(1, 1) = maphall_2(1, 1) - DC_128(1, 1); 53 | ~~~ 54 | 55 | 这样操作后,与先预处理再DCT变换的结果相比,计算MSE,为 7.1000e-28,非常小,说明两种算法计算结果相同。 56 | 57 | 本题代码位于`src/hw2_4_1.m`中。 58 | 59 | #### (2)实现二维DCT变换 60 | 61 | 二维DCT变换,首先是找到DCT算子**D**。我直接根据指导书的公式(2.5),在`matlab`中实现算子**D**。关键代码如下: 62 | 63 | ~~~matlab 64 | function D_mat = DCT2_D(input_dim) 65 | row = linspace(0, input_dim - 1, input_dim)'; 66 | col = linspace(1, input_dim * 2 - 1, input_dim); 67 | cos_mat = cos(row * col * pi / (2 * input_dim)); 68 | cos_mat(1, :) = cos_mat(1, :) / sqrt(2); 69 | D_mat = sqrt(2 / input_dim) * cos_mat; 70 | end 71 | ~~~ 72 | 73 | 得到DCT算子**D**后,就可以根据输入图像的维度,计算二维DCT变换。假设灰度图像维度为 [height, width],则其二维DCT变换结果可以用以下代码来求得 74 | 75 | ~~~matlab 76 | [height, width] = size(input_mat); 77 | dct_coef = DCT2_D(height) * double(input_mat) * DCT2_D(width)'; 78 | ~~~ 79 | 80 | 我将我实现的DCT变换函数,与`matlab`提供的`dct2`进行比较,计算MSE,为9.9944e-26。MSE非常小,可以认为两种方法计算结果相同。 81 | 82 | 本题代码位于`src/hw2_4_2.m`中,计算DCT算子的函数封装在`src/DCT2_D.m`中,计算二维DCT变换的函数封装在`src/DCT2.m`中。 83 | 84 | #### (3)部分DCT系数置零 85 | 86 | 将原图按 8*8 分块后,对每块作二维DCT变换,再将DCT系数矩阵右侧四列、左侧四列系数矩阵分别置零,再作逆DCT变换看结果。 87 | 88 | 理论上分析,DCT系数矩阵左上角元素代表低频分量,右下方系数表示横竖两个方向中迅速变换的高频分量。由于常见的图片通常颜色、亮度都是缓慢变换的,因此DCT系数矩阵越往左上方取值越大,越向右下方取值越小。由于低频分量值大,对图像影响大,高频分量值小,对图像影响小,所以,理论分析DCT系数矩阵右侧四列元素置零对图像影响较小,左侧四列元素置零则会对图像有很大影响。实际结果也正是如此,结果如下: 89 | 90 | ![hw2_4_3](./img/hw2_4_3.png) 91 | 92 | 本题代码位于`src/hw2_4_3.m`中。 93 | 94 | #### (4)DCT系数转置、逆时针旋转90°、旋转180° 95 | 96 | 若DCT系数矩阵**C**转置,考虑DCT变换的公式 97 | $$ 98 | C=dct(P)=DPD^T\\ 99 | P=idct(C)=D^TCD\\ 100 | D^{-1}=D^T 101 | $$ 102 | 所以**C**转置后再逆变换,结果为 103 | $$ 104 | D^TC^TD=(D^TCD)^T=P^T 105 | $$ 106 | 结果为原图的转置。 107 | 108 | 由于右上方的系数反映图像中横向变化的纹理强度,逆时针旋转90°后,原本右下方高频分量的元素来到了右上方。由于高频分量元素本身就较小,所以DCT系数矩阵逆时针旋转90°后再逆变换,得到的图像横向几乎不会变化,呈现很强的横向纹理。 109 | 110 | 若将DCT系数矩阵旋转180°,左上方低频分量将会和右下方高频分量换位,导致恢复的图像低频分量小,高频分量大,从而图像变化剧烈,呈现格子状的纹理。 111 | 112 | 实际运行时,结果与理论分析相近,如下 113 | 114 | ![hw2_4_4](./img/hw2_4_4.png) 115 | 116 | 本题代码位于`src/hw2_4_4.m`中。 117 | 118 | #### (5)系统特性 119 | 120 | 如果认为差分编码是一个系统,则这个系统方程为 121 | $$ 122 | y(n)=x(n-1)-x(n) 123 | $$ 124 | 使用`freqz`函数查看该系统频率响应如下 125 | 126 | ![hw2_4_5](./img/hw2_4_5.png) 127 | 128 | 由此可以看出,该系统可看作为高通滤波器。DC系数先进行差分编码再进行熵编码,说明DC系数含有较多的高频分量。 129 | 130 | 本题代码位于`src/hw2_4_5.m`中。 131 | 132 | #### (6)DC预测误差与Category 133 | 134 | 记DC预测误差为`delta`,Category为`C`,则它们之间的关系为 135 | $$ 136 | C=ceil(log2(abs(delta)+1)) 137 | $$ 138 | 由此可以推出,Category表示了DC预测误差的二进制表示位数。 139 | 140 | #### (7)设计实现Zig-Zag扫描 141 | 142 | 由于本次作业中,只需对 8*8 的矩阵进行 Zig-Zag 扫描,因此可以直接将各索引手动编排好,然后直接用 matlab 的数组索引功能完成扫描。扫描代码如下 143 | 144 | ~~~matlab 145 | function output = zig_zag(input) 146 | temp = reshape(input, [64, 1]); 147 | output = (temp([ 148 | 1, 9, 2, 3, 10, 17, 25, 18, ... 149 | 11, 4, 5, 12, 19, 26, 33, 41, ... 150 | 34, 27, 20, 13, 6, 7, 14, 21, ... 151 | 28, 35, 42, 49, 57, 50, 43, 36, ... 152 | 29, 22, 15, 8, 16, 23, 30, 37, ... 153 | 44, 51, 58, 59, 52, 45, 38, 31, ... 154 | 24, 32, 39, 46, 53, 60, 61, 54, ... 155 | 47, 40, 48, 55, 62, 63, 56, 64 ... 156 | ])); 157 | end 158 | ~~~ 159 | 160 | 首先,我先将输入的 `8*8` 的矩阵变为 `64*1` 的大小。然后,我根据 Zig-Zag 的规则,事先将理论的 Zig-Zag 扫描结果的各位置索引记好。最后,即可直接通过数组索引的方式完成扫描。 161 | 162 | 为了验证我这一算法是正确的,我设计了如下测试样例进行检验 163 | 164 | ~~~matlab 165 | test_input = [ 166 | [1, 2, 6, 7, 15, 16, 28, 29]; ... 167 | [3, 5, 8, 14, 17, 27, 30, 43]; ... 168 | [4, 9, 13, 18, 26, 31, 42, 44]; ... 169 | [10, 12, 19, 25, 32, 41, 45, 54]; ... 170 | [11, 20, 24, 33, 40, 46, 53, 55]; ... 171 | [21, 23, 34, 39, 47, 52, 56, 61]; ... 172 | [22, 35, 38, 48, 51, 57, 60, 62]; ... 173 | [36, 37, 49, 50, 58, 59, 63, 64] ... 174 | ]; 175 | ~~~ 176 | 177 | 这一矩阵经过 Zig-Zag 扫描后,理论上应该输出 1~64 的序列,使用我设计的算法扫描得到结果正是如此,说明算法实现无误。 178 | 179 | 本题代码位于`src/hw2_4_7.m`中。 180 | 181 | #### (8)对测试图像分块、DCT与量化 182 | 183 | 首先是预处理,灰度图各像素减去128;然后是将图像每个 8*8 的块进行DCT变换并点除量化矩阵;最后是将结果依次排列。 184 | 185 | 前两步的代码如下 186 | 187 | ~~~matlab 188 | hall_pre = double(hall_gray) - 128; 189 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 190 | ~~~ 191 | 192 | 重新排列时,我按照从左至右、从上至下的方式来遍历,代码如下 193 | 194 | ~~~matlab 195 | [height, width] = size(hall_quan); 196 | res = zeros([64, height * width / 64]); 197 | for i = 0: 1: height / 64 - 1 198 | for j = 1: 1: width 199 | res(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 200 | end 201 | end 202 | ~~~ 203 | 204 | 本题代码位于`src/hw2_4_8.m`中。 205 | 206 | #### (9)实现 JPEG 编码 207 | 208 | 关键点在于计算DC系数的码流和AC系数的码流。 209 | 210 | 首先是DC系数码流。DC系数就是第8问中量化矩阵的第一行,码流的计算需要先通过差分编码再进行熵编码。 211 | 212 | 熵编码时,可以利用DC预测误差与Category的关系:Category表示了DC预测误差的二进制表示位数,并利用给好的Huffman编码表DCTAB进行编码。对于每个预测误差,首先根据其Category确定对应的Huffman编码,然后将其幅度用二进制表示,接在其Huffman编码后。若幅度为0,无需编码幅度,Huffman编码已经包含了该信息。对于负数,幅度采用1补码,此时最高位为0。这样的方式表示每个预测误差,可以准确无误并容易解码。代码如下,其中`quan_mat`为量化矩阵: 213 | 214 | ~~~matlab 215 | dc_cof = quan_mat(1, :)'; 216 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 217 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 218 | dc_code = []; 219 | for i = 1: 1: length(dc_diff) 220 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 221 | if dc_diff(i) ~= 0 222 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 223 | if dc_diff(i) < 0 224 | mag_temp = ~mag_temp; 225 | end 226 | else 227 | mag_temp = []; 228 | end 229 | dc_code = [dc_code; cat_temp; mag_temp]; 230 | end 231 | ~~~ 232 | 233 | AC系数编码与DC系数稍有不同。AC系数是量化矩阵第2行及之后的元素,需要根据每一个非零系数值及其行程进行编码。AC系数中可能有较多的0,因此利用行程编码能较好地提高压缩率。我们通过提供的ACTAB,以非零数值与行程为索引,找到对应的Huffman编码,然后再在其后接上非零数值的二进制表示(负数为1补码),从而完成对AC系数的编码。 234 | 235 | 这其中用到两个符号: 236 | 237 | - ZRL:[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1],表示连续 16 个 0 238 | - EOB:[1, 0, 1, 0],表示每个块中编码完最后一个非零系数 239 | 240 | 我采用这样的方式进行编码:对每个块(也就是 63*1 的列),遍历时实时记录经过的零个数,当到第一个非零数值时,对记录的零个数求除 16 的商与余数,商为需要加上的 ZRL 的个数,余数为用于提供Huffman编码的索引,即行程。若一直到每个块最后,都没找到非零数值,则码流中添加 EOB 。 241 | 242 | 求解AC码流的代码如下 243 | 244 | ~~~matlab 245 | ac_cof = quan_mat(2: end, :); 246 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 247 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 248 | EOB = [1, 0, 1, 0]'; 249 | [ac_h, ac_w] = size(ac_size); 250 | ac_code = []; 251 | for i = 1: 1: ac_w 252 | run = 0; 253 | for j = 1: 1: ac_h 254 | if ac_size(j, i) == 0 255 | run = run + 1; 256 | else 257 | run_epoch = floor(run / 16); 258 | run_mod = mod(run, 16); 259 | run = 0; 260 | while run_epoch > 0 261 | ac_code = [ac_code; ZRL]; 262 | run_epoch = run_epoch - 1; 263 | end 264 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 265 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 266 | if ac_cof(j, i) < 0 267 | amp_temp = ~amp_temp; 268 | end 269 | ac_code = [ac_code; size_temp; amp_temp]; 270 | end 271 | end 272 | ac_code = [ac_code; EOB]; 273 | end 274 | ~~~ 275 | 276 | 至此,就完成了DC码流与AC码流的计算。 277 | 278 | 本题代码位于`src/hw2_4_9.m`中,编码函数封装在函数文件`src/Compress.m`中。 279 | 280 | #### (10)计算压缩比 281 | 282 | 计算时需统一单位,我这里用 bit 计算。原图 1 像素为 0~255,用 8bits 储存,因此压缩比计算公式为 283 | 284 | ~~~matlab 285 | compress_rate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 286 | ~~~ 287 | 288 | 计算结果为:6.4247 289 | 290 | 本题代码位于`src/hw2_4_10.m`中。 291 | 292 | #### (11)JPEG 解码 293 | 294 | 解码需分别对DC码流与AC码流解码。 295 | 296 | 由于Huffman编码为前缀编码,任何码字都不是其他码字的前缀,因此解码起来比较简单。对于一段码字,只要在Huffman编码表中匹配上了,那由于前缀码的特性,这段码字就是一段Huffman码。 297 | 298 | 因此,对DC码流进行解码时,逐段码流和Huffman编码表DCTAB进行比对,一旦比对上了,就反推这段码流对应的Category,然后接下来的长为Category的码流,代表的就是幅度。如果首位是1,则是正幅度;首位是0,则是负幅度;Category若为0,则幅度就为0。对整个DC码流进行如上遍历操作,很容易就将DC码流解码完成。解码完成后,得到的是差分编码后的结果,然后还要将差分编码再解码,得到原始的DC系数。代码如下 299 | 300 | ~~~matlab 301 | dc_diff = zeros([img_height * img_width / 64, 1]); 302 | last_idx = 1; 303 | cnt = 1; 304 | i = 1; 305 | while i <= length(dc_code) 306 | for j = 1: 1: size(DCTAB, 1) 307 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 308 | if j - 1 ~= 0 309 | mag_temp = dc_code(i + 1: i + j - 1)'; 310 | if mag_temp(1) == 1 311 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 312 | else 313 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 314 | end 315 | end 316 | last_idx = i + j; 317 | i = i + j; 318 | cnt = cnt + 1; 319 | break; 320 | end 321 | end 322 | i = i + 1; 323 | end 324 | dc_cof = zeros(size(dc_diff)); 325 | dc_cof(1) = dc_diff(1); 326 | for i = 2: 1: size(dc_cof, 1) 327 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 328 | end 329 | ~~~ 330 | 331 | AC码流解码过程类似,不过得注意两个特殊符号:ZRL 和 EOB。AC码流解码过程中,不仅要在Huffman编码表中去比对,也要与 ZRL 和 EOB 去比对,其余步骤和DC码流解码思路一样。代码如下 332 | 333 | ~~~matlab 334 | ac_cof = zeros([63, img_height * img_width / 64]); 335 | i = 1; 336 | last_idx = 1; 337 | row_cnt = 1; 338 | col_cnt = 1; 339 | while i <= length(ac_code) 340 | if isequal(ac_code(last_idx: i), EOB) 341 | col_cnt = col_cnt + 1; 342 | row_cnt = 1; 343 | last_idx = i + 1; 344 | i = last_idx; 345 | elseif isequal(ac_code(last_idx: i), ZRL) 346 | row_cnt = row_cnt + 16; 347 | last_idx = i + 1; 348 | i = last_idx; 349 | else 350 | for j = 1: 1: size(ACTAB, 1) 351 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 352 | row_cnt = row_cnt + ACTAB(j, 1); 353 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 354 | if amp_temp(1) == 1 355 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 356 | else 357 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 358 | end 359 | row_cnt = row_cnt + 1; 360 | last_idx = i + ACTAB(j, 2) + 1; 361 | i = last_idx; 362 | break; 363 | end 364 | end 365 | end 366 | i = i + 1; 367 | end 368 | ~~~ 369 | 370 | 得到解码后DC码流和AC码流后,两者合并得到量化矩阵,再经过第8问的逆过程与逆DCT变换,即可解码出原图。代码如下 371 | 372 | ~~~matlab 373 | whole_cof = [dc_cof'; ac_cof]; 374 | hall_quan = zeros([img_height * 8, img_width / 8]); 375 | for i = 0: 1: img_height / 8 - 1 376 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 377 | end 378 | img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 379 | ~~~ 380 | 381 | 接下来是评价编解码效果。 382 | 383 | 客观方法计算PSNR,PSNR计算公式为 384 | $$ 385 | PSNR=10lg(\frac{255^2}{MSE}) 386 | $$ 387 | 实际计算结果为:31.1874,是一个比较高的结果,因此说明图像失真较小。 388 | 389 | 主观上,原图与编解码后的图片如下 390 | 391 | ![hw2_4_11](./img/hw2_4_11.png) 392 | 393 | 二者差别不大,只在一些细微处有少许差别,说明压缩后图像失真较小。 394 | 395 | 本题代码位于`src/hw2_4_11.m`中,解码代码封装在函数文件`src/Decompress.m`中。 396 | 397 | #### (12)量化步长减半 398 | 399 | 要让量化步长减半,只需让提供的QTAB除以2即可。 400 | 401 | 量化步长减半后,PSNR从31.1874变为34.2067,图像失真程度减少;但压缩比从6.4247降低到4.4097,压缩程度减少。这说明,要保持高的压缩比,就要牺牲图像的还原度。 402 | 403 | 本题代码位于`src/hw2_4_12.m`中。 404 | 405 | #### (13)对雪花图像编码 406 | 407 | 对雪花图像编解码,原图与解码图像如下 408 | 409 | ![hw2_4_13](./img/hw2_4_13.png) 410 | 411 | 压缩比为 3.645,PSNR 为 22.9244。 412 | 413 | 可以看到,PSNR不算大,压缩比也较小。原因是,雪花图像并不美丽,像是随机生成的噪音,高频分量大,导致量化后 Category 可能偏大,导致 Huffman 编码较长,压缩比较小。同时由于高频分量经过量化后,失真较大,因此 PSNR 较小。 414 | 415 | 本题代码位于`src/hw2_4_13.m`中。 416 | 417 | ### 3.3 信息隐藏 418 | 419 | #### (1)空域隐藏和提取 420 | 421 | 空域信息隐藏,是将信息表示成二进制码流,再二进制码流的每位信息替换图像中各像素亮度分量的最低位。这一操作使用 matlab 提供的 `bitset`函数即可实现。代码如下 422 | 423 | ~~~matlab 424 | rand_info = logical(randi([0, 1], height, width)); 425 | hall_hide = bitset(hall_gray, 1, rand_info); 426 | ~~~ 427 | 428 | 经过编解码后,要再提取出隐藏的信息时,使用 matlab 提供的`bitand`方法,即可实现信息提取。 429 | 430 | ~~~matlab 431 | [dc_code, ac_code, img_height, img_width] = Compress(hall_hide, DCTAB, ACTAB, QTAB); 432 | hall_decompress = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 433 | decode_info = bitand(hall_decompress, uint8(ones([height, width]))); 434 | ~~~ 435 | 436 | 经过提取后,与原本隐藏的信息进行对比,准确率大约在 50% 左右,和随机蒙的差不多,说明其抗 JPEG 编码能力极差。 437 | 438 | 本题代码位于`src/hw3_4_1.m`中 439 | 440 | #### (2)变换域信息隐藏 441 | 442 | 方法一:信息位替换量化后DCT系数的最低位。关键代码如下 443 | 444 | ~~~matlab 445 | hall_pre = double(img) - 128; 446 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 447 | [height, width] = size(hall_quan); 448 | 449 | quan_mat = zeros([64, height * width / 64]); 450 | for i = 0: 1: height / 64 - 1 451 | for j = 1: 1: width 452 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 453 | end 454 | end 455 | quan_mat = double(bitset(int32(quan_mat), 1, info)); 456 | ~~~ 457 | 458 | 方法二:信息位替换若干量化后DCT系数。我这里选用量化系数中,较小值的位置去替换量化后DCT系数,这样对原图的影响较小。 459 | 460 | 确定替换位置的方法如下 461 | 462 | ~~~matlab 463 | threshold = 12; 464 | less_thre = QTAB <= threshold; 465 | less_thre_idx = find(less_thre); 466 | ~~~ 467 | 468 | 然后根据选取的位置,用信息位去替换 469 | 470 | ~~~matlab 471 | hall_pre = double(img) - 128; 472 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 473 | [height, width] = size(hall_quan); 474 | 475 | quan_mat = zeros([64, height * width / 64]); 476 | for i = 0: 1: height / 64 - 1 477 | for j = 1: 1: width 478 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 479 | end 480 | end 481 | last_bit = bitand(int32(quan_mat), int32(ones(size(quan_mat)))); 482 | for i = 1: 1: size(last_bit, 2) 483 | last_bit(hide_idx, i) = info(:, i); 484 | end 485 | quan_mat = double(bitset(int32(quan_mat), 1, last_bit)); 486 | ~~~ 487 | 488 | 方法三:隐藏信息用1,-1序列表示,再逐一将信息位追加在每个块 Zig-Zag 顺序的最后一个非零系数后;若原本该最后一个系数不为0,那就用信息位替换该系数。关键代码如下 489 | 490 | ~~~matlab 491 | hall_pre = double(img) - 128; 492 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 493 | [height, width] = size(hall_quan); 494 | 495 | quan_mat = zeros([64, height * width / 64]); 496 | for i = 0: 1: height / 64 - 1 497 | for j = 1: 1: width 498 | col_temp = i * width + j; 499 | quan_mat(:, col_temp) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 500 | for k = 64: -1 : 1 501 | if quan_mat(k, col_temp) ~= 0 502 | if k == 64 503 | quan_mat(64, col_temp) = info(col_temp); 504 | else 505 | quan_mat(k + 1, col_temp) = info(col_temp); 506 | end 507 | break; 508 | end 509 | end 510 | end 511 | end 512 | ~~~ 513 | 514 | 三种方法实现起来都比较简单,其信息隐藏部分分别封装在函数文件`dct_conceal_1.m`、`dct_conceal_2.m`、`dct_conceal_3.m`中,将信息隐藏后的系数逆变换为图片的代码,分别封装在函数文件`dct_reveal_1.m`、`dct_reveal_2.m`、`dct_reveal_3.m`中。 515 | 516 | 三种方法隐藏随机信息后,客观上的对比如下 517 | 518 | ![hw3_4_2_details](./img/hw3_4_2_details.png) 519 | 520 | 可以看到,ACC都是1,压缩率第一种方法最小。总体来看,第二种和第三种方法效果差不多。 521 | 522 | 三种方法隐藏随机信息后,呈现的图片结果如下 523 | 524 | ![hw3_4_2](./img/hw3_4_2.png) 525 | 526 | 主观上从图片上看,用第一种方法隐藏信息的结果较差,第二、第三种方法效果差不多。理论上分析,由于直接在量化后的矩阵加信息位,如果量化系数较大,则最后一位改为信息位后,对原图的影响较大。而第二种方法则是考虑了这一影响,选择了QTAB中元素较小的位置进行信息隐藏,这样就对原图的影响较小。第三种方法也是类似的思路,由于1与-1的变化对整个系数矩阵影响较小,所以信息隐藏后对原图也不会有太大影响。 527 | 528 | 本题代码位于`src/hw3_4_2.m`中。 529 | 530 | ### 3.4 人脸检测 531 | 532 | #### (1)训练人脸标准 533 | 534 | 如果样本人脸大小不一,图像不需要首先调整为相同大小,只需对每个像素的颜色遍历即可。 535 | 536 | 训练人脸标准时,需要将RGB颜色对应到颜色数n,代码为 537 | 538 | ~~~matlab 539 | r = floor(face_temp(j, k, 1) * 2^(L - 8)); 540 | g = floor(face_temp(j, k, 2) * 2^(L - 8)); 541 | b = floor(face_temp(j, k, 3) * 2^(L - 8)); 542 | n = r * 2^(2 * L) + g * 2^L + b; 543 | ~~~ 544 | 545 | 然后根据颜色索引 n,统计各个颜色出现的频率,然后再对该频率向量归一化,得到一张图片的人脸标准。 546 | 547 | 对每张训练集图片都计算相应的人脸标准向量,然后取平均,得到训练好的人脸标准向量。 548 | 549 | 对于 L 为 3,4,5 的情况,将对应的人脸标准绘成图,如下 550 | 551 | ![hw4_3_1](./img/hw4_3_1.png) 552 | 553 | 可以看到,不同的 L 值,人脸特征的趋势是一致的。L 值越大,颜色区分能力越强,分辨率越高。对于较小的 L 值,会使大范围内相近的颜色不能被区分开。同时,对于较高的 L 值,相近的颜色范围内求和就会得到较低的 L 值的图像。 554 | 555 | 本题代码位于`src/hw4_3_1.m`中。 556 | 557 | #### (2)实现人脸检测算法 558 | 559 | 首先,我们使用人脸图像训练集训练人脸特征向量,然后使用该特征向量进行进一步人脸检测。 560 | 561 | 我设计的人脸检测算法,先用小矩形在图像上移动,逐个计算小矩形内的颜色特征向量,然后与训练的人脸特征向量计算距离,距离公式为 562 | $$ 563 | d(u,v)=1-\Sigma_n\sqrt{(u_n,v_n)} 564 | $$ 565 | 将所有与人脸特征向量距离小于阈值的小矩形,全部存储下来。由于小矩形移动时,会有一定的重叠。下一步就是将所有重叠的矩形,整合成一个大矩形。这里使用的是暴力遍历,对每个储存下来的小矩形,与其他所有矩形进行比较,将所有可以合成的小矩形一并合成,计算复杂度为 O(n^2) 。代码如下 566 | 567 | ~~~matlab 568 | for i = 1: 1: length(x_less) 569 | if x_less(i) ~= -1 570 | for j = i + 1: 1: length(x_less) 571 | if j ~= i && x_less(j) ~= -1 572 | if x_less(i) == x_less(j) 573 | if y_less(j) <= y_large(i) + col_width && y_less(j) >= y_less(i) 574 | y_large(i) = y_large(j); 575 | x_large(i) = max(x_large(i), x_large(j)); 576 | x_less(j) = -1; 577 | end 578 | end 579 | if y_less(i) == y_less(j) 580 | if x_less(j) <= x_large(i) + row_width && x_less(j) >= x_less(i) 581 | x_large(i) = x_large(j); 582 | y_large(i) = max(y_large(i), y_large(j)); 583 | x_less(j) = -1; 584 | end 585 | end 586 | end 587 | end 588 | end 589 | end 590 | ~~~ 591 | 592 | 使用这种二维遍历合并小矩形后,部分区域会产生重叠的大矩形。接下来,为了呈现良好的人脸检测效果,需要将重叠的大矩形去重。重叠的情况我分为以下几种: 593 | 594 | 情况一:角重叠 595 | 596 | ![hw4_3_2_situ_1](./img/hw4_3_2_situ_1.png) 597 | 598 | 情况二:中心重叠 599 | 600 | ![hw4_3_2_situ_2](./img/hw4_3_2_situ_2.png) 601 | 602 | 情况三:一边重叠 603 | 604 | ![hw4_3_2_situ_3](./img/hw4_3_2_situ_3.png) 605 | 606 | 我根据重叠的面积占大矩形面积的比例,来判断两个矩形的重叠程度。如果重叠程度过大,则舍弃其中一个矩形,因为它们很可能是识别出的同一个人脸。至此,可以认为剩余的矩形都不再重叠。 607 | 608 | 然后,我移除那些面积较小的矩形,因为面积过于小的矩形一般来说识别的并不是人脸。 609 | 610 | 最后,我对所有检测矩形进行优化。我将窄而长的矩形稍微压缩一下,尽可能压成正方形。同时,矩形的中心要稍稍上移。这是因为,检测算法是根据肤色来确定的,而脖子部分和人脸的颜色接近,人脸检测时,容易将脖子也一并识别,所以矩形可能会呈现窄而宽,且中心偏下。如下图所示 611 | 612 | ![hw4_3_2_situ_4](./img/hw4_3_2_situ_4.png) 613 | 614 | 当然,如果一个矩形宽而矮,我也会进行一定的压缩一下,让它更接近正方形。 615 | 616 | 至此,就完成了人脸检测的工作。 617 | 618 | 对于 L=3,4,5 ,人脸检测的效果如下 619 | 620 | ![detect_face_1](./img/detect_face_1.png) 621 | 622 | 可以看到,对于大部分人脸识别效果都是不错的。少部分人脸由于靠的太近或衣服颜色问题,部分人脸被识别在了一起,但总体效果还是可以的。 623 | 624 | 在人脸检测过程中,我将小矩形的长宽都定为20,移动步长为5,面积重叠比例大于0.01则认为识别了同一张人脸。L=3,4,5 时,距离相近的阈值分别为 0.053,0.603,0.750,逐步上升。 625 | 626 | 本题代码位于`src/hw4_3_2.m`中,人脸检测算法封装在函数文件`src/DetectFace.m`中,测试图像为`图像处理所需资源/test1.png`。 627 | 628 | #### (3)图像旋转、拉长、变色 629 | 630 | 使用`imrotate`方法将图片旋转,使用`imresize`方法将图片拉长,使用`imadjust`方法将图片变色,再进行人脸检测。参数采用 L=3 。结果如下 631 | 632 | 旋转90°: 633 | 634 | ![rot90](./img/rot90.png) 635 | 636 | 拉长一倍: 637 | 638 | ![doublewidth](./img/doublewidth.png) 639 | 640 | 变色: 641 | 642 | ![adjustcolor](./img/adjustcolor.png) 643 | 644 | 从识别结果看,旋转90°的识别结果和原本的识别结果较不一样。拉长一倍和变色后,识别结果都和原本的识别结果相近,且拉长一倍后检测效果有所改善。 645 | 646 | 理论上分析,旋转90°后,由于图像色彩没有变化,所以人脸检测中储存的小矩形应当是和原本图像的结果是一样的。但是由于图像旋转了90°,导致合并小矩形以及之后的去重大矩形的过程中,和原图的结果不一致,导致最后的检测结果和原图不一致。 647 | 648 | 图像拉长一倍后,原本图像相近的色彩,在图像被拉长后,也显得容易区分了。所以在原本 L=3 的情况下区分不开的人脸,在图像被拉长后也能区分出来了。 649 | 650 | 在图像稍微变色后,识别结果和原图区别不大。这是因为图像整体颜色的改变,相当于人脸颜色整体改变,在多维空间中的人脸向量与未改变颜色前的向量,应当是平行关系。而检测距离是与检测的人脸向量和训练的标准向量的夹角有关的。整体改变颜色,不改变这个夹角,所以距离是基本不会变的。因此,图像稍微变色,对识别结果不会有太大的影响。 651 | 652 | 本题代码位于`src/hw4_3_3.m`中 653 | 654 | #### (4)重新选择人脸样本的训练标准 655 | 656 | 在进行人脸检测的实验时,由于仅仅是根据肤色去判断,因此人的手、脖子等与脸颜色接近的部分,也会被识别为人脸。而且如果有些人戴了眼镜,由于训练样本戴眼镜的较少,所以眼镜对识别结果也会有一些影响。因此,如果重新选择人脸样本训练标准的话,除了肤色,可能还得考虑人脸的进阶特征。具体方法是,首先通过肤色初筛人脸区域,然后进一步考虑识别的人脸区域有没有五官特征。五官特征可以由训练图像的梯度进行判断。 -------------------------------------------------------------------------------- /docs/实验报告.md: -------------------------------------------------------------------------------- 1 | # 实验报告:MATLAB 图像处理大作业 2 | 3 | --- 4 | 5 | 无03 唐昌礼 2020010694 6 | 7 | --- 8 | 9 | ## 一、实验目的 10 | 11 | 1. 了解计算机储存和处理图像的基础知识 12 | 2. 理解 JPEG 标准的基本原理,变换域编码和量化的基本思想,掌握 MATLAB 处理矩阵和图像的常用命令 13 | 3. 理解变换域信息隐藏的基本方法 14 | 4. 掌握基于肤色的人脸检测算法 15 | 16 | ## 二、实验平台 17 | 18 | 我的所有代码均在 MATLAB R2020b 版本上成功运行 19 | 20 | ## 三、实验内容 21 | 22 | ### 3.1 图像基础知识 23 | 24 | #### (1)在图像中画圆 25 | 26 | 首先确定圆的圆心,为测试图像的中心;然后确定圆的半径,为图像长、宽中的较小值。接下来根据圆的定义画圆:所有与圆心的距离等于半径的点构成一个圆。在这里,为了让画出的圆清晰可见,将所有与圆心距离在 0.98~1.02 倍半径之间的点全部标红,呈现在图像上。取出标红点的关键代码如下: 27 | 28 | ~~~matlab 29 | size_pic = size(hall_gray); 30 | idx = zeros([size_pic, 2]); 31 | for i = 1: 1: height 32 | for j = 1: 1: width 33 | idx(i, j, 1) = i; 34 | idx(i, j, 2) = j; 35 | end 36 | end 37 | circle_idx = ((idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 <= (1.02 * radius) ^ 2 & ... 38 | (idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 >= (0.98 * radius) ^ 2); 39 | ~~~ 40 | 41 | 最后效果如下: 42 | 43 | ![hw_1_3_2_circle](../img/hw_1_3_2_circle.jpg) 44 | 45 | #### (2)将测试图像涂成国际象棋状的“黑白格” 46 | 47 | 将测试图像分成 8*8 的区域,然后对每块区域根据国际象棋“黑白格”的规则染色。在黑色的地方,将原图对应区域置为0;白色的地方则保持不变。结果如下 48 | 49 | ![hw_1_3_2_chessboard](../img/hw_1_3_2_chessboard.jpg) 50 | 51 | 本题代码位于`src/hw1_3_2.m`中。 52 | 53 | ### 3.2 图像压缩编码 54 | 55 | #### (1)在变换域预处理 56 | 57 | 图像的预处理是将每个像素灰度值减128,若要在变换域进行,可以利用DCT变换的线性性来操作。 58 | 59 | 首先对原图像作二维DCT变换,然后对相同大小的、所有元素均为128的矩阵作二维DCT变换。由于全128的矩阵DCT变换结果只有直流分量,即左上角元素,所以只需让原图的DCT变换结果的直流分量减去全128矩阵DCT变换结果的直流分量即可。具体代码如下: 60 | 61 | ~~~matlab 62 | [part_h, part_w] = size(hall_part); 63 | DC_128 = dct2(zeros(part_h, part_w) + 128); 64 | maphall_2 = dct2(hall_part); 65 | maphall_2(1, 1) = maphall_2(1, 1) - DC_128(1, 1); 66 | ~~~ 67 | 68 | 这样操作后,与先预处理再DCT变换的结果相比,计算MSE,为 7.1000e-28,非常小,说明两种算法计算结果相同。 69 | 70 | 本题代码位于`src/hw2_4_1.m`中。 71 | 72 | #### (2)实现二维DCT变换 73 | 74 | 二维DCT变换,首先是找到DCT算子**D**。我直接根据指导书的公式(2.5),在`matlab`中实现算子**D**。关键代码如下: 75 | 76 | ~~~matlab 77 | function D_mat = DCT2_D(input_dim) 78 | row = linspace(0, input_dim - 1, input_dim)'; 79 | col = linspace(1, input_dim * 2 - 1, input_dim); 80 | cos_mat = cos(row * col * pi / (2 * input_dim)); 81 | cos_mat(1, :) = cos_mat(1, :) / sqrt(2); 82 | D_mat = sqrt(2 / input_dim) * cos_mat; 83 | end 84 | ~~~ 85 | 86 | 得到DCT算子**D**后,就可以根据输入图像的维度,计算二维DCT变换。假设灰度图像维度为 [height, width],则其二维DCT变换结果可以用以下代码来求得 87 | 88 | ~~~matlab 89 | [height, width] = size(input_mat); 90 | dct_coef = DCT2_D(height) * double(input_mat) * DCT2_D(width)'; 91 | ~~~ 92 | 93 | 我将我实现的DCT变换函数,与`matlab`提供的`dct2`进行比较,计算MSE,为9.9944e-26。MSE非常小,可以认为两种方法计算结果相同。 94 | 95 | 本题代码位于`src/hw2_4_2.m`中,计算DCT算子的函数封装在`src/DCT2_D.m`中,计算二维DCT变换的函数封装在`src/DCT2.m`中。 96 | 97 | #### (3)部分DCT系数置零 98 | 99 | 将原图按 8*8 分块后,对每块作二维DCT变换,再将DCT系数矩阵右侧四列、左侧四列系数矩阵分别置零,再作逆DCT变换看结果。 100 | 101 | 理论上分析,DCT系数矩阵左上角元素代表低频分量,右下方系数表示横竖两个方向中迅速变换的高频分量。由于常见的图片通常颜色、亮度都是缓慢变换的,因此DCT系数矩阵越往左上方取值越大,越向右下方取值越小。由于低频分量值大,对图像影响大,高频分量值小,对图像影响小,所以,理论分析DCT系数矩阵右侧四列元素置零对图像影响较小,左侧四列元素置零则会对图像有很大影响。实际结果也正是如此,结果如下: 102 | 103 | ![hw2_4_3](../img/hw2_4_3.png) 104 | 105 | 本题代码位于`src/hw2_4_3.m`中。 106 | 107 | #### (4)DCT系数转置、逆时针旋转90°、旋转180° 108 | 109 | 若DCT系数矩阵**C**转置,考虑DCT变换的公式 110 | $$ 111 | C=dct(P)=DPD^T\\ 112 | P=idct(C)=D^TCD\\ 113 | D^{-1}=D^T 114 | $$ 115 | 所以**C**转置后再逆变换,结果为 116 | $$ 117 | D^TC^TD=(D^TCD)^T=P^T 118 | $$ 119 | 结果为原图的转置。 120 | 121 | 由于右上方的系数反映图像中横向变化的纹理强度,逆时针旋转90°后,原本右下方高频分量的元素来到了右上方。由于高频分量元素本身就较小,所以DCT系数矩阵逆时针旋转90°后再逆变换,得到的图像横向几乎不会变化,呈现很强的横向纹理。 122 | 123 | 若将DCT系数矩阵旋转180°,左上方低频分量将会和右下方高频分量换位,导致恢复的图像低频分量小,高频分量大,从而图像变化剧烈,呈现格子状的纹理。 124 | 125 | 实际运行时,结果与理论分析相近,如下 126 | 127 | ![hw2_4_4](../img/hw2_4_4.png) 128 | 129 | 本题代码位于`src/hw2_4_4.m`中。 130 | 131 | #### (5)系统特性 132 | 133 | 如果认为差分编码是一个系统,则这个系统方程为 134 | $$ 135 | y(n)=x(n-1)-x(n) 136 | $$ 137 | 使用`freqz`函数查看该系统频率响应如下 138 | 139 | ![hw2_4_5](../img/hw2_4_5.png) 140 | 141 | 由此可以看出,该系统可看作为高通滤波器。DC系数先进行差分编码再进行熵编码,说明DC系数含有较多的高频分量。 142 | 143 | 本题代码位于`src/hw2_4_5.m`中。 144 | 145 | #### (6)DC预测误差与Category 146 | 147 | 记DC预测误差为`delta`,Category为`C`,则它们之间的关系为 148 | $$ 149 | C=ceil(log2(abs(delta)+1)) 150 | $$ 151 | 由此可以推出,Category表示了DC预测误差的二进制表示位数。 152 | 153 | #### (7)设计实现Zig-Zag扫描 154 | 155 | 由于本次作业中,只需对 8*8 的矩阵进行 Zig-Zag 扫描,因此可以直接将各索引手动编排好,然后直接用 matlab 的数组索引功能完成扫描。扫描代码如下 156 | 157 | ~~~matlab 158 | function output = zig_zag(input) 159 | temp = reshape(input, [64, 1]); 160 | output = (temp([ 161 | 1, 9, 2, 3, 10, 17, 25, 18, ... 162 | 11, 4, 5, 12, 19, 26, 33, 41, ... 163 | 34, 27, 20, 13, 6, 7, 14, 21, ... 164 | 28, 35, 42, 49, 57, 50, 43, 36, ... 165 | 29, 22, 15, 8, 16, 23, 30, 37, ... 166 | 44, 51, 58, 59, 52, 45, 38, 31, ... 167 | 24, 32, 39, 46, 53, 60, 61, 54, ... 168 | 47, 40, 48, 55, 62, 63, 56, 64 ... 169 | ])); 170 | end 171 | ~~~ 172 | 173 | 首先,我先将输入的 `8*8` 的矩阵变为 `64*1` 的大小。然后,我根据 Zig-Zag 的规则,事先将理论的 Zig-Zag 扫描结果的各位置索引记好。最后,即可直接通过数组索引的方式完成扫描。 174 | 175 | 为了验证我这一算法是正确的,我设计了如下测试样例进行检验 176 | 177 | ~~~matlab 178 | test_input = [ 179 | [1, 2, 6, 7, 15, 16, 28, 29]; ... 180 | [3, 5, 8, 14, 17, 27, 30, 43]; ... 181 | [4, 9, 13, 18, 26, 31, 42, 44]; ... 182 | [10, 12, 19, 25, 32, 41, 45, 54]; ... 183 | [11, 20, 24, 33, 40, 46, 53, 55]; ... 184 | [21, 23, 34, 39, 47, 52, 56, 61]; ... 185 | [22, 35, 38, 48, 51, 57, 60, 62]; ... 186 | [36, 37, 49, 50, 58, 59, 63, 64] ... 187 | ]; 188 | ~~~ 189 | 190 | 这一矩阵经过 Zig-Zag 扫描后,理论上应该输出 1~64 的序列,使用我设计的算法扫描得到结果正是如此,说明算法实现无误。 191 | 192 | 本题代码位于`src/hw2_4_7.m`中。 193 | 194 | #### (8)对测试图像分块、DCT与量化 195 | 196 | 首先是预处理,灰度图各像素减去128;然后是将图像每个 8*8 的块进行DCT变换并点除量化矩阵;最后是将结果依次排列。 197 | 198 | 前两步的代码如下 199 | 200 | ~~~matlab 201 | hall_pre = double(hall_gray) - 128; 202 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 203 | ~~~ 204 | 205 | 重新排列时,我按照从左至右、从上至下的方式来遍历,代码如下 206 | 207 | ~~~matlab 208 | [height, width] = size(hall_quan); 209 | res = zeros([64, height * width / 64]); 210 | for i = 0: 1: height / 64 - 1 211 | for j = 1: 1: width 212 | res(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 213 | end 214 | end 215 | ~~~ 216 | 217 | 本题代码位于`src/hw2_4_8.m`中。 218 | 219 | #### (9)实现 JPEG 编码 220 | 221 | 关键点在于计算DC系数的码流和AC系数的码流。 222 | 223 | 首先是DC系数码流。DC系数就是第8问中量化矩阵的第一行,码流的计算需要先通过差分编码再进行熵编码。 224 | 225 | 熵编码时,可以利用DC预测误差与Category的关系:Category表示了DC预测误差的二进制表示位数,并利用给好的Huffman编码表DCTAB进行编码。对于每个预测误差,首先根据其Category确定对应的Huffman编码,然后将其幅度用二进制表示,接在其Huffman编码后。若幅度为0,无需编码幅度,Huffman编码已经包含了该信息。对于负数,幅度采用1补码,此时最高位为0。这样的方式表示每个预测误差,可以准确无误并容易解码。代码如下,其中`quan_mat`为量化矩阵: 226 | 227 | ~~~matlab 228 | dc_cof = quan_mat(1, :)'; 229 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 230 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 231 | dc_code = []; 232 | for i = 1: 1: length(dc_diff) 233 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 234 | if dc_diff(i) ~= 0 235 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 236 | if dc_diff(i) < 0 237 | mag_temp = ~mag_temp; 238 | end 239 | else 240 | mag_temp = []; 241 | end 242 | dc_code = [dc_code; cat_temp; mag_temp]; 243 | end 244 | ~~~ 245 | 246 | AC系数编码与DC系数稍有不同。AC系数是量化矩阵第2行及之后的元素,需要根据每一个非零系数值及其行程进行编码。AC系数中可能有较多的0,因此利用行程编码能较好地提高压缩率。我们通过提供的ACTAB,以非零数值与行程为索引,找到对应的Huffman编码,然后再在其后接上非零数值的二进制表示(负数为1补码),从而完成对AC系数的编码。 247 | 248 | 这其中用到两个符号: 249 | 250 | - ZRL:[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1],表示连续 16 个 0 251 | - EOB:[1, 0, 1, 0],表示每个块中编码完最后一个非零系数 252 | 253 | 我采用这样的方式进行编码:对每个块(也就是 63*1 的列),遍历时实时记录经过的零个数,当到第一个非零数值时,对记录的零个数求除 16 的商与余数,商为需要加上的 ZRL 的个数,余数为用于提供Huffman编码的索引,即行程。若一直到每个块最后,都没找到非零数值,则码流中添加 EOB 。 254 | 255 | 求解AC码流的代码如下 256 | 257 | ~~~matlab 258 | ac_cof = quan_mat(2: end, :); 259 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 260 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 261 | EOB = [1, 0, 1, 0]'; 262 | [ac_h, ac_w] = size(ac_size); 263 | ac_code = []; 264 | for i = 1: 1: ac_w 265 | run = 0; 266 | for j = 1: 1: ac_h 267 | if ac_size(j, i) == 0 268 | run = run + 1; 269 | else 270 | run_epoch = floor(run / 16); 271 | run_mod = mod(run, 16); 272 | run = 0; 273 | while run_epoch > 0 274 | ac_code = [ac_code; ZRL]; 275 | run_epoch = run_epoch - 1; 276 | end 277 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 278 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 279 | if ac_cof(j, i) < 0 280 | amp_temp = ~amp_temp; 281 | end 282 | ac_code = [ac_code; size_temp; amp_temp]; 283 | end 284 | end 285 | ac_code = [ac_code; EOB]; 286 | end 287 | ~~~ 288 | 289 | 至此,就完成了DC码流与AC码流的计算。 290 | 291 | 本题代码位于`src/hw2_4_9.m`中,编码函数封装在函数文件`src/Compress.m`中。 292 | 293 | #### (10)计算压缩比 294 | 295 | 计算时需统一单位,我这里用 bit 计算。原图 1 像素为 0~255,用 8bits 储存,因此压缩比计算公式为 296 | 297 | ~~~matlab 298 | compress_rate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 299 | ~~~ 300 | 301 | 计算结果为:6.4247 302 | 303 | 本题代码位于`src/hw2_4_10.m`中。 304 | 305 | #### (11)JPEG 解码 306 | 307 | 解码需分别对DC码流与AC码流解码。 308 | 309 | 由于Huffman编码为前缀编码,任何码字都不是其他码字的前缀,因此解码起来比较简单。对于一段码字,只要在Huffman编码表中匹配上了,那由于前缀码的特性,这段码字就是一段Huffman码。 310 | 311 | 因此,对DC码流进行解码时,逐段码流和Huffman编码表DCTAB进行比对,一旦比对上了,就反推这段码流对应的Category,然后接下来的长为Category的码流,代表的就是幅度。如果首位是1,则是正幅度;首位是0,则是负幅度;Category若为0,则幅度就为0。对整个DC码流进行如上遍历操作,很容易就将DC码流解码完成。解码完成后,得到的是差分编码后的结果,然后还要将差分编码再解码,得到原始的DC系数。代码如下 312 | 313 | ~~~matlab 314 | dc_diff = zeros([img_height * img_width / 64, 1]); 315 | last_idx = 1; 316 | cnt = 1; 317 | i = 1; 318 | while i <= length(dc_code) 319 | for j = 1: 1: size(DCTAB, 1) 320 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 321 | if j - 1 ~= 0 322 | mag_temp = dc_code(i + 1: i + j - 1)'; 323 | if mag_temp(1) == 1 324 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 325 | else 326 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 327 | end 328 | end 329 | last_idx = i + j; 330 | i = i + j; 331 | cnt = cnt + 1; 332 | break; 333 | end 334 | end 335 | i = i + 1; 336 | end 337 | dc_cof = zeros(size(dc_diff)); 338 | dc_cof(1) = dc_diff(1); 339 | for i = 2: 1: size(dc_cof, 1) 340 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 341 | end 342 | ~~~ 343 | 344 | AC码流解码过程类似,不过得注意两个特殊符号:ZRL 和 EOB。AC码流解码过程中,不仅要在Huffman编码表中去比对,也要与 ZRL 和 EOB 去比对,其余步骤和DC码流解码思路一样。代码如下 345 | 346 | ~~~matlab 347 | ac_cof = zeros([63, img_height * img_width / 64]); 348 | i = 1; 349 | last_idx = 1; 350 | row_cnt = 1; 351 | col_cnt = 1; 352 | while i <= length(ac_code) 353 | if isequal(ac_code(last_idx: i), EOB) 354 | col_cnt = col_cnt + 1; 355 | row_cnt = 1; 356 | last_idx = i + 1; 357 | i = last_idx; 358 | elseif isequal(ac_code(last_idx: i), ZRL) 359 | row_cnt = row_cnt + 16; 360 | last_idx = i + 1; 361 | i = last_idx; 362 | else 363 | for j = 1: 1: size(ACTAB, 1) 364 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 365 | row_cnt = row_cnt + ACTAB(j, 1); 366 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 367 | if amp_temp(1) == 1 368 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 369 | else 370 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 371 | end 372 | row_cnt = row_cnt + 1; 373 | last_idx = i + ACTAB(j, 2) + 1; 374 | i = last_idx; 375 | break; 376 | end 377 | end 378 | end 379 | i = i + 1; 380 | end 381 | ~~~ 382 | 383 | 得到解码后DC码流和AC码流后,两者合并得到量化矩阵,再经过第8问的逆过程与逆DCT变换,即可解码出原图。代码如下 384 | 385 | ~~~matlab 386 | whole_cof = [dc_cof'; ac_cof]; 387 | hall_quan = zeros([img_height * 8, img_width / 8]); 388 | for i = 0: 1: img_height / 8 - 1 389 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 390 | end 391 | img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 392 | ~~~ 393 | 394 | 接下来是评价编解码效果。 395 | 396 | 客观方法计算PSNR,PSNR计算公式为 397 | $$ 398 | PSNR=10lg(\frac{255^2}{MSE}) 399 | $$ 400 | 实际计算结果为:31.1874,是一个比较高的结果,因此说明图像失真较小。 401 | 402 | 主观上,原图与编解码后的图片如下 403 | 404 | ![hw2_4_11](../img/hw2_4_11.png) 405 | 406 | 二者差别不大,只在一些细微处有少许差别,说明压缩后图像失真较小。 407 | 408 | 本题代码位于`src/hw2_4_11.m`中,解码代码封装在函数文件`src/Decompress.m`中。 409 | 410 | #### (12)量化步长减半 411 | 412 | 要让量化步长减半,只需让提供的QTAB除以2即可。 413 | 414 | 量化步长减半后,PSNR从31.1874变为34.2067,图像失真程度减少;但压缩比从6.4247降低到4.4097,压缩程度减少。这说明,要保持高的压缩比,就要牺牲图像的还原度。 415 | 416 | 本题代码位于`src/hw2_4_12.m`中。 417 | 418 | #### (13)对雪花图像编码 419 | 420 | 对雪花图像编解码,原图与解码图像如下 421 | 422 | ![hw2_4_13](../img/hw2_4_13.png) 423 | 424 | 压缩比为 3.645,PSNR 为 22.9244。 425 | 426 | 可以看到,PSNR不算大,压缩比也较小。原因是,雪花图像并不美丽,像是随机生成的噪音,高频分量大,导致量化后 Category 可能偏大,导致 Huffman 编码较长,压缩比较小。同时由于高频分量经过量化后,失真较大,因此 PSNR 较小。 427 | 428 | 本题代码位于`src/hw2_4_13.m`中。 429 | 430 | ### 3.3 信息隐藏 431 | 432 | #### (1)空域隐藏和提取 433 | 434 | 空域信息隐藏,是将信息表示成二进制码流,再二进制码流的每位信息替换图像中各像素亮度分量的最低位。这一操作使用 matlab 提供的 `bitset`函数即可实现。代码如下 435 | 436 | ~~~matlab 437 | rand_info = logical(randi([0, 1], height, width)); 438 | hall_hide = bitset(hall_gray, 1, rand_info); 439 | ~~~ 440 | 441 | 经过编解码后,要再提取出隐藏的信息时,使用 matlab 提供的`bitand`方法,即可实现信息提取。 442 | 443 | ~~~matlab 444 | [dc_code, ac_code, img_height, img_width] = Compress(hall_hide, DCTAB, ACTAB, QTAB); 445 | hall_decompress = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 446 | decode_info = bitand(hall_decompress, uint8(ones([height, width]))); 447 | ~~~ 448 | 449 | 经过提取后,与原本隐藏的信息进行对比,准确率大约在 50% 左右,和随机蒙的差不多,说明其抗 JPEG 编码能力极差。 450 | 451 | 本题代码位于`src/hw3_4_1.m`中 452 | 453 | #### (2)变换域信息隐藏 454 | 455 | 方法一:信息位替换量化后DCT系数的最低位。关键代码如下 456 | 457 | ~~~matlab 458 | hall_pre = double(img) - 128; 459 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 460 | [height, width] = size(hall_quan); 461 | 462 | quan_mat = zeros([64, height * width / 64]); 463 | for i = 0: 1: height / 64 - 1 464 | for j = 1: 1: width 465 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 466 | end 467 | end 468 | quan_mat = double(bitset(int32(quan_mat), 1, info)); 469 | ~~~ 470 | 471 | 方法二:信息位替换若干量化后DCT系数。我这里选用量化系数中,较小值的位置去替换量化后DCT系数,这样对原图的影响较小。 472 | 473 | 确定替换位置的方法如下 474 | 475 | ~~~matlab 476 | threshold = 12; 477 | less_thre = QTAB <= threshold; 478 | less_thre_idx = find(less_thre); 479 | ~~~ 480 | 481 | 然后根据选取的位置,用信息位去替换 482 | 483 | ~~~matlab 484 | hall_pre = double(img) - 128; 485 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 486 | [height, width] = size(hall_quan); 487 | 488 | quan_mat = zeros([64, height * width / 64]); 489 | for i = 0: 1: height / 64 - 1 490 | for j = 1: 1: width 491 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 492 | end 493 | end 494 | last_bit = bitand(int32(quan_mat), int32(ones(size(quan_mat)))); 495 | for i = 1: 1: size(last_bit, 2) 496 | last_bit(hide_idx, i) = info(:, i); 497 | end 498 | quan_mat = double(bitset(int32(quan_mat), 1, last_bit)); 499 | ~~~ 500 | 501 | 方法三:隐藏信息用1,-1序列表示,再逐一将信息位追加在每个块 Zig-Zag 顺序的最后一个非零系数后;若原本该最后一个系数不为0,那就用信息位替换该系数。关键代码如下 502 | 503 | ~~~matlab 504 | hall_pre = double(img) - 128; 505 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 506 | [height, width] = size(hall_quan); 507 | 508 | quan_mat = zeros([64, height * width / 64]); 509 | for i = 0: 1: height / 64 - 1 510 | for j = 1: 1: width 511 | col_temp = i * width + j; 512 | quan_mat(:, col_temp) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 513 | for k = 64: -1 : 1 514 | if quan_mat(k, col_temp) ~= 0 515 | if k == 64 516 | quan_mat(64, col_temp) = info(col_temp); 517 | else 518 | quan_mat(k + 1, col_temp) = info(col_temp); 519 | end 520 | break; 521 | end 522 | end 523 | end 524 | end 525 | ~~~ 526 | 527 | 三种方法实现起来都比较简单,其信息隐藏部分分别封装在函数文件`dct_conceal_1.m`、`dct_conceal_2.m`、`dct_conceal_3.m`中,将信息隐藏后的系数逆变换为图片的代码,分别封装在函数文件`dct_reveal_1.m`、`dct_reveal_2.m`、`dct_reveal_3.m`中。 528 | 529 | 三种方法隐藏随机信息后,客观上的对比如下 530 | 531 | ![hw3_4_2_details](../img/hw3_4_2_details.png) 532 | 533 | 可以看到,ACC都是1,压缩率第一种方法最小。总体来看,第二种和第三种方法效果差不多。 534 | 535 | 三种方法隐藏随机信息后,呈现的图片结果如下 536 | 537 | ![hw3_4_2](../img/hw3_4_2.png) 538 | 539 | 主观上从图片上看,用第一种方法隐藏信息的结果较差,第二、第三种方法效果差不多。理论上分析,由于直接在量化后的矩阵加信息位,如果量化系数较大,则最后一位改为信息位后,对原图的影响较大。而第二种方法则是考虑了这一影响,选择了QTAB中元素较小的位置进行信息隐藏,这样就对原图的影响较小。第三种方法也是类似的思路,由于1与-1的变化对整个系数矩阵影响较小,所以信息隐藏后对原图也不会有太大影响。 540 | 541 | 本题代码位于`src/hw3_4_2.m`中。 542 | 543 | ### 3.4 人脸检测 544 | 545 | #### (1)训练人脸标准 546 | 547 | 如果样本人脸大小不一,图像不需要首先调整为相同大小,只需对每个像素的颜色遍历即可。 548 | 549 | 训练人脸标准时,需要将RGB颜色对应到颜色数n,代码为 550 | 551 | ~~~matlab 552 | r = floor(face_temp(j, k, 1) * 2^(L - 8)); 553 | g = floor(face_temp(j, k, 2) * 2^(L - 8)); 554 | b = floor(face_temp(j, k, 3) * 2^(L - 8)); 555 | n = r * 2^(2 * L) + g * 2^L + b; 556 | ~~~ 557 | 558 | 然后根据颜色索引 n,统计各个颜色出现的频率,然后再对该频率向量归一化,得到一张图片的人脸标准。 559 | 560 | 对每张训练集图片都计算相应的人脸标准向量,然后取平均,得到训练好的人脸标准向量。 561 | 562 | 对于 L 为 3,4,5 的情况,将对应的人脸标准绘成图,如下 563 | 564 | ![hw4_3_1](../img/hw4_3_1.png) 565 | 566 | 可以看到,不同的 L 值,人脸特征的趋势是一致的。L 值越大,颜色区分能力越强,分辨率越高。对于较小的 L 值,会使大范围内相近的颜色不能被区分开。同时,对于较高的 L 值,相近的颜色范围内求和就会得到较低的 L 值的图像。 567 | 568 | 本题代码位于`src/hw4_3_1.m`中。 569 | 570 | #### (2)实现人脸检测算法 571 | 572 | 首先,我们使用人脸图像训练集训练人脸特征向量,然后使用该特征向量进行进一步人脸检测。 573 | 574 | 我设计的人脸检测算法,先用小矩形在图像上移动,逐个计算小矩形内的颜色特征向量,然后与训练的人脸特征向量计算距离,距离公式为 575 | $$ 576 | d(u,v)=1-\Sigma_n\sqrt{(u_n,v_n)} 577 | $$ 578 | 将所有与人脸特征向量距离小于阈值的小矩形,全部存储下来。由于小矩形移动时,会有一定的重叠。下一步就是将所有重叠的矩形,整合成一个大矩形。这里使用的是暴力遍历,对每个储存下来的小矩形,与其他所有矩形进行比较,将所有可以合成的小矩形一并合成,计算复杂度为 O(n^2) 。代码如下 579 | 580 | ~~~matlab 581 | for i = 1: 1: length(x_less) 582 | if x_less(i) ~= -1 583 | for j = i + 1: 1: length(x_less) 584 | if j ~= i && x_less(j) ~= -1 585 | if x_less(i) == x_less(j) 586 | if y_less(j) <= y_large(i) + col_width && y_less(j) >= y_less(i) 587 | y_large(i) = y_large(j); 588 | x_large(i) = max(x_large(i), x_large(j)); 589 | x_less(j) = -1; 590 | end 591 | end 592 | if y_less(i) == y_less(j) 593 | if x_less(j) <= x_large(i) + row_width && x_less(j) >= x_less(i) 594 | x_large(i) = x_large(j); 595 | y_large(i) = max(y_large(i), y_large(j)); 596 | x_less(j) = -1; 597 | end 598 | end 599 | end 600 | end 601 | end 602 | end 603 | ~~~ 604 | 605 | 使用这种二维遍历合并小矩形后,部分区域会产生重叠的大矩形。接下来,为了呈现良好的人脸检测效果,需要将重叠的大矩形去重。重叠的情况我分为以下几种: 606 | 607 | 情况一:角重叠 608 | 609 | ![hw4_3_2_situ_1](../img/hw4_3_2_situ_1.png) 610 | 611 | 情况二:中心重叠 612 | 613 | ![hw4_3_2_situ_2](../img/hw4_3_2_situ_2.png) 614 | 615 | 情况三:一边重叠 616 | 617 | ![hw4_3_2_situ_3](../img/hw4_3_2_situ_3.png) 618 | 619 | 我根据重叠的面积占大矩形面积的比例,来判断两个矩形的重叠程度。如果重叠程度过大,则舍弃其中一个矩形,因为它们很可能是识别出的同一个人脸。至此,可以认为剩余的矩形都不再重叠。 620 | 621 | 然后,我移除那些面积较小的矩形,因为面积过于小的矩形一般来说识别的并不是人脸。 622 | 623 | 最后,我对所有检测矩形进行优化。我将窄而长的矩形稍微压缩一下,尽可能压成正方形。同时,矩形的中心要稍稍上移。这是因为,检测算法是根据肤色来确定的,而脖子部分和人脸的颜色接近,人脸检测时,容易将脖子也一并识别,所以矩形可能会呈现窄而宽,且中心偏下。如下图所示 624 | 625 | ![hw4_3_2_situ_4](../img/hw4_3_2_situ_4.png) 626 | 627 | 当然,如果一个矩形宽而矮,我也会进行一定的压缩一下,让它更接近正方形。 628 | 629 | 至此,就完成了人脸检测的工作。 630 | 631 | 对于 L=3,4,5 ,人脸检测的效果如下 632 | 633 | ![detect_face_1](../img/detect_face_1.png) 634 | 635 | 可以看到,对于大部分人脸识别效果都是不错的。少部分人脸由于靠的太近或衣服颜色问题,部分人脸被识别在了一起,但总体效果还是可以的。 636 | 637 | 在人脸检测过程中,我将小矩形的长宽都定为20,移动步长为5,面积重叠比例大于0.01则认为识别了同一张人脸。L=3,4,5 时,距离相近的阈值分别为 0.053,0.603,0.750,逐步上升。 638 | 639 | 本题代码位于`src/hw4_3_2.m`中,人脸检测算法封装在函数文件`src/DetectFace.m`中,测试图像为`图像处理所需资源/test1.png`。 640 | 641 | #### (3)图像旋转、拉长、变色 642 | 643 | 使用`imrotate`方法将图片旋转,使用`imresize`方法将图片拉长,使用`imadjust`方法将图片变色,再进行人脸检测。参数采用 L=3 。结果如下 644 | 645 | 旋转90°: 646 | 647 | ![rot90](../img/rot90.png) 648 | 649 | 拉长一倍: 650 | 651 | ![doublewidth](../img/doublewidth.png) 652 | 653 | 变色: 654 | 655 | ![adjustcolor](../img/adjustcolor.png) 656 | 657 | 从识别结果看,旋转90°的识别结果和原本的识别结果较不一样。拉长一倍和变色后,识别结果都和原本的识别结果相近,且拉长一倍后检测效果有所改善。 658 | 659 | 理论上分析,旋转90°后,由于图像色彩没有变化,所以人脸检测中储存的小矩形应当是和原本图像的结果是一样的。但是由于图像旋转了90°,导致合并小矩形以及之后的去重大矩形的过程中,和原图的结果不一致,导致最后的检测结果和原图不一致。 660 | 661 | 图像拉长一倍后,原本图像相近的色彩,在图像被拉长后,也显得容易区分了。所以在原本 L=3 的情况下区分不开的人脸,在图像被拉长后也能区分出来了。 662 | 663 | 在图像稍微变色后,识别结果和原图区别不大。这是因为图像整体颜色的改变,相当于人脸颜色整体改变,在多维空间中的人脸向量与未改变颜色前的向量,应当是平行关系。而检测距离是与检测的人脸向量和训练的标准向量的夹角有关的。整体改变颜色,不改变这个夹角,所以距离是基本不会变的。因此,图像稍微变色,对识别结果不会有太大的影响。 664 | 665 | 本题代码位于`src/hw4_3_3.m`中 666 | 667 | #### (4)重新选择人脸样本的训练标准 668 | 669 | 在进行人脸检测的实验时,由于仅仅是根据肤色去判断,因此人的手、脖子等与脸颜色接近的部分,也会被识别为人脸。而且如果有些人戴了眼镜,由于训练样本戴眼镜的较少,所以眼镜对识别结果也会有一些影响。因此,如果重新选择人脸样本训练标准的话,除了肤色,可能还得考虑人脸的进阶特征。具体方法是,首先通过肤色初筛人脸区域,然后进一步考虑识别的人脸区域有没有五官特征。五官特征可以由训练图像的梯度进行判断。 670 | 671 | ## 四、总结 672 | 673 | 本次实验中,我了解了图像的基本知识,完成了 JPEG 编码、信息隐藏、人脸检测等项目,受益匪浅。 674 | 675 | 最后说明一下文件结构:目录`src`为所有文件代码,目录`图像处理所需资源`中包含了本次作业中所需的图片资源。 676 | -------------------------------------------------------------------------------- /img/adjustcolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/adjustcolor.png -------------------------------------------------------------------------------- /img/detect_face_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/detect_face_1.png -------------------------------------------------------------------------------- /img/doublewidth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/doublewidth.png -------------------------------------------------------------------------------- /img/hw2_4_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw2_4_11.png -------------------------------------------------------------------------------- /img/hw2_4_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw2_4_13.png -------------------------------------------------------------------------------- /img/hw2_4_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw2_4_3.png -------------------------------------------------------------------------------- /img/hw2_4_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw2_4_4.png -------------------------------------------------------------------------------- /img/hw2_4_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw2_4_5.png -------------------------------------------------------------------------------- /img/hw3_4_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw3_4_2.png -------------------------------------------------------------------------------- /img/hw3_4_2_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw3_4_2_details.png -------------------------------------------------------------------------------- /img/hw4_3_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw4_3_1.png -------------------------------------------------------------------------------- /img/hw4_3_2_situ_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw4_3_2_situ_1.png -------------------------------------------------------------------------------- /img/hw4_3_2_situ_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw4_3_2_situ_2.png -------------------------------------------------------------------------------- /img/hw4_3_2_situ_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw4_3_2_situ_3.png -------------------------------------------------------------------------------- /img/hw4_3_2_situ_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw4_3_2_situ_4.png -------------------------------------------------------------------------------- /img/hw_1_3_2_chessboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw_1_3_2_chessboard.jpg -------------------------------------------------------------------------------- /img/hw_1_3_2_circle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/hw_1_3_2_circle.jpg -------------------------------------------------------------------------------- /img/rot90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/img/rot90.png -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.jpg 2 | *.asv -------------------------------------------------------------------------------- /src/Compress.m: -------------------------------------------------------------------------------- 1 | function [dc_code, ac_code, img_height, img_width] = Compress(img, DCTAB, ACTAB, QTAB) 2 | hall_pre = double(img) - 128; 3 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 4 | [height, width] = size(hall_quan); 5 | quan_mat = zeros([64, height * width / 64]); 6 | for i = 0: 1: height / 64 - 1 7 | for j = 1: 1: width 8 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 9 | end 10 | end 11 | dc_cof = quan_mat(1, :)'; 12 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 13 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 14 | dc_code = []; 15 | for i = 1: 1: length(dc_diff) 16 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 17 | if dc_diff(i) ~= 0 18 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 19 | if dc_diff(i) < 0 20 | mag_temp = ~mag_temp; 21 | end 22 | else 23 | mag_temp = []; 24 | end 25 | dc_code = [dc_code; cat_temp; mag_temp]; 26 | end 27 | 28 | ac_cof = quan_mat(2: end, :); 29 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 30 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 31 | EOB = [1, 0, 1, 0]'; 32 | [ac_h, ac_w] = size(ac_size); 33 | ac_code = []; 34 | for i = 1: 1: ac_w 35 | run = 0; 36 | for j = 1: 1: ac_h 37 | if ac_size(j, i) == 0 38 | run = run + 1; 39 | else 40 | run_epoch = floor(run / 16); 41 | run_mod = mod(run, 16); 42 | run = 0; 43 | while run_epoch > 0 44 | ac_code = [ac_code; ZRL]; 45 | run_epoch = run_epoch - 1; 46 | end 47 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 48 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 49 | if ac_cof(j, i) < 0 50 | amp_temp = ~amp_temp; 51 | end 52 | ac_code = [ac_code; size_temp; amp_temp]; 53 | end 54 | end 55 | ac_code = [ac_code; EOB]; 56 | end 57 | 58 | [img_height, img_width] = size(img); 59 | end -------------------------------------------------------------------------------- /src/DCT2.m: -------------------------------------------------------------------------------- 1 | function [dct_coef] = DCT2(input_mat) 2 | [height, width] = size(input_mat); 3 | dct_coef = DCT2_D(height) * double(input_mat) * DCT2_D(width)'; 4 | end 5 | 6 | -------------------------------------------------------------------------------- /src/DCT2_D.m: -------------------------------------------------------------------------------- 1 | function D_mat = DCT2_D(input_dim) 2 | row = linspace(0, input_dim - 1, input_dim)'; 3 | col = linspace(1, input_dim * 2 - 1, input_dim); 4 | cos_mat = cos(row * col * pi / (2 * input_dim)); 5 | cos_mat(1, :) = cos_mat(1, :) / sqrt(2); 6 | D_mat = sqrt(2 / input_dim) * cos_mat; 7 | end 8 | 9 | -------------------------------------------------------------------------------- /src/Decompress.m: -------------------------------------------------------------------------------- 1 | function img = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB) 2 | dc_diff = zeros([img_height * img_width / 64, 1]); 3 | last_idx = 1; 4 | cnt = 1; 5 | i = 1; 6 | while i <= length(dc_code) 7 | for j = 1: 1: size(DCTAB, 1) 8 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 9 | if j - 1 ~= 0 10 | mag_temp = dc_code(i + 1: i + j - 1)'; 11 | if mag_temp(1) == 1 12 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 13 | else 14 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 15 | end 16 | end 17 | last_idx = i + j; 18 | i = i + j; 19 | cnt = cnt + 1; 20 | break; 21 | end 22 | end 23 | i = i + 1; 24 | end 25 | dc_cof = zeros(size(dc_diff)); 26 | dc_cof(1) = dc_diff(1); 27 | for i = 2: 1: size(dc_cof, 1) 28 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 29 | end 30 | 31 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 32 | EOB = [1, 0, 1, 0]'; 33 | ac_cof = zeros([63, img_height * img_width / 64]); 34 | i = 1; 35 | last_idx = 1; 36 | row_cnt = 1; 37 | col_cnt = 1; 38 | while i <= length(ac_code) 39 | if isequal(ac_code(last_idx: i), EOB) 40 | col_cnt = col_cnt + 1; 41 | row_cnt = 1; 42 | last_idx = i + 1; 43 | i = last_idx; 44 | elseif isequal(ac_code(last_idx: i), ZRL) 45 | row_cnt = row_cnt + 16; 46 | last_idx = i + 1; 47 | i = last_idx; 48 | else 49 | for j = 1: 1: size(ACTAB, 1) 50 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 51 | row_cnt = row_cnt + ACTAB(j, 1); 52 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 53 | if amp_temp(1) == 1 54 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 55 | else 56 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 57 | end 58 | row_cnt = row_cnt + 1; 59 | last_idx = i + ACTAB(j, 2) + 1; 60 | i = last_idx; 61 | break; 62 | end 63 | end 64 | end 65 | i = i + 1; 66 | end 67 | whole_cof = [dc_cof'; ac_cof]; 68 | hall_quan = zeros([img_height * 8, img_width / 8]); 69 | for i = 0: 1: img_height / 8 - 1 70 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 71 | end 72 | img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 73 | end 74 | 75 | -------------------------------------------------------------------------------- /src/DetectFace.m: -------------------------------------------------------------------------------- 1 | function [img, faces_num] = DetectFace(img, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area) 2 | x_less = []; 3 | y_large = []; 4 | i = row_width; 5 | while i <= size(img, 1) 6 | j = col_width; 7 | while j <= size(img, 2) 8 | feat_temp = get_face_feat(img(i - row_width + 1: i, j - col_width + 1: j, :), L); 9 | dist = 1 - sum(sqrt(feat_temp .* face_feature)); 10 | if dist < similarity_threshold 11 | x_less = [x_less, i - row_width + 1]; 12 | y_large = [y_large, j]; 13 | end 14 | j = j + step; 15 | end 16 | i = i + step; 17 | end 18 | x_large = x_less + (row_width - 1); 19 | y_less = y_large - (col_width - 1); 20 | 21 | % combine blocks 22 | for i = 1: 1: length(x_less) 23 | if x_less(i) ~= -1 24 | for j = i + 1: 1: length(x_less) 25 | if j ~= i && x_less(j) ~= -1 26 | if x_less(i) == x_less(j) 27 | if y_less(j) <= y_large(i) + col_width && y_less(j) >= y_less(i) 28 | y_large(i) = y_large(j); 29 | x_large(i) = max(x_large(i), x_large(j)); 30 | x_less(j) = -1; 31 | end 32 | end 33 | if y_less(i) == y_less(j) 34 | if x_less(j) <= x_large(i) + row_width && x_less(j) >= x_less(i) 35 | x_large(i) = x_large(j); 36 | y_large(i) = max(y_large(i), y_large(j)); 37 | x_less(j) = -1; 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | 45 | % remove overlap blocks 46 | face_idx_1 = x_less ~= -1; 47 | x_less = x_less(face_idx_1); 48 | x_large = x_large(face_idx_1); 49 | y_less = y_less(face_idx_1); 50 | y_large = y_large(face_idx_1); 51 | for i = 1: 1: length(x_less) 52 | if x_less(i) ~= -1 53 | area_i = (x_large(i) - x_less(i)) * (y_large(i) - y_less(i)); 54 | for j = 1: 1: length(x_less) 55 | if j ~= i && x_less(j) ~= -1 56 | if x_less(j) <= x_large(i) && x_less(j) >= x_less(i) && y_less(j) <= y_large(i) && y_less(j) >= y_less(i) 57 | area_overlap = (min(x_large(i), x_large(j)) - x_less(j)) * (min(y_large(i), y_large(j)) - y_less(j)); 58 | if area_overlap / area_i >= area_threshold 59 | x_less(j) = -1; 60 | end 61 | elseif x_less(j) >= x_less(i) && x_less(j) <= x_large(i) && y_less(i) <= y_less(j) && y_large(i) >= y_large(j) 62 | area_overlap = (min(x_large(i), x_large(j)) - x_less(j)) * (y_large(j) - y_less(j)); 63 | if area_overlap / area_i >= area_threshold 64 | x_less(j) = -1; 65 | end 66 | elseif y_less(j) >= y_less(i) && y_less(j) <= y_large(i) && x_less(i) <= x_less(j) && x_large(i) >= x_large(j) 67 | area_overlap = (min(y_large(i), y_large(j)) - y_less(j)) * (x_large(j) - x_less(j)); 68 | if area_overlap / area_i >= area_threshold 69 | x_less(j) = -1; 70 | end 71 | elseif x_large(j) >= x_less(i) && x_large(j) <= x_large(i) && y_less(i) <= y_less(j) && y_large(i) >= y_large(j) 72 | area_overlap = (x_large(j) - max(x_less(i), x_less(j))) * (y_large(j) - y_less(j)); 73 | if area_overlap / area_i >= area_threshold 74 | x_less(j) = -1; 75 | end 76 | elseif y_large(j) >= y_less(i) && y_large(j) <= y_large(i) && x_less(i) <= x_less(j) && x_large(i) >= x_large(j) 77 | area_overlap = (y_large(j) - max(y_less(i), y_less(j))) * (x_large(j) - x_less(j)); 78 | if area_overlap / area_i >= area_threshold 79 | x_less(j) = -1; 80 | end 81 | elseif x_less(j) >= x_less(i) && x_large(j) <= x_large(i) && y_less(i) >= y_less(j) && y_large(i) <= y_large(j) 82 | area_overlap = (x_large(j) - x_less(j)) * (y_large(i) - y_less(i)); 83 | if area_overlap / area_i >= area_threshold 84 | x_less(j) = -1; 85 | end 86 | end 87 | end 88 | end 89 | end 90 | end 91 | 92 | % remove small blocks 93 | for i = 1: 1: length(x_less) 94 | if x_less(i) ~= -1 95 | if (x_large(i) - x_less(i)) * (y_large(i) - y_less(i)) <= least_face_area 96 | x_less(i) = -1; 97 | end 98 | end 99 | end 100 | 101 | % optimize rectangle blocks 102 | for i = 1: 1: length(x_less) 103 | if x_less(i) ~= -1 104 | delta_x = x_large(i) - x_less(i); 105 | delta_y = y_large(i) - y_less(i); 106 | if delta_x - delta_y > delta_x / 2 107 | delta = floor((delta_x - delta_y) / 8); 108 | x_less(i) = x_less(i) + delta; 109 | x_large(i) = x_large(i) - 3 * delta; 110 | y_less(i) = max(y_less(i) - 2 * delta, 1); 111 | y_large(i) = min(y_large(i) + 2 * delta, size(img, 2)); 112 | elseif delta_y - delta_x > delta_y / 2 113 | delta = floor((delta_y - delta_x) / 4); 114 | y_less(i) = y_less(i) + 2 * delta; 115 | y_large(i) = y_large(i) - 2 * delta; 116 | x_less(i) = max(x_less(i) - 2 * delta, 1); 117 | x_large(i) = min(x_large(i) + 2 * delta, size(img, 1)); 118 | end 119 | end 120 | end 121 | 122 | faces_num = sum(x_less ~= -1); 123 | 124 | rect_face = logical(zeros(size(img))); 125 | edge = 1; 126 | for i = 1 : 1 : length(x_less) 127 | if x_less(i) ~= -1 128 | rect_face(max(x_less(i) - edge, 1): x_less(i) + edge, y_less(i) : y_large(i), 1) = true; 129 | rect_face(x_large(i) - edge: min(size(img, 1), x_large(i) + edge), y_less(i) : y_large(i), 1) = true; 130 | rect_face(x_less(i): x_large(i), max(1, y_less(i) - edge): y_less(i) + edge, 1) = true; 131 | rect_face(x_less(i): x_large(i), y_large(i) - edge: min(size(img, 2), y_large(i) + edge), 1) = true; 132 | end 133 | end 134 | img(rect_face) = uint8(255); 135 | end 136 | 137 | -------------------------------------------------------------------------------- /src/dct_conceal_1.m: -------------------------------------------------------------------------------- 1 | function [dc_code, ac_code, img_height, img_width] = dct_conceal_1(img, info, DCTAB, ACTAB, QTAB) 2 | hall_pre = double(img) - 128; 3 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 4 | [height, width] = size(hall_quan); 5 | 6 | quan_mat = zeros([64, height * width / 64]); 7 | for i = 0: 1: height / 64 - 1 8 | for j = 1: 1: width 9 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 10 | end 11 | end 12 | quan_mat = double(bitset(int32(quan_mat), 1, info)); 13 | 14 | dc_cof = quan_mat(1, :)'; 15 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 16 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 17 | dc_code = []; 18 | for i = 1: 1: length(dc_diff) 19 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 20 | if dc_diff(i) ~= 0 21 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 22 | if dc_diff(i) < 0 23 | mag_temp = ~mag_temp; 24 | end 25 | else 26 | mag_temp = []; 27 | end 28 | dc_code = [dc_code; cat_temp; mag_temp]; 29 | end 30 | 31 | ac_cof = quan_mat(2: end, :); 32 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 33 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 34 | EOB = [1, 0, 1, 0]'; 35 | [ac_h, ac_w] = size(ac_size); 36 | ac_code = []; 37 | for i = 1: 1: ac_w 38 | run = 0; 39 | for j = 1: 1: ac_h 40 | if ac_size(j, i) == 0 41 | run = run + 1; 42 | else 43 | run_epoch = floor(run / 16); 44 | run_mod = mod(run, 16); 45 | run = 0; 46 | while run_epoch > 0 47 | ac_code = [ac_code; ZRL]; 48 | run_epoch = run_epoch - 1; 49 | end 50 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 51 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 52 | if ac_cof(j, i) < 0 53 | amp_temp = ~amp_temp; 54 | end 55 | ac_code = [ac_code; size_temp; amp_temp]; 56 | end 57 | end 58 | ac_code = [ac_code; EOB]; 59 | end 60 | 61 | [img_height, img_width] = size(img); 62 | end 63 | 64 | -------------------------------------------------------------------------------- /src/dct_conceal_2.m: -------------------------------------------------------------------------------- 1 | function [dc_code, ac_code, img_height, img_width] = dct_conceal_2(img, info, hide_idx, DCTAB, ACTAB, QTAB) 2 | hall_pre = double(img) - 128; 3 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 4 | [height, width] = size(hall_quan); 5 | 6 | quan_mat = zeros([64, height * width / 64]); 7 | for i = 0: 1: height / 64 - 1 8 | for j = 1: 1: width 9 | quan_mat(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 10 | end 11 | end 12 | last_bit = bitand(int32(quan_mat), int32(ones(size(quan_mat)))); 13 | for i = 1: 1: size(last_bit, 2) 14 | last_bit(hide_idx, i) = info(:, i); 15 | end 16 | quan_mat = double(bitset(int32(quan_mat), 1, last_bit)); 17 | dc_cof = quan_mat(1, :)'; 18 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 19 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 20 | dc_code = []; 21 | for i = 1: 1: length(dc_diff) 22 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 23 | if dc_diff(i) ~= 0 24 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 25 | if dc_diff(i) < 0 26 | mag_temp = ~mag_temp; 27 | end 28 | else 29 | mag_temp = []; 30 | end 31 | dc_code = [dc_code; cat_temp; mag_temp]; 32 | end 33 | 34 | ac_cof = quan_mat(2: end, :); 35 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 36 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 37 | EOB = [1, 0, 1, 0]'; 38 | [ac_h, ac_w] = size(ac_size); 39 | ac_code = []; 40 | for i = 1: 1: ac_w 41 | run = 0; 42 | for j = 1: 1: ac_h 43 | if ac_size(j, i) == 0 44 | run = run + 1; 45 | else 46 | run_epoch = floor(run / 16); 47 | run_mod = mod(run, 16); 48 | run = 0; 49 | while run_epoch > 0 50 | ac_code = [ac_code; ZRL]; 51 | run_epoch = run_epoch - 1; 52 | end 53 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 54 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 55 | if ac_cof(j, i) < 0 56 | amp_temp = ~amp_temp; 57 | end 58 | ac_code = [ac_code; size_temp; amp_temp]; 59 | end 60 | end 61 | ac_code = [ac_code; EOB]; 62 | end 63 | 64 | [img_height, img_width] = size(img); 65 | 66 | end -------------------------------------------------------------------------------- /src/dct_conceal_3.m: -------------------------------------------------------------------------------- 1 | function [dc_code, ac_code, img_height, img_width] = dct_conceal_3(img, info, DCTAB, ACTAB, QTAB) 2 | hall_pre = double(img) - 128; 3 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 4 | [height, width] = size(hall_quan); 5 | 6 | quan_mat = zeros([64, height * width / 64]); 7 | for i = 0: 1: height / 64 - 1 8 | for j = 1: 1: width 9 | col_temp = i * width + j; 10 | quan_mat(:, col_temp) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 11 | for k = 64: -1 : 1 12 | if quan_mat(k, col_temp) ~= 0 13 | if k == 64 14 | quan_mat(64, col_temp) = info(col_temp); 15 | else 16 | quan_mat(k + 1, col_temp) = info(col_temp); 17 | end 18 | break; 19 | end 20 | end 21 | end 22 | end 23 | 24 | dc_cof = quan_mat(1, :)'; 25 | dc_diff = [dc_cof(1); dc_cof(1: end - 1) - dc_cof(2: end)]; 26 | dc_category = min(ceil(log2(abs(dc_diff) + 1)), 11); 27 | dc_code = []; 28 | for i = 1: 1: length(dc_diff) 29 | cat_temp = DCTAB(dc_category(i) + 1, 2: DCTAB(dc_category(i) + 1, 1) + 1)'; 30 | if dc_diff(i) ~= 0 31 | mag_temp = dec2bin(abs(dc_diff(i)))' - '0'; 32 | if dc_diff(i) < 0 33 | mag_temp = ~mag_temp; 34 | end 35 | else 36 | mag_temp = []; 37 | end 38 | dc_code = [dc_code; cat_temp; mag_temp]; 39 | end 40 | 41 | ac_cof = quan_mat(2: end, :); 42 | ac_size = min(ceil(log2(abs(ac_cof) + 1)), 10); 43 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 44 | EOB = [1, 0, 1, 0]'; 45 | [ac_h, ac_w] = size(ac_size); 46 | ac_code = []; 47 | for i = 1: 1: ac_w 48 | run = 0; 49 | for j = 1: 1: ac_h 50 | if ac_size(j, i) == 0 51 | run = run + 1; 52 | else 53 | run_epoch = floor(run / 16); 54 | run_mod = mod(run, 16); 55 | run = 0; 56 | while run_epoch > 0 57 | ac_code = [ac_code; ZRL]; 58 | run_epoch = run_epoch - 1; 59 | end 60 | size_temp = ACTAB(run_mod * 10 + ac_size(j, i), 4: ACTAB(run_mod * 10 + ac_size(j, i), 3) + 3)'; 61 | amp_temp = dec2bin(abs(ac_cof(j, i)))' - '0'; 62 | if ac_cof(j, i) < 0 63 | amp_temp = ~amp_temp; 64 | end 65 | ac_code = [ac_code; size_temp; amp_temp]; 66 | end 67 | end 68 | ac_code = [ac_code; EOB]; 69 | end 70 | 71 | [img_height, img_width] = size(img); 72 | end 73 | 74 | -------------------------------------------------------------------------------- /src/dct_reveal_1.m: -------------------------------------------------------------------------------- 1 | function [reveal_info, reveal_img] = dct_reveal_1(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB) 2 | dc_diff = zeros([img_height * img_width / 64, 1]); 3 | last_idx = 1; 4 | cnt = 1; 5 | i = 1; 6 | while i <= length(dc_code) 7 | for j = 1: 1: size(DCTAB, 1) 8 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 9 | if j - 1 ~= 0 10 | mag_temp = dc_code(i + 1: i + j - 1)'; 11 | if mag_temp(1) == 1 12 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 13 | else 14 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 15 | end 16 | end 17 | last_idx = i + j; 18 | i = i + j; 19 | cnt = cnt + 1; 20 | break; 21 | end 22 | end 23 | i = i + 1; 24 | end 25 | dc_cof = zeros(size(dc_diff)); 26 | dc_cof(1) = dc_diff(1); 27 | for i = 2: 1: size(dc_cof, 1) 28 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 29 | end 30 | 31 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 32 | EOB = [1, 0, 1, 0]'; 33 | ac_cof = zeros([63, img_height * img_width / 64]); 34 | i = 1; 35 | last_idx = 1; 36 | row_cnt = 1; 37 | col_cnt = 1; 38 | while i <= length(ac_code) 39 | if isequal(ac_code(last_idx: i), EOB) 40 | col_cnt = col_cnt + 1; 41 | row_cnt = 1; 42 | last_idx = i + 1; 43 | i = last_idx; 44 | elseif isequal(ac_code(last_idx: i), ZRL) 45 | row_cnt = row_cnt + 16; 46 | last_idx = i + 1; 47 | i = last_idx; 48 | else 49 | for j = 1: 1: size(ACTAB, 1) 50 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 51 | row_cnt = row_cnt + ACTAB(j, 1); 52 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 53 | if amp_temp(1) == 1 54 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 55 | else 56 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 57 | end 58 | row_cnt = row_cnt + 1; 59 | last_idx = i + ACTAB(j, 2) + 1; 60 | i = last_idx; 61 | break; 62 | end 63 | end 64 | end 65 | i = i + 1; 66 | end 67 | whole_cof = [dc_cof'; ac_cof]; 68 | reveal_info = bitand(int32(whole_cof), int32(ones(size(whole_cof)))); 69 | hall_quan = zeros([img_height * 8, img_width / 8]); 70 | for i = 0: 1: img_height / 8 - 1 71 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 72 | end 73 | reveal_img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 74 | end 75 | 76 | -------------------------------------------------------------------------------- /src/dct_reveal_2.m: -------------------------------------------------------------------------------- 1 | function [reveal_info, reveal_img] = dct_reveal_2(dc_code, ac_code, img_height, img_width, hide_idx, DCTAB, ACTAB, QTAB) 2 | dc_diff = zeros([img_height * img_width / 64, 1]); 3 | last_idx = 1; 4 | cnt = 1; 5 | i = 1; 6 | while i <= length(dc_code) 7 | for j = 1: 1: size(DCTAB, 1) 8 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 9 | if j - 1 ~= 0 10 | mag_temp = dc_code(i + 1: i + j - 1)'; 11 | if mag_temp(1) == 1 12 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 13 | else 14 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 15 | end 16 | end 17 | last_idx = i + j; 18 | i = i + j; 19 | cnt = cnt + 1; 20 | break; 21 | end 22 | end 23 | i = i + 1; 24 | end 25 | dc_cof = zeros(size(dc_diff)); 26 | dc_cof(1) = dc_diff(1); 27 | for i = 2: 1: size(dc_cof, 1) 28 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 29 | end 30 | 31 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 32 | EOB = [1, 0, 1, 0]'; 33 | ac_cof = zeros([63, img_height * img_width / 64]); 34 | i = 1; 35 | last_idx = 1; 36 | row_cnt = 1; 37 | col_cnt = 1; 38 | while i <= length(ac_code) 39 | if isequal(ac_code(last_idx: i), EOB) 40 | col_cnt = col_cnt + 1; 41 | row_cnt = 1; 42 | last_idx = i + 1; 43 | i = last_idx; 44 | elseif isequal(ac_code(last_idx: i), ZRL) 45 | row_cnt = row_cnt + 16; 46 | last_idx = i + 1; 47 | i = last_idx; 48 | else 49 | for j = 1: 1: size(ACTAB, 1) 50 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 51 | row_cnt = row_cnt + ACTAB(j, 1); 52 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 53 | if amp_temp(1) == 1 54 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 55 | else 56 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 57 | end 58 | row_cnt = row_cnt + 1; 59 | last_idx = i + ACTAB(j, 2) + 1; 60 | i = last_idx; 61 | break; 62 | end 63 | end 64 | end 65 | i = i + 1; 66 | end 67 | whole_cof = [dc_cof'; ac_cof]; 68 | total_bits = bitand(int32(whole_cof), int32(ones(size(whole_cof)))); 69 | reveal_info = zeros(length(hide_idx), size(total_bits, 2)); 70 | for i = 1: 1: size(total_bits, 2) 71 | reveal_info(:, i) = total_bits(hide_idx, i); 72 | end 73 | reveal_info = int32(reveal_info); 74 | hall_quan = zeros([img_height * 8, img_width / 8]); 75 | for i = 0: 1: img_height / 8 - 1 76 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 77 | end 78 | reveal_img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 79 | end 80 | 81 | -------------------------------------------------------------------------------- /src/dct_reveal_3.m: -------------------------------------------------------------------------------- 1 | function [reveal_info, reveal_img] = dct_reveal_3(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB) 2 | dc_diff = zeros([img_height * img_width / 64, 1]); 3 | last_idx = 1; 4 | cnt = 1; 5 | i = 1; 6 | while i <= length(dc_code) 7 | for j = 1: 1: size(DCTAB, 1) 8 | if isequal(dc_code(last_idx: i)', DCTAB(j, 2: DCTAB(j, 1) + 1)) 9 | if j - 1 ~= 0 10 | mag_temp = dc_code(i + 1: i + j - 1)'; 11 | if mag_temp(1) == 1 12 | dc_diff(cnt) = bin2dec(char(mag_temp + '0')); 13 | else 14 | dc_diff(cnt) = -bin2dec(char(~mag_temp + '0')); 15 | end 16 | end 17 | last_idx = i + j; 18 | i = i + j; 19 | cnt = cnt + 1; 20 | break; 21 | end 22 | end 23 | i = i + 1; 24 | end 25 | dc_cof = zeros(size(dc_diff)); 26 | dc_cof(1) = dc_diff(1); 27 | for i = 2: 1: size(dc_cof, 1) 28 | dc_cof(i) = dc_cof(i - 1) - dc_diff(i); 29 | end 30 | 31 | ZRL = [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]'; 32 | EOB = [1, 0, 1, 0]'; 33 | ac_cof = zeros([63, img_height * img_width / 64]); 34 | i = 1; 35 | last_idx = 1; 36 | row_cnt = 1; 37 | col_cnt = 1; 38 | while i <= length(ac_code) 39 | if isequal(ac_code(last_idx: i), EOB) 40 | col_cnt = col_cnt + 1; 41 | row_cnt = 1; 42 | last_idx = i + 1; 43 | i = last_idx; 44 | elseif isequal(ac_code(last_idx: i), ZRL) 45 | row_cnt = row_cnt + 16; 46 | last_idx = i + 1; 47 | i = last_idx; 48 | else 49 | for j = 1: 1: size(ACTAB, 1) 50 | if isequal(ac_code(last_idx: i)', ACTAB(j, 4: ACTAB(j, 3) + 3)) 51 | row_cnt = row_cnt + ACTAB(j, 1); 52 | amp_temp = ac_code(i + 1: i + ACTAB(j, 2))'; 53 | if amp_temp(1) == 1 54 | ac_cof(row_cnt, col_cnt) = bin2dec(char(amp_temp + '0')); 55 | else 56 | ac_cof(row_cnt, col_cnt) = -bin2dec(char(~amp_temp + '0')); 57 | end 58 | row_cnt = row_cnt + 1; 59 | last_idx = i + ACTAB(j, 2) + 1; 60 | i = last_idx; 61 | break; 62 | end 63 | end 64 | end 65 | i = i + 1; 66 | end 67 | whole_cof = [dc_cof'; ac_cof]; 68 | reveal_info = zeros(1, size(whole_cof, 2)); 69 | for i = 1: 1: length(reveal_info) 70 | for j = size(whole_cof, 1): -1: 1 71 | if whole_cof(j, i) ~= 0 72 | reveal_info(i) = whole_cof(j, i); 73 | break; 74 | end 75 | end 76 | end 77 | reveal_info = int32(reveal_info); 78 | hall_quan = zeros([img_height * 8, img_width / 8]); 79 | for i = 0: 1: img_height / 8 - 1 80 | hall_quan(i * 64 + 1: (i + 1) * 64, :) = whole_cof(:, i * img_width / 8 + 1: (i + 1) * img_width / 8); 81 | end 82 | reveal_img = uint8(blockproc(hall_quan, [64, 1], @(mat)(idct2(inv_zig_zag(mat.data) .* QTAB))) + 128); 83 | end 84 | 85 | -------------------------------------------------------------------------------- /src/get_face_feat.m: -------------------------------------------------------------------------------- 1 | function feature_temp = get_face_feat(face, L) 2 | face_temp = double(face); 3 | feature_temp = zeros([1, 2^(3 * L)]); 4 | for j = 1: 1: size(face_temp, 1) 5 | for k = 1: 1: size(face_temp, 2) 6 | r = floor(face_temp(j, k, 1) * 2^(L - 8)); 7 | g = floor(face_temp(j, k, 2) * 2^(L - 8)); 8 | b = floor(face_temp(j, k, 3) * 2^(L - 8)); 9 | n = r * 2^(2 * L) + g * 2^L + b; 10 | feature_temp(n + 1) = feature_temp(n + 1) + 1; 11 | end 12 | end 13 | feature_temp = feature_temp / size(face_temp, 1) / size(face_temp, 2); 14 | end 15 | 16 | -------------------------------------------------------------------------------- /src/hw1_3_2.m: -------------------------------------------------------------------------------- 1 | clear all, close all, clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | size_pic = size(hall_gray); 4 | height = size_pic(1); 5 | width = size_pic(2); 6 | radius = min(height, width) / 2; 7 | 8 | idx = zeros([size_pic, 2]); 9 | for i = 1: 1: height 10 | for j = 1: 1: width 11 | idx(i, j, 1) = i; 12 | idx(i, j, 2) = j; 13 | end 14 | end 15 | circle_idx = ((idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 <= (1.02 * radius) ^ 2 & ... 16 | (idx(:, :, 1) - (height + 1) / 2) .^ 2 + (idx(:, :, 2) - (width + 1) / 2) .^ 2 >= (0.98 * radius) ^ 2); 17 | circle = hall_color; 18 | for i = 1: 1: height 19 | for j = 1: 1: width 20 | if circle_idx(i, j) 21 | circle(i, j, 1) = 255; 22 | circle(i, j, 2) = 0; 23 | circle(i, j, 3) = 0; 24 | end 25 | end 26 | end 27 | 28 | subplot(1, 2, 1); 29 | imshow(circle); 30 | imwrite(circle, 'hw_1_3_2_circle.jpg'); 31 | 32 | chessboard = hall_color; 33 | for i = 1: 1: 8 34 | for j = 1: 1: 8 35 | if mod(i, 2) == 1 36 | if mod((i - 1) * 8 + j - 1, 2) == 1 37 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 1) = 0; 38 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 2) = 0; 39 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 3) = 0; 40 | end 41 | else 42 | if mod((i - 1) * 8 + j - 1, 2) == 0 43 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 1) = 0; 44 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 2) = 0; 45 | chessboard((i - 1) * height / 8 + 1: i * height / 8, (j - 1) * width / 8 + 1: j * width / 8, 3) = 0; 46 | end 47 | end 48 | end 49 | end 50 | subplot(1, 2, 2); 51 | imshow(chessboard); 52 | imwrite(chessboard, 'hw_1_3_2_chessboard.jpg'); 53 | -------------------------------------------------------------------------------- /src/hw2_4_1.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load("../图像处理所需资源/hall.mat"); 3 | [height, width] = size(hall_gray); 4 | hall_part = double(hall_gray(1: height / 4, 1: width / 4)); 5 | 6 | hall_direct = hall_part - 128; 7 | maphall_1 = dct2(hall_direct); 8 | 9 | [part_h, part_w] = size(hall_part); 10 | DC_128 = dct2(zeros(part_h, part_w) + 128); 11 | maphall_2 = dct2(hall_part); 12 | maphall_2(1, 1) = maphall_2(1, 1) - DC_128(1, 1); 13 | delta = sum((maphall_1 - maphall_2).^2, 'all') / part_h / part_w; 14 | 15 | subplot(1, 2, 1); 16 | imshow(rescale(hall_direct)); 17 | hall_transform = idct2(maphall_2); 18 | subplot(1, 2, 2); 19 | imshow(rescale(hall_transform)); 20 | disp(delta); 21 | -------------------------------------------------------------------------------- /src/hw2_4_10.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [dc_code, ac_code, img_height, img_width] = Compress(hall_gray, DCTAB, ACTAB, QTAB); 6 | compress_rate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 7 | disp(compress_rate); 8 | -------------------------------------------------------------------------------- /src/hw2_4_11.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [dc_code, ac_code, img_height, img_width] = Compress(hall_gray, DCTAB, ACTAB, QTAB); 6 | compress_rate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 7 | hall_decompress = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 8 | mse = sum((double(hall_gray) - double(hall_decompress)).^2, 'all') / img_height / img_width; 9 | psnr = 10 * log10(255^2 / mse); 10 | disp("PSNR = " + psnr); 11 | disp("Compress Rate = " + compress_rate); 12 | 13 | subplot(1, 2, 1); 14 | imshow(hall_gray); 15 | title('Origin'); 16 | subplot(1, 2, 2); 17 | imshow(hall_decompress); 18 | title('Decompress'); 19 | -------------------------------------------------------------------------------- /src/hw2_4_12.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | QTAB = QTAB / 2; 6 | [dc_code, ac_code, img_height, img_width] = Compress(hall_gray, DCTAB, ACTAB, QTAB); 7 | compress_rate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 8 | hall_decompress = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 9 | mse = sum((double(hall_gray) - double(hall_decompress)).^2, 'all') / img_height / img_width; 10 | psnr = 10 * log10(255^2 / mse); 11 | disp("PSNR = " + psnr); 12 | disp("Compress Rate = " + compress_rate); 13 | 14 | subplot(1, 2, 1); 15 | imshow(hall_gray); 16 | title('Origin'); 17 | subplot(1, 2, 2); 18 | imshow(hall_decompress); 19 | title('Decompress'); 20 | -------------------------------------------------------------------------------- /src/hw2_4_13.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/snow.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [dc_code, ac_code, img_height, img_width] = Compress(snow, DCTAB, ACTAB, QTAB); 6 | cprate = img_height * img_width * 8 / (length(dc_code) + length(ac_code)); 7 | dcp_snow = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 8 | [img_height, img_width] = size(dcp_snow); 9 | mse = sum((double(snow) - double(dcp_snow)).^2, 'all') / img_height / img_width; 10 | psnr = 10 * log10(255^2 / mse); 11 | disp("PSNR = " + psnr); 12 | disp("Compress Rate = " + cprate); 13 | 14 | subplot(1, 2, 1); 15 | imshow(snow); 16 | title('Origin'); 17 | subplot(1, 2, 2); 18 | imshow(dcp_snow); 19 | title('Decompress'); 20 | -------------------------------------------------------------------------------- /src/hw2_4_2.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load("../图像处理所需资源/hall.mat"); 3 | [height, width] = size(hall_gray); 4 | hall_part = double(hall_gray(1: height / 4, 1: width / 4)) - 128; 5 | dct_mine = DCT2(hall_part); 6 | dct_matlab = dct2(hall_part); 7 | delta = sum((dct_mine - dct_matlab).^2, 'all') / size(dct_mine, 1) / size(dct_mine, 2); 8 | disp(delta); 9 | -------------------------------------------------------------------------------- /src/hw2_4_3.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load("../图像处理所需资源/hall.mat"); 3 | [height, width] = size(hall_gray); 4 | hall_part = double(hall_gray(1: height / 1, 1: width / 1)) - 128; 5 | hall_part = blockproc(hall_part, [8, 8], @(mat)(dct2(mat.data))); 6 | hall_origin = uint8(blockproc(hall_part, [8, 8], @(mat)(idct2(mat.data) + 128))); 7 | 8 | hall_right = blockproc(hall_part, [8, 8], @(mat)(ClearRight(mat.data))); 9 | hall_right_origin = uint8(blockproc(hall_right, [8, 8], @(mat)(idct2(mat.data)) + 128)); 10 | 11 | hall_left = blockproc(hall_part, [8, 8], @(mat)(ClearLeft(mat.data))); 12 | hall_left_origin = uint8(blockproc(hall_left, [8, 8], @(mat)(idct2(mat.data)) + 128)); 13 | 14 | subplot(1, 3, 1); 15 | imshow(hall_origin); 16 | title("hall\_origin"); 17 | subplot(1, 3, 2); 18 | imshow(hall_right_origin); 19 | title("hall\_right"); 20 | subplot(1, 3, 3); 21 | imshow(hall_left_origin); 22 | title("hall\_left"); 23 | 24 | function mat = ClearRight(mat) 25 | [~, w] = size(mat); 26 | mat(:, w - 3: w) = 0; 27 | end 28 | 29 | function mat = ClearLeft(mat) 30 | mat(:, 1: 4) = 0; 31 | end 32 | -------------------------------------------------------------------------------- /src/hw2_4_4.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load("../图像处理所需资源/hall.mat"); 3 | [height, width] = size(hall_gray); 4 | hall_init = double(hall_gray(1: height / 1, 1: width / 1)) - 128; 5 | hall_part = blockproc(hall_init, [8, 8], @(mat)(dct2(mat.data))); 6 | hall_origin = uint8(blockproc(hall_part, [8, 8], @(mat)(idct2(mat.data) + 128))); 7 | 8 | hall_tran = blockproc(hall_init, [8, 8], @(mat)(dct2(mat.data)')); 9 | hall_tran_origin = uint8(blockproc(hall_tran, [8, 8], @(mat)(idct2(mat.data) + 128))); 10 | 11 | hall_90 = blockproc(hall_part, [8, 8], @(mat)(rot90(mat.data))); 12 | hall_90_origin = uint8(blockproc(hall_90, [8, 8], @(mat)(idct2(mat.data) + 128))); 13 | 14 | hall_180 = blockproc(hall_part, [8, 8], @(mat)(rot90(mat.data, 2))); 15 | hall_180_origin = uint8(blockproc(hall_180, [8, 8], @(mat)(idct2(mat.data) + 128))); 16 | 17 | subplot(1, 4, 1); 18 | imshow(hall_origin); 19 | title("hall\_origin"); 20 | subplot(1, 4, 2); 21 | imshow(hall_tran_origin); 22 | title("hall\_transpose"); 23 | subplot(1, 4, 3); 24 | imshow(hall_90_origin); 25 | title("hall\_90"); 26 | subplot(1, 4, 4); 27 | imshow(hall_180_origin); 28 | title("hall\_180"); 29 | -------------------------------------------------------------------------------- /src/hw2_4_5.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load("../图像处理所需资源/hall.mat"); 3 | x = [1, -1]; 4 | y = [1]; 5 | freqz(x, y); 6 | -------------------------------------------------------------------------------- /src/hw2_4_7.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | test_input = [ 3 | [1, 2, 6, 7, 15, 16, 28, 29]; ... 4 | [3, 5, 8, 14, 17, 27, 30, 43]; ... 5 | [4, 9, 13, 18, 26, 31, 42, 44]; ... 6 | [10, 12, 19, 25, 32, 41, 45, 54]; ... 7 | [11, 20, 24, 33, 40, 46, 53, 55]; ... 8 | [21, 23, 34, 39, 47, 52, 56, 61]; ... 9 | [22, 35, 38, 48, 51, 57, 60, 62]; ... 10 | [36, 37, 49, 50, 58, 59, 63, 64] ... 11 | ]; 12 | res = zig_zag(test_input); 13 | res_theoretical = linspace(1, 64, 64)'; 14 | disp(isequal(res, res_theoretical)); 15 | -------------------------------------------------------------------------------- /src/hw2_4_8.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | hall_pre = double(hall_gray) - 128; 5 | hall_quan = blockproc(hall_pre, [8, 8], @(mat)(zig_zag(round(dct2(mat.data) ./ QTAB)))); 6 | [height, width] = size(hall_quan); 7 | res = zeros([64, height * width / 64]); 8 | for i = 0: 1: height / 64 - 1 9 | for j = 1: 1: width 10 | res(:, i * width + j) = hall_quan(i * 64 + 1: (i + 1) * 64, j); 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/hw2_4_9.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [dc_code, ac_code, img_height, img_width] = Compress(hall_gray, DCTAB, ACTAB, QTAB); 6 | save('jpegcodes.mat', 'dc_code', 'ac_code', 'img_height', 'img_width'); 7 | -------------------------------------------------------------------------------- /src/hw3_4_1.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [height, width] = size(hall_gray); 6 | rand_info = logical(randi([0, 1], height, width)); 7 | hall_hide = bitset(hall_gray, 1, rand_info); 8 | [dc_code, ac_code, img_height, img_width] = Compress(hall_hide, DCTAB, ACTAB, QTAB); 9 | hall_decompress = Decompress(dc_code, ac_code, img_height, img_width, DCTAB, ACTAB, QTAB); 10 | decode_info = bitand(hall_decompress, uint8(ones([height, width]))); 11 | acc = sum(decode_info == rand_info, 'all') / height / width; 12 | disp("ACC = " + acc); 13 | -------------------------------------------------------------------------------- /src/hw3_4_2.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | load('../图像处理所需资源/hall.mat'); 3 | load('../图像处理所需资源/JpegCoeff.mat'); 4 | 5 | [height, width] = size(hall_gray); 6 | info_1 = int32(randi([0, 1], 64, height * width / 64)); 7 | [dc_code_1, ac_code_1, img_height, img_width] = dct_conceal_1(hall_gray, info_1, DCTAB, ACTAB, QTAB); 8 | [reveal_info_1, reveal_img_1] = dct_reveal_1(dc_code_1, ac_code_1, img_height, img_width, DCTAB, ACTAB, QTAB); 9 | 10 | threshold = 12; 11 | less_thre = QTAB <= threshold; 12 | less_thre_idx = find(less_thre); 13 | num_less_thre = sum(less_thre, 'all'); 14 | info_2 = int32(randi([0, 1], num_less_thre, height * width / 64)); 15 | [dc_code_2, ac_code_2, img_height, img_width] = dct_conceal_2(hall_gray, info_2, less_thre_idx, DCTAB, ACTAB, QTAB); 16 | [reveal_info_2, reveal_img_2] = dct_reveal_2(dc_code_2, ac_code_2, img_height, img_width, less_thre_idx, DCTAB, ACTAB, QTAB); 17 | 18 | info_3 = randi([0, 1], 1, height * width / 64); 19 | info_3 = int32((info_3 - 0.5) * 2); 20 | [dc_code_3, ac_code_3, img_height, img_width] = dct_conceal_3(hall_gray, info_3, DCTAB, ACTAB, QTAB); 21 | [reveal_info_3, reveal_img_3] = dct_reveal_3(dc_code_3, ac_code_3, img_height, img_width, DCTAB, ACTAB, QTAB); 22 | 23 | acc_1 = sum(reveal_info_1 == info_1, 'all') / img_height / img_width; 24 | acc_2 = sum(reveal_info_2 == info_2, 'all') / img_height / img_width * 64 / num_less_thre; 25 | acc_3 = sum(reveal_info_3 == info_3, 'all') / img_height / img_width * 64; 26 | disp("===================== ACC ====================="); 27 | disp("dct_hide_1 ACC = " + acc_1); 28 | disp("dct_hide_2 ACC = " + acc_2); 29 | disp("dct_hide_3 ACC = " + acc_3); 30 | 31 | compress_rate_1 = img_height * img_width * 8 / (length(dc_code_1) + length(ac_code_1)); 32 | compress_rate_2 = img_height * img_width * 8 / (length(dc_code_2) + length(ac_code_2)); 33 | compress_rate_3 = img_height * img_width * 8 / (length(dc_code_3) + length(ac_code_3)); 34 | disp("===================== Compress Rate ====================="); 35 | disp("compress_rate_1 = " + compress_rate_1); 36 | disp("compress_rate_2 = " + compress_rate_2); 37 | disp("compress_rate_3 = " + compress_rate_3); 38 | 39 | psnr_1 = 10 * log10(255^2 / (sum((double(hall_gray) - double(reveal_img_1)).^2, 'all') / img_height / img_width)); 40 | psnr_2 = 10 * log10(255^2 / (sum((double(hall_gray) - double(reveal_img_2)).^2, 'all') / img_height / img_width)); 41 | psnr_3 = 10 * log10(255^2 / (sum((double(hall_gray) - double(reveal_img_3)).^2, 'all') / img_height / img_width)); 42 | disp("===================== PSNR ====================="); 43 | disp("psnr_1 = " + psnr_1); 44 | disp("psnr_2 = " + psnr_2); 45 | disp("psnr_3 = " + psnr_3); 46 | 47 | subplot(2, 2, 1); 48 | imshow(hall_gray); 49 | title('Origin'); 50 | subplot(2, 2, 2); 51 | imshow(reveal_img_1); 52 | title('Reveal 1'); 53 | subplot(2, 2, 3); 54 | imshow(reveal_img_2); 55 | title('Reveal 2'); 56 | subplot(2, 2, 4); 57 | imshow(reveal_img_3); 58 | title('Reveal 3'); 59 | -------------------------------------------------------------------------------- /src/hw4_3_1.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | faces_num = 33; 3 | face_feature_3 = train_face("../图像处理所需资源/Faces", faces_num, 3); 4 | face_feature_4 = train_face("../图像处理所需资源/Faces", faces_num, 4); 5 | face_feature_5 = train_face("../图像处理所需资源/Faces", faces_num, 5); 6 | 7 | subplot(3, 1, 1); 8 | plot([0 : 1 : 2^(3 * 3) - 1], face_feature_3); 9 | title("L=3"); 10 | subplot(3, 1, 2); 11 | plot([0 : 1 : 2^(3 * 4) - 1], face_feature_4); 12 | title("L=4"); 13 | subplot(3, 1, 3); 14 | plot([0 : 1 : 2^(3 * 5) - 1], face_feature_5); 15 | title("L=5"); 16 | 17 | -------------------------------------------------------------------------------- /src/hw4_3_2.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | faces_num = 33; 3 | L = 3; 4 | fold_path = "../图像处理所需资源/Faces"; 5 | face_feature = train_face(fold_path, faces_num, L); 6 | img = imread("../图像处理所需资源/test1.png"); 7 | row_width = 20; 8 | col_width = 20; 9 | step = 5; 10 | similarity_threshold = 0.503; 11 | area_threshold = 0.01; 12 | least_face_area = 1000; 13 | [img_3, faces_num_3] = DetectFace(img, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 14 | subplot(3, 1, 1) 15 | imshow(img_3); 16 | title('L=3'); 17 | 18 | L = 4; 19 | face_feature = train_face(fold_path, faces_num, L); 20 | row_width = 20; 21 | col_width = 20; 22 | step = 5; 23 | similarity_threshold = 0.603; 24 | area_threshold = 0.01; 25 | least_face_area = 1000; 26 | [img_4, faces_num_4] = DetectFace(img, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 27 | subplot(3, 1, 2) 28 | imshow(img_4); 29 | title('L=4'); 30 | 31 | L = 5; 32 | face_feature = train_face(fold_path, faces_num, L); 33 | row_width = 20; 34 | col_width = 20; 35 | step = 5; 36 | similarity_threshold = 0.75; 37 | area_threshold = 0.01; 38 | least_face_area = 1000; 39 | [img_5, faces_num_5] = DetectFace(img, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 40 | subplot(3, 1, 3) 41 | imshow(img_5); 42 | title('L=5'); 43 | -------------------------------------------------------------------------------- /src/hw4_3_3.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | faces_num = 33; 3 | L = 3; 4 | fold_path = "../图像处理所需资源/Faces"; 5 | face_feature = train_face(fold_path, faces_num, L); 6 | img = imread("../图像处理所需资源/test1.png"); 7 | row_width = 20; 8 | col_width = 20; 9 | step = 5; 10 | similarity_threshold = 0.503; 11 | area_threshold = 0.01; 12 | least_face_area = 1200; 13 | 14 | img_rot = imrotate(img, -90); 15 | [img_rot, ~] = DetectFace(img_rot, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 16 | figure(1); 17 | imshow(img_rot); 18 | title('rotate 90^o'); 19 | 20 | img_dbw = imresize(img, [size(img, 1), 2 * size(img, 2)]); 21 | [img_dbw, ~] = DetectFace(img_dbw, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 22 | figure(2); 23 | imshow(img_dbw); 24 | title('double width'); 25 | 26 | img_adj = imadjust(img, [.1, .9]); 27 | [img_adj, ~] = DetectFace(img_adj, face_feature, L, similarity_threshold, area_threshold, row_width, col_width, step, least_face_area); 28 | figure(3); 29 | imshow(img_adj); 30 | title('adjust color'); 31 | -------------------------------------------------------------------------------- /src/inv_zig_zag.m: -------------------------------------------------------------------------------- 1 | function output = inv_zig_zag(input) 2 | idx = [ 3 | [1, 3, 4, 10, 11, 21, 22, 36]; 4 | [2, 5, 9, 12, 20, 23, 35, 37] 5 | [6, 8, 13, 19, 24, 34, 38, 49]; 6 | [7, 14, 18, 25, 33, 39, 48, 50]; 7 | [15, 17, 26, 32, 40, 47, 51, 58]; 8 | [16, 27, 31, 41, 46, 52, 57, 59]; 9 | [28, 30, 42, 45, 53, 56, 60, 63]; 10 | [29, 43, 44, 54, 55, 61, 62, 64] 11 | ]'; 12 | output = input(idx); 13 | 14 | % temp = [ 15 | % 1, 9, 2, 3, 10, 17, 25, 18, ... 16 | % 11, 4, 5, 12, 19, 26, 33, 41, ... 17 | % 34, 27, 20, 13, 6, 7, 14, 21, ... 18 | % 28, 35, 42, 49, 57, 50, 43, 36, ... 19 | % 29, 22, 15, 8, 16, 23, 30, 37, ... 20 | % 44, 51, 58, 59, 52, 45, 38, 31, ... 21 | % 24, 32, 39, 46, 53, 60, 61, 54, ... 22 | % 47, 40, 48, 55, 62, 63, 56, 64 ... 23 | % ]; 24 | % disp(temp(idx)); 25 | end 26 | 27 | -------------------------------------------------------------------------------- /src/jpegcodes.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/src/jpegcodes.mat -------------------------------------------------------------------------------- /src/train_face.m: -------------------------------------------------------------------------------- 1 | function face_feature = train_face(faces_path, faces_num, L) 2 | face_feature = zeros([1, 2^(3 * L)]); 3 | for i = 1: 1: faces_num 4 | face_temp = imread(faces_path + "/" + string(i) + ".bmp"); 5 | feature_temp = get_face_feat(face_temp, L); 6 | face_feature = face_feature + feature_temp; 7 | end 8 | face_feature = face_feature / faces_num; 9 | end 10 | 11 | -------------------------------------------------------------------------------- /src/zig_zag.m: -------------------------------------------------------------------------------- 1 | function output = zig_zag(input) 2 | temp = reshape(input, [64, 1]); 3 | output = (temp([ 4 | 1, 9, 2, 3, 10, 17, 25, 18, ... 5 | 11, 4, 5, 12, 19, 26, 33, 41, ... 6 | 34, 27, 20, 13, 6, 7, 14, 21, ... 7 | 28, 35, 42, 49, 57, 50, 43, 36, ... 8 | 29, 22, 15, 8, 16, 23, 30, 37, ... 9 | 44, 51, 58, 59, 52, 45, 38, 31, ... 10 | 24, 32, 39, 46, 53, 60, 61, 54, ... 11 | 47, 40, 48, 55, 62, 63, 56, 64 ... 12 | ])); 13 | end 14 | 15 | -------------------------------------------------------------------------------- /图像处理所需资源/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/.DS_Store -------------------------------------------------------------------------------- /图像处理所需资源/Faces/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/.DS_Store -------------------------------------------------------------------------------- /图像处理所需资源/Faces/1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/1.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/10.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/10.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/11.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/11.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/12.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/12.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/13.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/13.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/14.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/14.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/15.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/16.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/17.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/17.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/18.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/18.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/19.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/19.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/2.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/20.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/20.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/21.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/21.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/22.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/22.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/23.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/23.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/24.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/24.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/25.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/25.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/26.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/26.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/27.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/27.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/28.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/28.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/29.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/29.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/3.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/30.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/30.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/31.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/31.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/32.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/32.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/33.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/33.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/4.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/5.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/6.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/7.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/8.bmp -------------------------------------------------------------------------------- /图像处理所需资源/Faces/9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/Faces/9.bmp -------------------------------------------------------------------------------- /图像处理所需资源/JpegCoeff.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/JpegCoeff.mat -------------------------------------------------------------------------------- /图像处理所需资源/hall.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/hall.mat -------------------------------------------------------------------------------- /图像处理所需资源/snow.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/snow.mat -------------------------------------------------------------------------------- /图像处理所需资源/test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/test1.png -------------------------------------------------------------------------------- /图像处理所需资源/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TCL606/Image-Processing/8f519c3137fadb63b75deccdc229eb23f78da518/图像处理所需资源/test2.png --------------------------------------------------------------------------------