首頁 > 軟體

python skimage影象處理

2022-06-28 18:05:23

引言

基於python指令碼語言開發的數點陣圖片處理包,比如PIL,Pillow, opencv, scikit-image等。
PIL和Pillow只提供最基礎的數位影像處理,功能有限;

opencv實際上是一個c++庫,只是提供了python介面,更新速度非常慢。scikit-image是基於scipy的一款影象處理包,它將圖片作為numpy陣列進行處理,正好與matlab一樣,因此,我們最終選擇scikit-image進行數位影像處理。

scikit-image進行數位影像處理

Image讀出來的是PIL的型別,而skimage.io讀出來的資料是numpy格式的

import Image as img
import os
from matplotlib import pyplot as plot
from skimage import io,transform
#Image和skimage讀圖片
img_file1 = img.open('./CXR_png/MCUCXR_0042_0.png')
img_file2 = io.imread('./CXR_png/MCUCXR_0042_0.png')

輸出可以看出Img讀圖片的大小是圖片的(width, height);而skimage的是(height,width, channel), [這也是為什麼caffe在單獨測試時要要在程式碼中設定:transformer.set_transpose('data',(2,0,1)),因為caffe可以處理的圖片的資料格式是(channel,height,width),所以要轉換資料]

#讀圖片後資料的大小:
print "the picture's size: ", img_file1.size
print "the picture's shape: ", img_file2.shape
the picture's size:  (4892, 4020)
the picture's shape:  (4020, 4892)
#得到畫素:
print(img_file1.getpixel((500,1000)), img_file2[500][1000])
print(img_file1.getpixel((500,1000)), img_file2[1000][500])
print(img_file1.getpixel((1000,500)), img_file2[500][1000])

(0, 139)
(0, 0)
(139, 139)

Img讀出來的圖片獲得某點畫素用getpixel((w,h))可以直接返回這個點三個通道的畫素值
skimage讀出來的圖片可以直接img_file2[0][0]獲得,但是一定記住它的格式,並不是你想的(channel,height,width)

圖片資訊

如果我們想知道一些skimage圖片資訊

from skimage import io, data
img = data.chelsea()
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])#影象的畫素值

PIL image 檢檢視片資訊,可用如下的方法

print type(img)
print img.size  #圖片的尺寸
print img.mode  #圖片的模式
print img.format  #圖片的格式
print(img.getpixel((0,0)))#得到畫素:
#img讀出來的圖片獲得某點畫素用getpixel((w,h))可以直接返回這個點三個通道的畫素值
# 獲取影象的灰度值範圍
width = img.size[0]
height = img.size[1]
# 輸出圖片的畫素值
count = 0 
for i in range(0, width):
    for j in range(0, height):
        if img.getpixel((i, j))>=0 and img.getpixel((i, j))<=255:
            count +=1
print count
print(height*width)

skimage提供了io模組,顧名思義,這個模組是用來圖片輸入輸出操作的。為了方便練習,也提供一個data模組,裡面巢狀了一些範例圖片,我們可以直接使用。

skimage包的子模組

skimage包的全稱是scikit-image SciKit (toolkit for SciPy) ,它對scipy.ndimage進行了擴充套件,提供了更多的圖片處理功能。它是由python語言編寫的,由scipy 社群開發和維護。skimage包由許多的子模組組成,各個子模組提供不同的功能。主要子模組列表如下:

子模組名稱                 主要實現功能
io                            讀取、儲存和顯示圖片或視訊
data                       提供一些測試圖片和樣本資料
color                           顏色空間變換
filters             影象增強、邊緣檢測、排序濾波器、自動閾值等
draw               操作於numpy陣列上的基本圖形繪製,包括線條、矩形、圓和文字等
transform          幾何變換或其它變換,如旋轉、拉伸和拉東變換等
morphology          形態學操作,如開閉運算、骨架提取等
exposure              圖片強度調整,如亮度調整、直方圖均衡等
feature                        特徵檢測與提取等
measure                  影象屬性的測量,如相似性或等高線等
segmentation                          影象分割
restoration                           影象恢復
util                                  通用函數

從外部讀取圖片並顯示

讀取單張彩色rgb圖片,使用skimage.io.imread(fname)函數,帶一個引數,表示需要讀取的檔案路徑。顯示圖片使用skimage.io.imshow(arr)函數,帶一個引數,表示需要顯示的arr陣列(讀取的圖片以numpy陣列形式計算)。

from skimage import io
img=io.imread('d:/dog.jpg')
io.imshow(img)

讀取單張灰度圖片,使用skimage.io.imread(fname,as_grey=True)函數,第一個引數為圖片路徑,第二個引數為as_grey, bool型值,預設為False

from skimage import io
img=io.imread('d:/dog.jpg',as_grey=True)
io.imshow(img)

程式自帶圖片

skimage程式自帶了一些範例圖片,如果我們不想從外部讀取圖片,就可以直接使用這些範例圖片:

astronaut     航員圖片      coffee     一杯咖啡圖片   
lena          lena美女圖片   camera   拿相機的人圖片   
coins           硬幣圖片     moon    月亮圖片
checkerboard   棋盤圖片       horse   馬圖片   
page   書頁圖片              chelsea   小貓圖片     
hubble_deep_field    星空圖片   text   文字圖片
clock    時鐘圖片   immunohistochemistry   結腸圖片     

顯示這些圖片可用如下程式碼,不帶任何引數

from skimage import io, data
img=data.lena()
io.imshow(img)

圖片名對應的就是函數名,如camera圖片對應的函數名為camera(). 這些範例圖片存放在skimage的安裝目錄下面,路徑名稱為data_dir,我們可以將這個路徑列印出來看看

from skimage import data_dir
print(data_dir)

儲存圖片

使用io模組的imsave(fname,arr)函數來實現。第一個參數列示儲存的路徑和名稱,第二個參數列示需要儲存的陣列變數。

from skimage import io,data
img=data.chelsea()
io.imshow(img)
io.imsave('d:/cat.jpg',img)

儲存圖片的同時也起到了轉換格式的作用。如果讀取時圖片格式為jpg圖片,儲存為png格式,則將圖片從jpg圖片轉換為png圖片並儲存。

影象畫素的存取與裁剪

圖片讀入程式中後,是以numpy陣列存在的。因此對numpy陣列的一切功能,對圖片也適用。對陣列元素的存取,實際上就是對圖片畫素點的存取。

彩色圖片存取方式為:img[i,j,c]

i表示圖片的行數,j表示圖片的列數,c表示圖片的通道數(RGB三通道分別對應0,1,2)。座標是從左上角開始。

灰度圖片存取方式為:gray[i,j]

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

除了對畫素進行讀取,也可以修改畫素值。

例3:對小貓圖片隨機新增椒鹽噪聲

from skimage import io,data
import numpy as np
img=data.chelsea()
#隨機生成5000個椒鹽
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.imshow(img)

這裡用到了numpy包裡的random來生成亂數,randint(0,cols)表示隨機生成一個整數,範圍在0到cols之間。

用img[x,y,:]=255這句來對畫素值進行修改,將原來的三通道畫素值,變為255

通過對陣列的裁剪,就可以實現對圖片的裁剪。

例4:對小貓圖片進行裁剪

from skimage import io,data
img=data.chelsea()
roi=img[80:180,100:200,:]
io.imshow(roi)

對多個畫素點進行操作,使用陣列切片方式存取。切片方式返回的是以指定間隔下標存取 該陣列的畫素值。下面是有關灰度影象的一些例子:

img[i,:] = im[j,:] # 將第 j 行的數值賦值給第 i 行
img[:,i] = 100 # 將第 i 列的所有數值設為 100
img[:100,:50].sum() # 計算前 100 行、前 50 列所有數值的和
img[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
img[i].mean() # 第 i 行所有數值的平均值
img[:,-1] # 最後一列
img[-2,:] (or im[-2]) # 倒數第二行

最後我們再看兩個對畫素值進行存取和改變的例子:

例5:將lena圖片進行二值化,畫素值大於128的變為1,否則變為0

from skimage import io,data,color
img=data.lena()
img_gray=color.rgb2gray(img)
rows,cols=img_gray.shape
for i in range(rows):
    for j in range(cols):
        if (img_gray[i,j]<=0.5):
            img_gray[i,j]=0
        else:
            img_gray[i,j]=1
io.imshow(img_gray)

color模組的rgb2gray()函數

使用了color模組的rgb2gray()函數,將彩色三通道圖片轉換成灰度圖。轉換結果為float64型別的陣列,範圍為[0,1]之間。

將彩色三通道圖片轉換成灰度圖,最後變成unit8, float轉換為unit8是有資訊損失的。

img_path = 'data/dpclassifier/newtrain/test/1_0.png'
import Image as img
import os
from matplotlib import pyplot as plot
from skimage import io,transform, img_as_ubyte
img_file1 = img.open(img_path)
img_file1.show()
img_file2 = io.imread(img_path)
io.imshow(img_file2)
print(type(img_file1),img_file1.mode, type(img_file2),img_file2.shape, img_file2.dtype,img_file2.max(),img_file2.min(),img_file2.mean())
img_file22=skimage.color.rgb2gray(img_file2)
print(type(img_file22),img_file22.shape,img_file22.dtype,img_file22.max(),img_file22.min(),img_file22.mean() )
dst=img_as_ubyte(img_file22)
print(type(dst),dst.shape,dst.dtype, dst.max(), dst.min(), dst.mean())

結果

(<class 'PIL.PngImagePlugin.PngImageFile'>, 'RGB', <type 'numpy.ndarray'>, (420, 512, 3), dtype('uint8'), 255, 0, 130.9983863467262)
(<type 'numpy.ndarray'>, (420, 512), dtype('float64'), 1.0, 0.0, 0.5137191621440242)
(<type 'numpy.ndarray'>, (420, 512), dtype('uint8'), 255, 0, 130.9983863467262)

例6:

from skimage import io,data
img=data.chelsea()
reddish = img[:, :, 0] &gt;170
img[reddish] = [0, 255, 0]
io.imshow(img)

這個例子先對R通道的所有畫素值進行判斷,如果大於170,則將這個地方的畫素值變為[0,255,0], 即G通道值為255,R和B通道值為0。

影象資料型別及顏色空間轉換

在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

一張圖片的畫素值範圍是[0,255], 因此預設型別是unit8, 可用如下程式碼檢視資料型別

from skimage import io,data
img=data.chelsea()
print(img.dtype.name)

在上面的表中,特別注意的是float型別,它的範圍是[-1,1]或[0,1]之間。一張彩色圖片轉換為灰度圖後,它的型別就由unit8變成了float

1、unit8轉float

from skimage import data,img_as_float
img=data.chelsea()
print(img.dtype.name)
dst=img_as_float(img)
print(dst.dtype.name)

2、float轉uint8

from skimage import img_as_ubyte
import numpy as np
img = np.array([0, 0.5, 1], dtype=float)
print(img.dtype.name)
dst=img_as_ubyte(img)
print(dst.dtype.name)

float轉為unit8,有可能會造成資料的損失,因此會有警告提醒。*

除了這兩種最常用的轉換以外,其實有一些其它的型別轉換,如下表:

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]&lt;0.4):
            labels[i,j]=0
        elif(gray[i,j]&lt;0.75):
            labels[i,j]=1
        else:
            labels[i,j]=2
dst=color.label2rgb(labels)
io.imshow(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: 新的圖片尺寸

from skimage import transform,data
import matplotlib.pyplot as plt
img = data.camera()
dst=transform.resize(img, (80, 60))
plt.figure('resize')
plt.subplot(121)
plt.title('before resize')
plt.imshow(img,plt.cm.gray)
plt.subplot(122)
plt.title('before resize')
plt.imshow(dst,plt.cm.gray)
plt.show()

將camera圖片由原來的512x512大小,變成了80x60大小。從下圖中的座標尺,我們能夠看出來:

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倍

結果為:

(512, 512)
(51, 51)
(256, 128)
(1024, 1024)

3、旋轉 rotate

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

angle引數是個float型別數,表示旋轉的度數

resize用於控制在旋轉時,是否改變大小 ,預設為False

from skimage import transform,data
import matplotlib.pyplot as plt
img = data.camera()
print(img.shape) #圖片原始大小
img1=transform.rotate(img, 60) #旋轉90度,不改變大小 
print(img1.shape)
img2=transform.rotate(img, 30,resize=True) #旋轉30度,同時改變大小
print(img2.shape) plt.figure('resize')
plt.subplot(121)plt.title('rotate 60')
plt.imshow(img1,plt.cm.gray)
plt.subplot(122)
plt.title('rotate 30')
plt.imshow(img2,plt.cm.gray)
plt.show()

顯示結果:

4、影象金字塔

以多解析度來解釋影象的一種有效但概念簡單的結構就是影象金字塔。影象金字塔最初用於機器視覺和影象壓縮,一幅影象的金字塔是一系列以金字塔形狀排列的解析度逐步降低的影象集合。金字塔的底部是待處理影象的高解析度表示,而頂部是低解析度的近似。當向金字塔的上層移動時,尺寸和解析度就降低。
在此,我們舉一個高斯金字塔的應用範例,函數原型為:

skimage.transform.pyramid_gaussian(image, downscale=2)

downscale控制著金字塔的縮放比例

import numpy as np
import matplotlib.pyplot as plt
from skimage import data,transform
image = data.astronaut() #載入宇航員圖片
rows, cols, dim = image.shape #獲取圖片的行數,列數和通道數
pyramid = tuple(transform.pyramid_gaussian(image, downscale=2)) #產生高斯金字塔影象#共生成了log(512)=9幅金字塔影象,加上原始影象共10幅,pyramid[0]-pyramid[1]
composite_image = np.ones((rows, cols + cols / 2, 3), dtype=np.double) #生成背景composite_image[:rows, :cols, :] = pyramid[0] #融合原始影象
i_row = 0
for p in pyramid[1:]: 
       n_rows, n_cols = p.shape[:2] 
       composite_image[i_row:i_row + n_rows, cols:cols + n_cols] = p #迴圈融合9幅金字塔影象
       i_row += n_rows
plt.imshow(composite_image)
plt.show()

上右圖,就是10張金字塔影象,下標為0的表示原始影象,後面每層的影象行和列變為上一層的一半,直至變為1

除了高斯金字塔外,還有其它的金字塔,如:

skimage.transform.pyramid_laplacian(image, downscale=2)

對比度與亮度調整

影象亮度與對比度的調整,是放在skimage包的exposure模組裡面

1、gamma調整

原理:I=Ig

對原影象的畫素,進行冪運算,得到新的畫素值。公式中的g就是gamma值。

如果gamma>1, 新影象比原影象暗

如果gamma<1,新影象比原影象亮

函數格式為:

skimage.exposure.adjust_gamma(image, gamma=1)

gamma引數預設為1,原像不發生變化 。

from skimage import data, exposure, img_as_float
import matplotlib.pyplot as plt
image = img_as_float(data.moon())
gam1= exposure.adjust_gamma(image, 2) #調暗
gam2= exposure.adjust_gamma(image, 0.5) #調亮plt.figure('adjust_gamma',figsize=(8,8))
plt.subplot(131)plt.title('origin image')
plt.imshow(image,plt.cm.gray)plt.axis('off')
plt.subplot(132)
plt.title('gamma=2')
plt.imshow(gam1,plt.cm.gray)
plt.axis('off')
plt.subplot(133)
plt.title('gamma=0.5')
plt.imshow(gam2,plt.cm.gray)
plt.axis('off')
plt.show()

2、log對數調整

這個剛好和gamma相反

原理:I=log(I)

from skimage import data, exposure, img_as_float
import matplotlib.pyplot as plt
image = img_as_float(data.moon())
gam1= exposure.adjust_log(image) #對數調整
plt.figure('adjust_gamma',figsize=(8,8))
plt.subplot(121)plt.title('origin image')
plt.imshow(image,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('log')
plt.imshow(gam1,plt.cm.gray)
plt.axis('off')
plt.show()

3、判斷影象對比度是否偏低

函數:is_low_contrast(img)

返回一個bool型值

from skimage import data, exposure
image =data.moon()
result=exposure.is_low_contrast(image)
print(result)

輸出為False

4、調整強度

函數:

skimage.exposure.rescale_intensity(image, in_range='image', out_range='dtype')

in_range 表示輸入圖片的強度範圍,預設為'image', 表示用影象的最大/最小畫素值作為範圍

out_range 表示輸出圖片的強度範圍,預設為'dype', 表示用影象的型別的最大/最小值作為範圍
預設情況下,輸入圖片的[min,max]範圍被拉伸到[dtype.min, dtype.max],如果dtype=uint8, 那麼

dtype.min=0, dtype.max=255

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
mat=exposure.rescale_intensity(image)
print(mat)

輸出為[ 0 127 255]

即畫素最小值由51變為0,最大值由153變為255,整體進行了拉伸,但是資料型別沒有變,還是uint8

前面我們講過,可以通過img_as_float()函數將unit8型別轉換為float型,實際上還有更簡單的方法,就是乘以1.0

import numpy as np
image = np.array([51, 102, 153], dtype=np.uint8)
print(image*1.0)

即由[51,102,153]變成了[ 51. 102. 153.]

而float型別的範圍是[0,1],因此對float進行rescale_intensity 調整後,範圍變為[0,1],而不是[0,255]

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
tmp=image*1.0
mat=exposure.rescale_intensity(tmp)
print(mat)

結果為[ 0. 0.5 1. ]

如果原始畫素值不想被拉伸,只是等比例縮小,就使用in_range引數,如:

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
tmp=image*1.0
mat=exposure.rescale_intensity(tmp,in_range=(0,255))
print(mat)

輸出為:[ 0.2 0.4 0.6],即原畫素值除以255

如果引數in_range的[main,max]範圍要比原始畫素值的範圍[min,max] 大或者小,那就進行裁剪,如:

mat=exposure.rescale_intensity(tmp,in_range=(0,102))
print(mat)

輸出[ 0.5 1. 1. ],即原畫素值除以102,超出1的變為1

如果一個陣列裡面有負數,現在想調整到正數,就使用out_range引數。如:

import numpy as np
from skimage import exposure
image = np.array([-10, 0, 10], dtype=np.int8)
mat=exposure.rescale_intensity(image, out_range=(0, 127))
print(mat)

輸出[ 0 63 127]

直方圖與均衡化

在影象處理中,直方圖是非常重要,也是非常有用的一個處理要素。

在skimage庫中對直方圖的處理,是放在exposure這個模組中。

1、計算直方圖函數:

skimage.exposure.histogram(image, nbins=256)

在numpy包中,也提供了一個計算直方圖的函數histogram(),兩者大同小義。

返回一個tuple(hist, bins_center), 前一個陣列是直方圖的統計量,後一個陣列是每個bin的中間值

import numpy as np
from skimage import exposure,data
image =data.camera()*1.0
hist1=np.histogram(image, bins=2) #用numpy包計算直方圖hist2=exposure.histogram(image, nbins=2) #用skimage計算直方圖
print(hist1)
print(hist2)

輸出:

(array([107432, 154712], dtype=int64), array([ 0. , 127.5, 255. ]))
(array([107432, 154712], dtype=int64), array([ 63.75, 191.25]))

分成兩個bin,每個bin的統計量是一樣的,但numpy返回的是每個bin的兩端的範圍值,而skimage返回的是每個bin的中間值

2、繪製直方圖

繪圖都可以呼叫matplotlib.pyplot庫來進行,其中的hist函數可以直接繪製直方圖。

呼叫方式:

n, bins, patches = plt.hist(arr, bins=10, normed=0, facecolor='black', edgecolor='black',alpha=1,histtype='bar')

hist的引數非常多,但常用的就這六個,只有第一個是必須的,後面四個可選

arr: 需要計算直方圖的一維陣列

bins: 直方圖的柱數,可選項,預設為10

normed: 是否將得到的直方圖向量歸一化。預設為0

facecolor: 直方圖顏色

edgecolor: 直方圖邊框顏色

alpha: 透明度

histtype: 直方圖型別,‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’

返回值 :

n: 直方圖向量,是否歸一化由引數normed設定

bins: 返回各個bin的區間範圍

patches: 返回每個bin裡面包含的資料,是一個list

from skimage import data
import matplotlib.pyplot as plt
img=data.camera()
plt.figure("hist")
arr=img.flatten()
n, bins, patches = plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') 
plt.show()

其中的flatten()函數是numpy包裡面的,用於將二維陣列序列化成一維陣列。
是按行序列,如

mat=[[1 2 3
     4 5 6]]

經過 mat.flatten()後,就變成了

mat=[1 2 3 4 5 6]

3、彩色圖片三通道直方圖

一般來說直方圖都是徵對灰度圖的,如果要畫rgb影象的三通道直方圖,實際上就是三個直方圖的疊加。

from skimage import data
import matplotlib.pyplot as plt
img=data.lena()
ar=img[:,:,0].flatten()
plt.hist(ar, bins=256, normed=1,facecolor='r',edgecolor='r',hold=1)
ag=img[:,:,1].flatten()
plt.hist(ag, bins=256, normed=1, facecolor='g',edgecolor='g',hold=1)
ab=img[:,:,2].flatten()
plt.hist(ab, bins=256, normed=1, facecolor='b',edgecolor='b')
plt.show()

其中,加一個引數hold=1,表示可以疊加

4、直方圖均衡化

如果一副影象的畫素佔有很多的灰度級而且分佈均勻,那麼這樣的影象往往有高對比度和多變的灰度色調。直方圖均衡化就是一種能僅靠輸入影象直方圖資訊自動達到這種效果的變換函數。它的基本思想是對影象中畫素個數多的灰度級進行展寬,而對影象中畫素個數少的灰度進行壓縮,從而擴充套件取值的動態範圍,提高了對比度和灰度色調的變化,使影象更加清晰。

from skimage import data,exposure
import matplotlib.pyplot as plt
img=data.moon()
plt.figure("hist",figsize=(8,8))
arr=img.flatten()
plt.subplot(221)
plt.imshow(img,plt.cm.gray) #原始影象
plt.subplot(222)
plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始影象直方圖
img1=exposure.equalize_hist(img)
arr1=img1.flatten()
plt.subplot(223)
plt.imshow(img1,plt.cm.gray) #均衡化影象
plt.subplot(224)
plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()

CLAHE

skimage.exposure.``equalize_adapthist(image, kernel_size=None, clip_limit=0.01, nbins=256)

Contrast Limited Adaptive Histogram Equalization (CLAHE).

An algorithm for local contrast enhancement, that uses histograms computed over different tile regions of the image. Local details can therefore be enhanced even in regions that are darker or lighter than most of the image.

image : (M, N[, C]) ndarray

Input image.
kernel_size: integer or list-like, optionalDefines the shape of contextual regions used in the algorithm. If iterable is passed, it must have the same number of elements as image.ndim (without color channel). If integer, it is broadcasted to each image dimension. By default, kernel_size is 1/8 of image height by 1/8 of its width.

clip_limit : float, optional

Clipping limit, normalized between 0 and 1 (higher values give more contrast).
nbins : int, optional
Number of gray bins for histogram (“data range”).

| Returns: |

out : (M, N[, C]) ndarray

Equalized image.

http://scikit-image.org/docs/dev/api/skimage.exposure.html#equalize-adapthist

from skimage import data,exposure
import matplotlib.pyplot as plt
#%matplotlib notebook
clip_limitnumber=0.01
img=data.moon()
print(img.shape)
plt.figure("hist",figsize=(8,8))
arr=img.flatten()
plt.subplot(5,2,1)
plt.title('original')
plt.imshow(img,plt.cm.gray) #原始影象
plt.subplot(5,2,2)
plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始影象直方圖
# #img1=exposure.equalize_hist(img)
# img1=exposure.equalize_hist(img)
# arr1=img1.flatten()
# plt.subplot(6,2,3)
# plt.title('equalize_hist')
# plt.imshow(img1,plt.cm.gray) #均衡化影象
# plt.subplot(6,2,4)
# plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
# plt.show()
img2=exposure.equalize_adapthist(img, kernel_size=256, clip_limit=clip_limitnumber, nbins=256)
arr2=img2.flatten()
plt.subplot(5,2,3)
plt.title('equalize_adapthist-256-'+ str(clip_limitnumber))
plt.imshow(img2,plt.cm.gray) #均衡化影象
plt.subplot(5,2,4)
plt.hist(arr2, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img3=exposure.equalize_adapthist(img, kernel_size=128, clip_limit=clip_limitnumber, nbins=256)
arr3=img3.flatten()
plt.subplot(5,2,5)
plt.title('equalize_adapthist-128-'+ str(clip_limitnumber))
plt.imshow(img3,plt.cm.gray) #均衡化影象
plt.subplot(5,2,6)
plt.hist(arr3, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img4=exposure.equalize_adapthist(img, kernel_size=64, clip_limit=clip_limitnumber, nbins=256)
arr4=img4.flatten()
plt.subplot(5,2,7)
plt.title('equalize_adapthist-64-'+ str(clip_limitnumber))
plt.imshow(img4,plt.cm.gray) #均衡化影象
plt.subplot(5,2,8)
plt.hist(arr4, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img5=exposure.equalize_adapthist(img, kernel_size=32, clip_limit=clip_limitnumber, nbins=256)
arr5=img5.flatten()
plt.subplot(5,2,9)
plt.title('equalize_adapthist-32-'+ str(clip_limitnumber))
plt.imshow(img5,plt.cm.gray) #均衡化影象
plt.subplot(5,2,10)
plt.hist(arr5, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()

from skimage import data,exposure
import matplotlib.pyplot as plt
#%matplotlib notebook
kernel_sizenuber=64
img=data.moon()
print(img.shape)
plt.figure("hist",figsize=(8,8))
arr=img.flatten()
plt.subplot(5,2,1)
plt.title('original')
plt.imshow(img,plt.cm.gray) #原始影象
plt.subplot(5,2,2)
plt.hist(arr, bins=256, normed=1,edgecolor='None',facecolor='red') #原始影象直方圖
# #img1=exposure.equalize_hist(img)
# img1=exposure.equalize_hist(img)
# arr1=img1.flatten()
# plt.subplot(6,2,3)
# plt.title('equalize_hist')
# plt.imshow(img1,plt.cm.gray) #均衡化影象
# plt.subplot(6,2,4)
# plt.hist(arr1, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
# plt.show()
img2=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.001, nbins=256)
arr2=img2.flatten()
plt.subplot(5,2,3)
plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.001')
plt.imshow(img2,plt.cm.gray) #均衡化影象
plt.subplot(5,2,4)
plt.hist(arr2, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img3=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.005, nbins=256)
arr3=img3.flatten()
plt.subplot(5,2,5)
plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.005')
plt.imshow(img3,plt.cm.gray) #均衡化影象
plt.subplot(5,2,6)
plt.hist(arr3, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img4=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.01, nbins=256)
arr4=img4.flatten()
plt.subplot(5,2,7)
plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.01')
plt.imshow(img4,plt.cm.gray) #均衡化影象
plt.subplot(5,2,8)
plt.hist(arr4, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()
img5=exposure.equalize_adapthist(img, kernel_size=kernel_sizenuber, clip_limit=0.05, nbins=256)
arr5=img5.flatten()
plt.subplot(5,2,9)
plt.title('equalize_adapthist-'+ str(kernel_sizenuber)+'-0.05')
plt.imshow(img5,plt.cm.gray) #均衡化影象
plt.subplot(5,2,10)
plt.hist(arr5, bins=256, normed=1,edgecolor='None',facecolor='red') #均衡化直方圖
plt.show()

參考文獻

python數位影像處理

以上就是python skimage影象處理的詳細內容,更多關於python skimage影象處理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com