zoukankan      html  css  js  c++  java
  • python数字图像处理(一)图像的常见操作

    首先导入必要的库,使用Opencv读入图像,避免复杂的图像解析,同时使用Opencv作为算法的对比,由于使用环境为jupyter使用matplotlib直接可视化

    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    %matplotlib inline
    

    图片的存储

    图片实质上就是一个矩阵,一个640*320的灰白图像其实就是一个(640,320)的矩阵,每个坐标点的值就代表该像素点的灰度。

    通常我们使用0-256的值来表示灰度的深浅,在三通道图像中表达某个通道的深浅,256=16*16,一次当我们使用16进制来表达三通道图像某个像素点的颜色时通常写作#ffffab这种形式

    图片平移

    图片平移是最简单的操作了,直接使用坐标加减即可。
    例如将一个图像向右平移10个像素点实质就是把所有的像素点的横坐标加上10,如(0,0)坐标就会变成(10,0)。
    在下图的实现中不对图像进行resize,超出图像原本范围的全部舍弃。

    #使用matplotlib来显示opencv读取的图像
    dave = cv2.imread("dave.jpg")
    dave = cv2.cvtColor(dave,cv2.COLOR_BGR2RGB)
    plt.axis("off")
    plt.imshow(dave)
    plt.show()
    

    def translate(src,translate_x,translate_y):
        src_h,src_w = src.shape[:2]
        dst = np.zeros(src.shape,dtype=np.uint8)
        for row in range(src_h):
            for col in range(src_w):
                h = int(row-translate_y)
                w = int(col-translate_x)
                if h<src_h and h>=0 and w<src_w and w>=0:
                    dst[row][col] = src[h][w]
        return dst
    new_image = translate(dave,50,30)
    plt.axis("off")
    plt.imshow(new_image)
    plt.show()
    

    图片缩放

    在不考虑复用性的前提下,实验性的进行不插值缩放

    #在该代码中由于opencv读取默认为BGR将其转化为RGB图像
    gss = cv2.imread("Green_Sea_Shell.png")
    gss = cv2.cvtColor(gss,cv2.COLOR_BGR2RGB)
    plt.axis("off")
    plt.imshow(gss)
    plt.show()
    

    我们试着打印图像的分辨率,发现其为160*160的三通道图像,然后开始我们的实验性缩放

    print(gss.shape)
    #此处使用uint8格式的数据类型,gss将其转化为uint16是考虑到超出255的范围会出错,在结束后将其转化为原先的uint8类型
    new_image = np.zeros((80,80,3),dtype=np.uint8)
    gss = gss.astype(np.uint16)
    
    (160, 160, 3)
    

    试着直接比较两种插值,简化边界条件直接取四邻域均值
    > PS:此处这并非线性插值,只是取其领域均值看相比直接的缩放是否会减少其锯齿感

    for i in range(80):
        for j in range(80):
            #取靠近的四个像素点颜色的均值
            pixel_sum = gss[i*2+1][j*2]+gss[i*2-1][j*2]+gss[i][j*2+1]+gss[i][j*2-1]+gss[i*2][j*2]
            new_image[i][j] = (pixel_sum)/5
    plt.subplot(131)
    plt.axis("off")
    plt.title("Average")
    plt.imshow(new_image)
    
    for i in range(80):
        for j in range(80):
            new_image[i][j] = gss[i*2][j*2]
    plt.subplot(132)
    plt.axis("off")
    plt.imshow(new_image)
    plt.title("non_linear")
    
    gss = gss.astype(np.uint8)
    new_image = cv2.resize(gss,(80,80))
    plt.subplot(133)
    plt.title("opencv resize")
    plt.axis("off")
    plt.imshow(new_image)
    plt.show()
    

    从左至右分别为 均值插值 非线性差值 opencv实现的resize

    可以看出opencv默认的差值与非线性插值的区别,非线性插值的锯齿感更为明显

    new_image = np.zeros((320,320,3),dtype=np.uint8)
    for i in range(320):
        for j in range(320):
            new_image[i][j] = gss[int(i/2)][int(j/2)]
    plt.subplot(121)
    plt.axis("off")
    plt.title("non_linear")
    plt.imshow(new_image)
    plt.subplot(122)
    plt.axis("off")
    plt.title("origin")
    plt.imshow(gss)
    plt.show()
    

    上图为将其非线性插值放大,与原图的对比

    对上面的代码进行整理,处理下边界条件实现的不插值resize

    def resize(orign_image,shape):
        src_height, src_width = orign_image.shape[:2]
        scale_height, scale_width = shape
        sw = src_width/scale_width
        sh = src_height/scale_height
        if len(orign_image.shape)<3:
            scale_image = np.zeros(shape,dtype=np.uint8)
        else:
            scale_image = np.zeros((shape[0],shape[1],orign_image.shape[2]),dtype=np.uint8)
        def ceil(length, bound):
            if length>=bound:
                return int(bound-1)
            elif length<0:
                return 0
            else:
                return int(length)
        for i in range(scale_height):
            for j in range(scale_width):
                scale_image[i][j] = orign_image[ceil(i*sh,src_height)][ceil(j*sw,src_width)]
        return scale_image
    

    使用上面的代码可以实现不插值缩放,但是其复用性不强,将坐标变换单独抽离出来,实现下面的线性插值

    def bilinear_interpolate(im, y, x):
        x = np.asarray(x)
        y = np.asarray(y)
    
        x0 = np.floor(x).astype(int)
        x1 = x0 + 1
        y0 = np.floor(y).astype(int)
        y1 = y0 + 1
    
        x0 = np.clip(x0, 0, im.shape[1]-1);
        x1 = np.clip(x1, 0, im.shape[1]-1);
        y0 = np.clip(y0, 0, im.shape[0]-1);
        y1 = np.clip(y1, 0, im.shape[0]-1);
    
        Ia = im[ y0, x0 ]
        Ib = im[ y1, x0 ]
        Ic = im[ y0, x1 ]
        Id = im[ y1, x1 ]
    
        wa = (x1-x) * (y1-y)
        wb = (x1-x) * (y-y0)
        wc = (x-x0) * (y1-y)
        wd = (x-x0) * (y-y0)
    
        return wa*Ia + wb*Ib + wc*Ic + wd*Id
    
    def bilinear_resize(src,dsize):
        src_h,src_w = src.shape[:2]
        fh = dsize[0]/src_h
        fw = dsize[1]/src_w
        if len(src.shape)>3:
            dst = np.zeros(dsize,dtype=np.uint8)
        else:
            dst = np.zeros(dsize+src.shape[2:],dtype=np.uint8)
        for row in range(dst.shape[0]):
            for col in range(dst.shape[1]):
                dst[row][col] = bilinear_interpolate(src,row/fh,col/fw)
        return dst
    

    使用著名的lenna图来作为两种插值方式的对比

    lenna = cv2.imread("lena.jpg")
    lenna = cv2.cvtColor(lenna,cv2.COLOR_BGR2RGB)
    plt.imshow(lenna)
    plt.axis("off")
    plt.show()
    print(lenna.shape)
    

    (512, 512, 3)
    
    resize_lenna = cv2.resize(lenna,(64,64))
    plt.axis("off")
    plt.imshow(resize_lenna)
    plt.show()
    

    nearst_lenna = resize(lenna,(512,512))
    plt.subplot(121)
    plt.axis("off")
    plt.imshow(nearst_lenna)
    bilinear_lenna = bilinear_resize(lenna,(512,512))
    plt.subplot(122)
    plt.axis("off")
    plt.imshow(bilinear_lenna)
    plt.show()
    

    图片旋转的实现

    在下面的代码中使用numpy进行矩阵操作完成了图片的旋转

    首先简单的使用书上的旋转矩阵将原坐标映射到新的坐标上

    def rotate_image(src,rotate_angel):
        src_h, src_w = src.shape[:2]
        dsize = src.shape
        dst = np.zeros(src.shape,dtype=np.uint8)
        def _rotate_coodinate(x,y,angel):
            import math
            angel = angel/180*math.pi
            coodinate = np.array([x,y,1])
            rotate_matrix = np.array([[math.cos(angel),math.sin(angel),1],[-math.sin(angel),math.cos(angel),1],[0,0,1]])
            coodinate = coodinate.dot(rotate_matrix)
            x,y,_ = coodinate
            return int(x),int(y)
        for row in range(src_h):
            for col in range(src_w):
                dst_x,dst_y = _rotate_coodinate(col,row,rotate_angel)
                if dst_x < 0 or dst_x >= src_w or dst_y<0 or dst_y>=src_h:
                    pass
                else:
                    dst[dst_y][dst_x] = src[row][col]
        return dst
    new_image = rotate_image(lenna,-40)
    plt.axis("off")
    plt.imshow(new_image)
    plt.show()
    

    此处代码的缺陷有2点:
    * 图片偏离中心
    * 未反向映射导致,float转int时图像存在黑点
    对上述代码进行修改,进行反向映射.
    使用矩阵将其移动回中心,同时旋转矩阵为之前的逆

    def rotate_image(src,rotate_angel):
        src_h, src_w = src.shape[:2]
        dsize = src.shape
        dst = np.zeros(src.shape,dtype=np.uint8)
        def _rotate_coodinate(x,y,angel):
            import math
            angel = angel/180*math.pi
            coodinate = np.array([x,y,1])
            rotate_matrix = np.array([[math.cos(angel),-math.sin(angel),0],[math.sin(angel),math.cos(angel),0],[0,0,1]])
            rotate_center_first = np.array([[1,0,0],[0,-1,0],[-0.5*dsize[1],0.5*dsize[0],1]])
            rotate_center_last = np.array([[1,0,0],[0,-1,0],[0.5*dsize[1],0.5*dsize[0],1]])
            coodinate = coodinate.dot(rotate_center_first).dot(rotate_matrix).dot(rotate_center_last)
            x,y,_ = coodinate
            return int(x),int(y)
        for row in range(src_h):
            for col in range(src_w):
                dst_x,dst_y = _rotate_coodinate(col,row,rotate_angel)
                if dst_x < 0 or dst_x >= src_w or dst_y<0 or dst_y>=src_h:
                    pass
                else:
                    dst[row][col] = src[dst_x][dst_y]
        return dst
    
    new_image = rotate_image(lenna,-40)
    plt.imshow(new_image)
    plt.axis("off")
    plt.show()
    

    仿射变换

    垂直变换

    将单个点的坐标(x,y)转换为下面矩阵

    [egin{bmatrix} x & y & 1end{bmatrix} ]

    乘上下面矩阵进行垂直方向的偏移变换

    [egin{bmatrix} 1 & 0 & 0\ s_v & 1 & 0\ 0 & 0 & 1 end {bmatrix} ]

    def transform(src,s_v):
        src_h, src_w = src.shape[:2]
        dsize = src.shape
        dst = np.zeros(src.shape,dtype=np.uint8)
        def _vertical_transform(x,y,s_v):
            coodinate = np.array([x,y,1])
            transform_matrix = np.array([[1,0,0],[s_v,1,0],[0,0,1]])
            x,y,_ = coodinate.dot(transform_matrix)
            if x>=dsize[1]:
                x = dsize[1]-1
            if x<0:
                x = 0
            if y>=dsize[0]:
                y = dsize[0]-1
            if y<0:
                y = 0
            return int(x),int(y)
        for row in range(dst.shape[0]):
            for col in range(dst.shape[1]):
                dst_x,dst_y = _vertical_transform(col,row,s_v)
                dst[dst_y][dst_x] = src[row][col]
        return dst
    
    new_image = transform(lenna,0.5)
    plt.imshow(new_image)
    plt.axis("off")
    plt.show()
    

    水平变换

    def transform(src,s_h):
        src_h, src_w = src.shape[:2]
        dsize = src.shape
        dst = np.zeros(src.shape,dtype=np.uint8)
        def _vertical_transform(x,y,s_v):
            coodinate = np.array([x,y,1])
            transform_matrix = np.array([[1,s_h,0],[0,1,0],[0,0,1]])
            x,y,_ = coodinate.dot(transform_matrix)
            if x>=dsize[1]:
                x = dsize[1]-1
            if x<0:
                x = 0
            if y>=dsize[0]:
                y = dsize[0]-1
            if y<0:
                y = 0
            return int(x),int(y)
        for row in range(dst.shape[0]):
            for col in range(dst.shape[1]):
                dst_x,dst_y = _vertical_transform(col,row,s_h)
                dst[dst_y][dst_x] = src[row][col]
        return dst
    
    new_image = transform(lenna,0.3)
    plt.imshow(new_image)
    plt.axis("off")
    plt.show()
    

  • 相关阅读:
    数据可视化基础专题(九):Matplotlib 基础(一)坐标相关
    PrinterInfo (API: Objects) – Electron 中文开发手册
    Java中的多线程
    简介 (Service Worker) – Angular 中文开发手册
    :不确定 | :indeterminate (Basic User Interface)
    高度 | height (Basic Box Model)
    高度 | @viewport.height (Device Adaptation)
    高度 | @media.height (Media Queries)
    首字 | initial-letter (Inline Layout)
    颜色索引 | @media.color-index (Media Queries)
  • 原文地址:https://www.cnblogs.com/lynsyklate/p/7840973.html
Copyright © 2011-2022 走看看