CV入门——在Windows 10上安装detectron2,并介绍了一些基本图像操作
环境配置和detectron2安装
环境依赖
- Python 3.7.3
- Pytorch-1.6.0-cu101
- torchvision-0.7.0
- CUDA 11.0.3
- pycocotools
- Visual Studio 2019-16.7.2
安装 torch与torchvision
可以直接到Pytorch的whl仓库下载torch和torchvison的whl文件,然后执行以下命令进行安装。
1 | pip install filename.whl |
需要注意的是,下载torch和torchvison的whl文件时应有相同的cu并且与Python版本一致。
测试是否安装完成。
1 | python -c "import torch; print(torch.__version__)" |
也可以直接在pytorch官网,选择操作系统、安装工具、语言以及CUDA版本,然后直接执行官网给出的命令。
安装 fvcore和pycocotools
1 | pip install git+https://github.com/facebookresearch/fvcore |
安装 Visual Studio
在微软的官网上下载Visual Studio Installer,安装时添加工作负载->使用C++的桌面开发即可。
将以下路径添加到系统变量中(根据安装位置自行修改)。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\Hostx86\x86
测试cl是否正常。1
2
3
4
5> cl
用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.27.29111 版
版权所有(C) Microsoft Corporation。保留所有权利。
用法: cl [ 选项... ] 文件名... [ /link 链接选项... ]
安装 detectron2
1 | git clone https://github.com/facebookresearch/detectron2.git |
在setup.py中找到以下内容将其注释掉即可。1
# "pycocotools>=2.0.1",
随便下载一张图片,进行实例测试,(模型下载如果没有梯子会很慢)
1 | python demo/demo.py --config-file configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml --input input1.jpg --output ./out.jpg --opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl |
输入的out.jpg如下所示,bingo!
如果代码报错,那么大概率是torch和torchvision的版本问题,应该重新检查是否版本对应。
ninja错误
如果出现以下错误1
subprocess.CalledProcessError: Command '['ninja', '-v']' returned non-zero exit status 1.
在以下路径中做如下修改即可。1
2
3
4# 路径:{Your Path}\Lib\site-packages\torch\utils\cpp_extension.py
# command = ['ninja', '-v'] # 修改前
command = ['ninja', '--v'] # 修改后
cl编码错误
如果出现以下错误1
Error checking compiler version for cl
在以下路径中做如下修改即可。
1 | # 路径:{Your Path}\Lib\site-packages\torch\utils\cpp_extension.py |
cl连接错误
如果出现以下错误,
1 | detectron2 failed with exit status 1181 |
那么应该是cl中link.exe无法正确生成obj文件所致,本机解决方法暂时未知;但可以通过打包别人已安装好的detectron2中的以下路径覆盖到本地解决。
1 | {Your Path}\detectron2\build\temp.win-amd64-3.7\Release\{Your Path}\detectron2\detectron2\layers\csrc |
基础图像操作与处理
本章学习操作和处理图像的基础知识,将通过大量示例介绍处理图像所需的Python工具包,并学习用于读取图像、图像转换和缩放、计算导数、画图和保存结果等的基本工具。本章的内容将贯穿本书的剩余章节。
PIL: Python图像处理类库
PIL(Python Imaging Library)提供了通用的图像处理功能,比如图像缩放、裁剪、旋转、颜色转换等。
读取图像
使用PIL中最重要的Image模块,读取图像并返回PIL图像对象。
1 | from PIL import Image |
创建缩略图
使用PIL图像对象的thumbnail()方法,该方法接收一个元组参数,指定缩略图的大小。
1 | pil_im.thumbnail((128,128)) |
复制和粘贴图像区域
用crop()方法可以从一幅图像中裁剪指定区域,该区域由元组参数指定,格式为(左,上,右,下)。使用paste()方法可以将图像粘贴。
1 | box = (100,100,400,400) |
效果如下所示(害怕):
调整尺寸和旋转
调整尺寸可以直接使用resize()方法,参数同样是元组。
旋转图像可以使用逆时针方式表示旋转角度,调用rotate()方法
1 | out1 = pil_im.resize((128,128)) |
转换图像格式
通过Image的save方法,可以直接将图像保存为其他格式。
1 | from PIL import Image |
Matplotlib
处理数学运算、绘制图表,在图像上绘制点、直线和曲线时,尝试用Matplotlib库。
绘制图像、点和线
在CV中,常用来绘制兴趣点、对应点以及检测出的物体。
1 | from PIL import Image |
该例子的绘制结果如下 所示。show() 命令首先打开图形用户界面(GUI),然后新建一个图像窗口。该图形用户界面会循环阻断脚本,然后暂停,直到最后一个图像窗口关闭。在每个脚本里,只能调用一次show() 命令,而且通常是在脚本的结尾调用。
PyLab库绘图时的基本颜色格式命令如下
命令格式 | 颜色 |
---|---|
‘b’ | 蓝色 |
‘g’ | 绿色 |
‘r’ | 红色 |
‘c’ | 青色 |
‘m’ | 品红 |
‘y’ | 黄色 |
‘k’ | 黑色 |
‘w’ | 白色 |
PyLab库绘图时的基本线型格式命令如下
命令格式 | 颜色 |
---|---|
‘-‘ | 实线 |
‘—‘ | 虚线 |
‘:’ | 点线 |
PyLab库绘图时的基本绘制标记格式命令如下
命令格式 | 颜色 |
---|---|
‘.’ | 点 |
‘o’ | 圆圈 |
‘s’ | 正方形 |
‘*’ | 星形 |
‘+’ | 加号 |
‘x’ | 叉号 |
图像轮廓和直方图
绘制图像的轮廓(或者其他二维函数的等轮廓线)在工作中非常有用。因为绘制轮廓需要对每个坐标[x, y] 的像素值施加同一个阈值,所以首先需要将图像灰度化。使用contour()即可绘制轮廓。
1 | from PIL import Image |
图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。该(灰度)图像的直方图可以使用hist() 函数绘制,其第一个参数必须是一维数组输入,因此需要用flatten()方法按行优先展平,第二个参数指定了小区间的数目。
1 | figure() |
图像轮廓和直方图的效果如下所示(害怕*2):
交互式标注
有时候需要用户交互来标记某些点或者标注训练数据,使用ginput()函数就可以实现交互式标注。
1 | from PIL import Image |
首先绘制一幅图像,然后等待用户在绘图窗口的图像区域点击三次。程序将这些点击的坐标[x, y] 自动保存在列表中。
NumPy
NumPy是著名的Python科学计算工具包。NumPy 中的数组对象几乎贯穿用于本书的所有例子中,数组对象可以帮助实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。
图像数组表示
先前的例子中已经调用过array()方法将图像转换成NumPy的数组对象。NumPy的数组对象是多维的,可以用来表示向量、矩阵和图像。
对于图像数据,运行以下实例
1 | im = array(Image.open('./image/1.jpg')) |
输出为
1 | (763, 720, 3) uint8 |
其中每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。
在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。
数组中的元素可以使用下标访问,如果仅用一个下标,则该下标为行下标。位于坐标i、j,以及颜色通道k 的像素值可以像下面这样访问:
1 | value = im[i,j,k] |
同样也可以数组切片方式访问,例如:
1 | im[i,:] = im[j,:] # 将第j 行的数值赋值给第i 行 |
array()变换的逆操作可以用PIL的fromarray()函数完成:
1 | pil_im = Image.fromarray(im) |
图像灰度变换
将图像读入NumPy数组对象后,就可以对他们执行数学操作,如灰度变换:
1 | im = array(Image.open('./image/1.jpg').convert('L')) |
输出图像结果如下:
图像缩放
NumPy并没有提供对图像进行缩放处理的方法,可以使用PIL实现:
1 | def imresize(im,sz): |
直方图均衡化
直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同,
在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。
1 | def histeq(im,nbr_bins=256): |
该函数有两个输入参数,一个是灰度图像,一个是直方图中使用小区间的数目。函数返回直方图均衡化后的图像,以及用来做像素值映射的累积分布函数。注意,函数中使用到累积分布函数的最后一个元素(下标为-1),目的是将其归一化到0…1范围。
1 | """直方图均衡化""" |
直方图均衡化的输出结果如下。
图像平均
图像平均操作是减少图像噪声的一种简单方式,通常用于艺术特效。可以简单地从图像列表中计算出一幅平均图像。假设所有的图像具有相同的大小,可以将这些图像简单地相加,然后除以图像的数目,来计算平均图像。
1 | def compute_average(imlist): |
该函数可以计算多个图像的平均图像。也可以用mean()函数计算平均图像,但是可能会占用很多内存。
图像主成分分析(PCA)
PCA(Principal Component Analysis,主成分分析)是一个非常有用的降维技巧。它可以在使用尽可能少维数的前提下,尽量多地保持训练数据的信息。由于图像具有很高的维数,在许多计算机视觉应用中,我们经常使用降维操作。PCA 产生的投影矩阵可以被视为将原始坐标变换到现有的坐标系,坐标系中的各个坐标按照重要性递减排列。
将变平的图像堆积起来,我们可以得到一个矩阵,矩阵的一行表示一幅图像。在计算主方向之前,所有的行图像按照平均图像进行了中心化。我们通常使用SVD(SingularValue Decomposition,奇异值分解)方法来计算主成分;但当矩阵的维数很大时,SVD 的计算非常慢,所以此时通常不使用SVD 分解。
1 | from PIL import Image |
PCA的过程无需了解,用下面的脚本可以计算图像的主成分。
1 | from PIL import Image |
结果如下所示(害怕*3):
pickle模块
如果想要保存一些结果或者数据以方便后续使用,Python 中的pickle 模块非常有用。pickle 模块可以接受几乎所有的Python 对象,并且将其转换成字符串表示,该过程叫做封装(pickling)。从字符串表示中重构该对象,称为拆封(unpickling)。这些字符串表示可以方便地存储和传输。
1 | # 保存均值和主成分数据 |
使用with语句处理文件读写操作,可以自动打开和关闭文件(即使在文件打开时发生错误)。
1 | # 打开文件并保存 |
NumPy也提供读写文本文件的简单函数。
1 | savetxt('test.txt',x,'%i') |
SciPy
SciPy 是建立在NumPy 基础上, 用于数值运算的开源工具包。
SciPy 提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及图像处理功能。
图像模糊
图像的高斯模糊是非常经典的图像卷积例子。本质上是将灰度图像I和一个高斯核进行卷积操作:
其中 $G_\sigma$ 是标准差为 $\sigma$ 的二维高斯核,定义为:
高斯模糊通常是其他图像处理操作的一部分,比如图像插值操作、兴趣点计算以及很多其他应用。
SciPy 有用来做滤波操作的scipy.ndimage.filters 模块。该模块使用快速一维分离的方式来计算卷积。你可以像下面这样来使用它:
1 | from PIL import Image |
上面guassian_filter() 函数的最后一个参数表示标准差,其中$\sigma =0,5,10,15$的高斯滤波结果如下所示,$\sigma$越大,处理后的图像细节丢失越多。
如果打算模糊一幅彩色图像,只需简单地对每一个颜色通道进行高斯
模糊:
1 | im = array(Image.open('./image/1.jpg')) |
$\sigma = 5$时,彩色图像的高斯模糊结果如下。
图像导数
图像强度的变化可以用灰度图像$I$(对于彩色图像,通常对每个颜色通道分别计算导数)的x和y 方向导数 $I_x$ 和 $I_y$ 进行描述。
图像的梯度向量为 $\nabla I = [I_x , I_y]^T$。梯度有两个重要的属性,一是梯度的大小:
它描述了图像强度变化的强弱,一是梯度的角度:
描述了图像中在每个点(像素)上强度变化最大的方向。NumPy 中的arctan2() 函数返回弧度表示的有符号角度,角度的变化区间为-π…π。
可以用离散近似的方式来计算图像的导数。图像导数大多数可以通过卷积简单地实现:
对于 $D_x$ 和 $D_y$ ,通常选用Prewitt滤波器:
或Sobel滤波器:
这些导数滤波器可以使用scipy.ndimage.filters 模块的标准卷积操作来简单地实现:
1 | from PIL import Image |
使用Sobel导数滤波器的输出如下所示
这种方法计算图像导数需要滤波器尺度随着图像分辨率变化而变化,为了在图像噪声方面更稳健,以及在任意尺度上计算导数,我可以使用高斯导数滤波器:
其中$G_{\sigma x}$ 和 $G_{\sigma x}$ 表示 $G_\sigma$ 在$x$和$y$方向上的导数, $G_{\sigma x}$ 为标准差为 $\sigma$ 的高斯函数。
之前用于模糊的 filters.gaussian_filter()函数可以接收额外的参数,用于计算高斯导数。
1 | sigma = 5 # 标准差 |
该函数的第三个参数指定对每个方向计算哪种类型的导数,第二个参数为使用的标准差。
对象计数
形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。形态学通常用于处理二值图像,也可以用于灰度图像。二值图像是指图像的每个像素只能取两个值,通常是0和1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。
scipy.ndimage 中的morphology 模块可以实现形态学操作。你可以使用scipy.ndimage 中的measurements 模块来实现二值图像的计数和度量功能。计算该图像中的对象个数可以通过下面的脚本实现:
1 | from scipy.ndimage import measurements,morphology |
上面的脚本首先载入图像,通过阈值化确保该图像是二值图像。使用label() 函数寻找单个的物体,并且按照它们属于哪个对象将整数标签给像素赋值。下图是labels 数组的图像。图像的灰度值表示对象的标签。可以看到,在一些对象之间有一些小的连接。进行二进制开(binary open)操作,我们可以将其移除:
1 | # 形态学开操作更好地分离各个对象 |
经过开操作后的图像如下图所示。
binary_opening() 函数的第二个参数指定一个数组结构元素。该数组表示以一个像素为中心时使用的相邻像素。在这种情况下,我们在y 方向上使用9 个像素(上面4 个像素、像素本身、下面4 个像素),在x 方向上使用5 个像素。可以指定任意数组为结构元素,数组中的非零元素决定使用哪些相邻像素。参数iterations 决定执行该操作的次数。binary_closing() 函数实现相反的操作。
一些有用的SciPy模块
SciPy中的io和misc模块可以用于输入和输出。
读写.mat文件
数据以Matlab的.mat文件格式存储时,可以用scipy.io模块进行读取
1 | data = scipy.io.loadmat('test.mat') |
上面代码中data 对象包含一个字典,字典中的键对应于保存在原始.mat 文件中的变量名。由于这些变量是数组格式的,因此可以很方便地保存到.mat 文件中。仅需创建一个字典(其中要包含你想要保存的所有变量),然后使用savemat() 函数:
1 | data = {} |
以图像形式保存数组
将数组直接保存为图像文件非常有用。imsave() 函数可以从scipy.misc 模块中载入。要将数组im 保存到文件中,可以使用下面的命令:
1 | from scipy.misc import imsave |
scipy.misc 模块同样包含了著名的Lena 测试图像:
1 | lena = scipy.misc.lena() |
该脚本返回一个512×512 的灰度图像数组。