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)转换为下面矩阵

\[\begin{bmatrix} x & y & 1\end{bmatrix} \]

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

\[\begin{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()

posted @ 2017-11-15 21:10  lynskylate  阅读(11172)  评论(0编辑  收藏  举报