美文网首页
python图像处理之----skimage(调戏一下我们的女神

python图像处理之----skimage(调戏一下我们的女神

作者: 单细胞空间交响乐 | 来源:发表于2021-03-30 15:39 被阅读0次

    今天的我们来分享一点不一样的内容,让我们学一点基础的东西,用代码处理我们的图片。

    我们以一位女神(刘亦菲)的图片为例。

    src=http---www.yangzhi.net-file-upload-202004-20-2110132085.jpg&refer=http---www.yangzhi.jpg

    首先,导入模块,并读取图片

    from skimage import io,transform
    img = io.imread('liuyifei.jpg') 
    
    我们看看img读入了什么
    图片.png

    可见,skimage读入图片变成了向量,skimage读取图片的是(height,width, channel),channel就是管道,比如我们的图片都是RGB格式的,就是三个管道。

    我们来看一下读取图片的参数

    注意as_gray参数,默认是False,如果设置成True,我们来看一下(将图片处理成灰度图片)
    img = io.imread('liuyifei.jpg',as_gray = True) 
    img.shape
    (1200,1200)
    io.imsave('liuyifei_1.jpg',img)
    

    我们保存出来看一下


    liuyifei_1.jpg

    图片变成了灰色,可见管道不再是RGB格式
    如果我们想知道一些图片的基础信息

    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数组的一切功能,对图片也适用。对数组元素的访问,实际上就是对图片像素点的访问。
    例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)
    

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

    我们随机修改一些像素值,看看效果(随机修改了5000个像素点)

    import numpy as np
    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.imsave('liuyifei_2.jpg',img)
    
    liuyifei_2.jpg

    多了很多白点。

    通过对数组的裁剪,就可以实现对图片的裁剪。

    我们来裁剪一下

    roi=img[100:1000,100:500,:]
    io.imsave('liuyifei_3.jpg',roi)
    
    liuyifei_3.jpg

    只剩下刘亦菲美丽的眼睛和鼻子了。

    我们展示单个管道

    roi=img[:,:,0]
    roi=img[:,:,1]
    roi=img[:,:,2]
    io.imsave('liuyifei_4.jpg',roi)
    
    管道1 管道2 管道3

    老铁们能看出区别么????

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

    reddish = img[:, :, 0] >170
    img[reddish] = [0, 255, 0]
    io.imsave('liuyifei_5.jpg',img)
    

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


    liuyifei_5 (2).jpg

    美女与野兽啊!!!!

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

    在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
    

    首先来看一下我们默认的类型

    print(img.dtype.name)
    是uint8
    

    我们来转换一下:(转换成float)

    dst=img_as_float(img)
    io.imsave('liuyifei_6.jpg',dst)
    

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


    liuyifei_6.jpg

    看结果好像差不多。

    看看函数

    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转hsv

    from 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.imsave('liuyifei_7.jpg',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: 新的图片尺寸

    我们还是以刘亦菲为例

    dst=transform.resize(img, (80, 60,3))
    plt.figure('resize')
    plt.subplot(121)
    plt.title('before resize')
    plt.subplot(122)
    plt.title('before resize')
    plt.savefig('liuyifei_8.jpg')
    
    liuyifei_8.jpg

    将camera图片由原来的1200*1200大小,变成了80x60大小。从图中的坐标尺,我们能够看出来:
    有点der 😄!!!
    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倍
    

    3、旋转 rotate

    skimage.transform.rotate(image, angle[, ...],resize=False)
    

    angle参数是个float类型数,表示旋转的度数
    resize用于控制在旋转时,是否改变大小 ,默认为False

    print(img.shape) #图片原始大小
    img1=transform.rotate(img, 60) #旋转60度,不改变大小 
    img2=transform.rotate(img, 30,resize=True) #旋转30度,同时改变大小
    print(img2.shape) 
    plt.figure('resize')
    plt.subplot(121)
    plt.imshow(img1,plt.cm.gray)
    plt.title('rotate 60')
    plt.subplot(122)
    plt.imshow(img2,plt.cm.gray)
    plt.title('rotate 30')
    plt.savefig('liuyifei_7.jpg')
    
    liuyifei_7.jpg

    最后结束语,希望大家多多学习,很有意思。

    生活很好,有你更好!!!

    相关文章

      网友评论

          本文标题:python图像处理之----skimage(调戏一下我们的女神

          本文链接:https://www.haomeiwen.com/subject/yqfghltx.html