Python skimage快速图像处理(二)——Numpy速成

Python skimage快速图像处理(二)——Numpy速成

  • 简介
  • Numpy索引
  • 彩色图像
  • 坐标约定
  • 数组维数的顺序
  • 时间维度

简介

图像在scikit-images中用Numpy矩阵表示。因此,可以使用标准的NumPy方法来实现许多常见的操作:

from skimage import data
camera = data.camera()
type(camera)

查看图像的形状和像素数量:

camera.shape

(512, 512)

camera.size

262144

查看图像像素值的统计信息:

camera.min(), camera.max()

(0, 255)

118.31400299072266

表示图像的Numpy数组可以是不同的整数或浮点数类型。查看图像数据类型及其含义获得详细信息和scikit-images如何处理不同数据类型。

Numpy索引

Numpy索引可用于查看像素值或修改它们:

camera[10, 20]  # 获取第10行第20列的像素值

153

camera[3, 10] = 0  #将第3行第20列的像素值设为0

注意,在Numpy索引中,第一个维度(camera.shape[0])对应行,第二个维度(camera .shape[1])对应列,原点(camera[0,0])位于左上角。它匹配矩阵/线性代数表示法,但与笛卡尔坐标(x, y)相反。详细信息,参阅坐标约定。

除了单个像素外,还可以使用Numpy的切片功能访问/修改整个像素集的值:

camera[:10] = 0  #将头10行设为黑色0

掩码(布尔值掩码索引,可理解为PS中的蒙版):

mask = camera < 87
camera[mask] = 255  # 当掩码为真时设置该像素为白色255

花式索引(索引集索引):

import numpy as np
inds_r = np.arange(len(camera))  # 0到511的矩阵
inds_c = 4 * inds_r % len(camera)  # 0到511,间距为4的矩阵
camera[inds_r, inds_c] = 0

当您需要选择一组像素执行操作时,掩码非常有用。掩码可以是与图像形状相同的布尔数组,或可向图像形状传递的形状。
例如,定义一个感兴趣的区域,一个圆:

nrows, ncols = camera.shape
row, col = np.ogrid[:nrows, :ncols]  #快速产生二维行向量和列向量
cnt_row, cnt_col = nrows / 2, ncols / 2  #取半径
outer_disk_mask = ((row - cnt_row)**2 + (col - cnt_col)**2 > (nrows / 2)**2)  # 蒙版为当前坐标位置大于半径范围
camera[outer_disk_mask] = 0  # 当掩码为真时设置该像素为黑色255

查看图像

from skimage import io
%matplotlib inline
io.imshow(camera)

Python skimage快速图像处理(二)——Numpy速成_第1张图片
Numpy的布尔运算可以定义更复杂的掩码:

lower_half = row > cnt_row
lower_half_disk = np.logical_and(lower_half, outer_disk_mask)
camera = data.camera()
camera[lower_half_disk] = 0
io.imshow(camera)

Python skimage快速图像处理(二)——Numpy速成_第2张图片

彩色图像

上述所有内容对彩色图像同样适用。彩色图像是再多一个维度的Numpy数组:

cat = data.chelsea()
io.imshow(cat)

Python skimage快速图像处理(二)——Numpy速成_第3张图片

type(cat)

numpy.ndarray

cat.shape

(300, 451, 3)
这表明变量cat是一个300×451像素的图像,有三个通道(红、绿、蓝)。如前所述,我们可以获取并设置像素值:

cat[10, 20]

array([151, 129, 115], dtype=uint8)

cat[50, 60] = 0  #将第50行,第60列的像素设为黑色
cat[50, 61] = [0, 255, 0]  # 将第50行,第60列的像素设为绿色[red, green, blue]

同样地,可以对2D多通道图像使用2D布尔蒙版:

cat = data.chelsea()
reddish = cat[:, :, 0] > 160  # 红色的值大于160(0red 1green 2blue)
cat[reddish] = [0, 255, 0]  #设为绿色
io.imshow(cat)

Python skimage快速图像处理(二)——Numpy速成_第4张图片

坐标约定

因为scikit-image使用Numpy数组表示图像,所以坐标约定必须匹配。二维灰度图(如上图camera)通过行、列,缩写为(row, col)(r, c)进行索引,左上角像素的索引为(0,0)
在库的各个部分,你还将看到用rrcc引用行和列坐标。我们将这个约定与(x, y)区别开来,(x, y)通常表示标准的笛卡尔坐标,其中x是横坐标,y是纵坐标,原点在左下角。例如,Matplotlib轴使用这种坐标。
对于多通道图像,最后一个维用于彩色通道,用channelch表示。
最后,对于立体3D图像,如视频、磁共振成像(MRI)扫描、共聚焦显微镜等,我们将第一个维度称为plane,缩写为plnp
约定总结如下:

图片类型 约定
二维灰度图 (row, col)
二维多通道(如RGB) (row, col, ch)
三维灰度 (pln, row, col)
三维多通道 (pln, row, col, ch)

scikit-image中的许多函数可以直接对3D图像进行操作(以下代码执行时间略久):

im3d = np.random.rand(100, 1000, 1000) # 生成随机矩阵
from skimage import morphology
from scipy import ndimage as ndi
seeds = ndi.label(im3d < 0.1)[0] # 标记小于0.1的值
ws = morphology.watershed(im3d, seeds) # 分水岭算法

然而,多数情况下,第三个空间维度的分辨率低于其他两个维度。一些scikit-image函数提供了一个间距关键字spacing参数来帮助处理这类数据:

from skimage import segmentation
slics = segmentation.slic(im3d, spacing=[5, 1, 1], multichannel=False)  #k-means聚类分割图像

其他情况,处理必须在plane维度上完成。当第一个维度为plane(与约定一致),可以使用以下语法:

from skimage import filters
edges = np.empty_like(im3d)
for pln, image in enumerate(im3d):
    # 迭代第一个维度
    edges[pln] = filters.sobel(image)  #Sobel变换

数组维数的顺序

尽管坐标轴的标记可能看起来很随意,但是它对操作的速度有很大的影响。这是因为现代处理器不会从内存中仅检索一项,而是检索整个相邻块(称为预取的操作)。因此,处理内存中相邻的元素比处理分散的元素要快,即使操作的数量相同:

def in_order_multiply(arr, scalar):
    for plane in list(range(arr.shape[0])):
        arr[plane, :, :] *= scalar

def out_of_order_multiply(arr, scalar):
    for plane in list(range(arr.shape[2])):
        arr[:, :, plane] *= scalar
import time
im3d = np.random.rand(100, 1024, 1024)
t0 = time.time(); x = in_order_multiply(im3d, 5); t1 = time.time()
print("%.2f seconds" % (t1 - t0))

0.08 seconds

s0 = time.time(); x = out_of_order_multiply(im3d, 5); s1 = time.time()
print("%.2f seconds" % (s1 - s0))

1.65 seconds

print("Speedup: %.1fx" % ((s1 - s0) / (t1 - t0)))

Speedup: 21.3x
当最后/最右边的维度变得更大时,加速效果更显著。在开发算法时考虑数据局部性是值得的。特别地,scikit-image默认使用C-连续数组。当使用嵌套循环时,数组的最后/最右边的维度应该位于计算的最内层循环中。在上面的例子中,*= numpy操作符遍历所有剩余的维度。

时间维度

虽然scikit-image目前还没有提供专门处理时变3D数据的函数,但它与Numpy数组的兼容性使我们能够很自然地处理形如(t, pln, row, col, ch)的5D数组:

# 伪代码
for timepoint in image5d:
    # Each timepoint is a 3D multichannel image
    do_something_with(timepoint)

对上表进行补充如下:

数据类型类型 约定
二维灰度图 (row, col)
二维多通道(如RGB) (row, col, ch)
三维灰度 (pln, row, col)
三维多通道 (pln, row, col, ch)
二维彩色视频 (t, row, col, ch)
三维多通道视频 (t, pln, row, col, ch)

你可能感兴趣的:(Python,skimage)