题目原地址:https://github.com/gzr2017/ImageProcessing100Wen
我的github地址:https://github.com/LeonG7/image_100question
这是图像处理100题的题目记录以及我自己写的参考代码,感谢@gzr2017提供中文翻译。
所有代码由jupyter book软件导出,如需可执行源代码,请在我的github下载。
如有任何问题或错误,欢迎在评论区讨论和指正
读取图片:
import cv2
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
%matplotlib inline
#如果没有安装opencv的话,这里可以替换成matplotlib读取图片
img = cv2.imread("imori.jpg")
img_noise = cv2.imread("imori_noise.jpg")
#使用matplotlib输出图片,所以要将BGR格式预先转为RBG格式
img = img[:,:,[2,1,0]]
img_noise = img_noise[:,:,[2,1,0]]
将红色通道和蓝色通道交换,交换通道维的第一列和第三列即可。
imshow = plt.imshow(img)
plt.show()
img1 = img.copy()
img1 = img[:,:,[2,1,0]]
imshow = plt.imshow(img1)
通过公式
Y = 0.2126 R + 0.7152 G + 0.0722 B Y = 0.2126\ R + 0.7152\ G + 0.0722\ B Y=0.2126 R+0.7152 G+0.0722 B
计算得出单通道Y
matplotlib中的cmap = gray
#灰度化
def gray(img):
newimg = img[:,:,0]*0.2126 + img[:,:,1]*0.7152 + img[:,:,2]*0.0722
return newimg
imshow = plt.imshow(img)
plt.show()
img2 = gray(img)
imshow = plt.imshow(img2,cmap='gray')
把图像进行二值化吧。
计算出灰度化的值之后,对像素点进行判断:
y = { 0 ( if y < 128 ) 255 ( else ) y= \begin{cases} 0& (\text{if}\quad y < 128) \\ 255& (\text{else}) \end{cases} y={0255(ify<128)(else)
二值化是将图像使用黑和白两种颜色表示的方法。
#二值化
def thre(img,t=128):
img[img<t] = 0
img[img>=t] = 255
return img
imshow = plt.imshow(img)
plt.show()
img3 = gray(img)
img3 =thre(img3)
imshow = plt.imshow(img3,cmap='gray')
使用大津算法来二值化图像吧。
大津算法,也被称作最大类间方差法,是一种可以自动确定二值化中阈值的算法。
从类内方差和类间方差的比值计算得来:
即:
根据以上的式子,我们用以下的式子计算分离度 X X X
X = S b 2 S w 2 = S b 2 S t 2 − S b 2 X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2} X=Sw2Sb2=St2−Sb2Sb2
也就是说:
arg max t X = arg max t S b 2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2 argtmax X=argtmax Sb2
换言之,如果使 S b 2 = w 0 w 1 ( M 0 − M 1 ) 2 {S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2 Sb2=w0 w1 (M0−M1)2最大,就可以得到最好的二值化阈值 t t t。
求出一个阈值,使得类间方差最大化
#计算最大类间方差
def oturs(img):
t = 0
sd = 0
for i in range(255):
newsd = sfunc(img,i)
if newsd > sd:
sd = newsd
t = i
return t
#计算类间方差
def sfunc(img,t):
w = img > t
w1 = w.sum()/w.size
w0 = 1-w1
if w1==0 or w0==0:
return 0
m1 = np.mean(img[w])
m0 = np.mean(img[~w])
return w0*w1*(m0-m1)*(m0-m1)
imshow = plt.imshow(img)
plt.show()
img4 = img.copy()
img4 = gray(img4)
t = oturs(img4)
img4 = thre(img4,t)
imshow = plt.imshow(img4,cmap='gray')
将使用 HSV \text{HSV} HSV表示色彩的图像的色相反转吧!
HSV \text{HSV} HSV即使用**色相(Hue)、饱和度(Saturation)、明度(Value)**来表示色彩的一种方式。
色相:将颜色使用 0 ∘ 0^{\circ} 0∘到 36 0 ∘ 360^{\circ} 360∘表示,就是平常所说的颜色名称,如红色、蓝色。色相与数值按下表对应:
红 | 黄 | 绿 | 青色 | 蓝色 | 品红 | 红 |
---|---|---|---|---|---|---|
0 ∘ 0^{\circ} 0∘ | 6 0 ∘ 60^{\circ} 60∘ | 12 0 ∘ 120^{\circ} 120∘ | 18 0 ∘ 180^{\circ} 180∘ | 24 0 ∘ 240^{\circ} 240∘ | 30 0 ∘ 300^{\circ} 300∘ | 36 0 ∘ 360^{\circ} 360∘ |
饱和度:是指色彩的纯度,饱和度越低则颜色越黯淡( 0 ≤ S < 1 0\leq S < 1 0≤S<1);
明度:即颜色的明暗程度。数值越高越接近白色,数值越低越接近黑色( 0 ≤ V < 1 0\leq V < 1 0≤V<1);
从 RGB \text{RGB} RGB色彩表示转换到 HSV \text{HSV} HSV色彩表示通过以下方式计算:
RGB \text{RGB} RGB的取值范围为 [ 0 , 1 ] [0, 1] [0,1],令:
Max = max ( R , G , B ) Min = min ( R , G , B ) \text{Max}=\max(R,G,B)\\ \text{Min}=\min(R,G,B) Max=max(R,G,B)Min=min(R,G,B)
色相:
H = { 0 ( if Min = Max ) 60 G − R Max − Min + 60 ( if Min = B ) 60 B − G Max − Min + 180 ( if Min = R ) 60 R − B Max − Min + 300 ( if Min = G ) H=\begin{cases} 0&(\text{if}\ \text{Min}=\text{Max})\\ 60\ \frac{G-R}{\text{Max}-\text{Min}}+60&(\text{if}\ \text{Min}=B)\\ 60\ \frac{B-G}{\text{Max}-\text{Min}}+180&(\text{if}\ \text{Min}=R)\\ 60\ \frac{R-B}{\text{Max}-\text{Min}}+300&(\text{if}\ \text{Min}=G) \end{cases} H=⎩⎪⎪⎪⎨⎪⎪⎪⎧060 Max−MinG−R+6060 Max−MinB−G+18060 Max−MinR−B+300(if Min=Max)(if Min=B)(if Min=R)(if Min=G)
饱和度:
S = Max − Min S=\text{Max}-\text{Min} S=Max−Min
明度:
V = Max V=\text{Max} V=Max
从 HSV \text{HSV} HSV色彩表示转换到 RGB \text{RGB} RGB色彩表示通过以下方式计算:
C = S H ′ = H 60 X = C ( 1 − ∣ H ′ m o d 2 − 1 ∣ ) ( R , G , B ) = ( V − C ) ( 1 , 1 , 1 ) + { ( 0 , 0 , 0 ) ( if H is undefined ) ( C , X , 0 ) ( if 0 ≤ H ′ < 1 ) ( X , C , 0 ) ( if 1 ≤ H ′ < 2 ) ( 0 , C , X ) ( if 2 ≤ H ′ < 3 ) ( 0 , X , C ) ( if 3 ≤ H ′ < 4 ) ( X , 0 , C ) ( if 4 ≤ H ′ < 5 ) ( C , 0 , X ) ( if 5 ≤ H ′ < 6 ) C = S\\ H' = \frac{H}{60}\\ X = C\ (1 - |H' \mod 2 - 1|)\\ (R,G,B)=(V-C)\ (1,1,1)+\begin{cases} (0, 0, 0)& (\text{if H is undefined})\\ (C, X, 0)& (\text{if}\quad 0 \leq H' < 1)\\ (X, C, 0)& (\text{if}\quad 1 \leq H' < 2)\\ (0, C, X)& (\text{if}\quad 2 \leq H' < 3)\\ (0, X, C)& (\text{if}\quad 3 \leq H' < 4)\\ (X, 0, C)& (\text{if}\quad 4 \leq H' < 5)\\ (C, 0, X)& (\text{if}\quad 5 \leq H' < 6) \end{cases} C=SH′=60HX=C (1−∣H′mod2−1∣)(R,G,B)=(V−C) (1,1,1)+⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧(0,0,0)(C,X,0)(X,C,0)(0,C,X)(0,X,C)(X,0,C)(C,0,X)(if H is undefined)(if0≤H′<1)(if1≤H′<2)(if2≤H′<3)(if3≤H′<4)(if4≤H′<5)(if5≤H′<6)
请将色相反转(色相值加 180 180 180),然后再用 RGB \text{RGB} RGB色彩空间表示图片。
将RGB图片转为HSV,即色相、饱和度、明度
然后将色相反转,再转回RGB输出
def rgb2hsv(img):
#标准化
img = img/255
#将img拉成 [128*128,3] 方便计算
old_shape = img.shape
img = img.reshape(-1,3)
R,G,B = img[:,0],img[:,1],img[:,2]
max = np.max(img,axis=1)
min = np.min(img,axis=1)
hsv = np.zeros_like(img)
#计算色相
h_b = min == B
h_r = min == R
h_g = min == G
hsv[h_b,0] = 60*(G[h_b]-R[h_b])/(max[h_b]-min[h_b])+60
hsv[h_r,0] = 60*(B[h_r]-G[h_r])/(max[h_r]-min[h_r])+180
hsv[h_g,0] = 60*(R[h_g]-B[h_g])/(max[h_g]-min[h_g])+300
#饱和度
hsv[:,1] = max-min
#明度
hsv[:,2] = max
return hsv.reshape(old_shape)
def hsv2rgb(img):
#色相是环形的,所以要模360°
img[:,:,0] = np.mod(img[:,:,0],360)
old_shape = img.shape
img = img.reshape(-1,3)
c = img[:,1]
h = img[:,0]/60
x = c*(1-np.abs(np.mod(h,2)-1))
v = img[:,2]
#计算rgb
#先建立v-c的基础数组,v-c*(1,1,1)
v_c = (v-c).reshape(-1,1)
rgb = np.repeat(v_c,3,axis=1)
#划分条件,组合起来加到初始数组上
#这里将true和false视为1和0,相乘就是取并集
h0 = (h>=0)*(h<1)
zero = np.zeros(h0.sum())
rgb[h0] += np.vstack((c[h0],x[h0],zero)).T
h1 = (h>=1)*(h<2)
zero = np.zeros(h1.sum())
rgb[h1] += np.vstack((x[h1],c[h1],zero)).T
h2 = (h>=2)*(h<3)
zero = np.zeros(h2.sum())
rgb[h2] += np.vstack((zero,c[h2],x[h2])).T
h3 = (h>=3)*(h<4)
zero = np.zeros(h3.sum())
rgb[h3] += np.vstack((zero,x[h3],c[h3])).T
h4 = (h>=4)*(h<5)
zero = np.zeros(h4.sum())
rgb[h4] += np.vstack((x[h4],zero,c[h4])).T
h5 = (h>=5)*(h<6)
zero = np.zeros(h5.sum())
rgb[h5] += np.vstack((c[h5],zero,x[h5])).T
return rgb.reshape(old_shape)
imshow = plt.imshow(img)
plt.show()
img5 = img.copy()
img_hsv = rgb2hsv(img5)
img_hsv[:,:,0]+=180
img5 = hsv2rgb(img_hsv)
imshow = plt.imshow(img5)
我们将图像的值由 25 6 3 256^3 2563压缩至 4 3 4^3 43,即将 RGB \text{RGB} RGB的值只取 { 32 , 96 , 160 , 224 } \{32, 96, 160, 224\} {32,96,160,224}。这被称作色彩量化。色彩的值按照下面的方式定义:
val = { 32 ( 0 ≤ var < 64 ) 96 ( 64 ≤ var < 128 ) 160 ( 128 ≤ var < 192 ) 224 ( 192 ≤ var < 256 ) \text{val}= \begin{cases} 32& (0 \leq \text{var} < 64)\\ 96& (64\leq \text{var}<128)\\ 160&(128\leq \text{var}<192)\\ 224&(192\leq \text{var}<256) \end{cases} val=⎩⎪⎪⎪⎨⎪⎪⎪⎧3296160224(0≤var<64)(64≤var<128)(128≤var<192)(192≤var<256)
将RGB的【0~255】范围离散化至【32 , 96 , 160 , 224】
def dcolor(img):
img[img<64] = 32
img[(img>=64)*(img<128)] = 96
img[(img>=128)*(img<192)] = 160
img[(img>=192)*(img<256)] = 224
return img
imshow = plt.imshow(img)
plt.show()
img6 = img.copy()
img6 = dcolor(img)
imshow = plt.imshow(img6)
将图片按照固定大小网格分割,网格内的像素值取网格内所有像素的平均值。
我们将这种把图片使用均等大小网格分割,并求网格内代表值的操作称为池化(Pooling)。
池化操作是**卷积神经网络(Convolutional Neural Network)**中重要的图像处理方式。平均池化按照下式定义:
v = 1 ∣ R ∣ ∑ i = 1 R v i v=\frac{1}{|R|}\ \sum\limits_{i=1}^R\ v_i v=∣R∣1 i=1∑R vi
请把大小为 128 × 128 128\times128 128×128的imori.jpg
使用 8 × 8 8\times8 8×8的网格做平均池化。
一定范围内的像素点全部转为该范围的平均值。
def pooling(img,length,method):
for i in range(int(img.shape[0]/length)):
for j in range(int(img.shape[1]/length)):
area = img[i*length:(i+1)*length,j*length:(j+1)*length]
if(method=="avg"):
area[:,:,0] = area[:,:,0].mean()
area[:,:,1] = area[:,:,1].mean()
area[:,:,2] = area[:,:,2].mean()
if(method=="max"):
area[:,:,0] = area[:,:,0].max()
area[:,:,1] = area[:,:,1].max()
area[:,:,2] = area[:,:,2].max()
return img
imshow = plt.imshow(img)
plt.show()
img7 = img.copy()
img7 = pooling(img7,8,"avg")
imshow = plt.imshow(img7)
网格内的值不取平均值,而是取网格内的最大值进行池化操作。
一定范围内的像素点全部转为该范围的最大值
imshow = plt.imshow(img)
plt.show()
img8 = img.copy()
img8 = pooling(img8,8,"max")
imshow = plt.imshow(img8)
使用高斯滤波器( 3 × 3 3\times3 3×3大小,标准差 σ = 1.3 \sigma=1.3 σ=1.3)来对imori_noise.jpg
进行降噪处理吧!
高斯滤波器是一种可以使图像平滑的滤波器,用于去除噪声。可用于去除噪声的滤波器还有中值滤波器(参见问题十),平滑滤波器(参见问题十一)、LoG滤波器(参见问题十九)。
高斯滤波器将中心像素周围的像素按照高斯分布加权平均进行平滑化。这样的(二维)权值通常被称为卷积核(kernel)或者滤波器(filter)。
但是,由于图像的长宽可能不是滤波器大小的整数倍,因此我们需要在图像的边缘补 0 0 0。这种方法称作Zero Padding。并且权值 g g g(卷积核)要进行归一化操作( ∑ g = 1 \sum\ g = 1 ∑ g=1)。
按下面的高斯分布公式计算权值:
g ( x , y , σ ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 g(x,y,\sigma)=\frac{1}{2\ \pi\ \sigma^2}\ e^{-\frac{x^2+y^2}{2\ \sigma^2}} g(x,y,σ)=2 π σ21 e−2 σ2x2+y2
标准差 σ = 1.3 \sigma=1.3 σ=1.3的 8 − 8- 8−近邻高斯滤波器如下:
K = 1 16 [ 1 2 1 2 4 2 1 2 1 ] K=\frac{1}{16}\ \left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{matrix} \right] K=161 ⎣⎡121242121⎦⎤
1.先设定一个高斯核,高斯核的值由二维高斯函数计算得出
2.高斯核进行标准化,也就是九宫格的和为1
3.对于每一个像素点,以它为中心取和高斯核相同大小的方块(九宫格)
4.将高斯核和方块相乘后累加,得出的值就是这个新的中心像素点的高斯滤波值
def padding(img,s=1):
#先加行
rows = np.zeros((s,img.shape[1],img.shape[2]),dtype="uint8")
#再加列,这时候列长已经扩充 2*s
columns = np.zeros((img.shape[0]+(2*s),s,img.shape[2]),dtype="uint8")
#拼接
img = np.vstack((rows,img,rows))
img = np.hstack((columns,img,columns))
return img
def getarea(img,padding,method):
#建立一个新的数组,该数组用于保存计算的高斯滤波值,所以要去掉padding
result = np.zeros([img.shape[0]-2*padding,
img.shape[1]-2*padding,
img.shape[2]],dtype="uint8")
for i in range(int(img.shape[0]-2)):
for j in range(int(img.shape[1]-2)):
area = img[i:i+3,j:j+3]
if(method=="gaussian"):
#将像素值赋值到中心点
kernel = np.array([1,2,1,2,4,2,1,2,1])*(1/16)
result[i,j,:] = gaussFilter(area,kernel)
if(method=="median"):
result[i,j,:] = medianFilter(area)
return result
def gaussFilter(area,kernel):
#拉成一条,用矩阵的乘法完成滤波计算,参考卷积核计算方法
area = area.reshape(-1,3)
newk = np.dot(kernel,area)
newk = newk.astype(np.uint8)
return newk
imshow = plt.imshow(img_noise)
plt.show()
img9 = img_noise.copy()
img9 = padding(img9,1)
img9 = getarea(img9,1,"gaussian")
imshow = plt.imshow(img9)
使用中值滤波器( 3 × 3 3\times3 3×3大小)来对imori_noise.jpg
进行降噪处理吧!
中值滤波器是一种可以使图像平滑的滤波器。这种滤波器用滤波器范围内(在这里是 3 × 3 3\times3 3×3)像素点的中值进行滤波,请在这里也采用Zero Padding。
在滤波器中,取范围内的中值数填入中心点
def medianFilter(area):
#拉成一条,并且转置为横向
area = area.reshape(-1,3).T
#进行排序后取出中间值
newk = np.sort(area,axis=1)[:,4]
return newk
imshow = plt.imshow(img_noise)
plt.show()
img10 = img_noise.copy()
img10 = padding(img10,1)
img10 = getarea(img10,1,"median")
imshow = plt.imshow(img10)
原创首发,欢迎来我的博客留言讨论,我的博客主页:LeonG是什么意思?我的知乎专栏:LeonG与机器学习
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。