官术网_书友最值得收藏!

2.3 使用OpenCV和Python處理數據

數據世界充滿了各種各樣的數據類型。有時,這會使用戶很難區分用于特定值的數據類型。在此,我們將盡量保持簡單性,除保留標準數據類型的標量值之外,將所有內容都當成數組處理。因為圖像有寬度和高度,所以圖像將變成二維數組。一維數組可能是強度隨時間變化的一個聲音片段。

如果你經常使用OpenCV的C++應用程序接口(Application Programming Interface,API)并打算繼續這樣做的話,那么你可能會發現用C++處理數據會有點麻煩。你不但必須處理C++語言的語法,而且必須處理各種數據類型以及跨平臺的兼容性問題。

如果你使用OpenCV的Python API,就會最大程度上簡化這個過程,因為你可以自動訪問科學Python(Scientific Python,SciPy)社區中提供的大量開源包。一個相關示例是數值Python(Numerical Python,NumPy)包,大多數科學計算工具都是圍繞它來構建的。

2.3.1 開始一個新的IPython或Jupyter會話

在我們開始使用NumPy之前,我們需要打開一個IPython shell或者啟動一個Jupyter Notebook:

1)如第1章所述,打開一個終端,導航到OpenCV-ML目錄:

2)激活我們在第1章中創建的conda環境:

3)啟動一個新的IPython或者Jupyter會話:

如果你選擇啟動一個IPython會話,程序應該會向你發送類似于下面這樣的歡迎消息:

在以In [1]開頭的代碼行中,你可以輸入常規Python命令。另外,你還可以在輸入變量和函數名稱時按下Tab鍵,讓IPython自動完成Python命令。

提示

有限數量的Unix和macOS系統shell命令也可以工作,例如ls和pwd。你可以給shell命令添加前綴“!”(例如,!ping www.github.com),然后運行所有的shell命令。詳細信息請查閱官方IPython文獻,網址為https://ipython.org/ipython-doc/3/interactive/tutorial.html。

如果你選擇啟動一個Jupyter會話,在你的Web瀏覽器中應該會打開一個新的窗口,指向http://localhost:8888。如果你想要創建一個新的notebook,那么點擊右上角的New,選擇Notebooks(Python 3),如圖2-2所示。

圖2-2 在瀏覽器中打開一個Jupyter新窗口

這會打開一個新的窗口,如圖2-3所示。

圖2-3 Jupyter會話窗口

用In [ ]標記的單元格(看起來與之前的文本框很像)與IPython會話中的命令行類似。現在,可以開始輸入你的Python代碼了!

2.3.2 使用Python的NumPy包處理數據

如果你已經安裝了Anaconda,那么就假設你已經在虛擬環境中安裝了NumPy。如果你使用過Python的標準發行版或任何其他發行版,你可以訪問http://www.numpy.org,并按照所提供的安裝說明進行操作。

如前所述,如果你還不是Python專家,也無關緊要。誰知道呢,也許你剛剛從OpenCV的C++API轉向Python。一切都正常。我們想讓你快速了解一下如何開始使用NumPy。如果你是高級Python用戶,那么你可以直接跳過本節內容。

一旦你熟悉了NumPy,就會發現Python世界中的大多數科學計算都是圍繞NumPy構建的。這包括OpenCV,因此花在NumPy上的學習時間最終對你是有益的。

1. 導入NumPy

一旦啟動了一個新的IPython或者Jupyter會話,就可以導入Numpy模塊并按照以下步驟來驗證版本:

提示

記得在Jupyter Notebook中,鍵入命令后,你可以按下Ctrl+Enter,以執行一個單元格。或者,按下Shift+Enter以執行單元格,并自動插入或者選擇該單元格下面的單元格。依次單擊Help | Keyboard Shortcut以檢查所有的鍵盤快捷鍵,或者依次單擊Help | User Interface Tour以進行快速瀏覽。

此處討論的部分包,建議使用NumPy 1.8版本或后續版本。按照慣例,你會發現在科學Python領域中,大多數人導入NumPy都會使用np作為別名:

本章及本書的其余章節,我們都將遵循同樣的慣例。

2. 理解NumPy數組

你可能已經知道Python是一種弱類型的語言。這就意味著,你無論何時創建一個新變量,都不必指定數據類型。例如,下面的內容將自動表示為一個整數:

輸入下面內容以再次確認:

注意

因為標準Python實現是用C編寫的,所以每個Python對象本質上是一個偽C結構。這對于Python中的整數也是如此,實際上它是指向復合C結構的指針,包含的不僅僅是原始整數值。因此,用于表示Python整數的默認C數據類型將依賴于你的系統架構(即系統是32位還是64位平臺)。

更進一步,我們使用list()命令可以創建一個整數列表,這是Python中的標準多元素容器。range (x)函數將創建從0到x–1的所有整數。要輸出變量,你可以使用print函數,也可以直接輸入變量名字并按Enter:

類似地,我們通過讓Python遍歷整數列表int_list中的所有元素,并對每個元素應用str()函數(該函數將一個數轉換成一個字符串),來創建一個字符串列表:

可是,用列表進行數學運算并不是很靈活。例如,我們想要將int_list中的每個元素都乘以一個因子2。執行以下操作可能是一種簡單的方法——看看輸出結果是怎樣的:

Python創建了一個列表,其內容是int_list的所有元素生成了兩次,這并不是我們想要的!

這就是NumPy的用武之地。NumPy是專為簡化Python中的數組運算而設計的。我們可以快速將整數列表轉換為一個NumPy數組:

讓我們看看試著將數組中的每個元素相乘會怎么樣:

這次我們做對了!加法、減法、除法以及很多其他運算也是同樣的。

而且,每個NumPy數組都具有以下屬性:

  • ndim:維數。
  • shape:每一維的大小。
  • size:數組中元素的總數。
  • dtype:數組的數據類型(例如int、float、string等)。

讓我們來看看整數數組的上述屬性:

從這些輸出中,我們可以看到我們的數組只包含一維,其包含10個元素且所有元素都是64位的整數。當然,如果你在32位機器上執行這段代碼,你可能會得到dtype:int 32。

3. 通過索引訪問單個數組元素

如果你之前使用過Python的標準列表索引,那么你就不會發現NumPy中的索引有很多問題。在一維數組中,通過在方括號中指定所需的索引,可以訪問第i個值(從0開始計算),與Python列表一樣:

要從數組的末尾建立索引,可以使用負索引號:

切割數組還有一些其他很酷的技巧,如下所示:

建議你自己嘗試使用這些數組!

提示

NumPy中切割數組的一般形式與標準Python列表中的相同。使用x [start: stop: step]訪問數組x中的一個片段。如果沒有指定任何一個值,那么默認值為start=0、stop=size of dimension、step=1。

4. 創建多維數組

數組不必局限于列表。實際上,數組可以有任意維數。在機器學習中,通常我們至少要處理二維數組,列索引表示特定的特征值,行包含實際的特征值。

使用NumPy可以輕松地從頭開始創建多維數組。假設我們想要創建一個3行5列的數組,所有的元素都初始化為0。如果我們不指定數據類型,NumPy將默認使用float類型:

使用OpenCV時你可能就知道:這可以解釋為所有像素設置為0(黑色)的一個3×5的灰度圖像。例如,如果你想要創建具有3個顏色通道(R、G和B)2×4像素的一個小圖像,但是所有像素都設置為白色,我們將使用NumPy創建一個3×2×4的三維數組:

這里,第一維定義顏色通道(OpenCV中的藍色、綠色和紅色)。因此,如果這是真實的圖像數據,我們可以通過切割數組輕松地獲得第一個通道中的顏色信息:

在OpenCV中,圖像要么是值在0到1之間的32位浮點數組,要么是值在0到255之間的8位整數數組。因此,使用8位整數,通過指定NumPy的dtype屬性并將數組中的所有1乘以255,我們還可以創建一個2×4像素、全為白色的RGB圖像:

在后續章節中,我們還將學習更高級的數組操作。

2.3.3 用Python加載外部數據集

感謝SciPy社區,它提供了很多可以幫助我們獲得一些數據的資源。

一個特別有用的資源以scikit-learn的sklearn.datasets包的形式出現。這個包預裝了一些小數據集,我們不需要從外部網站下載任何文件。這些數據集包含以下內容:

  • load_boston:Boston數據集包含不同城區的房價以及一些有趣的特征,如城鎮的人均犯罪率、居住用地比例,以及非零售業務的數量。
  • load_iris:Iris數據集包含三種不同類型的鳶尾花(山鳶尾、花斑鳶尾和維吉尼亞鳶尾)以及描述花萼和花瓣的寬度和長度的4個特征。
  • load_diabetes:diabetes數據集依據患者的年齡、性別、體重指數、平均血壓以及6次血清測量值等特征,使我們可以將患者分類為糖尿病患者和非糖尿病患者。
  • load_digits:digits數據集包含數字0~9的8×8像素圖像。
  • load_linnerud:Linnerud數據集包含3個生理變量以及3個運動變量,測量了20名健身俱樂部的中年男性。

此外,scikit_learn允許我們直接從外部存儲庫下載數據集,例如:

  • fetch_olivetti_faces:Olivetti faces數據集包含40個不同主題,每個主題包含10個不同的圖像。
  • fetch_20newsgroups:20 newsgroup數據集包含20個主題,大約18 000篇新聞組帖子。

更好的是,可以從機器學習數據庫(http://openml.org)中直接下載數據集。例如,要下載Iris數據集,只需輸入以下命令:

Iris數據集共有150個樣本、4個特征——花萼長度、花萼寬度、花瓣長度和花瓣寬度。數據分為三類——山鳶尾、花斑鳶尾和維吉尼亞鳶尾。數據和標簽位于兩個獨立的容器中,我們可以根據下列操作查看:

此處,我們可以看到iris_data包含150個樣本,每個樣本包含4個特征(這就是shape中的數字是4的原因)。標簽存儲在iris_target中,其中每個樣本只有一個標簽。

我們可以進一步查看所有目標的值,但是我們不希望只輸出這些目標值。我們感興趣的是查看所有不同的目標值,使用NumPy可輕松實現:

注意

你應該聽說過另一個用于數據分析的Python庫是pandas(http://pandas.pydata.org)。pandas為數據庫和電子表格實現了幾個功能強大的數據操作。不管這個庫多么強大,此時,pandas對于我們來說有點太高級了。

2.3.4 使用Matplotlib可視化數據

如果我們不知道如何查看數據,那么知道如何加載數據的作用是有限的。謝天謝地,幸好還有Matplotlib

Matplotlib是建立在NumPy數組上的一個多平臺數據可視化庫——看吧,我說過NumPy還會再次出現的。在2002年,約翰·亨特(John Hunter)提出Matplotlib,最初的構思是設計為IPython的一個補丁,以便能夠從命令行啟用交互式MATLAB樣式繪圖。近幾年,更新、更炫酷的工具(例如,R語言中的ggplot和ggvis)層出不窮,最終取代了Matplotlib,可是Matplotlib仍然是一個經過良好測試的、非常重要的跨平臺圖形引擎。

1. 導入Matplotlib

你可能又走運了:如果你按照第1章中的建議安裝了完整的Python Anaconda,那么你已經安裝了Matplotlib,可以開始了。否則,你可能要訪問http://matplotlib.org以獲取安裝說明。

就像我們用縮寫np來表示NumPy一樣,我們也會用一些標準的縮寫來表示Matplotlib導入:

plt是我們最常用的一個接口,在本書中我們將常看到plt接口。

2. 生成一個簡單的圖形

言歸正傳,讓我們創建第一個圖形。

假設我們要繪制正弦函數sin(x)的一個簡單線圖。我們希望函數求x軸(0≤x≤10)上的所有值。我們將使用NumPy的linspace函數在x軸上創建一個線性空間,x值從0到10,共100個樣本點:

我們可以使用NumPy的sin函數求sin函數的所有x值,并通過調用plt的plot函數可視化結果:

你親自試過了嗎?發生什么了?有什么發現嗎?

問題是,這取決于你在何處運行這個腳本,你可能什么都看不到。以下是可以考慮的可能性:

  • 從.py腳本繪圖:如果你正從一個腳本運行matplotlib,那么你只需要調用plt,如下所示:

調用后,圖形就會顯示出來!

  • 從IPython shell繪圖:這實際上是以交互方式運行matplotlib的最便捷的方式之一。要顯示繪圖,你需要在啟動IPython之后,調用%matplotlib魔術命令:

然后,所有圖都會自動顯示出來,不必每次都調用plt.show()。

  • 從Jupyter Notebook繪圖:如果你從基于瀏覽器的Jupyter Notebook上查看這段代碼,你需要使用同樣的%matplotlib魔術命令。可是,你還可以選擇將圖形直接嵌入notebook中,這有兩種可能的結果:
    ◆ %matplotlib notebook將生成的交互式圖嵌入notebook中。
    ◆ %matplotlib inline將生成的靜態圖嵌入notebook中。

在本書中,我們通常會選擇內聯選項:

現在,讓我們再試一次:

上述命令給出的輸出如圖2-4所示。

圖2-4 應用內聯選項生成的圖

稍后,如果你想保存圖表,可以直接從IPython或Jupyter Notebook的選項中保存:

只要保證使用所支持的文件后綴即可,例如.jpg、.png、.tif、.svg、.eps或者.pdf。

提示

在導入matplotlib之后,運行plt.style.use(style_name),你可以更改繪圖的樣式。在plt.style.available中列出了所有可用的樣式。例如,試試plt.style.use('fivethirtyeight')、plt.style.use('ggplot')或者plt.style.use('seaborn-dark')。為了增加樂趣,可以運行plt.xkcd(),再嘗試繪制其他內容。

3. 可視化外部數據集的數據

作為本章的最后一個測試,讓我們可視化一些來自外部數據集的數據,例如scikit-learn的digits數據集。

具體來說,我們將需要3個可視化工具:

  • 用于實際數據的scikit-learn
  • 用于數據處理的NumPy
  • Matplotlib

首先,讓我們導入所有這些可視化工具:

第一步是實際加載數據:

如果我們沒有記錯的話,digits應該有2個不同的字段:一個是data字段,包含實際的圖像數據;另一個是target字段,包含圖像標簽。與其相信我們的記憶,不如讓我們研究一下digits對象。這通過輸入字段名稱、添加句點、再按下Tab鍵——digits.<TAB>來實現。這會顯示出digits對象還包含了一些其他字段,例如一個名為images的字段。images和data這2個字段似乎只是形狀不同:

在這兩個例子中,第一維都對應于數據集中的圖像數。但是data將所有像素排列在一個大的向量中,而images則保留了每個圖像的8×8空間排列。

因此,如果我們想繪制單張圖像,images字段可能更合適。首先,使用NumPy的數組切割,從數據集中抓取一張圖像:

這里,我們說想要抓取長為1797項的數組中的第一行,以及所有對應的8×8=64個像素。然后,我們可以使用plt的imshow函數繪制圖像:

上述命令給出的輸出如圖2-5所示。請注意,圖像是模糊的,因為我們將該圖像調整到了更大的尺寸。原始圖像的大小只有8×8。

圖2-5 生成單張圖像的示例結果

此外,我們還可以使用cmap參數指定一個彩圖。在默認情況下,Matplotlib使用MATLAB的默認彩圖jet。可是,對于灰度圖像,gray彩圖更有意義。

最后,我們可以利用plt的subplot函數繪制一組數字樣本。subplot函數與在MATLAB中一樣,我們指定行數、列數以及當前子圖的索引(從1開始)。我們將使用一個for循環遍歷數據集中的前10個圖像,每個圖像都有自己的子圖:

生成的輸出如圖2-6所示。

圖2-6 生成包含10個數字的一組子圖

提示

對于各種數據集,另一個很好的資源是本書作者邁克爾·貝耶勒的母校加州大學歐文分校的機器學習資源庫:http://archive.ics.uci.edu/ml/index.php。

2.3.5 使用C++中的OpenCV TrainData容器處理數據

為了完整起見,也為了那些堅持使用OpenCV的C++ API的人們,讓我們快速瀏覽OpenCV的TrainData容器,該容器允許我們從.csv文件加載數值數據。

除此之外,在C++中,ml模塊包含一個名為TrainData的類,該類提供了用C++處理數據的一個容器。它的功能僅限于讀取.csv文件中的數值數據(包含逗號分隔的值)。因此,如果我們希望使用的數據是一個組織良好的.csv文件,那么這個類將會為你節省很多時間。如果你的數據來自其他源文件,恐怕你的最佳選擇是使用一個合適的程序(例如OpenOffice或者Microsoft Excel)手動創建一個.csv文件。

TrainData類最重要的方法名為loadFromCSV,該方法接受以下參數:

  • const String& filename:輸入文件名。
  • int headerLineCount:開始時跳過的行數。
  • int responseStartIdx:第一個輸出變量的索引。
  • int responseEndIdx:最后一個輸出變量的索引。
  • const String& varTypeSpec:描述所有輸出變量數據類型的一個文本字符串。
  • char delimiter:用于分隔每行值的字符。
  • char missch:用于指定缺失測量值的字符。

如果在一個逗號分隔的文件中有一些不錯的全浮點數據,那么你可以按照下列方式加載:

該類提供了幾個便捷的函數,將數據拆分成訓練集和測試集,并訪問訓練集和測試集中的各個數據點。例如,如果你的文件包含100個樣本,那么你可以把前90個樣本分配給訓練集,剩下的10個樣本留給測試集。首先,調整訓練樣本和測試樣本可能是個好辦法:

接下來,很容易把所有訓練樣本都存儲在一個OpenCV矩陣中:

在https://docs.opencv.org/4.0.0/dc/d32/classcv_1_1m1_1_1TrainData.html中你可以找到本節介紹的所有相關函數。

除此之外,因為本書作者擔心TrainData容器及其用例可能有點過時了。因此,在本書的其余章節,我們將重點關注Python。

主站蜘蛛池模板: 姚安县| 三江| 宁远县| 宁化县| 美姑县| 三台县| 通山县| 白水县| 定陶县| 浦县| 大埔区| 江陵县| 蓬溪县| 绥阳县| 宾川县| 桂林市| 江门市| 辽阳县| 玉树县| 临沂市| 鄂托克旗| 昆明市| 凤阳县| 剑阁县| 临汾市| 乾安县| 彭泽县| 周宁县| 三河市| 岚皋县| 瑞昌市| 西盟| 中方县| 玉林市| 黑龙江省| 谢通门县| 两当县| 夏邑县| 涿州市| 昆明市| 江口县|