[171102] Python3 OpenCV3 圖像處理基礎(Python3 + Numpy + Matplotlib + OpenCV3 + PyQt5 ...)

原創內容,禁止轉載!


導讀:這是 Python OpenCV 圖像處理 專欄的第2篇文章,主要介紹了使用 Python OpenCV 圖像處理的環境搭建(三方庫安裝)、Python基本語法、Numpy基本操作、圖像處理基本操作、Matplotlib 繪圖和顯示圖像、OpenCV 圖像處理基本操作等。並給出使用 OpenCV 進行 Canny 邊緣檢測的例子。更多內容,請查看 [171101] Python3 OpenCV3 圖像處理專欄目錄。

開發環境搭建

正如先前所強調的,本專欄關注點在 Python OpenCV 圖像處理。需要自己搭建開發環境。由於我在 Ubuntu 16.04 LTS 上工作,使用 Python 3.5 和 OpenCV 3.3, 所以主要針對這兩個來說明,其他系統或版本「應該」大同小異吧。

1. 安裝 Python 3

## 其實 Ubuntu 16.04 系統自帶了 Python 3.5.2,因此不需要再安裝了?但是需要安裝一些開發環境。sudo apt-get update # 更新系統源sudo apt-get install python3 python3.5-dev libpython3.5-dev # 安裝基礎包sudo apt-get install python3-pip # 安裝 pip3 sudo pip3 install --upgrade pip # 更新 pip3 ## 測試 $ python3 --versionPython 3.5.2

2. 安裝 Numpy, Matplotlib, OpenCV 3.x, Scikit-Iamge等

需要說明的是,可以自己編譯最新的 OpenCV。雖然我是自己編譯的,但是我不想再費力討論這方面的東西。一方面自己笨拙講解不流暢;另一方面是網上關於編譯的資料到處都是,可以自己搜索參考;更重要的是,有好心者已經編譯好並公開了,因此可以直接安裝啦。

## 安裝庫sudo pip3 install numpy # 安裝 numpy,用於在Python中進行科學計算sudo pip3 install scipy # 安裝 scipy, 跟 numpy 是一家的 sudo pip3 install matplotlib # 安裝 matplotlib,用於顯示、繪圖等sudo pip3 install opencv-python opencv-contrib-python # 安裝 opencv sudo pip3 install scikit-image # 安裝 scikit-iamge## 確保 OpenCV 已經安裝好 $ python3 -c "import cv2;print(cv2.__version__)"3.3.0-dev

至此,環境基本上已經搭建結束。以後的任務就是開發啦。

3. 關於編輯器

「此之蜜糖,彼之砒霜」,「蘿蔔白菜,各有所愛」。 不討論編輯器好壞,不挑起無意義的爭論。但是還是有個小說明。目前我覺得WinX下使用 Sublime 編輯器挺好, Ubuntu下使用 Geany 編輯器寫代碼挺舒服的。如果想安裝 Geany,則可以參考如下:

## bash 控制台安裝 geany 編輯器及插件 $ sudo apt install geany geany-common geany-plugin* ## 打開任何你喜歡的編輯器或IDE,寫入下面一句話,並保存為 hello.py print("Hello, Python!")## bash控制台運行 Python 文件 $ python3 hello.py Hello, Python!

Python 基本語法

相信來到本專欄的讀者,有一定的編程基礎(或者已經是大佬了。。),這裡貼張圖再讓大家複習一下Python語法(注意圖中使用的是Python2,把每個 "print xxx" 語句變成 "print(xxx)"後才可以在 Python3 中執行)。

Python 示例(英文版):

這裡先介紹 Python標準類型可以參考官網:

[Python 的標準類型](https://docs.python.org/3.5/library/stdtypes.html)

Python基本類型

基本類型包括:數值型(int/float),布爾型, 字元串等

# --------------- 基本數據類型 -----------------------### (1) 數值型x = 123 # 整型 inty = 123.0 # 浮點型 floatprint(type(x), type(y)) # <class "int"> <class "float">## 加減乘除、整除、指數print(x, x+10, x-10, x*10, x/10, x//10, x**2)# 123 133 113 1230 12.3 12 15129print(y, y+10, y-10, y*10, y/10, y//10, y**2)# 123.0 133.0 113.0 1230.0 12.3 12.0 15129.0### (2) 布爾型true = Truefalse = Falseprint(type(true)) # <class "bool">## 布爾運算(與、或、非、異或)print(true and false, true or false, not true, not false, true != false)# False True False True True### (3) 字元串hello = "hello"python = "python"print(hello + " " + python) # hello pythonprint("{} {}".format(hello, python)) # hello pythons = "heLLo"print(s.capitalize()) # Hello # 首字母大寫print(s.lower()) # hello # 所有字母小寫print(s.upper()) # HELLO # 所有字母大寫print(s.replace("L", "(l)")) # he(l)(l)o # 字元串替換

Python 容器

內置容器包括列表(list), 元組(tuple), 字典 (dict), 集合(set)等 。

# ---------- 容器(containers) --------------------### (1) list (可變)列表,以方括弧表示x = [0,1,2,3,4,5,6] # 創建列表print(x[0], x[1], x[-1]) # 0, 1, 6 ## 序號從 0 開始, -1表示尾元素## 切片操作 (slice)print(x[1:5:2]) # [1, 3] ## 獲取子序列 start:end:stepprint(x[:4:-1]) # [6, 5] ## 獲取子序列,這裡的步長為為-負數,表示逆序print(x[:-3:-1]) # [6, 5] ## 獲取子序列,這裡的步長為為-負數,表示逆序x[3:6] = [5,4,3] # ## 賦值(注意保證個數相同)print(x) # [0, 1, 2, 5, 4, 3, 6]### (2) tuple (不可變)元組,以圓括弧創建x = (0,1,2,3,4,5,6) #創建元組print(x[0], x[1], x[-1]) # 0, 1, 6 ## 序號從 0 開始, -1表示尾元素## 元組切片與列表切片類似,不過不可以修改# x[3] = -3 # TypeError: "tuple" object does not support item assignment### (3) dict (可變但索引不重複)字典,以花括弧和冒號創建x = {"a":1, "b":2, "c":3} ## Python 3.6 中字典跟有所變動print(x) # {"b": 2, "a": 1, "c": 3}print(x.keys()) # dict_keys(["b", "a", "c"])print(x.items()) # dict_items([("b", 2), ("a", 1), ("c", 3)])print(x["a"], x["b"], x["c"]) # 1 2 3 ## 按關鍵字索引x["b"] = "B" # ## 修改print(x) # {"b": "B", "a": 1, "c": 3}

Python中的流程式控制制

流程式控制制,主要用到關鍵字有 for,in,while 等。

### ---- 流程式控制制 ------###(1) for - in 循環, if 條件xx = []for i in range(10): if i %3 ==0 or i %5 == 0: xx.append(i) ## 添加到列表中print(xx) # [0, 3, 5, 6, 9] ## 列印###(2) for - in 循環, 使用 enumerate 獲取序號for idx, x in enumerate(xx, start=1): print(idx, x)"""1 02 33 54 65 9"""###(3) 上述循環的等價列表生成式xx = []xx = [x for x in range(10) if x%3==0 or x%5==0]print(xx)

Python 中函數的定義與使用

# ------------- 函數 ----------------def func(x, k=2, b=-3): """ y = k*x + b """ return k*x+b## 列表生成式調用求值xy = [(i, func(i)) for i in range(5)]print(xy) # [(0, -3), (1, -1), (2, 1), (3, 3), (4, 5)]

Python中 類(class) 的定義與使用

# ------------ 類 ------------class Counter(object): """一個計數器類 """ cnt = 0 # 計數值(成員共享) def __init__(self): pass def __del__(self): pass @classmethod def show(cls): """顯示函數,用於輸出當前狀態""" print("cnt({})".format(cls.cnt)) @classmethod def inc(cls): """計數值加一""" cls.cnt += 1 @classmethod def dec(cls): """計數值減一""" cls.cnt -= 1Counter.inc() # +1Counter.show() # 顯示當前狀態:cnt(1)Counter.inc() # +1Counter.inc() # +1Counter.show() # 顯示當前狀態:cnt(3)counter = Counter() # 創建對象counter.show() # 顯示狀態: cnt(3)counter.dec() # -1counter.dec() # -1counter.show() # 顯示狀態: cnt(1)

Numpy 的使用

Numpy 是 Python中的科學計算工具包,可以說是Python中的Matlab。支持向量操作、切片操作、廣播,支持多種常用數據類型,內置豐富的線性代數、矩陣演算法。由於底層使用多為C語言實現,所以有著較快速度。同時以使用Python介面可以方便地使用 Python 的語法,擺脫靜態語言的臃腫,從而實現快速建模、計算和驗證。OpenCV 大部分以 C++ 實現,同時在生成的 Python 介面中, cv::Mat 則轉化為了 numpy.ndarray。因此想要使用 OpenCV 的 Python 庫,必須對 Numpy 有所了解才能勝任。這裡也給出簡要的介紹。

(1) numpy.ndarray 數組的創建

## --- 創建 np.ndarray 數組 ----import numpy as np# 使用 Python list 創建mat1 = np.array([[1,2,3], [4,5,6], [7,8,9]])# 使用 np.arange 創建mat2= np.arange(1,10).reshape(3,3)# 使用 zeros/ones/eye 創建mat3 = np.zeros((3,3))mat4 = np.ones((3,3))mat5 = np.eye(3)print(mat1)print(mat2)print(mat3)print(mat4)print(mat5)

(2) numpy.ndarray 的數據類型

## --- numpy 數據類型(默認 np.int64 或 np.float64) -----# 內置多種數據類型,如 np.uint8, np.int32, np.float32, np.float64a = np.array([[1.25, -16],[32,264.75]], np.float32) # 32位浮點型b = np.array(a, np.int32) # 32位整形c = b.astype("uint8") # 8位無符號整型(注意會發生溢出)print(a)"""[[ 1.25 -16. ] [ 32. 264.75]]"""print(b)"""[[ 1 -16] [ 32 264]]"""print(c)"""[[1 2 3] [4 5 6] [7 8 9]]"""

(3) 索引

## ----- 索引(單坐標、切片、掩模) ------mat = np.arange(1,10).reshape(3,3)print(mat)## 單坐標print(mat[2][2]) # 9print(mat[2,2]) # 9## 切片操作print(mat[:2,:2])"""[[1 2] [4 5]]"""## 掩模操作mask = mat>5print(mask)"""[[False False False] [False False True] [ True True True]]"""mat[mask] = 5print(mat)"""[[1 2 3] [4 5 5] [5 5 5]]"""

(4) 廣播 (broadcasting)

## ----- numpy 廣播機制(broadcasting) ------# https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html# 當操作兩個 numpy.ndarray 數組時,如果它們的維度滿足一定的關係,則可以進行廣播操作。x = np.arange(4) # (4,)xt = x.reshape(-1,1) # (4,1)z = np.ones((3,4)) # (3,4)print(x+xt)"""[[0 1 2 3] [1 2 3 4] [2 3 4 5] [3 4 5 6]]"""print(x+z)"""[[ 1. 2. 3. 4.] [ 1. 2. 3. 4.] [ 1. 2. 3. 4.]]"""

Matplotlib

[Matplotlib](http://matplotlib.org/) 是 Python 中的可視化庫,可以用來繪製高質量的 2D 折線圖、散點圖、柱狀圖,或者用來顯示圖像。分別參考

(1) Sample plots in Matplotlib

(2) Image tutorial - Matplotlib 2.1.0 documentation

# 使用 matplotlib 繪製一些列縮略圖(thumbnails),並顯示圖像 #from scipy.misc import imread, imresizeimport matplotlib.pyplot as pltimport matplotlib.image as mpimgimport numpy as np## (1) 繪製隨機噪點## 初始化隨機種子,並生成隨機坐標np.random.seed(0)data = np.random.randn(2, 100)## 創建畫布fig, axs = plt.subplots(2, 2, figsize=(5, 5))## 繪製子圖 axs[0, 0].hist(data[0])axs[1, 0].scatter(data[0], data[1])axs[0, 1].plot(data[0], data[1])axs[1, 1].hist2d(data[0], data[1])## 顯示 plt.show()## (2) 繪製圖像img = mpimg.imread("/home/auss/Pictures/test.png")imgx = img[:,:,0] # 取第一個通道## 創建畫布fig = plt.figure()## 繪製原始圖像,並加上顏色條axs = fig.add_subplot(1,3,1)ipt = plt.imshow(img)axs.set_title("origin")plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation="horizontal")## 繪製偽彩色圖像,並加上顏色條 axs = fig.add_subplot(1,3,2)ipt = plt.imshow(imgx,cmap="winter")axs.set_title("winter")plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation="horizontal")## 繪製直方圖axs = fig.add_subplot(1,3,3)ipt = plt.hist(imgx.ravel(), bins=256, range=(0, 1.0), fc="k", ec="k")axs.set_title("histogram")plt.show()

Matplot 繪製圖像(條狀圖、曲線圖、散點圖、偽彩色圖)

Matplotlib 顯示照片、偽彩色圖、直方圖

OpenCV 的簡單使用

鋪墊了這麼久,終於到了 OpenCV 了。 OpenCV 的 Python 介面名稱為 cv2。通常 OpenCV 內部的演算法已經很豐富了,並且提供了 highgui 模塊用於顯示圖像(不過可能有的沒有編譯該模塊)。如果需要進行拓展,則可以配合著 Numpy 進行計算,並結合 Matplotlib 進行顯示。

注意,matplotlib 中圖像通道為 RGB,而 OpenCV 中圖像通道為 BGR。因此進行顯示的時候,要注意交換通道的順序。

這裡給出一個 Canny 邊緣檢測的例子,涉及到圖像讀寫、色彩空間轉換、濾波、Canny邊緣檢測、掩模賦值操作等。

#!/usr/bin/python3# 2017.11.02 17:31:24 CST# 2017.11.02 17:51:13 CSTimport cv2import numpy as npimg = cv2.imread("firefox.png")## BGR => Gray; 高斯濾波; Canny 邊緣檢測gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)gaussed = cv2.GaussianBlur(gray, (3,3), 0)cannyed = cv2.Canny(gaussed, 10, 220)## 將灰度邊緣轉化為BGR cannyed2 = cv2.cvtColor(cannyed, cv2.COLOR_GRAY2BGR) ## 創建彩色邊緣 mask = cannyed > 0 # 邊緣掩模canvas = np.zeros_like(img) # 創建畫布canvas[mask] = img[mask] # 賦值邊緣## 保存res = np.hstack((img, cannyed2, canvas)) # 組合在一起 cv2.imwrite("result.png", res)        # 保存## 顯示 cv2.imshow("canny in opencv ", res) # 保持10s, 等待按鍵響應(超時或按鍵則進行下一步)key = 0xFF & cv2.waitKey(1000*10)if key in (ord("Q"), ord("q"), 27): ## 這部分用作演示用 print("Exiting...")## 銷毀窗口cv2.destroyAllWindows()

結果如題圖,或下圖:

Python OpenCV Canny 邊緣檢測

到這裡,背景介紹就結束啦。


Update1:

關於如何在 matplotlib 中實時繪製圖像,找到了一篇 StackOverflow 的答案:

How can I use matplotlib in real-time?

#!/usr/bin/python3# 2017.11.09 09:52:01 CST"""分別使用 OpenCV 和 matplotlib 繪製圖片參考:https://stackoverflow.com/questions/47172219/how-can-i-use-matplotlib-in-real-time"""import numpy as npimport cv2import matplotlib.pyplot as pltimport cv2cap = cv2.VideoCapture(0)plt.ion()print("Press ESC to exit!")while (True): ret, frame = cap.read() ##(1) display in cv2 cv2.imshow("frame", frame) if cv2.waitKey(33) & 0xFF == 27: break ##(2) display in matplotlib rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) plt.imshow(rgb, interpolation="nearest") plt.pause(0.001) plt.show()

Update2:

新文章,感覺應該跟這個放在一起的,使用 PyQt5 和 OpenCV 進行 Canny 邊緣檢測的互動式操作。

[171118] PyQt5 滑動條控制 Canny 邊緣閾值並繪製 OpenCV 圖像


手動撒花 。。。。

參考:

1. [Python Tutorials](https://docs.python.org/3/tutorial/)2. [Numpy quick start](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html)3. [Python Numpy Tutorials](http://cs231n.github.io/python-numpy-tutorial/)4. [Numpy for Matlab users](https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html)5. [Scitific Software topic](https://scipy.org/topical-software.html)6. [Numpy Reference](https://docs.scipy.org/doc/numpy/reference/)7. [Numpy User Guide](https://docs.scipy.org/doc/numpy/user/index.html)8. [opencv-python-pypi](https://pypi.python.org/pypi/opencv-python)9. [Python stdtype](https://docs.python.org/3.5/library/stdtypes.html)10. [Matplotlib](http://matplotlib.org/)11. [Matplotlib Turorials](http://matplotlib.org/tutorials/index.html)12. [Matplotlib Gallery](http://matplotlib.org/gallery/index.html)13. [pyplot Summary](http://matplotlib.org/api/pyplot_summary.html)14. [External Resources](http://matplotlib.org/resources/index.html)15. [Embedding in Qt4](http://matplotlib.org/gallery/user_interfaces/embedding_in_qt4_sgskip.html)16. [Matplotlib plot](http://matplotlib.org/tutorials/introductory/sample_plots.html#sphx-glr-tutorials-introductory-sample-plots-py)17. [Matplotlib imshow](http://matplotlib.org/tutorials/introductory/images.html#sphx-glr-tutorials-introductory-images-py)18. [Matplotlib Animation](https://matplotlib.org/devdocs/gallery/animation/animation_demo.html#sphx-glr-gallery-animation-animation-demo-py)19. [Python Nupy tutorials](http://cs231n.github.io/python-numpy-tutorial/)20. [How can I use matplotlib in real-time?](https://stackoverflow.com/questions/47172219/how-can-i-use-matplotlib-in-real-time)

推薦閱讀:

50行代碼實現人臉檢測
1.9【OpenCV圖像處理】平滑模糊濾波
最近在搞Kinect的指尖識別,手指都可以畫出來了,有什麼方法可以將每隻手指對應的名稱識別出來啊?
[171103] 基於縮略圖哈希值比較的圖像相似性檢索
元旦贈書 | 18本紙質書:OpenCV、Python和機器學習,總有一本適合你

TAG:OpenCV | 图像处理 | Python |