%matplotlib inline
import numpy as np
作者:Emmanuelle Gouillart, Gaël Varoquaux
原文: http://www.scipy-lectures.org/advanced/image_processing/index.html
这个部分解决用核心的科学模块NumPy和SciPy做基本的图像操作和处理。这个教程中涵盖的一些操作可能对于一些其他类型的多维度数据处理比对图像处理更加有用。特别是,子摸块scipy.ndimage提供了在N维Numpy数组上操作的方法。
也看一下: 对于更高级的图像处理和图像特有的程序,见专注于skimage模块教程Scikit-image: 图像处理。
图像 = 2-D 数值数组
(或者 3-D: CT, MRI, 2D + time; 4-D, ...)
这里, 图像 == Numpy 数组 np.array
本教程中使用的工具:
numpy
: 基础的数组操作scipy
: scipy.ndimage
专注于图像处理的子模块 (n维 图像)。见文档:from scipy import ndimage
图像处理中的常见任务:
章节内容
打开和写入图像文件
显示图像
基础操作
统计信息
几何图像变换
图像过滤
模糊/光滑
锐化
降噪
数学形态学
特征提取
边缘检测
分隔
测量对象属性: ndimage.measurements
将数组写入文件:
from scipy import misc
f = misc.face()
misc.imsave('face.png', f) # 使用图像模块 (PIL)
import matplotlib.pyplot as plt
plt.imshow(f)
plt.show()
从图像文件创建一个numpy数组:
from scipy import misc
face = misc.face()
misc.imsave('face.png', face) # 首先我们需要创建这个PNG文件
face = misc.imread('face.png')
type(face)
face.shape, face.dtype
对于8位的图像 (0-255) dtype是uint8
打开raw文件 (照相机, 3-D 图像)
face.tofile('face.raw') # 创建raw文件
face_from_raw = np.fromfile('face.raw', dtype=np.uint8)
face_from_raw.shape
face_from_raw.shape = (768, 1024, 3)
需要知道图像的shape和dtype (如何去分离数据类型)。
对于大数据, 使用np.memmap
来做内存映射:
face_memmap = np.memmap('face.raw', dtype=np.uint8, shape=(768, 1024, 3))
(数据从文件中读取,并没有加载到内存)
处理一组图像文件
for i in range(10):
im = np.random.random_integers(0, 255, 10000).reshape((100, 100))
misc.imsave('random_%02d.png' % i, im)
from glob import glob
filelist = glob('random*.png')
filelist.sort()
使用matplotlib
和imshow
在matplotlib图形内部
显示图像:
f = misc.face(gray=True) # 取回灰度图像
import matplotlib.pyplot as plt
plt.imshow(f, cmap=plt.cm.gray)
通过设置最小和最大值增加对比度:
plt.imshow(f, cmap=plt.cm.gray, vmin=30, vmax=200)
plt.imshow(f, cmap=plt.cm.gray, vmin=30, vmax=200)
# 删除座标轴和刻度
plt.axis('off')
画出轮廓线:
plt.imshow(f, cmap=plt.cm.gray, vmin=30, vmax=200)
# 删除座标轴和刻度
plt.axis('off')
plt.contour(f, [50, 200])
对于要精确检查的密度变量,使用interpolation='nearest'
:
plt.imshow(f[320:340, 510:530], cmap=plt.cm.gray)
plt.imshow(f[320:340, 510:530], cmap=plt.cm.gray, interpolation='nearest')
face = misc.face(gray=True)
face[0, 40]
# 切片
face[10:13, 20:23]
face[100:120] = 255
lx, ly = face.shape
X, Y = np.ogrid[0:lx, 0:ly]
mask = (X - lx / 2) ** 2 + (Y - ly / 2) ** 2 > lx * ly / 4
# 掩码(masks)
face[mask] = 0
# 象征索引(Fancy indexing)
face[range(400), range(400)] = 255
face = misc.face(gray=True)
face.mean()
face.max(), face.min()
np.histogram
练习
scikit-image
logo作为数组打开 (http://scikit-image.org/_static/img/logo.png), 或者在你电脑上的其他图像。matplotlib
显示图像数组。改变interpolation方法并且放大看一下差异。scipy.stats.scoreatpercentile
(读一下文本字符串!) 来饱和最黑5%的像素和最亮5%的像素。face = misc.face(gray=True)
lx, ly = face.shape
# 剪切
crop_face = face[lx / 4: - lx / 4, ly / 4: - ly / 4]
# up <-> down 翻转
flip_ud_face = np.flipud(face)
# 旋转
rotate_face = ndimage.rotate(face, 45)
rotate_face_noreshape = ndimage.rotate(face, 45, reshape=False)
高斯过滤器 来自 scipy.ndimage
:
from scipy import misc
face = misc.face(gray=True)
blurred_face = ndimage.gaussian_filter(face, sigma=3)
very_blurred = ndimage.gaussian_filter(face, sigma=5)
均匀过滤器
local_mean = ndimage.uniform_filter(face, size=11)
from scipy import misc
face = misc.face(gray=True).astype(float)
blurred_f = ndimage.gaussian_filter(face, 3)
通过添加Laplacian的近似值来增加边缘的权重:
filter_blurred_f = ndimage.gaussian_filter(blurred_f, 1)
alpha = 30
sharpened = blurred_f + alpha * (blurred_f - filter_blurred_f)
有噪音的脸:
from scipy import misc
f = misc.face(gray=True)
f = f[230:290, 220:320]
noisy = f + 0.4 * f.std() * np.random.random(f.shape)
高斯过滤器光滑了噪音... 以及边缘:
gauss_denoised = ndimage.gaussian_filter(noisy, 2)
绝大多数局部线性各向同性过滤器模糊图像 (ndimage.uniform_filter
)
中位数过滤器更好的保留的边缘:
med_denoised = ndimage.median_filter(noisy, 3)
中位数过滤器: 对直边缘的结果更好 (低曲度):
im = np.zeros((20, 20))
im[5:-5, 5:-5] = 1
im = ndimage.distance_transform_bf(im)
im_noise = im + 0.2 * np.random.randn(*im.shape)
im_med = ndimage.median_filter(im_noise, 3)
其他排名过滤器:ndimage.maximum_filter
、ndimage.percentile_filter
其他的局部非线性过滤器: Wiener (scipy.signal.wiener
) 等等.
非局部过滤器
练习: 降噪
也看一下: 在skimage.denoising
中有更多的的降噪过滤器可用,见教程Scikit-image: 图像处理.
看一下wikipedia上的数学形态学定义。
探索一个简单形状 (结构化的元素) 的图像,然后根据这个形状是如何局部适应或不适应这个图像来修改这个图像。
结构化元素:
el = ndimage.generate_binary_structure(2, 1)
el
el.astype(np.int)
风化(Erosion) = 最小值过滤器。用结构化元素所覆盖的最小值来替换像素的值。:
a = np.zeros((7,7), dtype=np.int)
a[1:6, 2:5] = 1
a
ndimage.binary_erosion(a).astype(a.dtype)
#风化删除了比结构小的对象
ndimage.binary_erosion(a, structure=np.ones((5,5))).astype(a.dtype)
扩大(Dilation):最大值过滤器:
a = np.zeros((5, 5))
a[2, 2] = 1
a
ndimage.binary_dilation(a).astype(a.dtype)
对于灰度值图像同样适用:
np.random.seed(2)
im = np.zeros((64, 64))
x, y = (63*np.random.random((2, 8))).astype(np.int)
im[x, y] = np.arange(8)
bigger_points = ndimage.grey_dilation(im, size=(5, 5), structure=np.ones((5, 5)))
square = np.zeros((16, 16))
square[4:-4, 4:-4] = 1
dist = ndimage.distance_transform_bf(square)
dilate_dist = ndimage.grey_dilation(dist, size=(3, 3), structure=np.ones((3, 3)))
Opening: erosion + dilation:
a = np.zeros((5,5), dtype=np.int)
a[1:4, 1:4] = 1; a[4, 4] = 1
a
# Opening 删除小对象
ndimage.binary_opening(a, structure=np.ones((3,3))).astype(np.int)
# Opening 也可以光滑转角
ndimage.binary_opening(a).astype(np.int)
应用: 去除噪音:
square = np.zeros((32, 32))
square[10:-10, 10:-10] = 1
np.random.seed(2)
x, y = (32*np.random.random((2, 20))).astype(np.int)
square[x, y] = 1
open_square = ndimage.binary_opening(square)
eroded_square = ndimage.binary_erosion(square)
reconstruction = ndimage.binary_propagation(eroded_square, mask=square)
n = 10
l = 256
im = np.zeros((l, l))
np.random.seed(1)
points = l*np.random.random((2, n**2))
im[(points[0]).astype(np.int), (points[1]).astype(np.int)] = 1
im = ndimage.gaussian_filter(im, sigma=l/(4.*n))
mask = (im > im.mean()).astype(np.float)
mask += 0.1 * im
img = mask + 0.2*np.random.randn(*mask.shape)
hist, bin_edges = np.histogram