介绍

我们非常熟悉结构化(表格)数据的预处理步骤。你可以找到缺失的值然后添补它,然后检测并处理异常值,等等这些步骤。这有助于我们建立更好、更健壮的机器学习模型。但是当我们处理图像数据时,应该如何进行预处理?

事实证明,在计算机视觉领域(图像、视频等等),预处理是一个至关重要的步骤。skimage是scikit-learn家族的一部分,它是一个非常有用的库,可以帮助我们开始学习。

在本文中,我们会介绍Python中使用skimage对图像进行一些简单但功能强大的预处理技术。

目录

  1. 什么是skimage?为什么要使用它?
  2. 使用skimage在Python中读取图像
  3. 调整图像大小
  4. 上下翻转图像
  5. 旋转不同角度
  6. 水平和垂直翻转
  7. 图像裁剪
  8. 改变图像亮度
  9. 使用滤镜

什么是skimage?为什么要使用它?

Python中有多个库和框架可让我们处理图像数据。那么,为什么要使用skimage?在深入研究本文之前,让我在这里回答。

Scikit-image或skimage是一个用于图像预处理的开源Python包。

如果你以前使用过sklearn,那么开始使用skimage将是小菜一碟。即使你完全不熟悉Python,skimage还是非常易于学习和使用的。

我真正喜欢skimage的地方在于它有一个结构良好的文档,列出了skimage中提供的所有模块,子模块和函数。以下链接是skimage包中所有子模块和函数的列表(https://scikit-image.org/docs/stable/api/api.html)

1.使用skimage读取图像

让我们从基础开始。第一步是学习如何使用skimage在Python中导入图像。

图像由称为像素的多个小方块组成。我下面显示的图像就是一个很好的例子。你在此处看到的小方块就是像素:

我们可以看到该图像沿垂直线有22个像素,沿水平线有16个像素。因此,此图像的大小将为22 x 16。

当我们使用scikit-image(或其他任何包)读取或加载图像时,我们看到该图像是以数字形式存储。这些数字称为像素值,它们代表图像中每个像素的强度。

使用skimage加载图像

在scikit-image包中,数据模块中提供了几个示例图像。假设我们想加载一个图像来执行一些实验。我们不需要使用外部图像,只需加载包中提供的图像之一即可。

这是执行此操作的Python代码:

from skimage.io import imread, imshow
from skimage import data image = data.astronaut()
imshow(image)

注意,我在这里使用了imshow函数来查看图像。

如果你不想使用包提供的图像而是想对你的系统里的图像进行加载的话,我们可以使用skimage中的imread函数。

我们可以读取两种格式的图像,彩色图像和灰度图像。我们将看到这两种方法的实际应用,并理解它们是如何不同的。

使用skimage从系统读取图像

imread函数有一个参数"as_gray",用于指定是否必须将图像转换为灰度图像。我们将从读取灰度格式的图像开始,所以将参数设置为true:

from skimage.io import imread, imshow
import matplotlib.pyplot as plt
%matplotlib inline image_gray = imread('images.jpeg', as_gray=True)
imshow(image_gray)

我们可以很容易地使用imshow函数查看图像。但这真的是图像的存储方式吗?让我们检查一下变量image_gray中有什么:

image_gray = imread('images.jpeg', as_gray=True)
print(image_gray.shape)
print(image_gray)
(258,195)
[[0.73586314 0.77115725 0.7907651 ... 0.11822745 0.11822745 0.11430588]
[0.65743176 0.70056902 0.72017686 ... 0.11822745 0.11430588 0.11430588]
[0.41401176 0.45714902 0.48067843 ... 0.11430588 0.11430588 0.11038431]
...
[0.73491725 0.73491725 0.73491725 ... 0.42055725 0.42055725 0.42055725]
[0.72594314 0.72986471 0.72986471 ... 0.41750667 0.41750667 0.41750667]
[0.72594314 0.72986471 0.72986471 ... 0.41750667 0.41750667 0.41750667]

变量以数字矩阵的形式来存储图像。如你所见,矩阵的形状为259 x 195。矩阵里的这些数字称为像素值,它们表示图像中像素的强度。

现在,我们将以原始颜色的格式加载图像。为此,我们必须将参数'as_gray'设置为False:

from skimage.io import imread, imshow
import matplotlib.pyplot as plt
%matplotlib inline image_color = imread('images.jpeg', as_gray=False)
print(image_color.shape)
imshow(image_color)
(258,195,3)

好了!我们这里有同样的图片,颜色不同。现在你可能想知道这两种格式之间的区别以及应该使用哪种格式?让我们一个一个来解决。

你注意到这个例子中图像的形状了吗?它是(258,195,3),而之前的形状是(258,195)。这里的三个维度表示图像中通道的数量。对于彩色图像,存储图像最流行的格式是RGB(红绿蓝)。

但是我们应该使用哪种格式呢?与灰度图像相比,彩色图像具有更多的信息,但是彩色图像的大小更大。RGB中的像素数是灰度图像的3倍多。当我们没有足够的计算资源时,处理彩色图像是一个巨大的挑战。

因此,灰度图像经常被用来减少计算复杂度。因此,如果数据集的大小很大,则可以选择灰度图像而不是彩色图像。

2.更改图像格式

在上一节中,我们讨论了可以加载图像的两种重要格式,RGB和灰度格式。在本节中,我们将学习如何将图像从一种格式转换为另一种格式。首先,我们将读取RGB格式的图像并将其转换为灰度格式。我们将在此处使用的函数是rgb2gray

from skimage.color import rgb2gray
img = imread('images.jpeg')
img_new = rgb2gray(img) plt.subplot(121), imshow(img)
plt.title('RGB Format') plt.subplot(122), imshow(img_new)
plt.title('Grayscale Format') plt.show()

其他两种流行的格式是HSV(色调,饱和度,明度)和HSL(色调,饱和度,亮度),它们是RGB格式的替代表示。让我简要解释这些术语的含义。

  • 色调(Hue)是色轮上的度数,其中0代表红色,120代表绿色,240代表蓝色,360再次代表红色。
  • 饱和度(Saturation)表示该颜色的百分比,其中0是白色,而100是全色。
  • 明度(Value)表示不同数量的黑色或白色混合。
  • 亮度(Lightness)是显示图像阴影的另一种方式,其中0为黑色,而1为白色。

下面显示的图片将使你的理解更清晰

将图像更改为这些格式中的任何一种格式都与转换为灰度的方法相同。我们可以使用函数rgb2hsl和rgb2hsv分别转换成HSL和HSV格式。这里我演示了如何将图像转换为HSV格式。

from skimage.color import rgb2hsv
img = imread('images.jpeg')
img_new = rgb2hsv(img) plt.subplot(121), imshow(img)
plt.title('RGB Format') plt.subplot(122), imshow(img_new)
plt.title('HSV Format') plt.show()

3.使用skimage调整图像大小

计算机视觉的最大挑战之一是,我们需要大量的数据来训练我们的模型。我们收集的数据通常有不同的来源,这可能会导致图像大小有不同的差异。从图像中提取特征或将其用于数据增强时可能就会出现问题。

理想情况下,当我们构建模型时,图像的大小应该是相同的。如果我们使用的是预训练模型,那么重要的是将输入数据调整大小并将其规范化为与最初训练网络时相同的格式。这就是为什么调整图像大小是一个重要的图像预处理步骤。

在这里,我们将使用skimage的resize功能。此函数的输入将是我们要更新的图像以及新图像所需的大小:

from skimage.transform import resize
img = imread('images.jpeg')
#缩放图片
img_resized = resize(img, (300, 300)) #显示图片
plt.subplot(121), imshow(img)
plt.title('Original Image')
plt.subplot(122), imshow(img_resized)
plt.title('Resized Image')
plt.show()

4.使用skimage重新缩放(放大/缩小)图片

重新缩放图像是另一种常见的计算机视觉技术。这意味着按特定比例缩放图像。例如,将每个图像的大小减小一半(缩小),或者将图像的大小增大2倍(放大)。

你可能会疑问说,我们可以简单地将resize函数用于此任务,有什么区别?

如果所有图像的原始尺寸都相同,例如(300,300),我们可以直接使用resize函数并指定所需的尺寸(150,150)。但是,如果图像的大小不同(如下图所示),则无法使用resize函数。这是因为每个图像的"一半"会有所不同。

你将在计算机视觉之旅中遇到很多类似这种情况的例子。

因此,在这里,我们可以使用rescale函数并指定缩放比例。该函数基于图像的原始尺寸,所有图像将以此比例缩放。

from skimage.transform import rescale
img = imread('images.jpeg')
img_rescaled = rescale(img, scale=(0.5, 0.5)) plt.subplot(121), imshow(img)
plt.title('Original Image') plt.subplot(122), imshow(img_rescaled)
plt.title('Rescaled Image') plt.show()

5.使用skimage以不同角度旋转图像

到目前为止,我们已经研究过调整图像的大小和缩放比例。让我们把重点转向看看如何改变图像的方向。但是在深入探讨之前,我们应该讨论为什么首先需要更改图像方向。

考虑以下图像。第一张图像略微倾斜(可能是由于相机方向所致)。

要解决此方向问题,我们需要将图像旋转一定角度。我们可以使用skimage的rotate函数,并指定旋转图像的角度:

from skimage.transform import rotate
image = imread('tilt_image.png') image_rotated = rotate(image, angle=45)
imshow(image_rotated)

看起来很棒!方向问题已解决。但是如果你仔细看,你会发现照片的四角被剪短了。这是因为,在旋转过程中,图像的大小保持不变,导致角附近的区域被裁剪。

在这种情况下,我们不会丢失任何重要信息,但情况可能并非总是如此。这个障碍由rotate函数中的resize参数来解决(默认情况下参数值为False):

from skimage.transform import rotate
image = imread('tilt_image.png') image_rotated = rotate(image, angle=45, resize=True)
imshow(image_rotated)

我们还可以将旋转概念用于数据增强。数据增强是一种使用可用数据生成更多样本以训练模型的技术。

假设你正在建立图像分类模型,以识别猫和狗的图像。看一下下面显示的示例图像。左侧的两个图像都将被归类为"狗",而右侧的两个图像将被归类为"猫":

我们在这里改变了什么?我们只是将图像旋转了180度并生成了新图像。也就是你只需在现有数据中的每张图像上添加一张新图像,即可将训练数据的大小增加一倍!

6.水平和垂直翻转图像

我们可以水平和垂直翻转图像。这样会沿水平/垂直轴创建镜像。我们可以将这种技术用于图像预处理和图像增强。

尽管在skimage中没有直接的功能,但是我们可以使用NumPy执行此任务。

NumPy提供flipudfliplr函数分别用于在水平和垂直轴上翻转图像。

函数的内部工作非常简单。对于水平翻转,行保持不变,而列的进行翻转。让我们用同样的猫狗的例子,并使用flip函数:

from numpy import fliplr, flipud
dog = imread('Puppy.jpg')
cat = imread('whiskers.jpg') dog_flip = fliplr(dog)
cat_flip = fliplr(cat) plt.subplot(141), imshow(dog)
plt.subplot(142), imshow(dog_flip)
plt.subplot(143), imshow(cat)
plt.subplot(144), imshow(cat_flip)
plt.show()

看起来不错!

7.裁剪图像

你之前肯定在手机上使用非常多次裁剪功能。

你也可以使用skimage在Python中裁剪图像。我们裁剪图像以去除图像中不需要的部分或聚焦于图像的特定部分。

假设我们有下面这张篮球比赛的图片(左图)。目前,图像的形状是1067 x 1600。现在,我想从图像的四个边都移去100个像素。这意味着我们从图像的上、下、左、右移去100个像素,从而聚焦在中心的物体上:

有两种方法可以实现此目的:

  • 首先,简单地指定你想要的新图像形状。在本例中,它是image[100:967, 100:1500]。这对于单个图像来说很好。如果我们有多个图像呢?我们不得不为每个图像都提到新的图像形状(不是很友好)。
  • 另一种方法是使用当前图像形状计算裁剪后的图像尺寸,可以使用image.shape命令来确定。所以新图像的高度是[100:img.shape[0]-100] ,宽度为 [100:img.shape[1]-100].

因此,让我们使用第二种方法裁剪上面的图像:

image = imread('warriors.jpg')

# 只选择图像的一部分
cropped = image[100:(img.shape[0]-100),100:(img.shape[1]-100)] plt.subplot(121), imshow(image)
plt.title('Original Image')
plt.subplot(122),imshow(cropped)
plt.title('Cropped Image') plt.show()

8.使用skimage更改图像亮度

尽管相机的功能最近有所进步,但低光成像仍然是一个令人头痛的问题。skimage会帮我们解决这个问题。

可以使用具有不同亮度的图像使我们的计算机视觉模型对光照条件的变化具有鲁棒性。

这对于在室外照明下工作的系统(例如,交通信号灯的闭路电视摄像机)非常重要。

可以使用skimage中的adjust_gamma函数更改图像的亮度,该函数使用一种称为gamma相关的方法。对于任何给定的图像,像素值首先在0-1之间归一化,然后乘以指定的伽玛值。得到的像素值被缩放到0-255范围。

对于大于1的伽玛,输出图像将比输入图像暗。当伽马小于1时,输出图像将比输入图像亮。

from skimage import exposure

#调节亮度
image = imread('images.jpeg')
image_bright = exposure.adjust_gamma(image, gamma=0.5,gain=1)
image_dark = exposure.adjust_gamma(image, gamma=1.5,gain=1) # 显示图像
plt.subplot(131), imshow(image)
plt.title('Original Image') plt.subplot(132),imshow(image_bright)
plt.title('Bright Image') plt.subplot(133),imshow(image_dark)
plt.title('Dark Image') plt.show()

9.在skimage中使用滤镜

我们可以使用滤镜(Filters)来修改或增强图像的特征。如果你曾经在社交媒体平台上玩过图像,就会对滤镜非常熟悉。

我们可以将滤镜用于各种目的,例如使图像平滑和锐化,去除噪声,突出显示图像中的特征和边缘等。

当我们在图像上应用滤镜时,每个像素值都会替换为使用周围像素值生成的新值。最简单的滤镜是中值滤镜,其中像素值被替换为相邻像素的中值。

from skimage.filters import median

image = imread('images.jpeg', as_gray=True)
image_median = median(image) # 显示图像
plt.subplot(121), imshow(image)
plt.title('Original Image') plt.subplot(122),imshow(image_median)
plt.title('Smooth Image') plt.show()

当我们想要突出显示图像的边缘时,我们可以使用另一个流行的滤镜,sobel滤镜。

from skimage.filters import sobel_h

image = imread('images.jpeg', as_gray=True)
image_sobelh = sobel_h(image) # 显示图像
plt.subplot(121), imshow(image)
plt.title('Original Image') plt.subplot(122),imshow(image_sobelh, cmap = True)
plt.title('Horizontal Edge') plt.show()

结尾

祝贺你在计算机视觉领域迈出了第一步!乍一看,这似乎是一个令人望而生畏的领域,但如果你有一个结构化的思维模式,并且对机器学习算法的工作原理有很好的理解,你很快就会发现处理图像和视频数据的细微差别。

我们还可以使用skimage做其他事情,比如从图像中提取边缘,或者向图像中添加噪声等等。我想让你以这个作为开始,并在Python中试用它们。这才是学习的方式!

欢迎关注磐创博客资源汇总站:http://docs.panchuang.net/

欢迎关注PyTorch官方中文教程站:http://pytorch.panchuang.net/

使用skimage处理图像数据的9个技巧的更多相关文章

  1. OpenCV中IplImage图像格式与BYTE图像数据的转换

    最近在将Karlsruhe Institute of Technology的Andreas Geiger发表在ACCV2010上的Efficent Large-Scale Stereo Matchin ...

  2. (转)原始图像数据和PDF中的图像数据

    比较原始图像数据和PDF中的图像数据,结果见表1.1.表1.1中各种“解码器”的解释见本文后续的“PDF支持的图像格式”部分,“PDF中的图像数据”各栏中的数据来自开源的PdfView.如果您有兴趣查 ...

  3. 图像处理工具包ImagXpress教程:Accusoft不同组件间的图像数据传递

    图像处理工具包ImagXpress的开发厂商Accusoft Pegasus旗下有多种图像处理相关的控件,但是这些图像处理控件之间的如何加传递图像数据呢?在ImagXpress 11版本之前,是需要将 ...

  4. RGB图像数据字符叠加,图像压缩(ijl库),YUV转RGB

    jackyhwei 发布于 2010-01-01 12:02 点击:3218次  来自:CSDN.NET 一些非常有用的图像格式转换及使用的源代码,包括RGB图像数据字符叠加,图像压缩(ijl库),Y ...

  5. vc/mfc获取rgb图像数据后动态显示及保存图片的方法

    vc/mfc获取rgb图像数据后动态显示及保存图片的方法 该情况可用于视频通信中获取的位图数据回放显示或显示摄像头捕获的本地图像 第一种方法 #include<vfw.h> 加载 vfw3 ...

  6. MATLAB获取“非免驱的相机或者摄像头”的图像数据

    Image Acquisition Toolbox™ Adaptor Kit 图像採集工具箱 当要使用MATLAB获取"非免驱的相机或者摄像头"的图像数据时,须要开发一个插件,MA ...

  7. 基于FPGA的Uart接收图像数据至VGA显示

    系统框图 前面我们设计了基于FPGA的静态图片显示,接下来我们来做做基于FPGA的动态图片显示,本实验内容为:由PC端上位机软件通过串口发送一幅图像数据至FPGA,FPGA内部将图像数据存储,最后扫描 ...

  8. Android图像数据传递到C++的一些坑

    最近在做一个Android图象识别的app, 通过相机预览或者是拍照功能获取图像数据,然后将图像数据传递到本地C++的图像识别so库.在这个过程中花的时间最多的就是数据传输问题.谨以此坑,警示未来!  ...

  9. 从二进制数据流中构造GDAL可以读取的图像数据

    在很多时候,我们的图像数据往往都不是文件方式存储在磁盘上,而是可能从网络或者数据库中获取的是二进制的图像数据流.最简单的方式和最容易想到的方式就是将这个文件流保存到磁盘上形成一个文件,然后再使用GDA ...

随机推荐

  1. python爬虫之selenium+打码平台识别验证码

    1.常用的打码平台:超级鹰.打码兔等 2.打码平台在识别图形验证码和点触验证码上比较好用 (1)12306点触验证码 from selenium import webdriver from selen ...

  2. 一起了解 .Net Foundation 项目 No.13

    .Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. MVVM Light To ...

  3. JZOJ 1301. treecut

    1301. treecut (Standard IO) Time Limits: 1000 ms Memory Limits: 131072 KB Description 有一个N个节点的无根树,各节 ...

  4. java异常和throw和throws的区别

    之前在编程中编译完成后,运行时,会遇见一些常见的错误,如NullPointerException,ArrayIndexOutOfBoundsException等等 在今天重新回顾学习了java异常,总 ...

  5. 从String到==和hashcode

    public static void main(String[] args) { String s1 = "ni"; String s2 = "hao"; St ...

  6. java线程间的协作

    本次内容主要讲等待/通知机制以及用等待/通知机制手写一个数据库连接池. 1.为什么线程之间需要协作 线程之间相互配合,完成某项工作,比如:一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行 ...

  7. ElementUI el-table 在flex下的宽度自适应问题

    BUG:在flex容器下面的一个flex:1的子容器里面写了个el-table用来展示列表数据,在做宽度自适应测试的时候发现该组件的宽度只会增加不会缩小. Debug:通过控制台发现组件生成的tabl ...

  8. scrapy 执行同个项目多个爬虫

    一开始我们默认都是只有一个爬虫的,所以执行的代码都是在项目下创建一个py文件 from scrapy import cmdline cmdline.execute('scrapy crawl 爬虫名' ...

  9. SpringBoot+AOP构建多数据源的切换实践

    针对微服务架构中常用的设计模块,通常我们都会需要使用到druid作为我们的数据连接池,当架构发生扩展的时候 ,通常面对的数据存储服务器也会渐渐增加,从原本的单库架构逐渐扩展为复杂的多库架构. 当在业务 ...

  10. MATLAB神经网络(2)之R练习

    1. AMORE 1.1 newff newff(n.neurons, learning.rate.global, momentum.global, error.criterium, Stao, hi ...