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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM