自《图像处理中的数学修炼》原书第一版于2017年2月上市以来,加印重印多次,在京东和当当等主流在线购书网站上已经累计有超过3200个购买评论,并且在这两个网站上的好评度都超过99%。结合第一版书籍读者给出的反馈,同时为了适应相关技术的快速发展,在首版发行三年之后的2020年,笔者再度与清华大学出版社合作,乘势推出全新的《图像处理中的数学修炼》(第2版)。
新版的《图像处理中的数学修炼》全书共分十二章(*第一版书一共是八章):其中前五章主要是图像处理中最重要、最常用的数学基础,这也是每个期望进行图像处理研究和学习的人必须要掌握的基础,这部分内容包括:
第六至第十二章(共7章)则结合图像处理领域比较流行的研究话题,举例说明前五章中数学基础在图像处理中的具体应用。这部分内容包括:
对于阅读本书时的困惑和问题,以及发现的书中的纰漏、错误等,欢迎在博文下方留言,或者直接发邮件给作者(邮件地址见博客网页版左侧栏目)。本文后面给出了本书中配套的相应代码。但是,需要特别提醒广大读者注意:
代码或者编程不是这本书的重点!
代码或者编程不是这本书的重点!
代码或者编程不是这本书的重点!
所以,请千万不要把这本书用来作为你入门图像处理编程的扫盲书!!!如果你更想学习如何用MATLAB进行图像处理应用方面的编程开发,可以参考我的另外一本书《数字图像处理:原理与实践(MATLAB版)》。
i=double(imread('vase.tif'));
[C,S]=wavedec2(i,2,'db1');
a2=appcoef2(C,S,'db1',2);
dh1=detcoef2('h',C,S,1);
dv1=detcoef2('v',C,S,1);
dd1=detcoef2('d',C,S,1);
dh2=detcoef2('h',C,S,2);
dv2=detcoef2('v',C,S,2);
dd2=detcoef2('d',C,S,2);
[x,y]=size(i);
img = zeros(x,y);
img(1:x/4,1:y/4) =im2uint8(mat2gray(a2));
img(((x/4)+1):y/2,1:y/4) = im2uint8(mat2gray(dv2));
img(((x/4)+1):x/2,1:y/4) = im2uint8(mat2gray(dv2));
img(1:x/4,((y/4)+1):y/2) = im2uint8(mat2gray(dh2));
img(((x/4)+1):x/2,((y/4)+1):y/2) = im2uint8(mat2gray(dd2));
img(((x/2)+1):x,1:y/2) = im2uint8(mat2gray(dv1));
img(1:x/2,((y/2)+1):y) = im2uint8(mat2gray(dh1));
img(((x/2)+1):x,((y/2)+1):y) = im2uint8(mat2gray(dd1));
imshow(img,[]);
X1 = imread('cathe1.bmp');
X2 = imread('cathe2.bmp');
XFUS = wfusimg(X1,X2,'sym4',5,'mean','max');
imshow(XFUS,[]);
X1 = imread('cathe1.bmp');
X2 = imread('cathe2.bmp');
M1 = double(X1) / 256;
M2 = double(X2) / 256;
N = 4;
wtype = 'sym4';
[c0,s0] = wavedec2(M1, N, wtype);
[c1,s1] = wavedec2(M2, N, wtype);
length = size(c1);
Coef_Fusion = zeros(1,length(2));
%低频系数的处理,取平均值
Coef_Fusion(1:s1(1,1)) = (c0(1:s1(1,1))+c1(1:s1(1,1)))/2;
%处理高频系数,取绝对值大者,这里用到了矩阵乘法
MM1 = c0(s1(1,1)+1:length(2));
MM2 = c1(s1(1,1)+1:length(2));
mm = (abs(MM1)) > (abs(MM2));
Y = (mm.*MM1) + ((~mm).*MM2);
Coef_Fusion(s1(1,1)+1:length(2)) = Y;
%重构
Y = waverec2(Coef_Fusion,s0,wtype);
imshow(Y,[]);
I = imread('noise_lena.bmp');
[thr,sorh,keepapp] = ddencmp('den','wv',I);
de_I = wdencmp('gbl',I,'sym4',2,thr,sorh,keepapp);
imwrite(im2uint8(mat2gray(de_I)), 'denoise_lena.bmp');
I = imread('baboon.bmp');
I1 = double(I);
T = hadamard(8);
myFun1 = @(block_struct)T*block_struct.data*T/64;
H = blockproc(I1, [8 8], myFun1);
H(abs(H)<3.5)=0;
myFun2 = @(block_struct)T*block_struct.data*T;
I2 = blockproc(H, [8 8], myFun2);
subplot(121), imshow(I1,[]), title('original image');
subplot(122), imshow(I2,[]), title('zipped image');
I = imread('baboon.bmp');
I1 = double(I);
[m n] =size(I);
sizi = 8;
num = 16;
%分块进行离散沃尔什变换
T = hadamard(sizi);
myFun1 = @(block_struct)T*block_struct.data*T/(sizi.^2);
hdcoe = blockproc(I1, [sizi, sizi], myFun1);
%重新排列系数
coe = im2col(hdcoe, [sizi, sizi], 'distinct');
coe_t = abs(coe);
[Y, ind] = sort(coe_t);
%舍去绝对值较小的系数
[m_c, n_c] = size(coe);
for i = 1:n_c
coe(ind(1:num, i), i)=0;
end
%重建图像
re_hdcoe = col2im(coe, [sizi, sizi], [m, n], 'distinct');
myFun2 = @(block_struct)T*block_struct.data*T;
re_s = blockproc(re_hdcoe, [sizi, sizi], myFun2);
subplot(121), imshow(I1,[]), title('original image');
subplot(122), imshow(re_s,[]), title('compressed image');
I = imread('baboon.bmp');
x = double(I)/255;
[m,n]=size(x);
y =[];
%拆解图像
for i = 1:m/8;
for j = 1:n/8;
ii = (i-1)*8+1;
jj = (j-1)*8+1;
y_app = reshape(x(ii:ii+7,jj:jj+7),1,64);
y=[y;y_app];
end
end
%KL变换
[COEFF,SCORE,latent] = princomp(y);
kl = y * COEFF;
kl1 = kl;
kl2 = kl;
kl3 = kl;
%置零压缩过程
kl1(:, 33:64)=0;
kl2(:, 17:64)=0;
kl3(:, 9:64)=0;
%KL逆变换
kl_i = kl*COEFF';
kl1_i = kl1*COEFF';
kl2_i = kl2*COEFF';
kl3_i = kl3*COEFF';
image = ones(256,256);
image1 = ones(256,256);
image2 = ones(256,256);
image3 = ones(256,256);
k=1;
%重组图像
for i = 1:m/8;
for j = 1:n/8;
y = reshape(kl_i(k, 1:64),8,8);
y1 = reshape(kl1_i(k, 1:64),8,8);
y2 = reshape(kl2_i(k, 1:64),8,8);
y3 = reshape(kl3_i(k, 1:64),8,8);
ii = (i-1)*8+1;
jj = (j-1)*8+1;
image(ii:ii+7,jj:jj+7) = y;
image1(ii:ii+7,jj:jj+7) = y1;
image2(ii:ii+7,jj:jj+7) = y2;
image3(ii:ii+7,jj:jj+7) = y3;
k=k+1;
end
end
function [f,noise] = mywiener2(g, nhood, noise)
if (nargin<3)
noise = [];
end
% Estimate the local mean of f.
localMean = filter2(ones(nhood), g) / prod(nhood);
% Estimate of the local variance of f.
localVar = filter2(ones(nhood), g.^2) / prod(nhood) - localMean.^2;
% Estimate the noise power if necessary.
if (isempty(noise))
noise = mean2(localVar);
end
% Compute result
% f = localMean + (max(0, localVar - noise) ./ ...
% max(localVar, noise)) .* (g - localMean);
%
% Computation is split up to minimize use of memory for temp arrays.
f = g - localMean;
g = localVar - noise;
g = max(g, 0);
f = localMean + ((f ./ max(localVar, noise)) .* g);
RGB = imread('saturn.png');
I = rgb2gray(RGB);
I = I(601:1000,1:600);
J = imnoise(I,'gaussian',0,0.005);
J = im2double(J);
K = mywiener2(J,[5 5]);
figure;
imshow(I), title('original image');
figure;
subplot(1,2,1), subimage(J), title('noised image');
subplot(1,2,2), subimage(K), title('denoised image');
【代码链接】
function q = guidedfilter(I, p, r, eps)
% - guidance image: I (should be a gray-scale/single channel image)
% - filtering input image: p (should be a gray-scale/single channel image)
% - local window radius: r
% - regularization parameter: eps
[hei, wid] = size(I);
N = boxfilter(ones(hei, wid), r);
mean_I = boxfilter(I, r) ./ N;
mean_p = boxfilter(p, r) ./ N;
mean_Ip = boxfilter(I.*p, r) ./ N;
% this is the covariance of (I, p) in each local patch.
cov_Ip = mean_Ip - mean_I .* mean_p;
mean_II = boxfilter(I.*I, r) ./ N;
var_I = mean_II - mean_I .* mean_I;
a = cov_Ip ./ (var_I + eps);
b = mean_p - a .* mean_I;
mean_a = boxfilter(a, r) ./ N;
mean_b = boxfilter(b, r) ./ N;
q = mean_a .* I + mean_b;
end
function imDst = boxfilter(imSrc, r)
% BOXFILTER O(1) time box filtering using cumulative sum
%
% - Definition imDst(x, y)=sum(sum(imSrc(x-r:x+r,y-r:y+r)));
% - Running time independent of r;
% - Equivalent to the function: colfilt(imSrc, [2*r+1, 2*r+1], 'sliding', @sum);
% - But much faster.
[hei, wid] = size(imSrc);
imDst = zeros(size(imSrc));
%cumulative sum over Y axis
imCum = cumsum(imSrc, 1);
%difference over Y axis
imDst(1:r+1, :) = imCum(1+r:2*r+1, :);
imDst(r+2:hei-r, :) = imCum(2*r+2:hei, :) - imCum(1:hei-2*r-1, :);
imDst(hei-r+1:hei, :) = repmat(imCum(hei, :), [r, 1]) - imCum(hei-2*r:hei-r-1, :);
%cumulative sum over X axis
imCum = cumsum(imDst, 2);
%difference over Y axis
imDst(:, 1:r+1) = imCum(:, 1+r:2*r+1);
imDst(:, r+2:wid-r) = imCum(:, 2*r+2:wid) - imCum(:, 1:wid-2*r-1);
imDst(:, wid-r+1:wid) = repmat(imCum(:, wid), [1, r]) - imCum(:, wid-2*r:wid-r-1);
end
I = double(imread('cat.bmp')) / 255;
p = I;
r = 4; % try r=2, 4, or 8
eps = 0.2^2; % try eps=0.1^2, 0.2^2, 0.4^2
O = guidedfilter(I, p, r, eps);
subplot(121), imshow(I);
subplot(122), imshow(O);
mountains = double(imread('./img/mountain.jpg'));
moon = double(imread('./img/moon.png'));
sizeSrc = size(mountains);
sizeDst = size(moon);
gradient_inner = moon(1:sizeDst(1)-2,2:sizeDst(2)-1,:)...
+ moon(3:sizeDst(1),2:sizeDst(2)-1,:)...
+ moon(2:sizeDst(1)-1,1:sizeDst(2)-2,:)...
+ moon(2:sizeDst(1)-1,3:sizeDst(2),:)...
- 4*moon(2:sizeDst(1)-1,2:sizeDst(2)-1,:);
Lap = [0, 1, 0;1, -4, 1;0, 1, 0];
I1 = conv2(double(moon(:,:,1)), double(Lap));
I2 = conv2(double(moon(:,:,2)), double(Lap));
I3 = conv2(double(moon(:,:,3)), double(Lap));
gradient_inner(:, :, 1) = I1(3:sizeDst(1),3:sizeDst(2));
gradient_inner(:, :, 2) = I2(3:sizeDst(1),3:sizeDst(2));
gradient_inner(:, :, 3) = I3(3:sizeDst(1),3:sizeDst(2));
dstX = 350;dstY = 100;
rebuilt = mountains(dstY:dstY+sizeDst(1)-1,dstX:dstX+sizeDst(2)-1,:);
for n = [1:1000]
rebuilt(2:2:sizeDst(1)-1,2:2:sizeDst(2)-1,:)= ...
(rebuilt(1:2:sizeDst(1)-2 , 2:2:sizeDst(2)-1,:)...
+rebuilt(3:2:sizeDst(1) , 2:2:sizeDst(2)-1,:)...
+rebuilt(2:2:sizeDst(1)-1 , 1:2:sizeDst(2)-2,:)...
+rebuilt(2:2:sizeDst(1)-1 , 3:2:sizeDst(2),:)...
-gradient_inner(1:2:sizeDst(1)-2 , 1:2:sizeDst(2)-2,:))/4;
rebuilt(3:2:sizeDst(1)-1,3:2:sizeDst(2)-1,:)= ...
(rebuilt(2:2:sizeDst(1)-2 , 3:2:sizeDst(2)-1,:)...
+rebuilt(4:2:sizeDst(1) , 3:2:sizeDst(2)-1,:)...
+rebuilt(3:2:sizeDst(1)-1 , 2:2:sizeDst(2)-2,:)...
+rebuilt(3:2:sizeDst(1)-1 , 4:2:sizeDst(2),:)...
-gradient_inner(2:2:sizeDst(1)-2 , 2:2:sizeDst(2)-2,:))/4;
rebuilt(3:2:sizeDst(1)-1,2:2:sizeDst(2)-1,:)= ...
(rebuilt(2:2:sizeDst(1)-2 , 2:2:sizeDst(2)-1,:)...
+rebuilt(4:2:sizeDst(1) , 2:2:sizeDst(2)-1,:)...
+rebuilt(3:2:sizeDst(1)-1 , 1:2:sizeDst(2)-2,:)...
+rebuilt(3:2:sizeDst(1)-1 , 3:2:sizeDst(2),:)...
-gradient_inner(2:2:sizeDst(1)-2 , 1:2:sizeDst(2)-2,:))/4;
rebuilt(2:2:sizeDst(1)-1 , 3:2:sizeDst(2)-1,:)= ...
(rebuilt(1:2:sizeDst(1)-2 , 3:2:sizeDst(2)-1,:)...
+rebuilt(3:2:sizeDst(1) , 3:2:sizeDst(2)-1,:)...
+rebuilt(2:2:sizeDst(1)-1 , 2:2:sizeDst(2)-2,:)...
+rebuilt(2:2:sizeDst(1)-1 , 4:2:sizeDst(2),:)...
-gradient_inner(1:2:sizeDst(1)-2 , 2:2:sizeDst(2)-2,:))/4;
end
mountains(dstY:sizeDst(1)+dstY-1,dstX:sizeDst(2)+dstX-1,:) = rebuilt;
figure,imshow(uint8(mountains));
【代码下载链接】
【补充代码链接】
【代码下载链接】
image = imread('Unequalized_Hawkes_Bay_NZ.jpg');
Img = rgb2gray(image);
[height,width]=size(Img);
NumPixel = zeros(1,256);%统计各灰度数目,共256个灰度级
for i = 1:height
for j = 1: width
%对应灰度值像素点数量增加一
%因为NumPixel的下标是从1开始,但是图像像素的取值范围是0~255,所以用NumPixel(Img(i,j) + 1)
NumPixel(Img(i,j) + 1) = NumPixel(Img(i,j) + 1) + 1;
end
end
ProbPixel = zeros(1,256);
for i = 1:256
ProbPixel(i) = NumPixel(i) / (height * width * 1.0);
end
CumuPixel = cumsum(ProbPixel);
CumuPixel = uint8(255 .* CumuPixel + 0.5);
for i = 1:height
for j = 1: width
Img(i,j) = CumuPixel(Img(i,j));
end
end
a = imread('couple.tiff');
R = a(:,:,1);
G = a(:,:,2);
B = a(:,:,3);
R = histeq(R, 256);
G = histeq(G, 256);
B = histeq(B, 256);
a(:,:,1) = R;
a(:,:,2) = G;
a(:,:,3) = B;
imshow(a)
Img = imread('couple.tiff');
hsvImg = rgb2hsv(Img);
V=hsvImg(:,:,3);
[height,width]=size(V);
V = uint8(V*255);
NumPixel = zeros(1,256);
for i = 1:height
for j = 1: width
NumPixel(V(i,j) + 1) = NumPixel(V(i,j) + 1) + 1;
end
end
ProbPixel = zeros(1,256);
for i = 1:256
ProbPixel(i) = NumPixel(i) / (height * width * 1.0);
end
CumuPixel = cumsum(ProbPixel);
CumuPixel = uint8(255 .* CumuPixel + 0.5);
for i = 1:height
for j = 1: width
V(i,j) = CumuPixel(V(i,j));
end
end
V = im2double(V);
hsvImg(:,:,3) = V;
outputImg = hsv2rgb(hsvImg);
imshow(outputImg);
img = imread('space.jpg');
rimg = img(:,:,1);
gimg = img(:,:,2);
bimg = img(:,:,3);
resultr = adapthisteq(rimg);
resultg = adapthisteq(gimg);
resultb = adapthisteq(bimg);
result = cat(3, resultr, resultg, resultb);
imshow(result);
clear;
img = imread('space.jpg');
cform2lab = makecform('srgb2lab');
LAB = applycform(img, cform2lab);
L = LAB(:,:,1);
LAB(:,:,1) = adapthisteq(L);
cform2srgb = makecform('lab2srgb');
J = applycform(LAB, cform2srgb);
imshow(J);
clc;
clear all;
Img = rgb2gray(imread('space.jpg'));
[h,w] = size(Img);
minV = double(min(min(Img)));
maxV = double(max(max(Img)));
imshow(Img);
NrX = 8;
NrY = 4;
HSize = ceil(h/NrY);
WSize = ceil(w/NrX);
deltay = NrY*HSize - h;
deltax = NrX*WSize - w;
tmpImg = zeros(h+deltay,w+deltax);
tmpImg(1:h,1:w) = Img;
new_w = w + deltax;
new_h = h + deltay;
NrPixels = WSize * WSize;
% NrBins - Number of greybins for histogram ("dynamic range")
NrBins = 256;
LUT = zeros(maxV+1,1);
for i=minV:maxV
LUT(i+1) = fix(i - minV);%i+1
end
Bin = zeros(new_h, new_w);
for m = 1 : new_h
for n = 1 : new_w
Bin(m,n) = 1 + LUT(tmpImg(m,n) + 1);
end
end
Hist = zeros(NrY, NrX, 256);
for i=1:NrY
for j=1:NrX
tmp = uint8(Bin(1+(i-1)*HSize:i*HSize, 1+(j-1)*WSize:j*WSize));
%tmp = tmpImg(1+(i-1)*HSize:i*HSize,1+(j-1)*WSize:j*WSize);
[Hist(i, j, :), x] = imhist(tmp, 256);
end
end
Hist = circshift(Hist,[0, 0, -1]);
ClipLimit = 2.5;
ClipLimit = max(1,ClipLimit * HSize * WSize/NrBins);
Hist = clipHistogram(Hist,NrBins,ClipLimit,NrY,NrX);
Map=mapHistogram(Hist, minV, maxV, NrBins, NrPixels, NrY, NrX);
yI = 1;
for i = 1:NrY+1
if i == 1
subY = floor(HSize/2);
yU = 1;
yB = 1;
elseif i == NrY+1
subY = floor(HSize/2);
yU = NrY;
yB = NrY;
else
subY = HSize;
yU = i - 1;
yB = i;
end
xI = 1;
for j = 1:NrX+1
if j == 1
subX = floor(WSize/2);
xL = 1;
xR = 1;
elseif j == NrX+1
subX = floor(WSize/2);
xL = NrX;
xR = NrX;
else
subX = WSize;
xL = j - 1;
xR = j;
end
UL = Map(yU,xL,:);
UR = Map(yU,xR,:);
BL = Map(yB,xL,:);
BR = Map(yB,xR,:);
subImage = Bin(yI:yI+subY-1,xI:xI+subX-1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
sImage = zeros(size(subImage));
num = subY * subX;
for i = 0:subY - 1
inverseI = subY - i;
for j = 0:subX - 1
inverseJ = subX - j;
val = subImage(i+1,j+1);
sImage(i+1, j+1)=(inverseI*(inverseJ*UL(val)+j*UR(val))...
+ i*(inverseJ*BL(val)+j*BR(val)))/num;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
output(yI:yI+subY-1, xI:xI+subX-1) = sImage;
xI = xI + subX;
end
yI = yI + subY;
end
output = output(1:h, 1:w);
figure, imshow(output, []);
【补充代码链接】
img = rgb2gray(imread('theatre.jpg'));
img_ref = rgb2gray(imread('rpic.jpg'));
[hgram,x] = imhist(img_ref);
J = histeq(img,hgram);
subplot(2,3,1),imshow(img),title('originalimage');
subplot(2,3,4),imhist(img),title('originalimage');
subplot(2,3,2),imshow(img_ref),title('referenceimage');
subplot(2,3,5),imhist(img_ref),title('referenceimage');
subplot(2,3,3),imshow(J),title('outputimage');
subplot(2,3,6),imhist(J),title('outputimage');
%求一幅图像的暗通道图,窗口大小为15*15
imageRGB = imread('picture.bmp');
imageRGB = double(imageRGB);
imageRGB = imageRGB./255;
dark = darkChannel(imageRGB);
% 选取暗通道图中最亮的0.1%像素,从而求得大气光
[m, n, ~] = size(imageRGB);
imsize = m * n;
numpx = floor(imsize/1000);
JDarkVec = reshape(dark,imsize,1);
ImVec = reshape(imageRGB,imsize,3);
[JDarkVec, indices] = sort(JDarkVec);
indices = indices(imsize-numpx+1:end);
atmSum = zeros(1,3);
for ind = 1:numpx
atmSum = atmSum + ImVec(indices(ind),:);
end
atmospheric = atmSum / numpx;
%求解透射率,并通过omega参数来选择保留一定程度的雾霾,以免损坏真实感
omega = 0.95;
im = zeros(size(imageRGB));
for ind = 1:3
im(:,:,ind) = imageRGB(:,:,ind)./atmospheric(ind);
end
dark_2 = darkChannel(im);
t = 1-omega*dark_2;
%通过导向滤波来获得更为精细的透射图
r = 60;
eps = 10^-6;
refined_t = guidedfilter_color(imageRGB, t, r, eps);
refinedRadiance = getRadiance(atmospheric, imageRGB, refined_t);
function dark = darkChannel(imRGB)
r=imRGB(:,:,1);
g=imRGB(:,:,2);
b=imRGB(:,:,3);
[m n] = size(r);
a = zeros(m,n);
for i = 1: m
for j = 1: n
a(i,j) = min(r(i,j), g(i,j));
a(i,j)= min(a(i,j), b(i,j));
end
end
d = ones(15,15);
fun = @(block_struct)min(min(block_struct.data))*d;
dark = blockproc(a, [15 15], fun);
dark = dark(1:m, 1:n);
【补充代码链接】
【代码下载链接】