python的窗口处理模块_python的图像处理模块

除了opencv专门用来进行图像处理,可以进行像素级、特征级、语义级、应用级的图像处理外,python中还有其他库用来进行简单的图像处理,比如图像的读入和保存、滤波、直方图均衡等简单的操作,下面对这些库进行详细的介绍。

目录

一、PIL库

一、安装命令sudo apt-get install python-imaging

二、Image模块

Image模块是在Python PIL图像处理中常见的模块,对图像进行基础操作的功能基本都包含于此模块内。如open、save、conver、show…等功能。

open类Image.open(file) ⇒ image

Image.open(file, mode) ⇒ image

要从文件加载图像,使用 open() 函数, 在 Image 模块:@zhangziju

from PIL import Image ##调用库

im = Image.open("E:\mywife.jpg") ##文件存在的路径

im.show()

需要知道的是在win的环境下im.show的方式为win自带的图像显示应用。打开并确认给定的图像文件。这个是一个懒操作;该函数只会读文件头,而真实的图像数据直到试图处理该数据才会从文件读取(调用load()方法将强行加载图像数据)。如果变量mode被设置,那必须是“r”。用户可以使用一个字符串(表示文件名称的字符串)或者文件对象作为变量file的值。文件对象必须实现read(),seek()和tell()方法,并且以二进制模式打开。

Save类im.save(outfile,options…)

im.save(outfile, format, options…)

若要保存文件,则使用 Image 类的 save() 方法,此时保存文件的文件名就变得十分重要了,除非指定格式,否则这个库将会以文件名的扩展名作为格式保存。使用给定的文件名保存图像。如果变量format缺省,如果可能的话,则从文件名称的扩展名判断文件的格式。该方法返回为空。关键字options为文件编写器提供一些额外的指令。如果编写器不能识别某个选项,它将忽略它。用户可以使用文件对象代替文件名称。在这种情况下,用户必须指定文件格式。文件对象必须实现了seek()、tell()和write()方法,且其以二进制模式打开。如果方法save()因为某些原因失败,这个方法将产生一个异常(通常为IOError异常)。如果发生了异常,该方法也有可能已经创建了文件,并向文件写入了一些数据。如果需要的话,用户的应用程序可以删除这个不完整的文件。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im)

im.save("E:\mywife.png") ## 将"E:\mywife.jpg"保存为"E:\mywife.png"

im = Image.open("E:\mywife.png") ##打开新的png图片

print(im.format, im.size, im.mode)

如下图,在指定路径下可看到新保存的png格式的图片。

三、format类im.format ⇒ string or None

这个属性标识了图像来源,如果图像不是从文件读取它的值就是None。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.format) ## 打印出格式信息

im.show()

如下图可以看到其format为”JPEG”。

四、Mode类im.mode ⇒ string

图像的模式,常见的mode 有 “L” (luminance) 表示灰度图像,“RGB”表示真彩色图像,和 “CMYK” 表示出版图像,表明图像所使用像素格式。如面为常见的nodes描述:modes           描述

1                   1位像素,黑和白,存成8位的像素

L                   8位像素,黑白

P                   8位像素,使用调色板映射到任何其他模式

RGB              3× 8位像素,真彩

RGBA           4×8位像素,真彩+透明通道

CMYK           4×8位像素,颜色隔离

YCbCr           3×8位像素,彩色视频格式

I                     32位整型像素

F                    32位浮点型像素@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.mode) ## 打印出模式信息

im.show()

如下图为图片的mode为“RGB”模式。

五、convert类im.convert(mode)⇒ image

将当前图像转换为其他模式,并且返回新的图像。当从一个调色板图像转换时,这个方法通过这个调色板来转换像素。如果不对变量mode赋值,该方法将会选择一种模式,在没有调色板的情况下,使得图像和调色板中的所有信息都可以被表示出来。当从一个颜色图像转换为黑白图像时,PIL库使用ITU-R601-2 luma转换公式:L = R * 299/1000 + G * 587/1000 + B * 114/1000

当转换为2位图像(模式“1”)时,源图像首先被转换为黑白图像。结果数据中大于127的值被设置为白色,其他的设置为黑色;这样图像会出现抖动。如果要使用其他阈值,更改阈值127,可以使用方法point()。为了去掉图像抖动现象,可以使用dither选项。from PIL import Image

im = Image.open("E:\mywife.jpg")

new_im = im.convert('P')

print(new_im.mode)

new_im.show()

如下,将图像转换为“P”模式。

对比原始图像。

这里写图片描述im.convert(“P”,**options) ⇒ image

这个与第一个方法定义一样,但是当“RGB”图像转换为8位调色板图像时能更好的处理。可供选择的选项为:

Dither=. 控制颜色抖动。默认是FLOYDSTEINBERG,与邻近的像素一起承担错误。不使能该功能,则赋值为NONE。

Palette=. 控制调色板的产生。默认是WEB,这是标准的216色的“web palette”。要使用优化的调色板,则赋值为ADAPTIVE。

Colors=. 当选项palette为ADAPTIVE时,控制用于调色板的颜色数目。默认是最大值,即256种颜色im.convert(mode,matrix) ⇒ image

使用转换矩阵将一个“RGB”图像转换为“L”或者“RGB”图像。变量matrix为4或者16元组。from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.mode)

rgb2xyz = (0.412453,0.357580, 0.180423, 0,

0.212671,0.715160, 0.072169, 0,

0.019334,0.119193, 0.950227, 0 )

new_im = im.convert("L", rgb2xyz)

print(new_im.mode)

new_im.show()

转换后效果

这里写图片描述

Size类

六、size类im.size ⇒ (width, height)

图像的尺寸,按照像素数计算,它的返回值为宽度和高度的二元组(width, height)。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size) ## 打印出尺寸信息

im.show()

如下图所示为图片的尺寸信息,750*560。

七、Palette类im.palette ⇒ palette or None

颜色调色板表格。如果图像的模式是“P”,则返回ImagePalette类的实例;否则,将为None。

如下为对非“P”模式下的图像进行palette信息显示。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.palette)

易知,返回值为空,none

对图像进行convert操作,转换成“P”模式@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

new_im = im.convert('P')

print(new_im.mode)

print(new_im.palette)

则返回值为ImagePalette类的实例。如下:

八、Info类im.info ⇒ dictionary

存储图像相关数据的字典。文件句柄使用该字典传递从文件中读取的各种非图像信息。大多数方法在返回新的图像时都会忽略这个字典;因为字典中的键并非标准化的,对于一个方法,它不能知道自己的操作如何影响这个字典。如果用户需要这些信息,需要在方法open()返回时保存这个字典。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.info)

九、new类Image.new(mode,size) ⇒ image

Image.new(mode, size,color) ⇒ image

使用给定的变量mode和size生成新的图像。Size是给定的宽/高二元组,这是按照像素数来计算的。对于单通道图像,变量color只给定一个值;对于多通道图像,变量color给定一个元组(每个通道对应一个值)。在版本1.1.4及其之后,用户也可以用颜色的名称,比如给变量color赋值为“red”。如果没有对变量color赋值,图像内容将会被全部赋值为0(为黑色)。如果变量color是空,图像将不会被初始化,即图像的内容全为0。这对向该图像复制或绘制某些内容是有用的。

如下为将图像设置为128x128大小的红色图像。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

n_im= Image.new("RGB", (128, 128), "#FF0000")

n_im.show()

显示效果如下:

如下图像为128x128大小的黑色图像,因为变量color不赋值的话,图像内容被设置为0,即黑色。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

n_im= Image.new("RGB", (128, 128))

n_im.show()

黑图像为128x128大小的绿色图像。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

n_im= Image.new("RGB", (128, 128),"green")

n_im.show()

十、Copy 类im.copy() ⇒ image

拷贝这个图像。如果用户想粘贴一些数据到这张图,可以使用这个方法,但是原始图像不会受到影响。from PIL import Image

im = Image.open("E:\mywife.jpg")

im_copy = im.copy()

图像im_copy和im完全一样。

十一、Crop类im.crop(box) ⇒ image

从当前的图像中返回一个矩形区域的拷贝。变量box是一个四元组,定义了左、上、右和下的像素坐标。用来表示在原始图像中截取的位置坐标,如box(100,100,200,200)就表示在原始图像中以左上角为坐标原点,截取一个100*100(像素为单位)的图像,为方便理解,如下为示意图box(b1,a1,b2,a2)。作图软件为Visio2016。这是一个懒操作。对源图像的改变可能或者可能不体现在裁减下来的图像中。为了获取一个分离的拷贝,对裁剪的拷贝调用方法load()。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

box = (300, 100, 700, 700) ##确定拷贝区域大小

region = im.crop(box) ##将im表示的图片对象拷贝到region中,大小为box

region.show()

如下图为box截取的图像区域显示。

十二、Paste类im.paste(image,box)

将一张图粘贴到另一张图像上。变量box或者是一个给定左上角的2元组,或者是定义了左,上,右和下像素坐标的4元组,或者为空(与(0,0)一样)。如果给定4元组,被粘贴的图像的尺寸必须与区域尺寸一样。如果模式不匹配,被粘贴的图像将被转换为当前图像的模式。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

box=[0,0,100,100]

im_crop = im.crop(box)

print(im_crop.size,im_crop.mode)

im.paste(im_crop, (100,100)) ##(100,100,0,0)

im.paste(im_crop, (400,400,500,500))

im.show()

如下图为paste操作:

十三、Filter类im.filter(filter) ⇒ image

返回一个使用给定滤波器处理过的图像的拷贝。具体参考图像滤波在ImageFilter 模块的应用,在该模块中,预先定义了很多增强滤波器,可以通过filter( )函数使用,预定义滤波器包括:BLUR、CONTOUR、DETAIL、EDGE_ENHANCE、EDGE_ENHANCE_MORE、EMBOSS、FIND_EDGES、SMOOTH、SMOOTH_MORE、SHARPEN。其中BLUR就是均值滤波,CONTOUR找轮廓,FIND_EDGES边缘检测,使用该模块时,需先导入。@zhangziju

from PIL import Image

from PIL import ImageFilter ## 调取ImageFilter

imgF = Image.open("E:\mywife.jpg")

bluF = imgF.filter(ImageFilter.BLUR) ##均值滤波

conF = imgF.filter(ImageFilter.CONTOUR) ##找轮廓

edgeF = imgF.filter(ImageFilter.FIND_EDGES) ##边缘检测

imgF.show()

bluF.show()

conF.show()

edgeF.show()

滤波处理下的gakki~

十四、Blend类Image.blend(image1,image2, alpha) ⇒ image

使用给定的两张图像及透明度变量alpha,插值出一张新的图像。这两张图像必须有一样的尺寸和模式。合成公式为:out = image1 (1.0 - alpha) + image2 alpha

若变量alpha为0.0,返回第一张图像的拷贝。若变量alpha为1.0,将返回第二张图像的拷贝。对变量alpha的值无限制。@zhangziju

from PIL import Image

im1 = Image.open("E:\mywife.jpg")

im2 = Image.open("E:\mywife2.jpg")

print(im1.mode,im1.size)

print(im2.mode,im2.size)

im = Image.blend(im1, im2, 0.2)

im.show()

需保证两张图像的模式和大小是一致的,如下为显示im1和im2的具体信息。

im1和im2按照第一张80%的透明度,第二张20%的透明度,合成为一张。

十五、Splitim.split() ⇒ sequence

返回当前图像各个通道组成的一个元组。例如,分离一个“RGB”图像将产生三个新的图像,分别对应原始图像的每个通道(红,绿,蓝)。from PIL import Image

im = Image.open("E:\mywife.jpg")

r,g,b = im.split()

print(r.mode)

print(r.size)

print(im.size)

十六、Composite类Image.composite(image1,image2, mask) ⇒ image

复合类使用给定的两张图像及mask图像作为透明度,插值出一张新的图像。变量mask图像的模式可以为“1”,“L”或者“RGBA”。所有图像必须有相同的尺寸。@zhangziju

from PIL import Image

im1 = Image.open("E:\mywife.jpg")

im2 = Image.open("E:\mywife2.jpg")

r,g,b = im1.split() ##分离出r,g,b

print(b.mode)

print(im1.mode,im1.size)

print(im2.mode,im2.size)

im = Image.composite(im1,im2,b)

im.show()

十七、Eval类Image.eval(image,function) ⇒ image

使用变量function对应的函数(该函数应该有一个参数)处理变量image所代表图像中的每一个像素点。如果变量image所代表图像有多个通道,那变量function对应的函数作用于每一个通道。注意:变量function对每个像素只处理一次,所以不能使用随机组件和其他生成器。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

def fun01(x):

return x*0.3

def fun02(y):

return y*2.0

im1_eval = Image.eval(im, fun01)

im2_eval = Image.eval(im, fun02)

im1_eval.show()

im2_eval.show()

在函数fun01和fun02下的图像显示。

十八、Merge类Image.merge(mode,bands) ⇒ image

合并类使用一些单通道图像,创建一个新的图像。变量bands为一个图像的元组或者列表,每个通道的模式由变量mode描述。所有通道必须有相同的尺寸。

变量mode与变量bands的关系:len(ImageMode.getmode(mode).bands)= len(bands)@zhangziju

from PIL import Image

im1 = Image.open("E:\mywife.jpg")

im2 = Image.open("E:\mywife2.jpg")

r1,g1,b1 = im1.split()

r2,g2,b2 = im2.split()

print(r1.mode,r1.size,g1.mode,g1.size)

print(r2.mode,r2.size,g2.mode,g2.size)

new_im=[r1,g2,b2]

print(len(new_im))

im_merge = Image.merge("RGB",new_im)

im_merge.show()

打印信息显示

merge操作

十九、Draft类im.draft(mode,size)

配置图像文件加载器,使得返回一个与给定的模式和尺寸尽可能匹配的图像的版本。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size,im.mode)

new_im = im.draft("L", (200,200))

print(new_im.size,new_im.mode)

new_im.show()

关键信息显示

转换效果

二十、Getbands类im.getbands()⇒ tuple of strings

返回包括每个通道名称的元组。例如,对于RGB图像将返回(“R”,“G”,“B”)。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.getbands())

这里写图片描述

二十一、Getbbox类im.getbbox() ⇒ 4-tuple or None

计算图像非零区域的包围盒。这个包围盒是一个4元组,定义了左、上、右和下像素坐标。如果图像是空的,这个方法将返回空。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.getbbox())

二十二、Getdata类im.getdata() ⇒ sequence

以包含像素值的sequence对象形式返回图像的内容。这个sequence对象是扁平的,以便第一行的值直接跟在第零行的值后面,等等。这个方法返回的sequence对象是PIL内部数据类型,它只支持某些sequence操作,包括迭代和基础sequence访问。使用list(im.getdata()),将它转换为普通的sequence。Sequence对象的每一个元素对应一个像素点的R、G和B三个值。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

sequ = im.getdata()

sequ0 = list(sequ)

print(sequ0[0])

print(sequ0[1])

print(sequ0[2])

可视化显示sequence0里面的数据。

打印显示结果,与前面对比。

二十三、Getextrema类im.getextrema() ⇒ 2-tuple

返回一个2元组,包括该图像中的最小和最大值。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.getextrema())

该方法返回了R/G/B三个通道的最小和最大值的2元组。

二十四、Getpixel类im.getpixel(xy) ⇒ value or tuple

返回给定位置的像素值。如果图像为多通道,则返回一个元组。该方法执行比较慢;如果用户需要使用python处理图像中较大部分数据,可以使用像素访问对象(见load),或者方法getdata()。@zahngziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.getpixel((0,0)))

print(im.getpixel((4,0)))

r,g,b = im.split()

print(b.getpixel((11,8)))

二十五、Histogram类im.histogram()⇒ list

返回一个图像的直方图。这个直方图是关于像素数量的list,图像中的每个象素值对应一个成员。如果图像有多个通道,所有通道的直方图会连接起来(例如,“RGB”图像的直方图有768个值)。二值图像(模式为“1”)当作灰度图像(模式为“L”)处理。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

imhis = im.histogram()

print(len(imhis))

print(imhis[0])

print(imhis[150])

print(imhis[300])im.histogram(mask)⇒ list

返回图像中模板图像非零地方的直方图。模板图像与处理图像的尺寸必须相同,并且要么是二值图像(模式为“1”),要么为灰度图像(模式为“L”)。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

r,g,b = im.split()

imhis = im.histogram()

print(r.mode)

print(len(imhis))

print(imhis[0])

print(imhis[150])

print(imhis[300])

二十六、Load类im.load()

为图像分配内存并从文件中加载它(或者从源图像,对于懒操作)。正常情况下,用户不需要调用这个方法,因为在第一次访问图像时,Image类会自动地加载打开的图像。在1.1.6及以后的版本,方法load()返回一个用于读取和修改像素的像素访问对象。这个访问对象像一个二维队列,如:pix = im.load()

print pix[x, y]

pix[x, y] =value

通过这个对象访问比方法getpixel()和putpixel()快很多。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

pix = im.load()

print(pix[0,2])im.paste(colour,box)

使用同一种颜色填充变量box对应的区域。对于单通道图像,变量colour为单个颜色值;对于多通道,则为一个元组。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

im.paste((256,256,0),(0,0,100,100)) ##(256,256,0)表示黄色

im.show()@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

im.paste("blue",(0,0,100,100)) ##或者“blue”

im.show()im.paste(image,box, mask)

使用变量mask对应的模板图像来填充所对应的区域。可以使用模式为“1”、“L”或者“RGBA”的图像作为模板图像。模板图像的尺寸必须与变量image对应的图像尺寸一致。如果变量mask对应图像的值为255,则模板图像的值直接被拷贝过来;如果变量mask对应图像的值为0,则保持当前图像的原始值。变量mask对应图像的其他值,将对两张图像的值进行透明融合,如果变量image对应的为“RGBA”图像,即粘贴的图像模式为“RGBA”,则alpha通道被忽略。用户可以使用同样的图像作为原图像和模板图像。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

box=[300,300,400,400]

im_crop =im.crop(box)

r,g,b =im_crop.split()

im.paste(im_crop, (200,200,300,300), r)

im.show()

二十七、Putdata类im.putdata(data)

im.putdata(data, scale, offset)

从sequence对象中拷贝数据到当前图像,从图像的左上角(0,0)位置开始。变量scale和offset用来调整sequence中的值:pixel = value*scale + offset

如果变量scale忽略,则默认为1.0。如果变量offset忽略,则默认为0.0。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

r, g, b = im.split()

print(

r.getpixel((0, 0)),

r.getpixel((1, 0)),

r.getpixel((2, 0)),

r.getpixel((3, 0)),

r.putdata([1, 2, 3, 4]),

r.getpixel((0, 0)),

r.getpixel((1, 0)),

r.getpixel((2, 0)),

r.getpixel((3, 0)),

)

二十八、Resize类im.resize(size) ⇒ image

im.resize(size, filter) ⇒ image

返回改变尺寸的图像的拷贝。变量size是所要求的尺寸,是一个二元组:(width, height)。变量filter为NEAREST、BILINEAR、BICUBIC或者ANTIALIAS之一。如果忽略,或者图像模式为“1”或者“P”,该变量设置为NEAREST。在当前的版本中bilinear和bicubic滤波器不能很好地适应大比例的下采样(例如生成缩略图)。用户需要使用ANTIALIAS,除非速度比质量更重要。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

region = im.resize((400, 400)) ##重新设定大小

region.show()

很明显由于大小的重新设定,图片的显示效果有所转变,gakki依然美腻~

二十九、Rotate类im.rotate(angle) ⇒ image

im.rotate(angle,filter=NEAREST, expand=0) ⇒ image

返回一个按照给定角度顺时钟围绕图像中心旋转后的图像拷贝。变量filter是NEAREST、BILINEAR或者BICUBIC之一。如果省略该变量,或者图像模式为“1”或者“P”,则默认为NEAREST。变量expand,如果为true,表示输出图像足够大,可以装载旋转后的图像。如果为false或者缺省,则输出图像与输入图像尺寸一样大。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

im_45 = im.rotate(45)

im_30 = im.rotate(30, Image.NEAREST,1)

print(im_45.size,im_30.size)

im_45.show()

im_30.show()

三十、Seek类im.seek(frame)

在给定的文件序列中查找指定的帧。如果查找超越了序列的末尾,则产生一个EOFError异常。当文件序列被打开时,PIL库自动指定到第0帧上。@zhangziju

from PIL import Image

im_gif = Image.open("E:\mywife.gif")

print(im_gif.mode)

im_gif.show() ##第0帧

im_gif.seek(3)

im_gif.show()

im_gif.seek(9)

im_gif.show()

来来来~这是gakki原图欣赏下~

这里写图片描述

查找帧seek()的效果如下:

三十一、Tell类im.tell() ⇒ integer

返回当前帧所处位置,从0开始计算。@zhangziju

from PIL import Image

im_gif = Image.open("E:\mywife.gif")

print(im_gif.tell())

im_gif.seek(8)

print(im_gif.tell())

三十二、Thumbnail类im.thumbnail(size)

im.thumbnail(size, filter)

修改当前图像,使其包含一个自身的缩略图,该缩略图尺寸不大于给定的尺寸。这个方法会计算一个合适的缩略图尺寸,使其符合当前图像的宽高比,调用方法draft()配置文件读取器,最后改变图像的尺寸。变量filter应该是NEAREST、BILINEAR、BICUBIC或者ANTIALIAS之一。如果省略该变量,则默认为NEAREST。注意:在当前PIL的版本中,滤波器bilinear和bicubic不能很好地适应缩略图产生。用户应该使用ANTIALIAS,图像质量最好。如果处理速度比图像质量更重要,可以选用其他滤波器。这个方法在原图上进行修改。如果用户不想修改原图,可以使用方法copy()拷贝一个图像。这个方法返回空。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

im.thumbnail((100,100))

三十三、Transform类im.transform(size,method, data) ⇒ image

im.transform(size, method, data, filter) ⇒ image

使用给定的尺寸生成一张新的图像,与原图有相同的模式,使用给定的转换方式将原图数据拷贝到新的图像中。在当前的PIL版本中,参数method为EXTENT(裁剪出一个矩形区域),AFFINE(仿射变换),QUAD(将正方形转换为矩形),MESH(一个操作映射多个正方形)或者PERSPECTIVE。变量filter定义了对原始图像中像素的滤波器。在当前的版本中,变量filter为NEAREST、BILINEAR、BICUBIC或者ANTIALIAS之一。如果忽略,或者图像模式为“1”或者“P”,该变量设置为NEAREST。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size)

imtra = im.transform((200, 200), Image.EXTENT, (0, 0, 500, 500))

print(imtra.size)

imtra.show()im.transform(size,EXTENT, data) ⇒ image

im.transform(size, EXTENT, data, filter) ⇒ image

从图像中裁剪一个区域。变量data为指定输入图像中两个坐标点的4元组(x0,y0,x1,y1)。输出图像为这两个坐标点之间像素的采样结果。例如,如果输入图像的(x0,y0)为输出图像的(0,0)点,(x1,y1)则与变量size一样。这个方法可以用于在当前图像中裁剪,放大,缩小或者镜像一个任意的长方形。它比方法crop()稍慢,但是与resize操作一样快。im.transform(size, AFFINE, data) ⇒ image

im.transform(size, AFFINE,data, filter) ⇒ image

对当前的图像进行仿射变换,变换结果体现在给定尺寸的新图像中。变量data是一个6元组(a,b,c,d,e,f),包含一个仿射变换矩阵的第一个两行。输出图像中的每一个像素(x,y),新值由输入图像的位置(ax+by+c, dx+ey+f)的像素产生,使用最接近的像素进行近似。这个方法用于原始图像的缩放、转换、旋转和裁剪。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size)

imtra = im.transform((200, 200), Image.AFFINE, (1,2,3,2,1,4))

print(imtra.size)

imtra.show()im.transform(size,QUAD, data) ⇒ image

im.transform(size, QUAD, data, filter) ⇒ image

输入图像的一个四边形(通过四个角定义的区域)映射到给定尺寸的长方形。变量data是一个8元组(x0,y0,x1,y1,x2,y2,x3,y3),它包括源四边形的左上,左下,右下和右上四个角。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size)

imtra = im.transform((200, 200), Image.QUAD, (0,0,0,500,600,500,600,0))

print(imtra.size)

imtra.show()im.transform(size,PERSPECTIVE, data) ⇒ image

im.transform(size, PERSPECTIVE, data, filter) ⇒ image

对当前图像进行透视变换,产生给定尺寸的新图像。变量data是一个8元组(a,b,c,d,e,f,g,h),包括一个透视变换的系数。对于输出图像中的每个像素点,新的值来自于输入图像的位置的(a x + b y + c)/(g x + h y + 1), (d x+ e y + f)/(g x + h y + 1)像素,使用最接近的像素进行近似。这个方法用于原始图像的2D透视。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

print(im.size)

imtra = im.transform((200, 200), Image.PERSPECTIVE, (1,2,3,2,1,6,1,2))

print(imtra.size)

imtra.show()

wocao!!!gakki不见了!!!

三十四、Transpose类im.transpose(method)⇒ image

返回当前图像的翻转或者旋转的拷贝。变量method的取值为:FLIP_LEFT_RIGHT,FLIP_TOP_BOTTOM,ROTATE_90,ROTATE_180,或ROTATE_270。@zhangziju

from PIL import Image

im = Image.open("E:\mywife.jpg")

im.rotate(45) #逆时针旋转 45 度角。

im.transpose(Image.FLIP_LEFT_RIGHT) #左右对换。

im.transpose(Image.FLIP_TOP_BOTTOM) #上下对换。

im.transpose(Image.ROTATE_90) #旋转 90 度角。

im.transpose(Image.ROTATE_180) #旋转 180 度角。

im.transpose(Image.ROTATE_270) #旋转 270 度角。

二、skimage库

基于python脚本语言开发的数字图片处理包,比如PIL,Pillow, opencv, scikit-image等。PIL和Pillow只提供最基础的数字图像处理,功能有限;opencv实际上是一个c++库,只是提供了python接口,更新速度非常慢。scikit-image是基于scipy的一款图像处理包,它将图片作为numpy数组进行处理,正好与matlab一样,因此,我们最终选择scikit-image进行数字图像处理。

Image读出来的是PIL的类型,而skimage.io读出来的数据是numpy格式的import Image as img

import os

from matplotlib import pyplot as plot

from skimage import io,transform

#Image和skimage读图片

img_file1 = img.open('./CXR_png/MCUCXR_0042_0.png')

img_file2 = io.imread('./CXR_png/MCUCXR_0042_0.png')

输出可以看出Img读图片的大小是图片的(width, height);而skimage的是(height,width, channel), [这也是为什么caffe在单独测试时要要在代码中设置:transformer.set_transpose('data',(2,0,1)),因为caffe可以处理的图片的数据格式是(channel,height,width),所以要转换数据]#读图片后数据的大小:

print "the picture's size: ", img_file1.size

print "the picture's shape: ", img_file2.shapethe picture's size: (4892, 4020)

the picture's shape: (4020, 4892)#得到像素:

print(img_file1.getpixel((500,1000)), img_file2[500][1000])

print(img_file1.getpixel((500,1000)), img_file2[1000][500])

print(img_file1.getpixel((1000,500)), img_file2[500][1000])(0, 139)

(0, 0)

(139, 139)

Img读出来的图片获得某点像素用getpixel((w,h))可以直接返回这个点三个通道的像素值

skimage读出来的图片可以直接img_file2[0][0]获得,但是一定记住它的格式,并不是你想的(channel,height,width)

一、图片信息

如果我们想知道一些skimage图片信息from skimage import io, data

img = data.chelsea()

io.imshow(img)

print(type(img)) #显示类型

print(img.shape) #显示尺寸

print(img.shape[0]) #图片高度

print(img.shape[1]) #图片宽度

print(img.shape[2]) #图片通道数

print(img.size) #显示总像素个数

print(img.max()) #最大像素值

print(img.min()) #最小像素值

print(img.mean()) #像素平均值

print(img[0][0])#图像的像素值

PIL image 查看图片信息,可用如下的方法print type(img)

print img.size #图片的尺寸

print img.mode #图片的模式

print img.format #图片的格式

print(img.getpixel((0,0)))#得到像素:

#img读出来的图片获得某点像素用getpixel((w,h))可以直接返回这个点三个通道的像素值# 获取图像的灰度值范围

width = img.size[0]

height = img.size[1]

# 输出图片的像素值

count = 0

for i in range(0, width):

for j in range(0, height):

if img.getpixel((i, j))>=0 and img.getpixel((i, j))<=255:

count +=1

print count

print(height*width)

skimage提供了io模块,顾名思义,这个模块是用来图片输入输出操作的。为了方便练习,也提供一个data模块,里面嵌套了一些示例图片,我们可以直接使用。

二、skimage包的子模块

skimage包的全称是scikit-image SciKit (toolkit for SciPy) ,它对scipy.ndimage进行了扩展,提供了更多的图片处理功能。它是由python语言编写的,由scipy 社区开发和维护。skimage包由许多的子模块组成,各个子模块提供不同的功能。主要子模块列表如下:子模块名称  主要实现功能

io 读取、保存和显示图片或视频

data 提供一些测试图片和样本数据

color 颜色空间变换

filters 图像增强、边缘检测、排序滤波器、自动阈值等

draw 操作于numpy数组上的基本图形绘制,包括线条、矩形、圆和文本等

transform 几何变换或其它变换,如旋转、拉伸和拉东变换等

morphology 形态学操作,如开闭运算、骨架提取等

exposure 图片强度调整,如亮度调整、直方图均衡等

feature 特征检测与提取等

measure 图像属性的测量,如相似性或等高线等

segmentation 图像分割

restoration 图像恢复

util 通用函数

从外部读取图片并显示

读取单张彩色rgb图片,使用skimage.io.imread(fname)函数,带一个参数,表示需要读取的文件路径。显示图片使用skimage.io.imshow(arr)函数,带一个参数,表示需要显示的arr数组(读取的图片以numpy数组形式计算)。from skimage import io

img=io.imread('d:/dog.jpg')

io.imshow(img)

读取单张灰度图片,使用skimage.io.imread(fname,as_grey=True)函数,第一个参数为图片路径,第二个参数为as_grey, bool型值,默认为Falsefrom skimage import io

img=io.imread('d:/dog.jpg',as_grey=True)

io.imshow(img)

skimage程序自带了一些示例图片,如果我们不想从外部读取图片,就可以直接使用这些示例图片:astronaut 航员图片 coffee 一杯咖啡图片

lena lena美女图片 camera 拿相机的人图片

coins 硬币图片 moon 月亮图片

checkerboard 棋盘图片 horse 马图片

page 书页图片 chelsea 小猫图片

hubble_deep_field 星空图片 text 文字图片

clock 时钟图片 immunohistochemistry 结肠图片

显示这些图片可用如下代码,不带任何参数from skimage import io, data

img=data.lena()

io.imshow(img)

图片名对应的就是函数名,如camera图片对应的函数名为camera(). 这些示例图片存放在skimage的安装目录下面,路径名称为data_dir,我们可以将这个路径打印出来看看from skimage import data_dir

print(data_dir)

保存图片

使用io模块的imsave(fname,arr)函数来实现。第一个参数表示保存的路径和名称,第二个参数表示需要保存的数组变量。from skimage import io,data

img=data.chelsea()

io.imshow(img)

io.imsave('d:/cat.jpg',img)

保存图片的同时也起到了转换格式的作用。如果读取时图片格式为jpg图片,保存为png格式,则将图片从jpg图片转换为png图片并保存。

图片信息

如果我们想知道一些图片信息from skimage import io, data

img = data.chelsea()

io.imshow(img)

print(type(img)) #显示类型

print(img.shape) #显示尺寸

print(img.shape[0]) #图片高度

print(img.shape[1]) #图片宽度

print(img.shape[2]) #图片通道数

print(img.size) #显示总像素个数

print(img.max()) #最大像素值

print(img.min()) #最小像素值

print(img.mean()) #像素平均值

print(img[0][0])#图像的像素值

三、图像像素的访问与裁剪

图片读入程序中后,是以numpy数组存在的。因此对numpy数组的一切功能,对图片也适用。对数组元素的访问,实际上就是对图片像素点的访问。

彩色图片访问方式为:img[i,j,c]

i表示图片的行数,j表示图片的列数,c表示图片的通道数(RGB三通道分别对应0,1,2)。坐标是从左上角开始。

灰度图片访问方式为:gray[i,j]

例1:输出小猫图片的G通道中的第20行30列的像素值from skimage import io,data

img=data.chelsea()

pixel=img[20,30,1]

print(pixel)

例2:显示红色单通道图片from skimage import io,data

img=data.chelsea()

R=img[:,:,0]

io.imshow(R)

除了对像素进行读取,也可以修改像素值。

例3:对小猫图片随机添加椒盐噪声from skimage import io,data

import numpy as np

img=data.chelsea()

#随机生成5000个椒盐

rows,cols,dims=img.shape

for i in range(5000):

x=np.random.randint(0,rows)

y=np.random.randint(0,cols)

img[x,y,:]=255

io.imshow(img)

这里用到了numpy包里的random来生成随机数,randint(0,cols)表示随机生成一个整数,范围在0到cols之间。用img[x,y,:]=255这句来对像素值进行修改,将原来的三通道像素值,变为255。通过对数组的裁剪,就可以实现对图片的裁剪。

例4:对小猫图片进行裁剪from skimage import io,data

img=data.chelsea()

roi=img[80:180,100:200,:]

io.imshow(roi)

对多个像素点进行操作,使用数组切片方式访问。切片方式返回的是以指定间隔下标访问 该数组的像素值。下面是有关灰度图像的一些例子:img[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行

img[:,i] = 100 # 将第 i 列的所有数值设为 100

img[:100,:50].sum() # 计算前 100 行、前 50 列所有数值的和

img[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)

img[i].mean() # 第 i 行所有数值的平均值

img[:,-1] # 最后一列

img[-2,:] (or im[-2]) # 倒数第二行

最后我们再看两个对像素值进行访问和改变的例子:

例5:将lena图片进行二值化,像素值大于128的变为1,否则变为0from skimage import io,data,color

img=data.lena()

img_gray=color.rgb2gray(img)

rows,cols=img_gray.shape

for i in range(rows):

for j in range(cols):

if (img_gray[i,j]<=0.5):

img_gray[i,j]=0

else:

img_gray[i,j]=1

io.imshow(img_gray)

使用了color模块的rgb2gray()函数,将彩色三通道图片转换成灰度图。转换结果为float64类型的数组,范围为[0,1]之间。将彩色三通道图片转换成灰度图,最后变成unit8, float转换为unit8是有信息损失的。img_path = 'data/dpclassifier/newtrain/test/1_0.png'

import Image as img

import os

from matplotlib import pyplot as plot

from skimage import io,transform, img_as_ubyte

img_file1 = img.open(img_path)

img_file1.show()

img_file2 = io.imread(img_path)

io.imshow(img_file2)

print(type(img_file1),img_file1.mode, type(img_file2),img_file2.shape, img_file2.dtype,img_file2.max(),img_file2.min(),img_file2.mean())

img_file22=skimage.color.rgb2gray(img_file2)

print(type(img_file22),img_file22.shape,img_file22.dtype,img_file22.max(),img_file22.min(),img_file22.mean() )

dst=img_as_ubyte(img_file22)

print(type(dst),dst.shape,dst.dtype, dst.max(), dst.min(), dst.mean())

结果(, 'RGB', , (420, 512, 3), dtype('uint8'), 255, 0, 130.9983863467262)

(, (420, 512), dtype('float64'), 1.0, 0.0, 0.5137191621440242)

(, (420, 512), dtype('uint8'), 255, 0, 130.9983863467262)

例6:from skimage import io,data

img=data.chelsea()

reddish = img[:, :, 0] >170

img[reddish] = [0, 255, 0]

io.imshow(img)

这个例子先对R通道的所有像素值进行判断,如果大于170,则将这个地方的像素值变为[0,255,0], 即G通道值为255,R和B通道值为0。

四、图像数据类型及颜色空间转换

在skimage中,一张图片就是一个简单的numpy数组,数组的数据类型有很多种,相互之间也可以转换。这些数据类型及取值范围如下表所示:Data type Range

uint8 0 to 255

uint16 0 to 65535

uint32 0 to 232

float -1 to 1 or 0 to 1

int8 -128 to 127

int16 -32768 to 32767

int32 -231 to 231 - 1

一张图片的像素值范围是[0,255], 因此默认类型是unit8, 可用如下代码查看数据类型from skimage import io,data

img=data.chelsea()

print(img.dtype.name)

在上面的表中,特别注意的是float类型,它的范围是[-1,1]或[0,1]之间。一张彩色图片转换为灰度图后,它的类型就由unit8变成了float

1、unit8转floatfrom skimage import data,img_as_float

img=data.chelsea()

print(img.dtype.name)

dst=img_as_float(img)

print(dst.dtype.name)

2、float转uint8from skimage import img_as_ubyte

import numpy as np

img = np.array([0, 0.5, 1], dtype=float)

print(img.dtype.name)

dst=img_as_ubyte(img)

print(dst.dtype.name)

float转为unit8,有可能会造成数据的损失,因此会有警告提醒。除了这两种最常用的转换以外,其实有一些其它的类型转换,如下表:Function name Description

img_as_float Convert to 64-bit floating point.

img_as_ubyte Convert to 8-bit uint.

img_as_uint Convert to 16-bit uint.

img_as_int Convert to 16-bit int.

如前所述,除了直接转换可以改变数据类型外,还可以通过图像的颜色空间转换来改变数据类型。常用的颜色空间有灰度空间、rgb空间、hsv空间和cmyk空间。颜色空间转换以后,图片类型都变成了float型。所有的颜色空间转换函数,都放在skimage的color模块内。

例:rgb转灰度图from skimage import io,data,color

img=data.lena()

gray=color.rgb2gray(img)

io.imshow(gray)

其它的转换,用法都是一样的,列举常用的如下:skimage.color.rgb2grey(rgb)

skimage.color.rgb2hsv(rgb)

skimage.color.rgb2lab(rgb)

skimage.color.gray2rgb(image)

skimage.color.hsv2rgb(hsv)

skimage.color.lab2rgb(lab)

实际上,上面的所有转换函数,都可以用一个函数来代替skimage.color.convert_colorspace(arr, fromspace, tospace)

表示将arr从fromspace颜色空间转换到tospace颜色空间。

例:rgb转hsvfrom skimage import io,data,color

img=data.lena()

hsv=color.convert_colorspace(img,'RGB','HSV')

io.imshow(hsv)

在color模块的颜色空间转换函数中,还有一个比较有用的函数是

skimage.color.label2rgb(arr), 可以根据标签值对图片进行着色。以后的图片分类后着色就可以用这个函数。

例:将lena图片分成三类,然后用默认颜色对三类进行着色from skimage import io,data,color

import numpy as np

img=data.lena()

gray=color.rgb2gray(img)

rows,cols=gray.shape

labels=np.zeros([rows,cols])

for i in range(rows):

for j in range(cols):

if(gray[i,j]<0.4):

labels[i,j]=0

elif(gray[i,j]<0.75):

labels[i,j]=1

else:

labels[i,j]=2

dst=color.label2rgb(labels)

io.imshow(dst)

五、图像的绘制

实际上前面我们就已经用到了图像的绘制,如:io.imshow(img)

这一行代码的实质是利用matplotlib包对图片进行绘制,绘制成功后,返回一个matplotlib类型的数据。因此,我们也可以这样写:import matplotlib.pyplot as plt

plt.imshow(img)

imshow()函数格式为:matplotlib.pyplot.imshow(X, cmap=None)

X: 要绘制的图像或数组。cmap: 颜色图谱(colormap), 默认绘制为RGB(A)颜色空间。其它可选的颜色图谱如下列表:颜色图谱 描述

autumn 红-橙-黄

bone 黑-白,x线

cool 青-洋红

copper 黑-铜

flag 红-白-蓝-黑

gray 黑-白

hot 黑-红-黄-白

hsv hsv颜色空间, 红-黄-绿-青-蓝-洋红-红

inferno 黑-红-黄

jet 蓝-青-黄-红

magma 黑-红-白

pink 黑-粉-白

plasma 绿-红-黄

prism 红-黄-绿-蓝-紫-...-绿模式

spring 洋红-黄

summer 绿-黄

viridis 蓝-绿-黄

winter 蓝-绿

用的比较多的有gray,jet等,如:plt.imshow(image,plt.cm.gray)

plt.imshow(img,cmap=plt.cm.jet)

在窗口上绘制完图片后,返回一个AxesImage对象。要在窗口上显示这个对象,我们可以调用show()函数来进行显示,但进行练习的时候(ipython环境中),一般我们可以省略show()函数,也能自动显示出来。from skimage import io,data

img=data.astronaut()

dst=io.imshow(img)

print(type(dst))

io.show()

可以看到,类型是'matplotlib.image.AxesImage'。显示一张图片,我们通常更愿意这样写:import matplotlib.pyplot as plt

from skimage import io,data

img=data.astronaut()

plt.imshow(img)

plt.show()

matplotlib是一个专业绘图的库,相当于matlab中的plot,可以设置多个figure窗口,设置figure的标题,隐藏坐标尺,甚至可以使用subplot在一个figure中显示多张图片。一般我们可以这样导入matplotlib库:import matplotlib.pyplot as plt

也就是说,我们绘图实际上用的是matplotlib包的pyplot模块。用figure函数和subplot函数分别创建主窗口与子图。分开并同时显示宇航员图片的三个通道from skimage import data

import matplotlib.pyplot as plt

img=data.astronaut()

plt.figure(num='astronaut',figsize=(8,8)) #创建一个名为astronaut的窗口,并设置大小

plt.subplot(2,2,1) #将窗口分为两行两列四个子图,则可显示四幅图片

plt.title('origin image') #第一幅图片标题

plt.imshow(img) #绘制第一幅图片

plt.subplot(2,2,2) #第二个子图

plt.title('R channel') #第二幅图片标题

plt.imshow(img[:,:,0],plt.cm.gray) #绘制第二幅图片,且为灰度图

plt.axis('off') #不显示坐标尺寸

plt.subplot(2,2,3) #第三个子图

plt.title('G channel') #第三幅图片标题

plt.imshow(img[:,:,1],plt.cm.gray) #绘制第三幅图片,且为灰度图

plt.axis('off') #不显示坐标尺寸

plt.subplot(2,2,4) #第四个子图

plt.title('B channel') #第四幅图片标题

plt.imshow(img[:,:,2],plt.cm.gray) #绘制第四幅图片,且为灰度图

plt.axis('off') #不显示坐标尺寸

plt.show() #显示窗口

在图片绘制过程中,我们用matplotlib.pyplot模块下的figure()函数来创建显示窗口,该函数的格式为:matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None)

所有参数都是可选的,都有默认值,因此调用该函数时可以不带任何参数,其中:num: 整型或字符型都可以。如果设置为整型,则该整型数字表示窗口的序号。如果设置为字符型,则该字符串表示窗口的名称。用该参数来命名窗口,如果两个窗口序号或名相同,则后一个窗口会覆盖前一个窗口。

figsize: 设置窗口大小。是一个tuple型的整数,如figsize=(8,8)

dpi: 整形数字,表示窗口的分辨率。

facecolor: 窗口的背景颜色。

edgecolor: 窗口的边框颜色。

用figure()函数创建的窗口,只能显示一幅图片,如果想要显示多幅图片,则需要将这个窗口再划分为几个子图,在每个子图中显示不同的图片。我们可以使用subplot()函数来划分子图,函数格式为:matplotlib.pyplot.subplot(nrows, ncols, plot_number)nrows: 子图的行数。

ncols: 子图的列数。

plot_number: 当前子图的编号。

如:plt.subplot(2,2,1)

则表示将figure窗口划分成了2行2列共4个子图,当前为第1个子图。我们有时也可以用这种写法:plt.subplot(221)

两种写法效果是一样的。每个子图的标题可用title()函数来设置,是否使用坐标尺可用axis()函数来设置,如:plt.subplot(221)

plt.title("first subwindow")

plt.axis('off')

用subplots来创建显示窗口与划分子图

除了上面那种方法创建显示窗口和划分子图,还有另外一种编写方法也可以,如下例:import matplotlib.pyplot as plt

from skimage import data,color

img = data.immunohistochemistry()

hsv = color.rgb2hsv(img)

fig, axes = plt.subplots(2, 2, figsize=(7, 6))

ax0, ax1, ax2, ax3 = axes.ravel()

ax0.imshow(img)

ax0.set_title("Original image")

ax1.imshow(hsv[:, :, 0], cmap=plt.cm.gray)

ax1.set_title("H")

ax2.imshow(hsv[:, :, 1], cmap=plt.cm.gray)

ax2.set_title("S")

ax3.imshow(hsv[:, :, 2], cmap=plt.cm.gray)

ax3.set_title("V")

for ax in axes.ravel():

ax.axis('off')

fig.tight_layout() #自动调整subplot间的参数

直接用subplots()函数来创建并划分窗口。注意,比前面的subplot()函数多了一个s,该函数格式为:matplotlib.pyplot.subplots(nrows=1, ncols=1)。

nrows: 所有子图行数,默认为1。

ncols: 所有子图列数,默认为1。

返回一个窗口figure, 和一个tuple型的ax对象,该对象包含所有的子图,可结合ravel()函数列出所有子图,如:fig, axes = plt.subplots(2, 2, figsize=(7, 6))

ax0, ax1, ax2, ax3 = axes.ravel()

创建了2行2列4个子图,分别取名为ax0,ax1,ax2和ax3, 每个子图的标题用set_title()函数来设置,如:ax0.imshow(img)

ax0.set_title("Original image")

如果有多个子图,我们还可以使用tight_layout()函数来调整显示的布局,该函数格式为:matplotlib.pyplot.tight_layout(pad=1.08, h_pad=None, w_pad=None, rect=None)

所有的参数都是可选的,调用该函数时可省略所有的参数。pad: 主窗口边缘和子图边缘间的间距,默认为1.08

h_pad, w_pad: 子图边缘之间的间距,默认为 pad_inches

rect: 一个矩形区域,如果设置这个值,则将所有的子图调整到这个矩形区域内。

一般调用为:plt.tight_layout() #自动调整subplot间的参数

其它方法绘图并显示

除了使用matplotlib库来绘制图片,skimage还有另一个子模块viewer,也提供一个函数来显示图片。不同的是,它利用Qt工具来创建一块画布,从而在画布上绘制图像。

例:from skimage import data

from skimage.viewer import ImageViewer

img = data.coins()

viewer = ImageViewer(img)

viewer.show()

最后总结一下,绘制和显示图片常用到的函数有:函数名 功能 调用格式

figure 创建一个显示窗口 plt.figure(num=1,figsize=(8,8)

imshow 绘制图片 plt.imshow(image)

show 显示窗口 plt.show()

subplot 划分子图 plt.subplot(2,2,1)

title 设置子图标题(与subplot结合使用) plt.title('origin image')

axis 是否显示坐标尺 plt.axis('off')

subplots 创建带有多个子图的窗口 fig,axes=plt.subplots(2,2,figsize=(8,8))

ravel 为每个子图设置变量 ax0,ax1,ax2,ax3=axes.ravel()

set_title 设置子图标题(与axes结合使用) ax0.set_title('first window')

tight_layout 自动调整子图显示布局 plt.tight_layout()

六、图像的批量处理

有些时候,我们不仅要对一张图片进行处理,可能还会对一批图片处理。这时候,我们可以通过循环来执行处理,也可以调用程序自带的图片集合来处理。图片集合函数为:skimage.io.ImageCollection(load_pattern,load_func=None)

这个函数是放在io模块内的,带两个参数,第一个参数load_pattern, 表示图片组的路径,可以是一个str字符串。第二个参数load_func是一个回调函数,我们对图片进行批量处理就可以通过这个回调函数实现。回调函数默认为imread(),即默认这个函数是批量读取图片。先看一个例子:import skimage.io as io

from skimage import data_dir

str=data_dir + '/*.png'

coll = io.ImageCollection(str)

print(len(coll))

显示结果为25, 说明系统自带了25张png的示例图片,这些图片都读取了出来,放在图片集合coll里。如果我们想显示其中一张图片,则可以在后加上一行代码:io.imshow(coll[10])

如果一个文件夹里,我们既存放了一些jpg格式的图片,又存放了一些png格式的图片,现在想把它们全部读取出来,该怎么做呢?import skimage.io as io

from skimage import data_dir

str='d:/pic/*.jpg:d:/pic/*.png'

coll = io.ImageCollection(str)

print(len(coll))

注意这个地方'd:/pic/.jpg:d:/pic/.png' ,是两个字符串合在一起的,第一个是'd:/pic/.jpg', 第二个是'd:/pic/.png' ,合在一起后,中间用冒号来隔开,这样就可以把d:/pic/文件夹下的jpg和png格式的图片都读取出来。如果还想读取存放在其它地方的图片,也可以一并加进去,只是中间同样用冒号来隔开。io.ImageCollection()这个函数省略第二个参数,就是批量读取。如果我们不是想批量读取,而是其它批量操作,如批量转换为灰度图,那又该怎么做呢?那就需要先定义一个函数,然后将这个函数作为第二个参数,如:from skimage import data_dir,io,color

def convert_gray(f):

rgb=io.imread(f)

return color.rgb2gray(rgb)

str=data_dir+'/*.png'

coll = io.ImageCollection(str,load_func=convert_gray)

io.imshow(coll[10])

这种批量操作对视频处理是极其有用的,因为视频就是一系列的图片组合from skimage import data_dir,io,color

class AVILoader:

video_file = 'myvideo.avi'

def __call__(self, frame):

return video_read(self.video_file, frame)

avi_load = AVILoader()

frames = range(0, 1000, 10) # 0, 10, 20, ...ic =io.ImageCollection(frames, load_func=avi_load)

这段代码的意思,就是将myvideo.avi这个视频中每隔10帧的图片读取出来,放在图片集合中。

得到图片集合以后,我们还可以将这些图片连接起来,构成一个维度更高的数组,连接图片的函数为:skimage.io.concatenate_images(ic)

带一个参数,就是以上的图片集合,如:from skimage import data_dir,io,color

coll = io.ImageCollection('d:/pic/*.jpg')

mat=io.concatenate_images(coll)

使用concatenate_images(ic)函数的前提是读取的这些图片尺寸必须一致,否则会出错。我们看看图片连接前后的维度变化:from skimage import data_dir,io,color

coll = io.ImageCollection('d:/pic/*.jpg')

print(len(coll)) #连接的图片数量

print(coll[0].shape) #连接前的图片尺寸,所有的都一样

mat=io.concatenate_images(coll)

print(mat.shape) #连接后的数组尺寸

显示结果:2

(870, 580, 3)

(2, 870, 580, 3)

可以看到,将2个3维数组,连接成了一个4维数组

如果我们对图片进行批量操作后,想把操作后的结果保存起来,也是可以办到的。

例:把系统自带的所有png示例图片,全部转换成256256的jpg格式灰度图,保存在d:/data/文件夹下*

改变图片的大小,我们可以使用tranform模块的resize()函数,后续会讲到这个模块。from skimage import data_dir,io,transform,color

import numpy as np

def convert_gray(f):

rgb=io.imread(f) #依次读取rgb图片

gray=color.rgb2gray(rgb) #将rgb图片转换成灰度图

dst=transform.resize(gray,(256,256)) #将灰度图片大小转换为256*256

return dst str=data_dir+'/*.png'

coll = io.ImageCollection(str,load_func=convert_gray)

for i in range(len(coll)):

io.imsave('d:/data/'+np.str(i)+'.jpg',coll[i]) #循环保存图片

七、图像的形变与缩放

图像的形变与缩放,使用的是skimage的transform模块,函数比较多,功能齐全。

1、改变图片尺寸resize

函数格式为:skimage.transform.resize(image, output_shape)image: 需要改变尺寸的图片

output_shape: 新的图片尺寸from skimage import transform,data

import matplotlib.pyplot as plt

img = data.camera()

dst=transform.resize(img, (80, 60))

plt.figure('resize')

plt.subplot(121)

plt.title('before resize')

plt.imshow(img,plt.cm.gray)

plt.subplot(122)

plt.title('before resize')

plt.imshow(dst,plt.cm.gray)

plt.show()

将camera图片由原来的512x512大小,变成了80x60大小。从下图中的坐标尺,我们能够看出来:

2、按比例缩放rescale

函数格式为:skimage.transform.rescale(image, scale[, ...])

scale参数可以是单个float数,表示缩放的倍数,也可以是一个float型的tuple,如[0.2,0.5],表示将行列数分开进行缩放from skimage import transform,data

img = data.camera()

print(img.shape) #图片原始大小

print(transform.rescale(img, 0.1).shape) #缩小为原来图片大小的0.1

print(transform.rescale(img, [0.5,0.25]).shape) #缩小为原来图片行数一半,列数四分之一

print(transform.rescale(img, 2).shape) #放大为原来图片大小的2倍

结果为:(512, 512)

(51, 51)

(256, 128)

(1024, 1024)

3、旋转rotateskimage.transform.rotate(image, angle[, ...],resize=False)

angle参数是个float类型数,表示旋转的度数

resize用于控制在旋转时,是否改变大小 ,默认为Falsefrom skimage import transform,data

import matplotlib.pyplot as plt

img = data.camera()

print(img.shape) #图片原始大小

img1=transform.rotate(img, 60) #旋转90度,不改变大小

print(img1.shape)

img2=transform.rotate(img, 30,resize=True) #旋转30度,同时改变大小

print(img2.shape) plt.figure('resize')

plt.subplot(121)plt.title('rotate 60')

plt.imshow(img1,plt.cm.gray)

plt.subplot(122)

plt.title('rotate 30')

plt.imshow(img2,plt.cm.gray)

plt.show()

4、图像金字塔

以多分辨率来解释图像的一种有效但概念简单的结构就是图像金字塔。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。当向金字塔的上层移动时,尺寸和分辨率就降低。在此,我们举一个高斯金字塔的应用实例,函数原型为:skimage.transform.pyramid_gaussian(image, downscale=2)

downscale控制着金字塔的缩放比例import numpy as np

import matplotlib.pyplot as plt

from skimage import data,transform

image = data.astronaut() #载入宇航员图片

rows, cols, dim = image.shape #获取图片的行数,列数和通道数

pyramid = tuple(transform.pyramid_gaussian(image, downscale=2)) #产生高斯金字塔图像#共生成了log(512)=9幅金字塔图像,加上原始图像共10幅,pyramid[0]-pyramid[1]

composite_image = np.ones((rows, cols + cols / 2, 3), dtype=np.double) #生成背景composite_image[:rows, :cols, :] = pyramid[0] #融合原始图像

i_row = 0

for p in pyramid[1:]:

n_rows, n_cols = p.shape[:2]

composite_image[i_row:i_row + n_rows, cols:cols + n_cols] = p #循环融合9幅金字塔图像

i_row += n_rows

plt.imshow(composite_image)

plt.show()

上右图,就是10张金字塔图像,下标为0的表示原始图像,后面每层的图像行和列变为上一层的一半,直至变为1

除了高斯金字塔外,还有其它的金字塔,如:skimage.transform.pyramid_laplacian(image, downscale=2)

八、对比度与亮度调整

图像亮度与对比度的调整,是放在skimage包的exposure模块里面

1、gamma调整

原理:I=Ig

对原图像的像素,进行幂运算,得到新的像素值。公式中的g就是gamma值。

如果gamma>1, 新图像比原图像暗

如果gamma<1,新图像比原图像亮

函数格式为:skimage.exposure.adjust_gamma(image, gamma=1)

gamma参数默认为1,原像不发生变化 。from skimage import data, exposure, img_as_float

import matplotlib.pyplot as plt

image = img_as_float(data.moon())

gam1= exposure.adjust_gamma(image, 2) #调暗

gam2= exposure.adjust_gamma(image, 0.5) #调亮plt.figure('adjust_gamma',figsize=(8,8))

plt.subplot(131)plt.title('origin image')

plt.imshow(image,plt.cm.gray)plt.axis('off')

plt.subplot(132)

plt.title('gamma=2')

plt.imshow(gam1,plt.cm.gray)

plt.axis('off')

plt.subplot(133)

plt.title('gamma=0.5')

plt.imshow(gam2,plt.cm.gray)

plt.axis('off')

plt.show()

2、log对数调整

这个刚好和gamma相反

原理:I=log(I)from skimage import data, exposure, img_as_float

import matplotlib.pyplot as plt

image = img_as_float(data.moon())

gam1= exposure.adjust_log(image) #对数调整

plt.figure('adjust_gamma',figsize=(8,8))

plt.subplot(121)plt.title('origin image')

plt.imshow(image,plt.cm.gray)

plt.axis('off')

plt.subplot(122)

plt.title('log')

plt.imshow(gam1,plt.cm.gray)

plt.axis('off')

plt.show()

3、判断图像对比度是否偏低

函数:is_low_contrast(img)

返回一个bool型值from skimage import data, exposure

image =data.moon()

result=exposure.is_low_contrast(image)

print(result)

输出为False

4、调整强度

函数:skimage.exposure.rescale_intensity(image, in_range='image', out_range='dtype')

in_range 表示输入图片的强度范围,默认为'image', 表示用图像的最大/最小像素值作为范围

out_range 表示输出图片的强度范围,默认为'dype', 表示用图像的类型的最大/最小值作为范围

默认情况下,输入图片的[min,max]范围被拉伸到[dtype.min, dtype.max],如果dtype=uint8, 那么dtype.min=0, dtype.max=255import numpy as np

from skimage import exposure

image = np.array([51, 102, 153], dtype=np.uint8)

mat=exposure.rescale_intensity(image)

print(mat)

输出为[ 0 127 255]

即像素最小值由51变为0,最大值由153变为255,整体进行了拉伸,但是数据类型没有变,还是uint8

前面我们讲过,可以通过img_as_float()函数将unit8类型转换为float型,实际上还有更简单的方法,就是乘以1.0import numpy as np

image = np.array([51, 102, 153], dtype=np.uint8)

print(image*1.0)

即由[51,102,153]变成了[ 51. 102. 153.]

而float类型的范围是[0,1],因此对float进行rescale_intensity 调整后,范围变为[0,1],而不是[0,255]import numpy as np

from skimage import exposure

image = np.array([51, 102, 153], dtype=np.uint8)

tmp=image*1.0

mat=exposure.rescale_intensity(tmp)

print(mat)

结果为[ 0. 0.5 1. ]

如果原始像素值不想被拉伸,只是等比例缩小,就使用in_range参数,如:import numpy as np

from skimage import exposure

image = np.array([51, 102, 153], dtype=np.uint8)

tmp=image*1.0

mat=exposure.rescale_intensity(tmp,in_range=(0,255))

print(mat)

输出为:[ 0.2 0.4 0.6],即原像素值除以255

如果参数in_range的[main,max]范围要比原始像素值的范围[min,max] 大或者小,那就进行裁剪,如:mat=exposure.rescale_intensity(tmp,in_range=(0,102))

print(mat)

输出[ 0.5 1. 1. ],即原像素值除以102,超出1的变为1

如果一个数组里面有负数,现在想调整到正数,就使用out_range参数。如:import numpy as np

from skimage import exposure

image = np.array([-10, 0, 10], dtype=np.int8)

mat=exposure.rescale_intensity(image, out_range=(0, 127))

print(mat)

输出[ 0 63 127]

九、直方图与均衡化

在图像处理中,直方图是非常重要,也是非常有用的一个处理要素。在skimage库中对直方图的处理,是放在exposure这个模块中。

1、计算直方图

函数:skimage.exposure.histogram(image, nbins=256)

在numpy包中,也提供了一个计算直方图的函数histogram(),两者大同小义。返回一个tuple(hist, bins_center), 前一个数组是直方图的统计量,后一个数组是每个bin的中间值import numpy as np

from skimage import exposure,data

image =data.camera()*1.0

hist1=np.histogram(image, bins=2) #用numpy包计算直方图hist2=exposure.histogram(image, nbins=2) #用skimage计算直方图

print(hist1)

print(hist2)

输出:(array([107432, 154712], dtype=int64), array([ 0. , 127.5, 255. ]))

(array([107432, 154712], dtype=int64), array([ 63.75, 191.25]))

分成两个bin,每个bin的统计量是一样的,但numpy返回的是每个bin的两端的范围值,而skimage返回的是每个bin的中间值

2、绘制直方图

绘图都可以调用matplotlib.pyplot库来进行,其中的hist函数可以直接绘制直方图。调用方式:n, bins, patches = plt.hist(arr, bins=10, normed=0, facecolor='black', edgecolor='black',alpha=1,histtype='bar')

hist的参数非常多,但常用的就这六个,只有第一个是必须的,后面四个可选arr: 需要计算直方图的一维数组

bins: 直方图的柱数,可选项,默认为10

normed: 是否将得到的直方图向量归一化。默认为0

facecolor: 直方图颜色

edgecolor: 直方图边框颜色

alpha: 透明度

histtype: 直方图类型,‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’

返回值 :n: 直方图向量,是否归一化由参数normed设定

bins: 返回各个bin的区间范围

patches: 返回每个bin里面包含的数据,是一个listfrom skimage import data

import matplotlib.pyplot as plt

img=data.camera()

plt.figure("hist")

arr=img.flatten()

n, bins, patches = plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red')

plt.show()

其中的flatten()函数是numpy包里面的,用于将二维数组序列化成一维数组。

是按行序列,如mat=[[1 2 3

4 5 6]]

经过 mat.flatten()后,就变成了mat=[1 2 3 4 5 6]

3、彩色图片三通道直方图

一般来说直方图都是征对灰度图的,如果要画rgb图像的三通道直方图,实际上就是三个直方图的叠加。from skimage import data

import matplotlib.pyplot as plt

img=data.lena()

ar=img[:,:,0].flatten()

plt.hist(ar, bins=256, normed=1,facecolor='r',edgecolor='r',hold=1)

ag=img[:,:,1].flatten()

plt.hist(ag, bins=256, normed=1, facecolor='g',edgecolor='g',hold=1)

ab=img[:,:,2].flatten()

plt.hist(ab, bins=256, normed=1, facecolor='b',edgecolor='b')

plt.show()

其中,加一个参数hold=1,表示可以叠加

4、直方图均衡化

如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。from skimage import data,exposure

import matplotlib.pyplot as plt

img=data.moon()

plt.figure("hist",figsize=(8,8))

arr=img.flatten()

plt.subplot(221)

plt.imshow(img,plt.cm.gray) #原始图像

plt.subplot(222)

plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始图像直方图

img1=exposure.equalize_hist(img)

arr1=img1.flatten()

plt.subplot(223)

plt.imshow(img1,plt.cm.gray) #均衡化图像

plt.subplot(224)

plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

十、CLAHE

skimage.exposure.``equalize_adapthist(image, kernel_size=None, clip_limit=0.01, nbins=256)

An algorithm for local contrast enhancement, that uses histograms computed over different tile regions of the image. Local details can therefore be enhanced even in regions that are darker or lighter than most of the image.

image : (M, N[, C]) ndarrayInput image.

kernel_size: integer or list-like, optional

Defines the shape of contextual regions used in the algorithm. If iterable is passed, it must have the same number of elements as image.ndim (without color channel). If integer, it is broadcasted to each image dimension. By default, kernel_size is 1/8 of image height by 1/8 of its width.

clip_limit : float, optionalClipping limit, normalized between 0 and 1 (higher values give more contrast).

nbins : int, optional

Number of gray bins for histogram (“data range”).

| Returns: |

out : (M, N[, C]) ndarrayEqualized image.from skimage import data,exposure

import matplotlib.pyplot as plt

#%matplotlib notebook

clip_limitnumber=0.01

img=data.moon()

print(img.shape)

plt.figure("hist",figsize=(8,8))

arr=img.flatten()

plt.subplot(5,2,1)

plt.title('original')

plt.imshow(img,plt.cm.gray) #原始图像

plt.subplot(5,2,2)

plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始图像直方图

# #img1=exposure.equalize_hist(img)

# img1=exposure.equalize_hist(img)

# arr1=img1.flatten()

# plt.subplot(6,2,3)

# plt.title('equalize_hist')

# plt.imshow(img1,plt.cm.gray) #均衡化图像

# plt.subplot(6,2,4)

# plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

# plt.show()

img2=exposure.equalize_adapthist(img, kernel_size=256, clip_limit=clip_limitnumber, nbins=256)

arr2=img2.flatten()

plt.subplot(5,2,3)

plt.title('equalize_adapthist-256-'+ str(clip_limitnumber))

plt.imshow(img2,plt.cm.gray) #均衡化图像

plt.subplot(5,2,4)

plt.hist(arr2, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img3=exposure.equalize_adapthist(img, kernel_size=128, clip_limit=clip_limitnumber, nbins=256)

arr3=img3.flatten()

plt.subplot(5,2,5)

plt.title('equalize_adapthist-128-'+ str(clip_limitnumber))

plt.imshow(img3,plt.cm.gray) #均衡化图像

plt.subplot(5,2,6)

plt.hist(arr3, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img4=exposure.equalize_adapthist(img, kernel_size=64, clip_limit=clip_limitnumber, nbins=256)

arr4=img4.flatten()

plt.subplot(5,2,7)

plt.title('equalize_adapthist-64-'+ str(clip_limitnumber))

plt.imshow(img4,plt.cm.gray) #均衡化图像

plt.subplot(5,2,8)

plt.hist(arr4, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img5=exposure.equalize_adapthist(img, kernel_size=32, clip_limit=clip_limitnumber, nbins=256)

arr5=img5.flatten()

plt.subplot(5,2,9)

plt.title('equalize_adapthist-32-'+ str(clip_limitnumber))

plt.imshow(img5,plt.cm.gray) #均衡化图像

plt.subplot(5,2,10)

plt.hist(arr5, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()from skimage import data,exposure

import matplotlib.pyplot as plt

#%matplotlib notebook

kernel_sizenuber=64

img=data.moon()

print(img.shape)

plt.figure("hist",figsize=(8,8))

arr=img.flatten()

plt.subplot(5,2,1)

plt.title('original')

plt.imshow(img,plt.cm.gray) #原始图像

plt.subplot(5,2,2)

plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始图像直方图

# #img1=exposure.equalize_hist(img)

# img1=exposure.equalize_hist(img)

# arr1=img1.flatten()

# plt.subplot(6,2,3)

# plt.title('equalize_hist')

# plt.imshow(img1,plt.cm.gray) #均衡化图像

# plt.subplot(6,2,4)

# plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

# plt.show()

img2=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.001, nbins=256)

arr2=img2.flatten()

plt.subplot(5,2,3)

plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.001')

plt.imshow(img2,plt.cm.gray) #均衡化图像

plt.subplot(5,2,4)

plt.hist(arr2, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img3=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.005, nbins=256)

arr3=img3.flatten()

plt.subplot(5,2,5)

plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.005')

plt.imshow(img3,plt.cm.gray) #均衡化图像

plt.subplot(5,2,6)

plt.hist(arr3, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img4=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.01, nbins=256)

arr4=img4.flatten()

plt.subplot(5,2,7)

plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.01')

plt.imshow(img4,plt.cm.gray) #均衡化图像

plt.subplot(5,2,8)

plt.hist(arr4, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

img5=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.05, nbins=256)

arr5=img5.flatten()

plt.subplot(5,2,9)

plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.05')

plt.imshow(img5,plt.cm.gray) #均衡化图像

plt.subplot(5,2,10)

plt.hist(arr5, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方图

plt.show()

三、各种imread函数的区别与联系

先来看看常用的读取图片的方式:PIL.Image.open

scipy.misc.imread

scipy.ndimage.imread

cv2.imread

matplotlib.image.imread

skimge

caffe.io.load_iamge

这些方法可以分为四大家族:

一、PILPIL.Image.open + numpy

scipy.misc.imread

scipy.ndimage.imread

这些方法都是通过调用PIL.Image.open 读取图像的信息; PIL.Image.open 不直接返回numpy对象,可以用numpy提供的函数进行转换,参考Image和Ndarray互相转换; 其他模块都直接返回numpy.ndarray对象,通道顺序为RGB,通道值得默认范围为0-255。

二、matplotlibmatplot.image.imread

从名字中可以看出这个模块是具有matlab风格的,直接返回numpy.ndarray格式通道顺序是RGB,通道值默认范围0-255。

三、opencvcv2.imread

使用opencv读取图像,直接返回numpy.ndarray 对象,通道顺序为BGR ,注意是BGR,通道值默认范围0-255。

四、skimageskimage.io.imread: 直接返回numpy.ndarray 对象,通道顺序为RGB,通道值默认范围0-255。

caffe.io.load_image: 没有调用默认的skimage.io.imread,返回值为0-1的float型数据,通道顺序为RGB

关于图像的一些说明:

可以使用matplotlib的pyplot模块的show也可以使用cv2的imshow方法,对于这些方法只要你传入的参数是numpy.ndarray(通道值范围0-255) 就可以正常显示,不存在区别,这也可以看出numpy在python中的重要地位;但是cv2.imshow方法由于它针对的是cv2的imread 所以它内部会做通道顺序的变化,传入为BGR转换为RGB,所以你如果传入RGB显示的就是BGR了。

以下是测试代码

运行环境为windows10+python3.6#encoding=utf8

from PIL import Image

import numpy as np

import cv2

import matplotlib.pyplot as plt # plt 用于显示图片

import matplotlib.image as mpimg # mpimg 用于读取图片

import skimage

import sys

from skimage import io

#PIL

#相关:scipy.misc.imread, scipy.ndimage.imread

#misc.imread 提供可选参数mode,但本质上是调用PIL,具体的模式可以去看srccode或者document

#https://github.com/scipy/scipy/blob/v0.17.1/scipy/misc/pilutil.py

imagepath='test1.jpg'

im1=Image.open(imagepath)

im1=np.array(im1)#获得numpy对象,RGB

print(type(im1))

print(im1.shape)

#2 opencv

im2=cv2.imread(imagepath)

print(type(im2))#numpy BGR

print(im2.shape)#[width,height,3]

#3 matplotlib 类似matlab的方式

im3 = mpimg.imread(imagepath)

print(type(im3))#np.array

print(im3.shape)

#4 skimge 

#caffe.io.load_iamge()也是调用的skimage实现的,返回的是0-1 float型数据

im4 = io.imread(imagepath)

print(type(im4))#np.array

print(im4.shape)

#print(im4)

# cv2.imshow('test',im4)

# cv2.waitKey()

#统一使用plt进行显示,不管是plt还是cv2.imshow,在python中只认numpy.array,但是由于cv2.imread 的图片是BGR,cv2.imshow 时相应的换通道显示

plt.subplot(221)

plt.title('PIL read')

plt.imshow(im1)

plt.subplot(222)

plt.title('opencv read')

plt.imshow(im2)

plt.subplot(223)

plt.title('matplotlib read')

plt.imshow(im3)

plt.subplot(224)

plt.title('skimage read')

plt.imshow(im4)

#plt.axis('off') # 不显示坐标轴

plt.show()

##################################### cmd output################################

#

# (851, 1279, 3)

#

# (851, 1279, 3)

#

# (851, 1279, 3)

#

# (851, 1279, 3)

测试结果:

四、深度学习框架自带的图像处理函数

tensorflow提供了简单的图像处理函数,可以对图像进行预处理,可以尽量避免模型受到无关因素的影响。大部分图像识别问题中,通过图像预处理过程可以提高模型的准确率。封装在类tf.image中。

一、API列表

函数列表为:adjust_brightness(): 调整RGB或灰度图像的亮度。

adjust_contrast(): 调整RGB或灰度图像的对比度。

adjust_gamma(): 对输入图像执行伽玛校正。

adjust_hue(): 调整RGB图像的色调。

adjust_saturation(): 调整RGB图像的饱和度。

central_crop(): 裁剪图像的中心区域。

convert_image_dtype(): 将图像转换为dtype,如果需要可以缩放它的值。

crop_and_resize(): 从输入的图像张量中提取作物并对其进行双线性调整(可能)。

crop_to_bounding_box(): 将图像裁剪到指定的边框。

decode_and_crop_jpeg(): 将图像裁剪到指定的边框。

decode_bmp(): 将bmp编码图像的第一帧解码为uint8张量。

decode_gif(): 将gif编码图像的第一帧解码为uint8张量。

decode_image(): bmp、gif、jpeg的解码函数

decode_jpeg(): 将jpeg编码的图像解码为uint8张量。

decode_png(): 将png编码的图像解码为uint8或uint16张量。

draw_bounding_boxes(): 在一批图像上绘制边框。

encode_jpeg(): 在一幅图像中进行JPEG编码。

encode_png(): 在一幅图像中进行PNG编码。

extract_glimpse(): 从一个输入张量中提取一点点。

extract_jpeg_shape(): 提取jpeg编码图像的形状信息。

flip_left_right(): 水平翻转图像(从左到右)。

flip_up_down(): 垂直翻转图片(上下颠倒)。

grayscale_to_rgb(): 将一个或多个图像从灰度转换为RGB。

hsv_to_rgb(): 将一个或多个图像从HSV转换为RGB。

image_gradients(): 返回每个颜色通道的图像梯度(dy, dx)。

is_jpeg(): 检查'内容'编码的JPEG图像。

non_max_suppression(): 贪婪地选择按得分降序排列的边界框子集。

pad_to_bounding_box(): 用0填充指定高度和宽度的图像。

per_image_standardization(): 线性尺度图像具有零均值和单位范数。

psnr(): 返回a和b之间的峰值信噪比。

random_brightness(): 用一个随机因素调整图像的亮度。

random_contrast(): 用随机因素调整图像的对比度。

random_flip_left_right(): 随机水平翻转图像(从左到右)。

random_flip_up_down(): 随机垂直翻转图像(上下颠倒)。

random_hue(): 通过随机因素调整RGB图像的色调。

random_saturation(): 通过随机因素调整RGB图像的饱和度。

resize_area(): 使用面积插值调整图像的大小。

resize_bicubic(): 使用双三次插值调整图像的大小。

resize_bilinear(): 使用双线性插值调整图像的大小。

resize_image_with_crop_or_pad(): 裁剪和/或将图像填充到目标宽度和高度。

resize_images(): 使用指定的方法调整图像的大小。

resize_nearest_neighbor(): 使用最近邻插值调整图像的大小。

rgb_to_grayscale(): 将一个或多个图像从RGB转换为灰度。

rgb_to_hsv(): 将一个或多个图像从RGB转换为HSV。

rgb_to_yiq(): 将一个或多个图像从RGB转换为YIQ。

rgb_to_yuv(): 将一个或多个图像从RGB转换为YUV。

rot90(): 逆时针旋转图像90度。

sample_distorted_bounding_box(): 为图像生成一个随机扭曲的边框。

sobel_edges(): 返回一个包含索贝尔边映射的张量。

ssim(): 计算img1和img2之间的SSIM索引。

ssim_multiscale(): 计算img1和img2之间的MS-SSIM。

total_variation(): 计算并返回一个或多个图像的总变化量。

transpose_image(): 通过交换高度和宽度维度来转置图像。

yiq_to_rgb(): 将一个或多个图像从YIQ转换为RGB。

yuv_to_rgb(): 将一个或多个图像从YUV转换为RGB。

二、tensorflow图像处理函数例程

1、图像编码处理

一张RGB彩色模型的图像可以看成一个三维矩阵,矩阵中的每一个数表示了图像上不同的位置,不同颜色的亮度。然而图像在存储时并不是直接记录这些矩阵中的数字,而是记录经过压缩编码之后的结果。所以要将一张图片还原成一个三维矩阵,需要解码过程。tensorflow提供了jpeg和png格式图像的编码/解码的函数。以下代码示范了如何使用tensorflow中对jpeg格式图像进行编码/解码。# matplotlib.pyplot是一个python的画图工具。下面的代码将使用这个工具

# 来可视化经过tensorflow处理的图像。

import matplotlib.pyplot as plt

import tensorflow as tf

# 读取图像的原始数据

image_raw_data = tf.gfile.FastGFile("/path/to/picture", 'r').read()

with tf.Session() as sess:

# 对图像进行jpeg的格式解码从而得到图像对应的三维矩阵。tensorflow还提供了

# tf.image.decode_png 函数对png格式的图像进行解码。解码之后的结果为一个

# 张量,在使用它的取值之前需要明确调用运行的过程。

img_data = tf.image.decode_jpeg(image_raw_data)

print img_data.eval()

# 输出解码之后的三维矩阵,上面这一行代码将输出以下内容。

'''

[[[165 160 138]

...,

[105 140 50]

[[166 161 139]

...,

[106 139 48]]

...,

[207 200 181]

...,

[106 81 50]]]

'''# 使用pyplot工具可视化得到的图像。

plt.imshow(img_data.eval())

plt.show()

# 将表示一张图像的三维矩阵重新按照jpeg格式编码并存入文件中。打开这张图片,

# 可以得到和原始图像一样的图像。

encoded_image = tf.image.encode_jpeg(img_data)

with tf.gfile.GFile("/path/to/output", "wb") as f:

f.write(encode_image.eval())

2、图像大小调整

一般来说,网络上获取的图像大小是不固定,但神经网络输入节点的个数是固定的,所以在将图像的像素作为输入提供给神经网路之前,需要先将图像的大小统一。这就是图像大小调整需要完成的任务。图像大小调整有两种方式,第一种是通过算法使得新的图像尽量保存原始图像上的所有信息。tensorflow提供了4种不同的方法,并且将它们封装到了tf.image.resize_images函数。以下代码示范了如何使用这个函数。# 加载原始图像,定义会话等过程和图像编码处理中代码一致,

# 假设img_data是已经解码的图像。

# 首先将图片数据转化为实数类型。这一步将0-255的像素值转化为 0.0-1.0 范围内的实数,

# 大多数图像处理API支持整数和实数类型的输入,如果输入是整数类型,这些API会

# 在内部将输入转化为实数后处理,再将输出转化为整数。如果有多个处理步骤,在整数和

# 实数之间的反复转化将导致精度损失,因此推进在图像处理前将其转化为实数类型。

# 下面的样例子将略去这一步骤,假设img_data是经过类型转化的图像

img_data = tf.image.convert_image_dtype(img_data, dtype=tf.float32)

# 通过tf.image.resize_images函数调整图像的大小。这个函数第一个参数为原始图像,

# 第二个和第三个参数为调整后图像的大小,method参数给出了调整图像大小的算法。

# 注意,如果输入数据时unit8格式,那么输出将是0-255内的实数,不方便后续处理。

resized = tf.image.resize_images(img_data, [300, 300], method=0)

# 通过pyplot可视化过程和图像编码处理中给出的代码一致

下表给出了tf.image.resize_images函数的method参数取值对应的图像大小调整算法。

Method取值     图像大小调整算法0     双线性插值(Bilinear interpolation)

1     最邻近发(Nearest nighbor interpolation)

2     双三次插值(Bicubic interpolation)

3     面积插值法(Area interpolation)

不同算法调整出来的结果会有细微差别,但不会相差太远。除了将整张图像信息完整保存,tensorflow还提供了API对图像进行剪裁或者填充。以下代码展示了通过tf.image.resize_image_with_crop_or_pad函数来调整图像大小的功能。# 通过tf.image.resize_image_with_crop_or_pad函数调整图像的大小。这个函数的

# 第一个参数为原始图像,后面两个参数是调整后的目标图像大小。如果原始图像的尺寸大于目标

# 图像,那么这个函数会自动截取原始图像中的部分。如果目标图像

# 大于原始图像,这个函数会自动在原始图像的四周填充全0背景。因为原

# 始图像的大小为1797*2673,所以下面的第一个命令会自动剪裁,而第二个命令会自动填充。

croped = tf.image.resize_image_crop_or_pad(img_data, 1000, 1000)

padded = tf.image.resize_image_crop_or_pad(img_data, 3000, 3000)

tensorflow还支持通过比例调整图像大小,以下代码给出了一个样例:# 通过tf.image.central_crop函数可以按比例剪裁图像。这个函数的第一个参数为原始图

# 像,第二个为调整比例,这个比例需要时一个(0,1]的实数。

central_cropped = tf.image.central_crop(img_data, 0.5)

上面介绍的图像剪切函数都是截取或者填充图像中间的部分。tensorflow也提供了tf.image.crop_to_bounding_box函数和tf.image.pad_to_bounding_box函数来剪切或者填充给定区域的图像。这两个函数都要求给出的尺寸满足一定的要求,否则程序会报错。比如在使用tf.image.crop_to_bounding_box函数时,tensorflow要求提供的图像尺寸要大于目标尺寸,也就是要求原始图像能够剪切目标图像的大小。

3、图像翻转

tensorflow提供了一些函数来支持对图像的翻转。以下代码实现了将图像上下翻转、左右翻转以及沿对角线翻转的功能。# 将图像上下翻转

flipped = tf.image.flip_up_down(img_data)

# 将图像左右翻转

flipped = tf.image.flip_left_right(img_data)

# 将图像沿对角线

transposed = tf.image.transpose_image(img_data)

在很多图像识别问题中,图像的翻转不应该影响识别的结果。于是在训练图像识别的神经网络模型时,可以随机地翻转训练图像,这样训练得到的模型可以识别不同角度的实体。比如假设在训练模型中所有的猫头都是向右的,那么训练出来的模型就无法很好地识别猫头向左的猫。虽然这个问题可以通过收集更多的训练数据来解决,但是通过随机翻转识别训练图像的方式可以在零成本的情况下很大程度地缓解该问题。所以随机翻转训练图像时一种很常用的图像预处理方式。tensorflow提供了方便的API完成随机图像翻转的过程。

# 以50%概率上下翻转

    flipped = tf.image.random_flip_up_down(img_data)

# 以50%概率左右翻转图像

    flipped = tf.image.random_flip_left_right(img_data)

4、图像色彩调整

和图像翻转类似,调整图像的亮度、对比度、饱和度和色相在很多图像识别应用中都不会影响识别的结果。所以在训练神经网络模型时,可以随机调整训练图像的这些属性,从而使得到的模型尽可能小地受到无关因素的影响。tensorflow提供了调整这些色彩相关属性的API。以下代码显示了如何修改图像的亮度。# 将图像的亮度-0.5

adjusted = tf.image.adjust_brightness(img_data, -0.5)

# 色彩调整的API可能导致像素的实数超出0.0-1.0的范围,因此在输出最终图像前需要

# 将其值截断在0.0-1.0范围区间,否则不仅图像无法正常可视化,以此为输入的神经网络

# 的训练质量也可能受到影响。

# 如果对图像进行多项处理操作,那么这一截断过程应在所有处理完成后进行。举例而言,

# 假如对图像进行多项处理操作,那么这一截断过程应当在所有处理完成后进行。举例而言。

# 假如对图像一次提高亮度和减少对比度,那么第二个操作可能将第一个操作生成的部分

# 过亮的像素回到不超过1.0的范围内,因此在第一个操作后不应该立即截断。

# 下面的样例假设截断操作在最终可视化图像前进行。

adjusted = tf.clip_by_value(adjusted, 0.0, 1.0)

# 将图像的亮度+0.5

adjuested = tf.image.adjusted_brightness(img_data, 0.5)

# 在(-max_delta, max_delta)的范围随机调整图像的亮度。

adjusted = tf.image.random_brightness(image, max_delta)

以下代码显示了如何调整图像的对比度:# 将图像的对比度减少到0.5倍

adjusted = tf.image.adjust_contrast(img_data, 0.5)

# 将图像的对比度增加5倍

adjusted = tf.image.adjust_contrast(img_data, 5)

# 在[lower, upper]的范围随机调整图的对比度

adjusted = tf.image.random_contrast(image, lower, upper)

以下代码显示了如何调整图像的色相:# 下面4条命令分别将色相加0.1, 0.3, 0.6和0.9

adjusted = tf.image.adjust_hue(img_data, 0.1)

adjusted = tf.image.adjust_hue(img_data, 0.3)

adjusted = tf.image.adjust_hue(img_data, 0.6)

adjusted = tf.image.adjust_hue(img_data, 0.9)

# 在[-max_delta, max_delta]的范围内随机调整图像的色相。max_delta的取值在[0, 0.5]之间

adjusted = tf.image.random_hue(image, max_delta)

以下代码显示了如何调整图像的饱和度:# 将图像的饱和度-5

adjusted = tf.image.adjust_saturation(img_data, -5)

# 将图像的饱和度+5

adjusted = tf.image.adjust_saturation(img_data, +5)

# 在[lower, upper]的范围内随机调整图像的饱和度

adjusted = tf.images.random_satutation(image, lower, upper)

除了调整图像的亮度、对比度、饱和度和色相,tensorflow还提供API来完成图像标准化的操作。这个操作就是将图像上的亮度均值变为0,方差变为1.以下代码实现了这个功能。# 将代表一张图像的三维矩阵中的数字均值变为0,方差变为1

adjusted = tf.image.pet_standerdization(img_data)

5、处理框标准

在很多图像识别任务的数据集中,图像中需要关注的物体通常会被标注框圈出来。tensorflow提供了一些工具来处理标注框。以下代码展示了如何通过tf.image.draw_bounding_boxes函数加入标注框。# 将图像缩小一些,这样可视化能让标注框更加清楚。

img_data = tf.image.resize_images(img_data, [180, 26], method=1)

# tf.image.draw_bounding_boxes函数要求图像矩阵中的数字为实数,所以需要先将

# 图像转化为实数类型。tf.images.draw_bounding_boxes函数图像的输入是一个

# batch的数据,也就是多张图像组成的四维矩阵,所以需要将解码之后的图像矩阵加一维。

batched = tf.expand_dims(

tf.images.convert_image_dtype(img_data, tf.float32), 0)

# 给出每一张图像的所有标注框。一个标注框有4个数字,分别代表[ymin, xmin, ymax, xmax]

# 注意这里给出的数字都是图像的相对位置。比如在180*267的图像中,

# [0.35, 0.47, 0.5, 0.56]代表了从(63, 125)到(90, 150)的图像。

boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])

result = tf.image.draw_bounding_boxes(batched, boxes)

和随机翻转图像、随机调整颜色类似,随机截取图像上有信息含量的部分也是一个提高模型健壮性(robustness)的一种方式。这样可以使训练得到的模型不受被识别物体大小的影响。以下程序中展示了如何通过tf.image.sample_distored_bounding_box函数来完成截取图像的过程。boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7],[0.35, 0.47, 0.5, 0.56]]])

# 可以通过提供标注框的方式来告诉随机截取图像的算法哪些部分是“有信息量”的

# min_object_covered=0.4 表示截取部分至少包含某个标注框40%的内容。

begin, size, bbox_for_draw = tf.image.sample_distorted_bounding_box(

tf.shape(img_data), bounding_boxes=boxes,

min_object_covered=0.4)

# 通过标注框可视化随机截取得到的图像。

batched = tf.expand_dims(tf.images.covert_image_dtype(img_data, tf.float32), 0)

image_with_box = tf.image.draw_bounding_boxes(batched, bbox_for_draw)

# 截取随机出来的图像。因为算法带有随机成分,所以

# 每次得到的结果会有所不同

distorted_image = tf.slice(img_data, begin, size)

6、图像预处理完整例程

在解决真实的图像识别问题时,一般同时使用多种处理方法。这节将给出一个完整的样例程序展示如何将不同的图像处理函数结合成一个完成了从图像片段截取,到图像大小调整再到图像翻转及色彩调整的整个图像预处理过程。import tensorflow as tf

import numpy as np

from matplotlib import pyplot as plt

# 给定一张图像,随机调整图像的色彩。因为调整亮度、对比度、饱和度和色相的顺序会影

# 响最后得到的结果,所以可以定义多种不同的顺序。具体使用哪一种顺序可以在训练

# 数据预处理时随机地选择一种。这样可以进一步降低无关因素对模型的影响。

def distort_color(image, color_ordering=0):

if color_ordering == 0:

image = tf.image.random_brightness(image, max_delta=32. / 255.)

image = tf.image.random_saturation(image, lower=0.5, upper=1.5)

image = tf.image.random_hue(image, max_delta=0.2)

image = tf.image.random_contrast(image, lower=0.5, upper=1.5)

elif color_ordering == 1:

image = tf.image.random_saturation(image, lower=0.5, upper=1.5)

image = tf.image.random_brightness(image, max_delta=32. / 255.)

image = tf.image.random_contrast(image, lower=0.5, upper=1.5)

image = tf.image.random_hue(image, max_delta=0.2)

elif color_ordering == 2:

# 还可以定义其他的排列,但在这里就不再一一列出。

...

return tf.clip_by_value(image, 0.0, 1.0)

# 给定一张解码后的图像、目标图像的尺寸以及图像上的标注框,此函数可以对给出的图像进行预

# 处理。这个函数的输入图像时图像识别问题中原始的训练图像,而输出则是神经网络模型的输入

# 层。注意这里只处理模型的训练数据,对于预测的数据,一般不需要随机变换的步骤。

def preprocess_for_train(image, height, width, bbox):

# 如果没有提供标注框,则认为整个图像就是需要关注的部分。

if bbox is None:

bbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=tf.float32, shape=[1, 1, 4])

# 转换图像张量的类型。

if image.dtype != tf.float32:

image = tf.image.convert_image_dtype(image, dtype=tf.float32)

# 随机截取图像,减少需要关注的物体大小对图像识别算法的影响。

bbox_begin, bbox_size, _ = tf.image.sample_distorted_bounding_box(

tf.shape(image), bounding_boxes=bbox)

distorted_image = tf.slice(image, bbox_begin, bbox_size)

# 将随机截取的图像调整为神经网络输入层的大小。大小调整的算法是随机选择的。

distorted_image = tf.image.resize_images(

distorted_image, [height, width], method=np.random.randint(2))

# 随机左右翻转图像。

distorted_image = tf.image.random_flip_left_right(distorted_image)

# 使用一种随机的顺序调整图像色彩。

distorted_image = distort_color(distorted_image, np.random.randint(2))

return distorted_image

image_raw_data = tf.gfile.FastGFile("timg.jpeg", "rb").read()

with tf.Session() as sess:

img_data = tf.image.decode_jpeg(image_raw_data)

boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])

# 运行6次获得6种不同的图像

for i in range(6):

# 将图像的尺寸调整为299*299

result = preprocess_for_train(img_data, 299, 299, boxes)

plt.imshow(result.eval())

plt.show()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值