Python圖像處理(一)基本操作


(僅個人學習摘抄)

腳本命名千萬千萬不要用python里面的庫還有什么文件名一樣,會出錯!!!

1. PIL


注意:打開圖片的時候,地址斜杠再Python中有其他含義,所以要避免歧義

更多函數使用:https://effbot.org/imagingbook/image.htm

 

 

2. Matplotlib

2.1 繪制圖像、點和線



輸出的圖片,y 軸的原點是從上面往下的增加的,和其他的坐標系顯示有點不一樣。因為在 PyLab 庫中,左上角是坐標原點

圖像中,點標記和線默認的顏色是藍色。

不顯示坐標軸:axis('off')




matplotlib_1.py

 

2.2 圖像輪廓和直方圖

繪制圖像的輪廓(或者其他二位函數的等輪廓線),首先需要將圖像灰度化:

from PIL import Image from pylab import *

# 讀取圖像到數組
im = array(Image.open(r'D:\test\pic\test.jpg').convert('L')) # 新建一個圖像
figure() # 不使用顏色信息
gray() # 在原點的左上角顯示輪廓圖像
contour(im, origin='image') axis('equal') axis('off') # 直方圖
figure() hist(im.flatten(),128) show()

 


  圖像的直方圖用來表征圖像像素值的分布情況。用一定數目的小區間(bin)來指定表征像素值的范圍,每個小區間會得到落入該小區間表示范圍的像素數目。圖像的直方圖可以使用 hist() 函數繪制:

    figure()

    hist(im.flatten(),128)

    show()

hist() 函數的第二個參數指定小區間的數目。因為 hist() 只接受一維數組作為輸入,所以在繪制直方圖之前,必須先對圖像進行壓平處理。flatten() 將任意數組按照行優先准則轉換成一維數組。

 

2.3 交互式標注

通過 ginput() 函數實現交互式標注

# 交互式標注
from PIL import Image from pylab import * im = array(Image.open(r'D:\test\pic\test.jpg')) imshow(im) print('Please click 3 plots') x = ginput(3) print('you click:', x) show()

在繪圖區域單擊三次,將會顯示點擊的坐標。

 

 

3. NumPy

3.1 圖像數組表示

  調用 array() 方法將圖像轉換成 NumPy 的數組對象,NumPy 中的數組十多維的,可以用來表示向量、矩陣和圖像。



  每行的第一個元組表示圖像數組的大小(行、列、顏色通道),緊接着的字符串表示數組元素的數據類型。因為圖像通常被編碼成無符號八位整數(uint8),所以在第一種情況下,對圖像進行灰度化處理,並且在創建數組時使用額外的參數 “f”;該參數將數據類型轉換為浮點型。注意:由於灰度圖像沒有顏色信息,所以在形狀元組中,它只有兩個數值

  數組中的元素可以使用下標訪問。位於坐標 i、j,以及顏色通道 k 的像素值可以如下訪問:

    value = im[i, j, k]

  多個數組元素可以使用數組切片方式訪問。切片方式返回的是以指定間隔下標訪問該數組的元素值。


如果僅使用一個下標,則該下標為行下標。

 

3.2 灰度變換


array() 變換的相反操作用 PIL 的 fromarray() 函數完成:

    pil_im = Image.fromarray(im)

如果之前一些操作將“uint8”數據類型轉換為其他數據類型,那么在創建 PIL 圖像之前,需要將數據類型轉換回來:

    pil_im = Image.fromarray(uint8(im))

如果不確定輸入的數據類型,安全起見,先轉換回來。注意,NumPy 總是將數組數據類型轉換成能夠表示數據的“最低”數據類型。對浮點數做乘積或除法操作會使整數類型的數組變成浮點型。

 

3.3 圖像縮放


 

3.4 直方圖均衡化

  直方圖均衡化是指將一幅圖像的灰度直方圖變平,使變換后的圖像中每個灰度值的分布概率都相同。直方圖均衡化是對圖像灰度值進行歸一化的非常好的方法,並且可以增強圖像的對比度

  直方圖均衡化的變換函數是圖像中像素值的累積分布函數(cumulative distribution function,簡稱為 cdf,將像素值的范圍映射到目標范圍的歸一化操作)。


  interp() 函數有兩個輸入參數,一個是灰度圖像,一個是直方圖中使用小區間的數目。函數返回直方圖均衡化后的圖像,和用來做像素值映射的累積分布函數。cdf[-1] 是為了歸一化到 0~1 范圍。

  直方圖均衡化后圖像的對比度增強了,原先圖像灰色區域的細節變得清晰

調用其他 .py 文件中的函數,如上例:

① import imtools

  imtools.histeq(im)

② from imtools import histeq

  histeq(im)

 

3.5 圖像平均

  圖像平均是減少噪聲的一種簡單的方式。


  該函數可以直接跳過不能打開的圖像。還可以使用 mean() 函數計算平均圖像。mean() 函數需要將所有的圖像堆積到一個數組中,比較占內存。

 

3.6 圖像的主成分分析(PCA)

  PCA(Principal Component Analysis,主成分分析)是一個非常有用的降維技巧。它可以在使用盡可能少維數的前提下,盡量多的保持訓練數據的信息。一幅 100 X 100 像素的小灰度圖像,也有 10000維,可以看成 10000 維空間中的一個點。PCA 產生的投影矩陣可以被視為將原始坐標變換到現有的坐標系,坐標系中的各個坐標按照重要性遞減排列。

  為了對圖像數據進行 PCA 變換,圖像需要轉換成一維向量表示,可以用 NumPy 類庫中的 flatten() 進行變換

  將變平的圖像堆積起來,可以得到一個矩陣,矩陣的一行表示一幅圖像。在計算主方向之前,所有的行圖像按照平均圖像進行了中心化。我們通常使用 SVD(Singular Value Decomposition,奇異值分解)方法來計算主成分;但當矩陣維數很大時,SVD 的計算非常慢,此時通常不用 SVD 分解。

PCA 操作代碼:

from PIL import Image from numpy import *

def pca(x): ''' 主成分分析: 輸入:矩陣 X,其中該矩陣中存儲訓練數據,每一行為一條訓練數據 返回:投影矩陣(按照維度的重要性排序)、方差和均值'''

    # 獲取維數
    num_data,dim = X.shape # 數據中心化
    mean_X = X.mean(axis=0) X = X - mean_X if dim>num_data: # PCA-使用緊致技巧
    M = dot(X,X.T)            # 協方差矩陣
    e,EV = linalg.eigh(M)    # 特征值和特征向量
    tmp = dot(X.T,EV).T     # 這就是緊致技巧
    V = tmp[::-1]            # 由於最后的特征向量是我們所需要的,所以需要將其逆轉
    S = aqrt(e)[::-1]        # 由於特征值是按照遞增順序排列的,所以需要將其逆轉
    for i in range(V.shape[1]): V[:i] /= S else: # PCA- 使用 SVD 方法
        U,S,V = linalg.svd(X) V = V[:num_data]    # 僅僅返回前 num_data 維數據才合理

# 返回投影矩陣、方差和均值
return V,S,mean_X

  該函數首先通過減去每一維的均值將數據中心化,然后計算協方差矩陣對應最大特征值的特征向量,此時可以使用簡明的技巧或者 SVD 分解。這里使用 range() 函數,該函數的輸入參數為一個整數 n,函數返回整數 0~(n-1) 的一個列表。也可以使用 arange() 函數返回一個數組,xrange() 函數返回一個產生器(可能會提升速度)。

  如果數據個數小於向量的維數,不用 SVD 分解,而是計算維數更小的協方差矩陣 XXT 的特征向量。通過計算對應前 k(k 是降維后的維數)最大特征值的特征向量,可以使上面的 PCA 操作更快。

  對圖像進行 PCA 變換,圖像的名稱保存在列表 imlist 中,調用 pca.py 文件。


  圖像從一維表示轉換成二維圖像,使用 reshape() 函數。

 

3.7 pickle 模塊

  pickle 模塊可以接受幾乎所有的 Python 對象,並將其轉換成字符串表示,該過程叫做封裝(pickling)。從字符串表示中重構該對象,稱為拆封(unpickling)。這些字符串表示可以方便的存儲和傳輸。

例如保存上面的圖像的平均圖像和主成分:


在上例中,許多對象可以保存在同一個文件中。pickle 模塊中有很多不同的協議可以生成 ,pkl 文件;如果不確定的話,最好以二進制文件的形式讀取和寫入。在其他 Python 會話中載入數據,只需要使用 load() 方法:


  注意,載入對象的順序必須和先前保存的一樣。Python 中有一個用 C 語言寫的優化版本,叫做 cpickle 模塊,該模塊和標准 pickle 模塊完全兼容。

  使用 with 語句處理文件的讀寫操作,可以自動打開和關閉文件(即使在文件打開時發生錯誤)。


 

 

4. SciPy

4.1 圖像模糊

  圖像的高斯模糊是非常經典的圖像卷積例子。本質上,圖形模糊就是將(灰度)圖像 I 和一個高斯核進行卷積操作:

        Iσ = I*Gσ

其中 * 表示卷積操作;Gσ 是標准差為 σ 的二維高斯核,定義為:

        

高斯模糊通常是圖像處理操作的一部分,比如圖像插值操作,興趣點計算等。

  SciPy 有用來做濾波操作的 scipy.ndimage.filters 模塊。該模塊使用快速一維分離的方式來計算卷積。

代碼:scipy_1.py 

 

 

4.2 圖像導數

  圖像強度變化情況可以用灰度圖像 I 的 x 和 y 方向導數 IxIy 描述。


  圖像導數大多數可以通過卷積簡單的實現:

      Ix=I*DxIy = I*Dy

對於 Dx 和 Dy,通常選擇 Prewitt 濾波器:

    

Sobel 濾波器:

    

 

from PIL import Image from numpy import *
from pylab import *
from scipy.ndimage import filters im = array(Image.open(r'D:\test\pic\test.jpg').convert('L')) # Sobel 導數濾波器
imx = zeros(im.shape) filters.sobel(im,1,imx) imy = zeros(im.shape) filters.sobel(im,0,imy) magnitude = sqrt(imx**2+imy**2)

 

  Sobel() 函數第二個參數用來表示選擇 x 或者 y 方向導數(1:x 方向;0:y 方向),第三個參數保存輸出的變量。正導數顯示為亮的像素,負導數顯示為暗的像素,灰色區域表示導數的值接近於零

缺點:濾波器的尺寸需要隨着圖像分辨率的變化而變化。為了在噪聲方面更加穩健,以及在任意尺度上計算導數,使用高斯導數濾波器。

        Ix=I*GσxIy = I*Gσy

其中,Gσx 和 Gσy 表示 Gσ 在 x 和 y 方向上的導數,Gσ 為標准差為 σ 的高斯函數。


  該函數的第三個參數指定對每個方向計算哪種類型的導數,第二個參數為使用的標准差。

 

4.3 形態學:對象計數

  形態學(或數學形態學)是度量和分析基本形狀的圖像處理方法的基本框架與集合。形態學通常用於處理二值圖像,但是也能夠用於灰度圖像。二值圖像是指圖像的每個像素只能取兩個值,通常是 0 和 1。二值圖像通常是,在計算物體的數目,或者度量其大小時,對一幅圖像進行閾值化后的結果。

 

4.4 一些有用的 SciPy 模塊

1、讀寫 .mat 文件

讀取 Matlab 的 .mat 文件,使用 scipy.io 模塊:

    data = scipy.io.loadmat('test.mat')

data 對象包含一個字典,字典中的鍵對應於保存在原始 ,mat 文件中的變量名。由於這些變量是數組格式的,因此可以很方便的保存到 ,mat 文件中。只需創建一個字典(其中包含你想要保存的所有變量),然后使用 savemat() 函數:

    data = {}

    data['X'] = x

    scipy.io.savemat('test.mat',data)

上面保存的是數組 x,所以當讀到 Matlab 中時,變量的名字仍為 x。

2、以圖象形式保存數組

imsave() 函數可以從 scipy.misc 模塊中載入。要將數組 im 保存到文件中,可以使用下面的命令:

    from scipy.misc import imsave

    imsave('test.jpg',im)

scipy.misc 模塊同樣包含了著名的 Lena 測試圖像:

    lena = scipy.misc.lena()

返回一個 512X512 的灰度圖像數組。

 

 

5. 圖像去噪

  ROF(Rudin-Osher-Fatemi)去噪模型。ROF 模型具有很好的性質:處理后的圖像更平滑,同時保持圖像邊緣和結構信息。

ROF 模型去噪:

 

from numpy import *

def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100): ''' 使用 A.Chanbolle(2005)在公式(11)中的計算步驟實現 Rudin-Osher-Fatemi(ROF)去噪模型 輸入:含有噪聲的輸入圖像(灰度圖像)、U的初始值、YV正則項權值、步長、停業條件 輸出:去噪和去除紋理后的圖像、紋理殘留''' m,n = im.shape        # 噪聲圖像的大小

    # 初始化
    U = U_init Px = im         # 對偶域的 x 分量
    Py = im         # 對偶域的 y 分量
    error = 1

    while(error > tolerance): Uold = U # 原始變量的梯度
        GradUx = roll(U,-1,axis=1)-U         # 變量 U 梯度的 x 分量
        GradUy = roll(U,-1,axis=0)-U         # 變量 U 梯度的 y 分量

        # 更新對偶變量
        PxNew = Px + (tau/tv_weight)*GradUx PyNew = Py + (tau/tv_weight)*GradUy NormNew = maximum(1,sqrt(PxNew**2+PyNew**2)) Px = PxNew/NormNew        # 更新 x 分量(對偶)
        Py = PyNew/NormNew        # 更新 y 分量(對偶)

        # 更新原始變量
        RxPx = roll(Px,1,axis=1)        # 對 x 分量進行向右 x 軸平移
        RyPy = roll(Py,1,axis=0)        # 對 y 分量進行向右 y 軸平移
 DivP = (Px-RxPx)+(Py-RyPy)        # 對偶域的散度
        U = im + tv_weight*DivP            # 更新原始變量

        # 更新誤差
        error = linalg.norm(U-Uold)/sqrt(n*m) return U,im-U            # 去噪后的圖像和紋理殘留

 

利用一個合成圖像調用該函數:

from PIL import Image from numpy import *
from numpy import random from scipy.ndimage import filters import rof # 使用噪聲創建合成圖像
im = zeros((500,500)) im[100:400,100:400] = 128 im[200:300,200:300] = 255 im = im + 30*random.standard_normal((500,500)) G = filters.gaussian_filter(im,10) U,T = rof.denoise(im,im) im = Image.fromarray(im) im.show() G = Image.fromarray(G) G.show() U = Image.fromarray(U) U.show()

ROF 模型去噪后的圖像保留了邊緣和圖像的結構信息,同時模糊了“噪聲”。

 

 

學習書目《Python計算機視覺編程》


免責聲明!

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



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