学习如何使得图像符合预训练模型的需求,或者用其他数据集的图像来测试自己的模型。

- 调整大小

- 缩放

- HWC和CHW,数据通道交换

- RGB和BGR,颜色通道的交换

- Caffe2的图像预处理

Ipython Notebook的教程在这里获取

在这一节中,我们将会展示如何从本地文件或网络链接载入一个图像,并能用于其他的教程和例子。当然,我们将继续深入多种预处理,这些预处理都是使用Caffe2时非常有必要的的。

Mac OSx Prerequisites

首先,确保你有Python的这些模块。

sudo pip install scikit-image scipy matplotlib

然后,我们开始载入这些模块

%matplotlib inline
import skimage
import skimage.io as io
import skimage.transform
import sys
import numpy as np
import math
from matplotlib import pyplot
import matplotlib.image as mpimg
print("Required modules imported.")

Test an Image

在下面的代码块中,用 IMAGE_LOCATION去载入你想要测试的图像。改变其内容,并重新看看整个教程,你会看到对于不同的图片格式会有不同的处理。如果你想尝试自己的图像,把它改为你的图像路径或者远程URL。当你使用远程URL时,必须确保这个URL指向一个普通的图像文件类型和后缀,一些长的表示符或者字符串可能会导致程序中断。


Color Issues

记住,如果你载入的图像来自智能手机,那么你可能会遇到图像颜色格式问题。在下面我们将会展示在RGB和BGR对一张图像的影响。确保图像数据和你想象中的一致。

Caffe Uses BGR Order

Caffe使用了OpenCV,而OpenCV处理图像是Blue-Green-Red (BGR) 形式的。而不是通用的RGB形式,所以在Caffe2中,图像的格式也是BGR。从长远来看,这种做法在很多方面是有益的,当你使用不同的计算机和库。但是这也是困惑的起源。

# 你可以载入本地图片或者远程连接

# 第一种方案,使用本地图像
#IMAGE_LOCATION = 'images/cat.jpg'
# 第二种线路使用网络图像,图像是一朵花
IMAGE_LOCATION = "https://cdn.pixabay.com/photo/2015/02/10/21/28/flower-631765_1280.jpg"
#第三种线路使用网络图像,网络图像有很多人
#IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/1/18/NASA_Astronaut_Group_15.jpg"
# 第四种使用一个网络图像,是一个竖图
#IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/9/9a/Ducreux1.jpg" img = skimage.img_as_float(skimage.io.imread(IMAGE_LOCATION)).astype(np.float32) # 显示原始图像
pyplot.figure()
pyplot.subplot(1,2,1)
pyplot.imshow(img)
pyplot.axis('on')
pyplot.title('Original image = RGB') #交换颜色通道并显示交换后的的BGR图像
imgBGR = img[:, :, (2, 1, 0)]
pyplot.subplot(1,2,2)
pyplot.imshow(imgBGR)
pyplot.axis('on')
pyplot.title('OpenCV, Caffe2 = BGR')



由上面的例子中,你可以看到,不同的顺序是相当重要的。接下来的代码块中,我们将会图像转换为BGR顺序,这样Caffe2才能正确处理它。

不,稍等。关于颜色还有些有趣的东西。

Caffe Prefers CHW Order

什么是CHW?还有HWC。这两个都是来源于图像处理。

- H:Height

- W:Width

- C:Channel

深入了解图像在内存分配中的顺序。你可能注意到,当我们第一次载入图像时,我们进行了一些有趣的转换。这些数据转换就像把一幅图像当做一个魔方来玩。我们看到的是魔方的顶层,操作下面的层,可以改变看到的东西。

在GPU下,Caffe2需要的图像数据是CHW,在CPU下,一般需要的顺序是HWC。基本上,你需要CHW的顺序,并确保转换为CHW这步包含在你的图像预处理。把RGB转换为BGR,然后把HWC转换为CHW。这里的C就转换后的BGR。你可能会问,为什么呢?原因在于,在GPU上使用cuDNN库能获得非常大的加速,而cuDNN只使用CHW。总的来说,这样做能更快。

有了上面两步,你可能会觉得够了吧?不,你还是太年轻了。我们还需要resize(调整大小),crop(剪切),可能还需要些旋转和镜像。


Rotation and Mirroring

来自智能手机的相片普遍存在着旋转或者镜像,有时,我们可以通过照片中的EXIF信息进行修正。但是并不是都这么幸运。

Library for Handling Mobile Images

下面展示的是旋转图像和镜像图像

# 对于这样的图像如何知道它是竖屏模式?
ROTATED_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/8/87/Cell_Phone_Tower_in_Ladakh_India_with_Buddhist_Prayer_Flags.jpg"
imgRotated = skimage.img_as_float(skimage.io.imread(ROTATED_IMAGE)).astype(np.float32)
pyplot.figure()
pyplot.imshow(imgRotated)
pyplot.axis('on')
pyplot.title('Rotated image') #这种图像是给司机用后视镜看的
MIRROR_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/2/27/Mirror_image_sign_to_be_read_by_drivers_who_are_backing_up_-b.JPG"
imgMirror = skimage.img_as_float(skimage.io.imread(MIRROR_IMAGE)).astype(np.float32)
pyplot.figure()
pyplot.imshow(imgMirror)
pyplot.axis('on')
pyplot.title('Mirror image')





然我们做一些变换。同时,这些技巧可能能够帮到你,例如,你无法获取图像的EXIF信息,那么你可以对图像进行旋转,翻转,从而产生很多副本,对于这些图像,用你的模型全部跑一遍。当检测的置信度足够高时,找到了你需要的方向。

代码如下:

#下面代码实现图像的左右翻转
imgMirror = np.fliplr(imgMirror)
pyplot.figure()
pyplot.imshow(imgMirror)
pyplot.axis('off')
pyplot.title('Mirror image')

#逆时针旋转90度
imgRotated = np.rot90(imgRotated)
pyplot.figure()
pyplot.imshow(imgRotated)
pyplot.axis('off')
pyplot.title('Rotated image')


Sizing

下面的例子先将图像resize到256x256大小,然后从中剪切出224x224大小,因为网络的输入大小是224x224。

#模型的输入是224x224大小,因此需要resize或者crop
# (1) Resize 图像 256*256, 然后剪切中心部分
input_height, input_width = 224, 224
print("Model's input shape is %dx%d") % (input_height, input_width)
img256 = skimage.transform.resize(img, (256, 256))
pyplot.figure()
pyplot.imshow(img256)
pyplot.axis('on')
pyplot.title('Resized image to 256x256')
print("New image shape:" + str(img256.shape))

输出

Model's input shape is 224x224
New image shape:(256, 256, 3)



注意resize有可能在一定程度上扭曲图像。你在测试时必须考虑这个问题,因为这会影响到你的模型输出的结果。花和动物被拉长或者压缩一点可能不会太大问题。但是面部特征就不一定行了。现在尝试另一种缩放图像的策略,并保持图像的比例不变。

Rescaling

保持图像的比例关系,并将最小的一边缩放到和网络的输入大小一致。在我们的例子中,网络的输入是224x224。

  • 横向(Landscape):限制高度进行resize
  • 纵向(Portrait):限制宽度进行resize
print("Original image shape:" + str(img.shape) + " and remember it should be in H, W, C!")
print("Model's input shape is %dx%d") % (input_height, input_width)
aspect = img.shape[1]/float(img.shape[0])#宽/高
print("Orginal aspect ratio: " + str(aspect))
if(aspect>1):
# 横向 - 宽图像
res = int(aspect * input_height)#译者认为这里应该为input_width
imgScaled = skimage.transform.resize(img, (input_width, res))#译者认为这里应该为input_height
if(aspect<1):
# 竖向 - 高图像
res = int(input_width/aspect)#译者认为这里应该为input_height
imgScaled = skimage.transform.resize(img, (res, input_height))#译者认为这里应该为input_width
if(aspect == 1):
imgScaled = skimage.transform.resize(img, (input_width, input_height))#译者认为这里应该为 input_height, input_width
pyplot.figure()
pyplot.imshow(imgScaled)
pyplot.axis('on')
pyplot.title('Rescaled image')
print("New image shape:" + str(imgScaled.shape) + " in HWC")

输出

Original image shape:(751, 1280, 3) and remember it should be in H, W, C!
Model's input shape is 224x224
Orginal aspect ratio: 1.70439414115
New image shape:(224, 381, 3) in HWC

Cropping

这里有很多策略可以使用。我们可以比例不变的将图像缩小到一边大小符合网络的输入,然后从图像中间剪切出一块。但是如果不进行缩放,可能只能剪切到图像中花的一部分。所以我们还是需要缩放。

下面我们提供三种剪切的策略:

  1. 直接从图像中间取出你需要的大小的patch
  2. resize到一个很接近网络输入大小的正方形,然后从中间抓取
  3. 保持图像比例不变的缩放,然后从中间截取一部分
# 傻瓜式的从中间剪切
print("Original image shape:" + str(img.shape) + " and remember it should be in H, W, C!")
def crop_center(img,cropx,cropy):
y,x,c = img.shape
startx = x//2-(cropx//2) #python中//表示取结果的整数
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx] pyplot.figure()
# Original image
imgCenter = crop_center(img,224,224)
pyplot.subplot(1,3,1)
pyplot.imshow(imgCenter)
pyplot.axis('on')
pyplot.title('Original') # 从256x256的变形图像中剪切中间的224x224
img256Center = crop_center(img256,224,224)
pyplot.subplot(1,3,2)
pyplot.imshow(img256Center)
pyplot.axis('on')
pyplot.title('Squeezed') # Scaled image
imgScaledCenter = crop_center(imgScaled,224,224)
pyplot.subplot(1,3,3)
pyplot.imshow(imgScaledCenter)
pyplot.axis('on')
pyplot.title('Scaled')
Original image shape:(751, 1280, 3) and remember it should be in H, W, C!

注意:内存上保存始终是H,W,C

图像输出:



看起来好像最后一个比较好。第二种方法也不差,不过,这和很难说,要在你的模型上进行大批量测试才知道。如果你的模型在训练时使用不同比例的图像,并且直接将他们压缩到一个正方形,那么久而久之,你的模型将从压缩图像上学到那些物体被压缩时的样子,所以也能做出判断。但是如果你的模型专注于细节,比如面部特征,特征点,或者一些非常细微的元素,那么图像信息的丢失和变形将会带来非常大的误差。

更好的策略

更好的方法是,把你的图像缩放到最接近真实数据,然后在图像边缘填补信息,填补的信息不能对你的模型产生影响,也就是你的模型会忽略掉这些信息。这个方法,我们会在另外一个教程中给出,因为,这个教程已经讲了不少了。

Upscaling

如果你想要跑的图像很小,怎么办?在我们的例子中,我们网络的输入是224x224,但是如果遇到下面的128x128的图像大小呢?



最常用的方法就是,用skimage的工具把一个小的正方形图像变到一个大的正方形图像。resize的默认参数是1,对应着使用双线性插值。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
print "Original image shape: ", imgTiny.shape
imgTiny224 = skimage.transform.resize(imgTiny, (224, 224))
print "Upscaled image shape: ", imgTiny224.shape
# Plot original
pyplot.figure()
pyplot.subplot(1, 2, 1)
pyplot.imshow(imgTiny)
pyplot.axis('on')
pyplot.title('128x128')
# Plot upscaled
pyplot.subplot(1, 2, 2)
pyplot.imshow(imgTiny224)
pyplot.axis('on')
pyplot.title('224x224')
Original image shape:  (128, 128, 4)
Upscaled image shape: (224, 224, 4)



看到没,输出是 (224, 224, 4)。等等,为什么是4?前面所有例子都是3。当我们使用一个png文件时,它是由四个通道的。第四个通道代表的是‘模糊度’或者‘透明度’.无论怎么样,我们仍然能很好地处理它,不过,要留意这个通道数。现在让我们先转换成CHW,然后放大图像。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
print "Image shape before HWC --> CHW conversion: ", imgTiny.shape
#交换坐标系HWC to CHW
imgTiny = imgTiny.swapaxes(1, 2).swapaxes(0, 1)
print "Image shape after HWC --> CHW conversion: ", imgTiny.shape
imgTiny224 = skimage.transform.resize(imgTiny, (224, 224))
print "Image shape after resize: ", imgTiny224.shape
try:
pyplot.figure()
pyplot.subplot(1, 2, 1)
pyplot.imshow(imgTiny)#交换顺序后无法显示
pyplot.axis('on')
pyplot.title('128x128')
except:
print "Here come bad things!"
# 如果你想看到错误,反注释掉下面一行
#raise



什么都没显示,对吧,因为存储顺序调换了。但是通道数仍然是4.

现在让我们展示一个例子,一个比你网络输入小的图像,并且不是正方形的。来自于一个只能给出矩形图像的显微镜。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
imgTinySlice = crop_center(imgTiny, 128, 56)
# Plot original
pyplot.figure()
pyplot.subplot(2, 1, 1)
pyplot.imshow(imgTiny)
pyplot.axis('on')
pyplot.title('Original')
# Plot slice
pyplot.figure()
pyplot.subplot(2, 2, 1)
pyplot.imshow(imgTinySlice)
pyplot.axis('on')
pyplot.title('128x56')
# Upscale?
print "Slice image shape: ", imgTinySlice.shape
imgTiny224 = skimage.transform.resize(imgTinySlice, (224, 224))
print "Upscaled slice image shape: ", imgTiny224.shape
# Plot upscaled
pyplot.subplot(2, 2, 2)
pyplot.imshow(imgTiny224)
pyplot.axis('on')
pyplot.title('224x224')
Slice image shape:  (56, 128, 4)
Upscaled slice image shape: (224, 224, 4)

通道数没变。





这是一个非常严重的错误,例如正常的细胞都是接近圆形,而病变细胞则是镰刀形的。在这种情况下,你怎么办?这很依赖于你的模型和你的模型是如何训练出来的。在某些情况下,可以通过给图像填充白色或者黑色的,或者噪声的边缘解决这个问题。

下面我们继续讨论,我们已经说过BGR和CHW的问题了,但是在caffe2中还需要考虑一个就是batch term,也就是N,图像的个数。

Final Preprocessing and the Batch Term
# 如果你想尝试不同策略的剪切
# swap out imgScaled with img (original) or img256 (squeezed)
imgCropped = crop_center(imgScaled,224,224)
print "Image shape before HWC --> CHW conversion: ", imgCropped.shape
# (1)HWC->CHW
imgCropped = imgCropped.swapaxes(1, 2).swapaxes(0, 1)
print "Image shape after HWC --> CHW conversion: ", imgCropped.shape pyplot.figure()
for i in range(3):
# pyplot subplot 索引和MATLAB的一样,从1开始
pyplot.subplot(1, 3, i+1)
pyplot.imshow(imgCropped[i])
pyplot.axis('off')
pyplot.title('RGB channel %d' % (i+1)) # (2) RGB->BGR
imgCropped = imgCropped[(2, 1, 0), :, :]
print "Image shape after BGR conversion: ", imgCropped.shape
# 以下代码后面用到,现在没用
# (3) 减均值,由于skimage 读取的图像在[0,1]之间,所以我们需要乘以255,使像素范围回到[0,255]
#mean_file = os.path.join(CAFFE_ROOT, 'python/caffe/imagenet/ilsvrc_2012_mean.npy')
#mean = np.load(mean_file).mean(1).mean(1)
#img = img * 255 - mean[:, np.newaxis, np.newaxis] pyplot.figure()
for i in range(3):
pyplot.subplot(1, 3, i+1)
pyplot.imshow(imgCropped[i])
pyplot.axis('off')
pyplot.title('BGR channel %d' % (i+1))
# (4)最后由于Caffe2要求输入要有一个batch ,所以我们可以一次传递多张图像,我们仅仅让batch size=1,还要保证数据类型是 np.float32
imgCropped = imgCropped[np.newaxis, :, :, :].astype(np.float32)
print 'Final input shape is:', imgCropped.shape

输出:

Image shape before HWC --> CHW conversion:  (224, 224, 3)
Image shape after HWC --> CHW conversion: (3, 224, 224)
Image shape after BGR conversion: (3, 224, 224)
Final input shape is: (1, 3, 224, 224)





在上面的输出中,你应该注意到如下变化:

  1. HWC->CHW,图像的通道数3,由最后移到了前面
  2. RGB->BGR,蓝色和红色分量进行了交换
  3. 输入数据的最后形状,在前面添加了batch size。所以数据格式是(1, 3, 224, 224)
    • 1是图像的个数
    • 3是图像的通道数
    • 224是高
    • 224是宽

这一教程到此结束。转载请注明出处:http://www.jianshu.com/c/cf07b31bb5f2

Caffe2 图像预处理(Image Pre-Processing)[6]的更多相关文章

  1. 图像预处理第9步:存为.bmp文件

    //图像预处理第9步:将最终标准化后的字符图像分为单个单个的HDIB保存,并存为.bmp文件 void CChildView::OnImgprcToDibAndSave() { unsigned ch ...

  2. 『TensorFlow』第九弹_图像预处理_不爱红妆爱武装

    部分代码单独测试: 这里实践了图像大小调整的代码,值得注意的是格式问题: 输入输出图像时一定要使用uint8编码, 但是数据处理过程中TF会自动把编码方式调整为float32,所以输入时没问题,输出时 ...

  3. 基于OpenCV的火焰检测(一)——图像预处理

    博主最近在做一个基于OpenCV的火焰检测的项目,不仅可以检测图片中的火焰,还可以检测视频中的火焰,最后在视频检测的基础上推广到摄像头实时检测.在做这个项目的时候,博主参考了很多相关的文献,用了很多种 ...

  4. 第二讲_图像数据处理Image Data Processing

    第二讲_图像数据处理Image Data Processing 深度模型出现后被弱化,但是思想的影子在深度模型中可以看到的 图片存储原理 RGB颜色空间:三通道(b,g,r),加法混色 CMY(K): ...

  5. TensorFlow图像预处理完整样例

    参考书 <TensorFlow:实战Google深度学习框架>(第2版) 以下TensorFlow程序完成了从图像片段截取,到图像大小调整再到图像翻转及色彩调整的整个图像预处理过程. #! ...

  6. python+opencv 图像预处理

    一 python 生成随机字符串序列+ 写入到图片上 from PIL import Image,ImageDraw,ImageFont import numpy as np import rando ...

  7. [opencv]图像预处理方案及方式

    像识别中,图像质量的好坏直接影响识别算法的设计与效果精度,那么除了能在算法上的优化外,预处理技术在整个项目中占有很重要的因素,然而人们往往忽略这一点. 图像预处理,将每一个文字图像分检出来交给识别模块 ...

  8. Matlab 图像预处理

    %%%%%%%%%%%%%%%%% %%降采样 clear all im={}; %创建字典保存读取的图片 dis=dir('F:\kaggle_data_zip\Sample\*.jpeg');%% ...

  9. pytorch中检测分割模型中图像预处理探究

    Object Detection and Classification using R-CNNs 目标检测:数据增强(Numpy+Pytorch) - 主要探究检测分割模型数据增强操作有哪些? - 检 ...

随机推荐

  1. Codeforces Round #610 (Div. 2)C(贪心,思维)

    沿时间轴枚举,发现关键时间点在于新题目被锁定的前一时间,那是新的题目还没有被锁定并且距离旧的题目已经被锁定的最晚时间,对这些时间点进行操作 #define HAVE_STRUCT_TIMESPEC # ...

  2. bootstrap的pillbox使用

    使用bootstrap的cameo模版,搭建了一个cms系统,使用pillbox做显示的时候,出现点击×失败的问题. 分析了一下pillbox这个控件的使用方法. pillbox的样例在cameo/f ...

  3. 线上BUG定位神器(阿尔萨斯)-Arthas2019-0801

    1.下载这个jar 2.运行这个jar 3.选取你需要定位的问题应用进程 然后各种trace -j xx.xxx.xx.className methodName top -n 3 这个后面要补充去看, ...

  4. Python学习(二)——Python基础

    基础 1.文件后缀名: 后缀名必须是.py,否则导入模块时会出错 2.执行方式: python解释器 .py文件路径 python 进入解释器 实时输入并获取执行结果 3.解释器路径 #!/usr/b ...

  5. phpstorm 断点调试

    1.设置php的xdebug 在php.ini中设置 [XDebug] xdebug.profiler_output_dir="H:\phystudy\PHPTutorial\tmp\xde ...

  6. make工具简介

    在Linux C/C++的开发过程中,当源代码文件较少时,我们可以手动使用gcc或g++进行编译链接,但是当源代码文件较多且依赖变得复杂时,我们就需要一种简单好用的工具来帮助我们管理.于是,make应 ...

  7. LeetCode练题——35. Search Insert Position

    1.题目 35. Search Insert Position Easy 1781214Add to ListShare Given a sorted array and a target value ...

  8. cglib用法

    CGLib动态代理的介绍及用法(单回调.多回调.不处理.固定值.懒加载) 参照: https://blog.csdn.net/difffate/article/details/70552056 前面介 ...

  9. ES6-let声明变量

    在es6中除了var还可以用let申明变量,并且建议使用let而不要再使用var,两者有以下区别: 1.let不能重复声明变量 var name = 'tom'; var name = 'jack'; ...

  10. 这里是常见的HTTP状态码

    遇到过的HTTP状态码 200(Ok):请求成功,服务器成功返回网页. 403(Forbidden):服务器已经理解请求,但是拒绝执行它. 404(Not Found):请求失败,请求所希望得到的资源 ...