来得和仓储,平民化的人为智能

作者:国际新闻

原标题:手把手教你用OpenCV和Python实现图像和视频神经风格迁移(代码)

几个月前,我写了一篇关于如何使用CNN尤其是VGG16来分类图像的教程,该模型能够以很高的精确度识别我们日常生活中的1000种不同种类的物品。

不管从哪个角度来说,科技领域的科技树都在从人工智能的方向在发展,从最初的数据挖掘,到前几年的大数据,然后是机器学习,到机器学习的细分领域深度学习,都在逐渐让人工智能的想象成为现实,在自然语言、图像和声音领域,深度学习已经展现了令人叹为观止的能力。

如opencv开发前的准备工作中所说,此系列文章是在学习Practical Python and OpenCV(点击下载)这本书的一些记录,发出来的文章跳过了第三章对RGB,以及numpy简单操作等介绍,请大家下载原书查看,在原书中对一下段落已进行翻译注释。文章系列完整展示代码点击下载

code

将下面文档存为load_display_save.py

#-*- coding:utf-8 -*-
from __future__ import print_function #1
import argparse  #2
import cv2 #3

ap = argparse.ArgumentParser() #4
ap.add_argument("-i", "--image", required = True,
                help = "Path to the image") #5
args = vars(ap.parse_args()) #6

image1 = cv2.imread(args["image"]) #7
print("width: {}pixels".format(image1.shape[1])) #8
print("hight: {}pixels".format(image1.shape[0])) #9
print("channels: {}".format(image1.shape[2])) #10

cv2.imshow("恐龙啊", image1) #11
cv2.waitKey(0) #12
cv2.imwrite("恐龙图片.jpg", image1) #13

3983金沙官网 1

那时,模型还是和Keras包分开的,我们得从free-standing GitHub repo上下载并手动安装;现在模型已经整合进Keras包,原先的教程也已经不再适用,所以我决定写一篇新的教程。

今天,这里展示一个人工智能的经典用例——图像识别。

Translation

详细解释

#1:
引入future包,以便于我们使用print(),而不是print,这会让我们的程序在python2.7或者python3下都可以运行
#2:
引入argparse包,是为了在命令行中解析所输入的命令
#3:
引入OpenCV库
#4,5,6:
这三句程序主要是为了解析从命令行输入的参数,简单介绍一下argparse的用法,一般分为四步:

import argparse  #导包
parser = argparse.ArgumentParser() #创建ArgumentParser对象
parser.add_argument() #通过add_argument()告诉ArgumentParser如何将命令行中的参数转化成所需要的对象
parser.parse_args() #存储和使用通过add_argument()得到的信息,检查命令行的参数,并将参数转化成合理的使用类型

现在再回到原来的代码中,在原代码中:

ap.add_argument("-i", "--image", required = True,
                help = "Path to the image")

1.这第一个参数和第二个参数是什么意思呢?大家回想在命令行中输入
python -h和输入python --help是不是都可以得到关于python的帮助文档呢,这里的第一个参数-i就可以看成是--image的简写形式。第二个参数是"--image",这里的image同时也相当于声明了一个变量,后面是会用到的,在#7就用到了。
2.第三个参数什么意思呢?意思你在命令输入python load_display_save.py后面必须加上“-i”或者"--image"
3.第四个参数是帮助参数,就是解释你这个python load_display_save.py -i后面跟的参数是什么,可以看懂在这后面应该跟图片的路径

args = vars(ap.parse_args()) #6

这里为什么要是用vars()呢?是为了能够像字典一样访问 ap.parse_args()的值,即现在 args["image"]=ap.parse_args()
关于argparse的更多内容可以访问python官网:
官方解释
官方使用教程

#7:
通过#6得到了图片的路径,现在通过OpenCV的imread()函数从路径读取图片,cv2.imread()返回的是用numpy数组表示的图片
#8,9,10:
输出的分别是图片的宽,高,和颜色通道的数量,其实image1.shape是一个三元组(高,宽,颜色通道),如果是黑白图片的话则只是二元组(高,宽),要注意的是shape[0]是高,shape[1]是宽,及shape[0]是y,shape[1]是x,在图像中的坐标系如下:

3983金沙官网 2

图像坐标系

#11:
以名称为"恐龙啊"将图片显示

#12:
cv2.waitKey()表示暂停脚本的执行直到在键盘输入一个按键,用“0”作为参数表示可以使用任何按键作为继续脚本执行的按钮

#13:
通过脚本将刚才展示的图片以名称为"恐龙图片.jpg"存储在和脚本同样的目录之下,

新智元推荐

在教程中,你将学习到如何编写一个Python脚本来分类你自己的图像。

代码参考自  

import numpy as np

import cv2

import argparse

import imutils

ap = argparse.ArgumentParser()

ap.add_argument("-i","--image", required =True,

help ="Path to the image")

args = vars(ap.parse_args())

image = cv2.imread(args["image"])

cv2.imshow("Original", image)

cv2.waitKey(0)

M = np.float32([[1,0,25],[0,1,50]])

#定义我们的平移矩阵M

#矩阵M被定义为一个浮点数组 - 这很重要,因为OpenCV期望这个矩阵是浮点类型的。

#矩阵的第一行是[1,0,tx],其中tx是像素的数量,我们将左右移动图像。

#tx的负值会将图像左移,正值会将图像向右移

#我们将矩阵的第二行定义为[0,1,ty],其中ty是我们将向上或向下移动图像的像素数量。

#ty的负值会使图像向上移动,正值会使图像向下移动。

shifted =cv2.warpAffine(image ,M ,(image.shape[1], image.shape[0]))

#warpAffine第一个参数是我们想要移动的图像,第二个参数是我们的平移矩阵M.最后,我们手动提供图像的尺寸(宽度和高度)作为第三个参数

cv2.imshow("Shifted Up and Left",shifted)

M = np.float32([[1,0,-50],[0,1,-90]])

shifted =cv2.warpAffine(image ,M ,(image.shape[1], image.shape[0]))

cv2.imshow("Shifted Up and Left",shifted)

cv2.waitKey(0)

执行

在终端输入:

python load_display_save.py -i images/pic.png

得到结果

3983金沙官网 3

结果展示

3983金沙官网 4

生成图片

width: 528pixels
hight: 449pixels
channels: 3

得到图片的宽为528像素,高位449像素,颜色通道的数量为3,即为RGB,同时可以惊奇的发现,我们打开了png的图片然后轻易的将它存成jpg的格式,接下来我们将探索如何得到在图片中的像素和控制它们。


转载请注明出处:
CSDN:楼上小宇__home:http://blog.csdn.net/u014265347
简书:楼上小宇:http://www.jianshu.com/u/1621b29625df

来源:pyimagesearch

博客结构

先上结果,代码附在最后,免得没看完就关掉。

以上多次使用warpAffine重复性很高而且 使用起来不方便,我们可以定义一个叫imutils.py的模块封装这个方法如下:

编译:Bing

1.简要说明一下这几个网络架构;

3983金沙官网 5

import numpy as np

import cv2

def  translate(image, x, y):

     M = np.float32([[1,0, x], [0,1, y]])

    shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))

     return shifted

【新智元导读】在这篇教程中,作者Adrian Rosebrock将教你如何用OpenCV、Python和深度学习技术对图像和实时视频进行神经风格迁移。

2.使用Python编写代码:载入训练好的模型并对输入图像分类;

其实这是一个挺不错的思路,对于人工智能的应用来说,并非一切都必须从头来过,对于已有模型的二次开发也许存在着非常大的机会。

再实现上面平移的动作:

2015年,Gatsys等人在论文A Neural Algorithm of Artistic Style中提出了最初的神经风格迁移算法。2016年,Johnson等人发表了Perceptual Losses for Real-Time Style Transfer and Super-Resolutioin一文,将神经网络迁移作为用感知损失处理超分辨率问题的框架。结果表明该算法比Gatys等人的方法快了三倍。接下来,我将介绍如何在自己的图像和视频流中应用神经风格迁移。

3.审查一些样本图片的分类结果。

这是一个人工智能逐渐下沉的时代。

shifted =imutils.translate(image,0,100)

cv2.imshow("Shifted Up and Left",shifted)

用OpenCV进行神经风格迁移

Keras中最新的深度学习图像分类器

以下是代码。

Rotation 旋转一个角度q的图像。

首先说明的一点是,今天讨论的方法在一个CPU上可以达到近乎实时的效果,如果在GPU上则完全可以实现实时效果。

Keras提供了五种开箱即用型的CNN:

# coding=utf-8

import numpy as np

import cv2

import argparse

ap = argparse.ArgumentParser()

ap.add_argument("-i","--image",required =True,help ="Path to the image")

args =vars(ap.parse_args())

image = cv2.imread(args["image"])

cv2.imshow("Original",image)

(h,w) = image.shape[:2]

center = (w//2,h//2)

M = cv2.getRotationMatrix2D(center,45,1.0)

#cv2.getRotationMatrix2D(center, degrees , scale)

#center为需要围绕旋转的点,当我们旋转图像时,我们需要指定我们要旋转的点。

#在大多数情况下,你会想要围绕图像的中心旋转;然而,

#OpenCV允许你指定你想旋转的任意点

# degrees 旋转的角度

# scale 比例 这里你可以指定一个浮点值,其中1.0意味着使用相同的图像转换。但是,如果您指定的值为2.0,则图像的大小将加倍。类似地,0.5的值将图像的大小减半。

#就像我们定义矩阵来翻译图像一样,我们也定义了一个矩阵来旋转图像。我们只需要调用cv2.getRotationMatrix2D方法,而不是使用NumPy手工构造矩阵

rotated = cv2.warpAffine(image, M,(w,h))

cv2.imshow("Rotated by 45 Degrees", rotated)

M = cv2.getRotationMatrix2D(center,-90,1.0)

rotated = cv2.warpAffine(image, M ,(w,h))

cv2.imshow("Rotated by -90 Degrees",rotated)

rotated = imutils.rotate(image,60,None,0.5)

cv2.imshow("Rotated by imutils",rotated)

cv2.waitKey(0)

首先我们会简单塔伦下什么是神经风格迁移,以及它是如何运作的。之后我们会用OpenCV和Python动手操作。

1.VGG16

from keras.applications import ResNet50

封装rotate方法

什么是神经风格迁移?

2.VGG19

from keras.applications import InceptionV3

工具类imutils.py

3983金沙官网 6

3.ResNet50

from keras.applications import Xception # tensforlow only

def rotate(image, angle ,center= None,scale =1.0):

    (h,w)= image.shape[:2]

   ifcenterisNone:

    center =(w /2,h/2)

    M = cv2.getRotationMatrix2D(center,angle,scale)

    rotated = cv2.warpAffine(image, M ,(w,h))

    return rotated

从左至右:我们的内容图像;风格图像;输出的风格迁移图像

4.Inception V3

from keras.applications import VGG16

调用方式:

神经风格迁移主要有两个过程:

5.Xception

from keras.applications import VGG19

rotated = imutils.rotate(image,60,None,0.5)

  • 提取某张图片的风格
  • 将该种风格应用到另一张图片上

什么是ImageNet

from keras.applications import imagenet_utils

resize 调整大小

上图就是将梵高著名的画作《星夜》的风格应用到普通的生活照上,我们保留了原照片中的山、人物和啤酒等所有内容,但全部替换成了梵高的油画风格。

ImageNet曾是一个计算机视觉研究项目:打标签并分类成22000个不同物品种类。然而,当我们在讨论深度学习和CNN的时候,“ImageNet”意味着ImageNet Large Scale Visual Recognition Challenge,简写为ILSVRC。

from keras.applications.inception_v3 import preprocess_input

import numpy as np

import cv2

import argparse

import imutils

ap = argparse.ArgumentParser()

ap.add_argument("-i","--image",required =True,help ="Path to the image")

args = vars(ap.parse_args())

image = cv2.imread(args["image"])

cv2.imshow("Original",image)

r =150.0/image.shape[3983金沙官网,1]

#定义新图片的宽度为150,为了计算新图片的高度,计算出新图片宽度和当前图片宽度的比例。

dim = (150,int(image.shape[0]*r))

#新图片的宽高

resized = cv2.resize(image , dim, interpolation = cv2.INTER_AREA)

#cv2.resize(image,dim,interpolation) 

#image 需要调整的图片    dim 新图片的尺寸

#最后一个参数是我们的插值方法,它是在幕后处理实际图像大小调整的算法

#cv2.INTER_AREA,cv2.INTER_LINEAR,cv2.INTER_CUBIC,cv2.INTER_NEAREST

# interpolation 可选参数

# INTER_NEAREST - a nearest-neighbor interpolation

# INTER_LINEAR - a bilinear interpolation (used by default)

# INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.

# INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood

# INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood

NTER_NEAREST - 最近邻居插值

INTER_LINEAR - 双线性插值(默认使用)

INTER_AREA - 使用像素区域关系重采样。这可能是图像抽取的首选方法,因为它可以产生无莫尔效应的结果。但是当图像放大时,它与INTER_NEAREST方法类似。

INTER_CUBIC -4x4像素邻域上的双三次插值

INTER_LANCZOS4 -8x8像素邻域上的Lanczos插值

cv2.imshow("resized(width)",resized)

cv2.waitKey(0)

问题就是,我们应该如何定义一个神经网络,让它执行神经风格迁移呢?

ILSVRC的目的是训练一个能够正确识别图像并分类的模型:模型使用约120万张图像用作训练,5万张图像用作验证,10万张图像用作测试。

from keras.preprocessing.image import img_to_array

Flipping 旋转

神经风格迁移如何工作?

这1000种分类涵盖了我们的日常生活接触到的东西,具体列表请点击。

from keras.preprocessing.image import load_img

我们可以在x或y轴周围翻转图像,甚至可以翻转图像(We can flip an image around either the x or y axis, or even both.)

3983金沙官网 7

在图像分类上,ImageNet竞赛已经是计算机视觉分类算法事实上的评价标准——而自2012年以来,排行榜就被CNN和其它深度学习技术所统治。

import numpy as np

import argparse

import cv2

ap = argparse.ArgumentParser()

ap.add_argument("-i","--image",required =True,help ="Path to the image")

args = vars(ap.parse_args())

image = cv2.imread(args["image"])

cv2.imshow("Original",image)

flipped = cv2.flip(image,1)

cv2.imshow("Flipped Horizontally",flipped)

#使用1的翻转代码值表示我们将水平地围绕y轴翻转图像。

flipped = cv2.flip(image,0)

# 指定一个0的翻转代码表示我们想要垂直翻转图像,围绕X轴

cv2.imshow("Flipped Vertically",flipped)

flipped = cv2.flip(image,-1)

# 使用负向翻转代码将图像翻转两个轴。

cv2.imshow("Flipped Horizontally&Vertically",flipped)

cv2.waitKey(0)

在Gatys等人提出的首篇论文中,神经风格迁移算法不需要新的架构。相反,我们可以用一个预训练网络(通常在ImageNet上进行的预训练),并且定义一个损失函数,能让我们达到风格迁移的目标,然后对损失函数不断优化。

过去几年中ImageNet竞赛里表现优异的模型在Keras中均有收录。通过迁移学习,这些模型在ImageNet外的数据集中也有着不错的表现。

import argparse

Cropping 裁剪

那么,这里的问题就不是“该用什么神经网络”了,而是“该用什么损失函数”。

VGG16和VGG19

import cv2

图片的裁剪使用NumPy数组切片来完成图像裁剪

答案包括:内容损失、风格损失和总变差损失。每个部分都是单独计算,然后在一个元损失函数中结合。通过将元损失函数最小化,我们将依次对内容、风格和总变差损失进行优化。

3983金沙官网 8

ap = argparse.ArgumentParser()

import numpy as np

import argparse

import cv2

ap = argparse.ArgumentParser()

ap.add_argument("-i","--image",required =True, help="Path to the image")

args = vars(ap.parse_args())

image = cv2.imread(args["image"])

cv2.imshow("Original",image)

#来得和仓储,平民化的人为智能。NumPy数组中高度在前面,宽度在后面

cropped = image[30:220,10:335]

#所以我们需要截取的区域值定义需要按照numpy的格式,如上[starty:endy,startx:endx]

# 1.Start y: The starting y coordinate. In this case, we

# start at y = 30.

# 2. End y: The ending y coordinate. We will end our crop

# at y = 220.

# 3. Start x: The starting x coordinate of the slice. We start

# the crop at x = 10.

# 4. End x: The ending x-axis coordinate of the slice. Our

# slice ends at x = 335.

cv2.imshow("update",cropped)

cv2.waitKey(0)

虽然Gatys等人的方法能生成不错的神经风格迁移结果,但是它的速度非常慢。2016年,Johnson等人在Gatys的基础上提出的全新算法速度快了三倍,但同时也存在着缺点,即用户不能随机选择想要应用的风格图像。用户首先要训练一个网络,生成你想要的风格。网络训练好后,你可以将它应用到任意内容图像上。

图1:VGG网络架构

ap.add_argument("-i", "--image", required=True,


然而到了2017年,Ulyanov等人发表了Instance Normalization: The Missing Ingredient for Fast Stylization一文,他们表示将batch normalization替换成instance normalization(然后在训练和测试时都应用instance normalization),可以达到更快的效果,并且艺术效果也更好。

VGG网络架构于2014年出现在Simonyan和Zisserman中的论文中,《Very Deep Convolutional Networks for Large Scale Image Recognition》。

    help="path to the input image")

凡事往简单处想,往认真处行。

更多文章请关注我的博客:https://harveyyeung.github.io

项目结构

该架构仅仅使用堆放在彼此顶部、深度不断增加的3×3卷积层,并通过max pooling来减小volume规格;然后是两个4096节点的全连接层,最后是一个softmax分类器。“16”和“19”代表网络中权重层的数量:

ap.add_argument("-model", "--model", type=str, default="vgg16",

在开始今天的教程前,请先下载我提供的资料(点击文末原文地址获取资料)。准备好了脚本、模型和图像后,你可以用tree指令检查项目的结构:

3983金沙官网 9

    help="name of pre-trained network to use")

1$ tree --dirsfirst

在2014年的时候,16还有19层网络还是相当深的,Simonyan和Zisserman发现训练VGG16和VGG19很有难度,于是选择先训练小一些的版本。这些小的网络收敛后被用来作为初始条件训练更大更深的网络——这个过程被称为预训练(pre-training)。

args = vars(ap.parse_args())

2.

预训练很有意义,但是消耗大量时间、枯燥无味,在整个网络都被训练完成前无法进行下一步工作。

MODELS = {

3├── images

如今大部分情况下,我们已经不再使用预训练,转而采用Xaiver/Glorot初始化或者MSRA初始化(有时也被称作He et al.初始化,详见《Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》)。如果你感兴趣,可以从这篇文章中理解到weight initialization的重要性以及深度神经网络的收敛——《All you need is a good init, Mishkin and Matas 》。

    "vgg16": VGG16,

4│ ├── baden_baden.jpg

VGGNet有两个不足:

    "vgg19": VGG19,

5│ ├── giraffe.jpg

1.训练很慢;

    "inception": InceptionV3,

6│ ├── jurassic_park.jpg

2.weights很大。

    "xception": Xception,

7│ └── messi.jpg

由于深度以及全连接节点数量的原因,VGG16的weights超过533MB,VGG19超过574MB,这使得部署VGG很令人讨厌。虽然在许多深度学习图像分类问题中我们仍使用VGG架构,但是小规模的网络架构更受欢迎(比如SqueezeNet, GoogleNet 等等)。

    "resnet": ResNet50

8├── models

ResNet

}

9│ ├── eccv16

与AlexNet、OverFeat还有VGG这些传统顺序型网络架构不同,ResNet的网络结构依赖于微架构模组(micro-architecture modules,也被称为network-in-network architectures) 。

if args["model"] not in MODELS.keys():

10│ │ ├── composition_vii.t7

微架构模组指构成网络架构的“积木”,一系列的微架构积木(连同你的标准CONV,POOL等)共同构成了大的架构。

    raise AssertionError("The --model command line argument should be a key in the `MODELS` dictionary")

11│ │ ├── la_muse.t7

ResNet于2015年出现在He et al的论文《Deep Residual Learning for Image Recognition》中,它的出现很有开创性意义,证明极深的网络也可以通过标准SGD(以及一个合理的初始化函数)来训练:

if args["model"] in ("inception", "xception"):

12│ │ ├── starry_night.t7

3983金沙官网 10

    inputShape = (299, 299)

13│ │ └── the_wave.t7

图3:He et al.于2015年提出的残差模组

    preprocess = preprocess_input

14│ └── instance_norm

在2016年的著作《Identity Mappings in Deep Residual Networks》中,他们证实了可以通过更新残差模组(residual module)来使用标志映射(identify mappings),达到提高精度的目的。

else:

15│ ├── candy.t7

3983金沙官网 11

    inputShape = (224, 224)

16│ ├── feathers.t7

图4:原始残差模组 使用预激活(pre-activation)更新的残差模组

    preprocess = imagenet_utils.preprocess_input

17│ ├── la_muse.t7

尽管ResNet比VGG16还有VGG19要深,weights却要小,因为使用了全局平均池化(global average pooling),而不是全连接层。

print("[INFO] loading {}...".format(args["model"]))

18│ ├── mosaic.t7

Inception V3

Network = MODELS[args["model"]]

19│ ├── starry_night.t7

“Inception”微架构于2014年出现在Szegedy的论文中,《Going Deeper with Convolutions》。

model = Network(weights="imagenet")

20│ ├── the_scream.t7

3983金沙官网 12

print("[INFO] loading and pre-processing image..." )

21│ └── udnie.t7

图5:GoogleNet中使用的Inception模组原型

image = load_img(args["image"], target_size=inputShape)

22├── neural_style_transfer.py

Inception模组的目的是扮演一个“多级特征提取器”,在网络相同的模组内计算1×1、3×3还有5×5的卷积——这些过滤器的输出在输入至网络下一层之前先被堆栈到channel dimension。

image = img_to_array(image)

23├── neural_style_transfer_examine.py

该架构的原型被称为GoogleNet,后继者被简单的命名为Inception vN,N代表Google推出的数字。

image = np.expand_dims(image, axis=0)

24└── neural_style_transfer_video.py

Keras中的Inception V3架构来自于Szegedy et al.的后续论文,《Rethinking the Inception Architecture for Computer Vision》,该论文打算通过更新inception模组来提高ImageNet分类的准确度。

image = preprocess(image)

如果你从下载了.zip文件,就无需上网找其他素材了。我在其中提供了很多测试用的图像和模型。同时还有三种Python脚本。

Inception V3比VGG还有ResNet都要小,约96MB。

print("[INFO] classifying image with '{}'...".format(args["model"]))

开始神经风格迁移

Xception

preds = model.predict(image)

接下来让我们用OpenCV和Python进行神经风格迁移的实践。

3983金沙官网 13

P = imagenet_utils.decode_predictions(preds)

首先打开neural_style_transfer.py文件,插入如下代码:

图6:Xception架构

for (i, (imagenetID, label, prob)) in enumerate(P[0]):

1# import the necessary packages

Xception是被François Chollet提出的, 后者是Keras库的作者和主要维护者。

    print("{}. {}: {:.2f}%".format(i 1, label, prob * 100))

2importargparse

Xception是Inception架构的扩展,用depthwise独立卷积代替Inception标准卷积。

orig = cv2.imread(args["image"])

3importimutils

关于Xception的出版物《Deep Learning with Depthwise Separable Convolutions》可以在这里找到。

(imagenetID, label, prob) = P[0][0]

4importtime

Xception最小仅有91MB。

cv2.putText(orig, "Label: {}, {:.2f}%".format(label, prob * 100),

5importcv2

SqueezeNet

    (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)

6

3983金沙官网 14

cv2.imshow("Classification", orig)

7# construct the argument parser and parse the arguments

Figure 7:“fire”模组,由一个“squeeze”和一个“expand”模组组成。(Iandola et al., 2016)

cv2.waitKey(0)

8ap = argparse.ArgumentParser()

仅仅4.9MB的SqueezeNet架构能达到AlexNet级别的精确度(~57% rank-1 and ~80% rank-5),这都归功于“fire”模组的使用。然而SqueezeNet的训练很麻烦,我会在即将出版的书——《Deep Learning for Computer Vision with Python》——中介绍如何训练SqueezeNet来处理ImageNet数据集。

9ap.add_argument("-m", "--model", required=True,

使用Python和Keras通过VGGNet,ResNet,Inception和Xception对图像分类

10help="neural style transfer model")

新建一个文件,命名为classify_image.py,编辑插入下列代码

11ap.add_argument("-i", "--image", required=True,

1# import the necessary packages

12help="input image to apply neural style transfer to")

2from keras.applications import ResNet50

13args = vars(ap.parse_args())

3from keras.applications import InceptionV3

首先,我们导入所需的包并解析命令行参数。

4from keras.applications import Xception # TensorFlow ONLY

导入的有:

5from keras.applications import VGG16

  • imutils:这个包可以通过pip install --upgrade imutils安装。最近我发布了imutils==0.5.1,所以不要忘记更新!
  • OpenCV:你需要一个OpenCV 3.4或者更高版本。

6from keras.applications import VGG19

该脚本下需要两个命令行:

7from keras.applications import imagenet_utils

  • --model:神经风格迁移的模型路径。在“下载”区中,我加入了11中经过与训练的模型。
  • --image:需要进行风格迁移的图像(输入图像)。在其中我放入了四张图片。

8from keras.applications.inception_v3 import preprocess_input

你不需要改变命令行代码,参数会在运行过程中进行处理。如果你不熟悉这一过程,可以阅读我另一篇文章:

9from keras.preprocessing.image import img_to_array

www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/

10from keras.preprocessing.image import load_img

接下来的部分比较有趣,我们要下载图像和模型,然后计算神经风格迁移:

11import numpy as np

15# load the neural style transfer model from disk

12import argparse

16print("[INFO] loading style transfer model...")

13import cv2

17net = cv2.dnn.readNetFromTorch(args["model"])

第2-13行导入需要的包,其中大部分都属于Keras。

18

第2-6行分别导入ResNet,Inception V3, Xception, VGG16, 还有VGG19——注意Xception只兼容TensorFlow后端。

19# load the input image, resize it to have a width of 600 pixels, and

第7行导入的image_utils包包含了一系列函数,使得对图片进行前处理以及对分类结果解码更加容易。

20# then grab the image dimensions

余下的语句导入其它有用的函数,其中NumPy用于数学运算,cv2用于与OpenCV结合。

21image = cv2.imread(args["image"])

15# construct the argument parse and parse the arguments

22image = imutils.resize(image, width=600)

16ap = argparse.ArgumentParser()

23(h, w) = image.shape[:2]

17ap.add_argument("-i", "--image", required=True,

24

18help="path to the input image")

25# construct a blob from the image, set the input, and then perform a

19ap.add_argument("-model", "--model", type=str, default="vgg16",

26# forward pass of the network

20help="name of pre-trained network to use")

27blob = cv2.dnn.blobFromImage(image, 1.0, (w, h),

21args = vars(ap.parse_args

28(103.939, 116.779, 123.680), swapRB=False, crop=False)

--image为希望进行分类的图像的路径。

29net.setInput(blob)

--model为选用的CNN的类别,默认为VGG16。

30start = time.time()

23# define a dictionary that maps model names to their classes

31output = net.forward()

24# inside Keras

32end = time.time()

25MODELS = {

在这部分代码中,我们进行了:

26"vgg16": VGG16,

  • 将下载的预训练神经风格迁移模型称为net(第17行);
  • 下载输入图像并调整尺寸(21和22行);
  • 用均值减法创建blob(27和28行);
  • 执行forward,获取output图像(31行)。

27"vgg19": VGG19,

接下来,重要的是对输出图像进行后处理:

28"inception": InceptionV3,

34# reshape the output tensor, add back in the mean subtraction, and

29"xception": Xception, # TensorFlow ONLY

35# then swap the channel ordering

30"resnet": ResNet50

36output = output.reshape((3, output.shape[2], output.shape[3]))

31}

37output[0] = 103.939

32

38output[1] = 116.779

33# esnure a valid model name was supplied via command line argument

39output[2] = 123.680

34if args["model"] not in MODELS.keys():

40output /= 255.0

35raise AssertionError("The --model command line argument should "

41output = output.transpose(1, 2, 0)

36"be a key in the `MODELS` dictionary")

最后一步是将输出图像显示在屏幕上:

第25-31行定义了一个词典,将类映射到对应的模型名称。

43# show information on how long inference took

如果没有在该词典中找到“--model”,就会报错。

44print("[INFO] neural style transfer took {:.4f} seconds".format(

输入一个图像到一个CNN中会返回一系列键值,包含标签及对应的概率。

45end - start))

ImageNet采用的图像尺寸一般为224×224,227×227,256×256, and299×299,但是并不是绝对。

46

VGG16,VGG19以及ResNet接受224×224的输入图像, 而Inception V3和Xception要求为299×299,如下代码所示:

47# show the images

38# initialize the input image shape (224x224 pixels) along with

48cv2.imshow("Input", image)

39# the pre-processing function (this might need to be changed

49cv2.imshow("Output", output)

40# based on which model we use to classify our image)

50cv2.waitKey(0)

41inputShape =

神经风格迁移结果

42preprocess = imagenet_utils.preprocess_input

当你下载好文件后,打开终端执行以下命令:

43

1$ python neural_style_transfer.py --image images/giraffe.jpg

44# if we are using the InceptionV3 or Xception networks, then we

2--model models/eccv16/the_wave.t7

45# need to set the input shape to [rather than ]

3[INFO] loading style transfer model...

46# and use a different image processing function

4[INFO] neural style transfer took 0.3152seconds

47if args["model"] in ("inception", "xception"):

3983金沙官网 15

48inputShape =

现在,对命令行参数做简单改变,然后用《侏罗纪公园》中的截图作为内容图像,进行风格迁移:

49preprocess = preprocess_input

1$ python neural_style_transfer.py --image images/jurassic_park.jpg

这里我们初始化inputShape为224×224像素,初始化预处理函数为keras.preprocess_input——执行mean subtraction运算。

2--model models/instance_norm/the_scream.t7

如果使用Inception或者Xception,inputShape需要改为299×299像素,预处理函数改为separate pre-processing函数。

3[INFO] loading style transfer model...

下一步就是从磁盘载入网络架构的weights,并实例化模型:

4[INFO] neural style transfer took 0.1202seconds

51# load our the network weights from disk (NOTE: if this is the

3983金沙官网 16

52# first time you are running this script for a given network, the

另一个例子:

53# weights will need to be downloaded first -- depending on which

1$ python neural_style_transfer.py --image images/messi.jpg

54# network you are using, the weights can be 90-575MB, so be

2--model models/instance_norm/udnie.t7

55# patient; the weights will be cached and subsequent runs of this

3[INFO] loading style transfer model...

56# script will be *much* faster)

4[INFO] neural style transfer took 0.1495seconds

57print("[INFO] loading {}...".format(args["model"]))

3983金沙官网 17

58Network = MODELS[args["model"]]

这是我最喜欢的案例,感觉都能当做酒吧的装饰画了。

59model = Network(weights="imagenet")

实时神经风格迁移

注意:VGG16和VGG19的weights大于500MB,ResNet的约等于100MB,Inception和Xception的介于90-100MB之间。如果这是你第一次运行某个网络,这些weights会自动下载到你的磁盘。下载时间由你的网络速度决定,而且下载完成后,下一次运行代码不再需要重新下载。

上面我们讲了如何在单一图像上应用风格迁移,现在我们要把这一过程放在视频上。

61# load the input image using the Keras helper utility while ensuring 62# the image is resized to `inputShape`, the required input dimensions 63# for the ImageNet pre-trained network 64print("[INFO] loading and pre-processing image...") 65image = load_img(args["image"], target_size=inputShape) 66image = img_to_array 6768# our input image is now represented as a NumPy array of shape 69# (inputShape[0], inputShape[1], 3) however we need to expand the 70# dimension by making the shape (1, inputShape[0], inputShape[1], 3) 71# so we can pass it through thenetwork 72 image = np.expand_dims(image, axis=0) 7374# pre-process the image using the appropriate function based on the 75# model that has been loaded (i.e., mean subtraction, scaling, etc.) 76image = preprocess

大致流程和图像处理差不多,在这一脚本中,我们将:

第65行从磁盘载入输入图像,并使用提供的inputShape初始化图像的尺寸。

  • 利用一个特殊的Python迭代器,它可以让我们在模型路径中循环使用所有可用的神经风格迁移模型。
  • 启动网络摄像头视频流,我们会(近乎)实时处理摄像头的帧。对于某些较大的模型,系统可能会慢一些。
  • 在每一帧上应用风格迁移,对输出进行后处理,并将结果显示在屏幕上。
  • 如果用户按下“n”键,我们将把迭代器循环运用到下一个神经风格迁移模型上,不用重启脚本。

第66行将图像从PIL/Pillow实例转换成NumPy矩阵,矩阵的shape为(inputShape[0], inputShape[1], 3)。

首先,打开neural_style_transfer_video.py文件,插入以下代码:

因为我们往往使用CNN来批量训练/分类图像,所以需要使用np.expand_dims在矩阵中添加一个额外的维度,如第72行所示;添加后矩阵shape为(1, inputShape[0], inputShape[1], 3)。如果你忘记添加这个维度,当你的模型使用.predict时会报错。

1# import the necessary packages

最后,第76行使用合适的预处理函数来执行mean subtraction/scaling。

2fromimutils.video importVideoStream

下面将我们的图像传递给网络并获取分类结果:

3fromimutils importpaths

78# classify the image

4importitertools

79print("[INFO] classifying image with '{}'...".format(args["model"]))

5importargparse

80preds = model.predict

6importimutils

81P = imagenet_utils.decode_predictions

7importtime

82

8importcv2

83# loop over the predictions and display the rank-5 predictions

9

84# probabilities to our terminal

10# construct the argument parser and parse the arguments

85for (i, (imagenetID, label, prob)) in enumerate:

11ap = argparse.ArgumentParser()

86print("{}. {}: {:.2f}%".format(i 1, label, prob * 100))

12ap.add_argument("-m", "--models", required=True,

第80行调用.predict函数,并从CNN返回预测值。

13help="path to directory containing neural style transfer models")

第81行的.decode_predictions函数将预测值解码为易读的键值对:标签、以及该标签的概率。

14args = vars(ap.parse_args())

第85行和86行返回最可能的5个预测值并输出到终端。

之后,创建模型路径迭代器:

案例的最后一件事,是通过OpenCV从磁盘将输入图像读取出来,在图像上画出最可能的预测值并显示在我们的屏幕上。

16# grab the paths to all neural style transfer models in our 'models'

88# load the image via OpenCV, draw the top prediction on the image,

17# directory, provided all models end with the '.t7' file extension

89# and display the image to our screen

18modelPaths = paths.list_files(args["models"], validExts=(".t7",))

90orig = cv2.imread(args["image"])

19modelPaths = sorted(list(modelPaths))

91(imagenetID, label, prob) = P[0][0]

20

92cv2.putText(orig, "Label: {}, {:.2f}%".format(label, prob * 100),

21# generate unique IDs for each of the model paths, then combine the

93, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)

22# two lists together

94cv2.imshow("Classification", orig)

23models = list(zip(range(0, len(modelPaths)), (modelPaths)))

95cv2.waitKey

24

VGGNet, ResNet, Inception,和Xception的分类结果

25# use the cycle function of itertools that can loop over all model

所有的例子都是使用2.0以上版本的Keras以及TensorFlow后台做的。确保你的TensorFlow版本大于等于1.0,否则会报错。所有例子也都使用Theano后端做过测试,工作良好。

26# paths, and then when the end is reached, restart again

案例需要的图片以及代码请前往原文获取。

27modelIter = itertools.cycle(models)

使用VGG16分类:

28(modelID, modelPath) = next(modelIter)

1$ python classify_image.py --image images/soccer_ball.jpg --model vgg16

一旦我们开始在while循环中处理帧,“n”按键就会在迭代器中下载“下一个”模型。

3983金沙官网 18

为了创建模型迭代器,我们:

图8:使用VGG16来分类足球

  • 搜集所有神经风格迁移模型并分类(18和19行)
  • 为每种模型分配ID(23行)
  • 利用itertools和cycle创建迭代器(27行)。

输出为:soccer_ball,精确度为93.43%。

让我们开始下载第一个模型并对视频进行处理:

如果要使用VGG19,只需要替换下--network参数。

30# load the neural style transfer model from disk

1$ python classify_image.py --image images/bmw.png --model vgg19

31print("[INFO] loading style transfer model...")

3983金沙官网 19

32net = cv2.dnn.readNetFromTorch(modelPath)

图9:使用VGG19来分类汽车

33

输出为:convertible,精确度为91.76%。然而,我们看一下其它的4个结果:sports car, 4.98%;limousine,1.06%;car wheel,0.75%(技术上正确,因为图中确实出现了轮子)。

34# initialize the video stream, then allow the camera sensor to warm up

从下面的例子,我们可以看到类似的结果:

35print("[INFO] starting video stream...")

1$ python classify_image.py --image images/clint_eastwood.jpg --model resnet

36vs = VideoStream(src=0).start()

3983金沙官网 20

37time.sleep(2.0)

图10:使用ResNet分类.

38print("[INFO] {}. {}".format(modelID 1, modelPath))

ResNet成功将图像分类为revolver,精确度69.79%。有趣的是rifle为7.74%,assault rifle为5.63%。考虑到revolver的观察角度还有相对于手枪来说巨大的枪管,CNN得出这么高的概率也是合理的。

在32行,我们读取了第一个模型利用的路径。在36和37行,启动了视频,从摄像头中采集帧。

1$ python classify_image.py --image images/jemma.png --model resnet

之后在帧与帧之间进行循环:

3983金沙官网 21

40# loop over frames from the video file stream

图11:使用ResNet对狗进行分类

41whileTrue:

狗的种类被正确识别为beagle,精确度94.48%。

42# grab the frame from the threaded video stream

然后我试着分类《加勒比海盗》中的图片:

43frame = vs.read()

1$ python classify_image.py --image images/boat.png --model inception

44

3983金沙官网 22

45# resize the frame to have a width of 600 pixels (while

图12:使用ResNet对沉船进行分类

46# maintaining the aspect ratio), and then grab the image

尽管ImageNet中有“boat”这个类别,Inception网络仍然正确地将该场景识别为“ wreck”,精确度96.29%。其它的标签,比如“seashore”, “canoe”, “paddle”,还有“breakwater”,也都相关,在特定的案例中也绝对正确。

47# dimensions

下一个例子是我办公室的长沙发,使用Inception进行分类。

48frame = imutils.resize(frame, width=600)

1$ python classify_image.py --image images/office.png --model inception

49orig = frame.copy()

3983金沙官网 23

50(h, w) = frame.shape[:2]

图13:使用Inception V3分类

51

Inception准确地识别出图中有“table lamp”,精确度69.68%。其它的标签也完全正确:“studio couch”,“window shade”, “lampshade”, 还有“pillow”。

52# construct a blob from the frame, set the input, and then perform a

下面是Xception:

53# forward pass of the network

1$ python classify_image.py --image images/scotch.png --model xception

54blob = cv2.dnn.blobFromImage(frame, 1.0, (w, h),

3983金沙官网 24

55(103.939, 116.779, 123.680), swapRB=False, crop=False)

图14:使用Xception分类

56net.setInput(blob)

Xception将图片正确分类为“barrels”。

57output = net.forward()

最后一个例子使用VGG16:

接着进行后处理并将输出图像展示出来:

1$ python classify_image.py --image images/tv.png --model vgg16

59# reshape the output tensor, add back in the mean subtraction, and

3983金沙官网 25

60# then swap the channel ordering

图15:使用VGG16分类。

61output = output.reshape((3, output.shape[2], output.shape[3]))

图片来自于《巫师3:狂猎》。VGG16的第一个预测值为“home theatre”,前5个预测中还有一个“television/monitor”,预测很合理。

62output[0] = 103.939

正如你从上述例子中所看到的,使用IamgeNet数据集训练的模型能够准确识别大量日常生活中的物品。希望你能在自己的项目中用到这些代码。

63output[1] = 116.779

之后呢?

64output[2] = 123.680

如果你想从头开始训练自己的深度学习网络,该怎么做?

65output /= 255.0

我的新书能够帮助你做到这些,从一个为深度学习的菜鸟成长为专家。

66output = output.transpose(1, 2, 0)

3983金沙官网 26

67

总结

68# show the original frame along with the output neural style

在今天的博客中,我们回顾了5种卷积神经网络:

69# transfer

1.VGG16

70cv2.imshow("Input", frame)

2.VGG19

71cv2.imshow("Output", output)

3.ResNet50

72key = cv2.waitKey(1) & 0xFF

4.Inception V3

对按键的处理:

5.Xception

74# if the `n` key is pressed (for "next"), load the next neural

然后是使用这些架构对你自己的图像进行分类。

75# style transfer model

如果你对深度学习还有卷积神经网络有更多的兴趣,一定要看一看我的新书《Deep Learning for Computer Vision with Python》,现在就可以进行订购。

76ifkey == ord("n"):

博客代码下载。

77# grab the next neural style transfer model model and load it

关于作者:

78(modelID, modelPath) = next(modelIter)

Adrian Rosebrock,企业家兼博士,推出了两个成功的图像搜索引擎:ID My Pill和Chic Engine。

79print("[INFO] {}. {}".format(modelID 1, modelPath))

本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。

80net = cv2.dnn.readNetFromTorch(modelPath)

文章原标题《ImageNet: VGGNet, ResNet, Inception, and Xception with Keras》,作者:Adrian Rosebrock,译者:杨辉,审阅:董昭男,附件为原文的pdf。

81

文章为简译,更为详细的内容,请查看原文

82# otheriwse, if the `q` key was pressed, break from the loop

附件下载:VGGNet, ...[].1494134453.pdf

83elifkey == ord("q"):

84break

85

86# do a bit of cleanup

87cv2.destroyAllWindows()

88vs.stop()

两种不同的按键会对脚本运行产生不同的影响:

  • “n”:抓取下一个模型的路径和ID,并进行下载。如果我们已经获取上一个模型,迭代器就会从头开始循环。
  • “q”:按下q会退出while循环。

实时风格迁移的结果

执行以下命令就可以在视频上运用风格迁移啦:

1$ python neural_style_transfer_video.py --models models

3983金沙官网 27

可以看到,只需要按一个按键就能轻松地进行循环。下面是我自己做的demo视频:

结语

今天的教程是教大家如何用OpenCV和Python在图片和视频上运用神经风格迁移。具体来说,我们用的模型是Johnson等人于2016年提出的,你可以在我提供的链接中下载。希望这篇教程对你有用!

(本文经授权转载自公众号“论智 ID:jqr_AI”)

返回搜狐,查看更多

责任编辑:

本文由3983金沙官网发布,转载请注明来源

关键词: