# 數位影像處理
僑光科技大學 資訊科技系
2019/09/09 ~ 2020/01/11
資科二忠、二孝:星期四第5、6、7節
授課老師:高吉隆
電子信箱:[kevinkao888@gmail.com](mailto://kevinkao888@gmail.com)
[專業教室使用規則](https://hackmd.io/@nu1gpWVGTf2ERQf4G8_eUg/S1hjuuNLB#/)
###### tags: `講義`
---
# 課程介紹
* 課程大綱
* 每週目標
* 評分標準
* 使用教科書
----
## 課程大綱
* 數位影像處理是介紹各種處理影像的技術方法
* 研究如何使用<font color="red">程式語言</font>方式來處理影像的技術
* 並非介紹使用<font color="red">應用程式</font>方式來處理影像的操作
* 本課程介紹各種常用影像處理的程式
並且使用<font color="red">Python</font>程式語言來做影像處理
* 課程內容分為
* Python程式語言介紹、函數使用
* turtle圖形繪製、pillow圖形處理
* PyGame遊戲開發、OpenCV影像處理
* tkinter視窗圖形化應用程式
* 2D視覺化統計圖表、產生QRCode
----
## 每週目標
* 前 9 週上課目標
* 課程講解:Python、turtle、pillow、PyGame
* 作業繳交:程式解說及指導2次
* 期中報告:專題實作
* 後 9 週上課目標
* 課程講解:OpenCV影像處理、tkinter、2D統計圖、QRCode
* 作業繳交:程式解說及指導1次
* 期末報告:專題實作
----
|週|日期| 前 9 週上課目標 |
|:-:|:-:|--------------- |
|1|09/12|課程簡介、環境安裝|
|2|09/19|Python介紹|
|3|09/26|Python函數|
|4|10/03|第1次小考、作業1說明|
|5|10/10|雙十節國慶日、作業1繳交|
|6|10/17|turtle圖形繪製|
|7|10/24|pillow圖形處理、作業2說明|
|8|10/31|重點複習、作業實作|
|9|11/07|期中考、作業2繳交|
----
|週|日期|後 9 週上課目標 |
|:-:|:-:| --------------- |
|10|11/14|Numpy介紹及實作|
|11|11/21|Matplotlib介紹及實作|
|12|11/28|OpenCV基本影像處理|
|13|12/05|進階影像處理|
|14|12/12|進階影像處理實作、作業3說明|
|15|12/19|期末報告分組討論|
|16|12/26|期末報告實作、作業3繳交|
|17|01/02|期末報告實作|
|18|01/09|期末報告繳交|
----
## 評分標準
* 平時成績:40%
* 上課互動:10%
* 第1次小考:10% (第4週)
* 第1、2次作業:10% 10% (第5、7週)
* 期中成績:20%
* 期中考:20% (第9週)
* 期末成績:40%
* 第3次作業:10% (第15週)
* 期末報告:30% (第18週)
----
## 第一次小考
* 原始平均57分,最高分84分
* 全部加15分,補考不加分,未到考以30分計
![](https://i.imgur.com/cHhSRru.png)
----
## 作業一
* 問題整理(滿分10分)
* 未繳交:3分
* 補交:4-5分
* 未能正確執行(語法錯誤):6分
* 如非Python語法
* 未能正確執行(羅輯錯誤):7分
* 如錯誤使用for
* 正確執行、未寫註解:8分
* 正確執行、有寫註解:9分
* 正確執行、有寫註解、技巧寫法:10分
----
## 期中考
* 原始平均54分,全部加16分,最高分90分
* 補考不加分,未到考以30分計
![](https://i.imgur.com/FhTNycQ.png)
----
## 作業三
* 期末報告規畫報告
* 紙張為1-3頁
* 內容包括:
* 組員名單
* 系統名稱
* 系統概述
* 功能說明
* 版面設計
* 請於12/26(四)上課前上傳至Zuvio
* 上傳前請轉換成 pdf 檔
----
### 期末報告
* 實作數位影像專題:使用 Python & OpenCV
* 填寫題目:請置換掉我的題目
* 填寫網址:[連結](https://hackmd.io/_fTwmOYVQ0KNlP8d7eg3AQ)
* 請於12/26(三)前上傳作業至Zuvio
* 帳號:s學號@ocu.edu.tw
* 密碼:123(預設密碼,請自行更改)
---
### 使用參考書一
* 書名:一步到位Python程式設計
* 出版社:旗標
* 作者:陳惠貞
![](https://media.taaze.tw/showLargeImage.html?sc=11100868188&width=340&height=474 =300x400)
----
### 使用參考書二
* 書名:最佳案例Python套件12堂課
* 出版社:台科大
* 作者:勁樺科技
![](https://i.imgur.com/tbjbFtB.png =300x400)
----
### 使用參考書三
* 書名:極速+好用 用c++及python平行實作opencv
* 出版社:佳魁數位
* 作者:張平
![](https://im2.book.com.tw/image/getImage?i=https://www.books.com.tw/img/001/081/68/0010816823_bc_01.jpg&v=5c8a49c2&w=655&h=609 =500x400)
---
### Python 環境安裝
* Python:[https://www.python.org/downloads/](https://www.python.org/downloads/)
* 開發環境一:Visual Studio Code
* [下載](https://vscode.cdn.azure.cn/stable/b37e54c98e1a74ba89e03073e5a3761284e3ffb0/VSCodeSetup-ia32-1.38.1.exe)
* 開發環境二:Anaconda3
* [下載](https://www.anaconda.com/distribution/)
* 開啟程式Spyder
* Spyder線上說明:[連結](https://docs.spyder-ide.org/)
* Python教學網頁:[W3Schools/Python](https://www.w3schools.com/python/default.asp)
----
### Visual Studio Code 之程式更新
* 請開啟 Terminal->New Terminal
* 請將以下指令分開執行,並檢查有無錯誤訊息
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install --upgrade pip
```
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install -U pillow
```
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install -U numpy
```
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install -U matplotlib
```
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install -U opencv-python
```
```python=
C:\Users\Student\AppData\Local\Programs\Python\Python38-32\python.exe -m pip install -U pylint
```
---
# 使用 turtle
```python=
import turtle
```
----
## 使用 turtle.Pen 元件
* forward(dist):往畫筆方向前進dist距離
* backward(dist):往畫筆方向後退dist距離
* right(angle):畫筆方向往右轉angle角度
* left(angle):畫筆方向往左轉angle角度
* pendown():將畫筆放下,開始畫圖
* penup():將畫筆拿起,暫停畫圖
* pencolor(color):設定畫筆顏色為color
* fillcolor(color):設定填滿顏色為color
* begin_fill():開始填滿顏色
* end_fill():結束填滿顏色
----
### 使用 turtle 繪製正方形
* 繪製一條直線
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
p.forward(100) #用畫筆畫出一條長度100的線
turtle.done() #結束畫圖
```
* 繪製一個正方形
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
turtle.done() #結束畫圖
```
----
* 請加入for迴圈,將程式簡化
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
for i in range(1, 5): #使用迴圈i=1...4
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
turtle.done() #結束畫圖
```
* 請加入side變數,初始設定為4
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
side = 4 #設定變數為邊數為4
for i in range(1, side+1): #使用迴圈i=1...4
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
turtle.done() #結束畫圖
```
----
* 請加入函數 drawTriangle繪製三角形
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
def drawTriangle(): #定義畫三角形函數
side = 3 #設定變數為邊數為3
for i in range(1, side+1): #使用迴圈i=1...4
p.forward(100) #用畫筆畫出一條長度100的線
p.left(120) #將畫筆方向向左轉90度
drawTriangle() #呼叫正方形函數
turtle.done() #結束畫圖
```
* 請加入def函數,將程式模組化
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
def drawSquare(): #定義畫正方形函數
side = 4 #設定變數為邊數為4
for i in range(1, side+1): #使用迴圈i=1...4
p.forward(100) #用畫筆畫出一條長度100的線
p.left(90) #將畫筆方向向左轉90度
drawSquare() #呼叫正方形函數
turtle.done() #結束畫圖
```
----
* 請加入函數drawShape繪製多邊形
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
def drawShape(side): #定義畫多邊形函數
for i in range(1, side+1): #使用迴圈i=1...4
p.forward(100) #用畫筆畫出一條長度100的線
p.left(360.0//side) #將畫筆方向向左轉90度
drawShape(4) #呼叫4邊形函數
drawShape(6) #呼叫6邊形函數
turtle.done() #結束畫圖
```
* 將函數drawShape加入參數length
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
def drawShape(side, length): #定義畫多邊形函數
for i in range(1, side+1): #使用迴圈i=1...4
p.forward(length) #用畫筆畫出一條長度100的線
p.left(360.0//side) #將畫筆方向向左轉90度
drawShape(4, 100) #呼叫4邊形函數
drawShape(6, 50) #呼叫6邊形函數
turtle.done() #結束畫圖
```
----
* 請繪製2個分開的多邊形
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
def drawShape(side, length): #定義畫多邊形函數
for i in range(0, side): #使用迴圈i=1...4
p.forward(length) #用畫筆畫出一條長度100的線
p.left(360.0//side) #將畫筆方向向左轉90度
drawShape(4, 100) #呼叫4邊形函數
p.penup() #將畫筆拿起
p.forward(150) #將畫筆往前移動150
p.pendown() #將畫筆放下
drawShape(6, 50) #呼叫6邊形函數
turtle.done() #結束畫圖
```
----
* 請繪製1個圓形改變畫筆顏色、並填滿顏色
```python=
import turtle #載入 turtle 模組
p = turtle.Pen() #以變數p建立畫筆物件
p.color('red')
def drawShape(side, length): #定義畫多邊形函數
p.fillcolor('blue')
p.begin_fill()
for i in range(0, side): #使用迴圈i=1...4
p.forward(length) #用畫筆畫出一條長度100的線
p.left(360.0//side) #將畫筆方向向左轉90度
p.end_fill()
drawShape(4, 100) #呼叫4邊形函數
turtle.done() #結束畫圖
```
---
# 使用 PIL (Python Image Library)
* 線上說明文件:[連結](https://pillow.readthedocs.io/en/stable/)
* 數十種圖檔格式的讀寫能力
* 支援黑白、灰階、自訂調色盤、RGB、CMYK等
* 基本的操作:裁切、平移、旋轉、改變尺寸、調置、剪下與貼上等
* 強化圖形:亮度、色調、對比、銳利度
* 十數種濾鏡,用在 Python 程式裡面,提供批次化處理的能力
* 繪圖製點、線、面、幾何形狀、填滿、文字等
----
## RGB 顏色的組成
* R:紅色(暗)(0~255)(亮)
* G:綠色(暗)(0~255)(亮)
* B:藍色(暗)(0~255)(亮)
![](https://ocu.tw/class/image/RGB.png =400x400)
----
### RGB 通道的組成
* (暗)(0~255)(亮)
| R 紅色 | G 綠色 | B 藍色 |
| --- | --- | --- |
|![](https://ocu.tw/class/image/RGB_R.png =400x250)|![](https://ocu.tw/class/image/RGB_G.png =400x250)|![](https://ocu.tw/class/image/RGB_B.png =400x250)|
----
### RGB 通道的範例
![](https://ocu.tw/class/image/RGB_Sample.jpg =600x600)
---
## 使用 Image in PIL
```python=
from PIL import Image
```
----
### 讀出圖片的屬性
* 下載範例圖片:[下載Lenna.png](http://ocu.tw/download/Lenna.png)
* 請將Lenna.png存在與py檔相同目錄
* 讀取範例圖檔之檔案屬性
```python=
from PIL import Image
image = Image.open("Lenna.png")
print(image.size)
```
* 常用的屬性如下
* Image.size:圖片的尺寸,如(512, 512)
* Image.format:圖片的檔案格式,如PNG
* Image.mode:圖片的色彩模式,如RGB
* Image.width:圖片的寬度,如512像素
* Image.height:圖片的高度,如512像素
----
### 讀出圖片並顯示
```python=
from PIL import Image
image = Image.open("Lenna.png")
image.show()
```
* 將圖片轉換成黑白影像
```python=
from PIL import Image
image = Image.open("Lenna.png")
output = image.convert("1")
output.show()
```
* 常用的模式如下:
* 1:1 bit黑白影像
* L:8 bit黑白影像(灰階)
* RGB:3 x 8 bit彩色影像
* CMYK:4 x 8 bit彩色影像(印刷使用)
----
|3x8bit彩色|1 bit黑白|
|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna.png =250x250)|![](https://ocu.tw/class/image/Lenna_Mode_1.png =250x250)|
|8 bit黑白(灰階)|8 bit彩色|
|![](https://ocu.tw/class/image/Lenna_Mode_L.png =250x250)|![](https://ocu.tw/class/image/Lenna_Mode_P.png =250x250)|
----
### 將圖片另存檔案
```python=
from PIL import Image
image = Image.open("Lenna.png")
image.save("Lenna.jpg")
image.close()
```
* 常用的格式如下:
* BMP:Windows Bitmp(原始顏色)
* JPEG:具8bit彩色,且具彩色高壓縮率
* GIF:具8bit或256色彩色影像
* PNG:可攜式網路圖形,具無失真壓縮
* TIFF:可增加標籤的文件檔格式
* PDF:Adobe建立之通用文件檔案
----
### 產生底色為紅色之空白影像
```python=
from PIL import Image
image = Image.new("RGB",(300,300),"red")
image.save("Lenna_New.png")
```
![](https://ocu.tw/class/image/Lenna_New.png =300x300)
----
### 將圖片旋轉影像
```python=
from PIL import Image
image = Image.open("Lenna.png")
output = image.transpose(Image.FLIP_LEFT_RIGHT)
output.show()
```
* 常用轉換方式如下:
* Image.ROTATE_90/180/270:旋轉特定角度
* Image.FLIP_LEFT_RIGHT:左右翻轉
* Image.FLIP_TOP_BOTTOM:上下翻轉
* 若需旋轉特定角度,請使用rotate方法
```python=
from PIL import Image
image = Image.open("Lenna.png")
output = image.rotate(45)
output.show()
```
----
### 將圖片改變尺寸
* 在圖片上繪出多種形狀、
```python=
from PIL import Image
image = Image.open("Lenna.png")
small_image = image.resize((256,256))
small_image.save("Lenna_Resize.png")
```
* 改變大小時,重新取樣的方法
* Image.NEAREST:預設方法
* Image.BILINEAR:輸入圖像中2x2環境上的線性插值
* Image.BICUBIC
* Image.LANCZOS
----
### 將圖片貼上其他影像
* 在圖片上貼上其他影像
```python=
from PIL import Image
image = Image.open("Lenna.png")
small_image = image.resize((256,256))
image.paste(small_image,(0,0))
image.save("Lenna_Paste.png")
```
![](https://ocu.tw/class/image/Lenna_Paste.png =300x300)
----
### 將圖片剪下部分影像
* 在圖片上貼上其他影像
```python=
from PIL import Image
image = Image.open("Lenna.png")
crop = image.crop((100,100,356,356))
crop.save("Lenna_Crop.png")
```
![](https://ocu.tw/class/image/Lenna_Crop.png =300x300)
----
### 混合二張影像
* 背景圖:[下載](https://ocu.tw/class/image/Back.png)
```python=
from PIL import Image
front = Image.open("Lenna.png")
back = Image.open("Back.png")
image = Image.blend(front, back, 0.5)
image.save("Lenna_Blend.png")
```
|前景圖|背景圖|疊合後
|:-:|:-:|:-|
|![](https://ocu.tw/class/image/Lenna.png =200x150)|![](https://ocu.tw/class/image/Back.png =200x150)|![](https://ocu.tw/class/image/Lenna_Blend.png =200x150)
----
### 利用通道處理變暗/亮
```python=
from PIL import Image
image = Image.open("Lenna.png")
image_eval = Image.eval(image, lambda a: a*0.5)
image_eval.save("Lenna_Dark.png")
image_eval = Image.eval(image, lambda a: 127 + a*0.5)
image_eval.save("Lenna_Bright.png")
```
|原始圖|變暗|變亮
|:-:|:-:|:-|
|![](https://ocu.tw/class/image/Lenna.png =200x150)|![](https://ocu.tw/class/image/Lenna_Dark.png =200x150)|![](https://ocu.tw/class/image/Lenna_Bright.png =200x150)
----
## 使用 ImageDraw in PIL
* 可在圖片上加上文字
* 線上說明:[連結](https://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html)
```python=
from PIL import ImageDraw
```
----
### 將圖片的左上角加文字
```python=
from PIL import Image, ImageDraw
image = Image.open("Lenna.png")
draw = ImageDraw.Draw(image)
draw.text((10,10), "Lenna")
image.show()
```
|左上角加文字|加上各種形狀|加大型紅字
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Text.png =200x150)|![](https://ocu.tw/class/image/Lenna_Shape.png =200x150)|![](https://ocu.tw/class/image/Lenna_Font.png =200x150)
* 數位影像座標系:採左上座標為(0, 0)
* 請參考:Draw.text(xy,text,fill=None, font=None)
----
### 將圖片加上有顏色的繪圖
* 在圖片上繪出多種形狀、
```python=
from PIL import Image, ImageDraw
image = Image.open("Lenna.png")
draw = ImageDraw.Draw(image)
draw.point([(5,5)],"black")
draw.line([(10,10),(10,100)],"blue")
draw.rectangle([(100,100),(200,200)],"green")
draw.ellipse([(300,400),(500,500)],"red")
image.show()
```
----
## 使用 ImageFont in PIL
* 在圖片上使用自訂字型貼上文字
* 線上說明:[連結](https://pillow.readthedocs.io/en/3.1.x/reference/ImageFont.html)
```python=
from PIL import ImageFont
```
----
### 將圖片加上有顏色及字型的文字
```python=
from PIL import Image, ImageDraw, ImageFont
image = Image.open("Lenna.png")
arial = ImageFont.truetype("C:\\Windows\\Fonts\\arial.ttf",100)
draw = ImageDraw.Draw(image)
draw.text((0,0),"Lenna","red",arial)
image.show()
```
* 使用其他ImageDraw的方法
* Draw.point(xy,color)
* Draw.line(xy,color)
* Draw.rectangle(xy,color)
* Draw.ellipse(xy,color)
---
## 使用 ImageEnhance in PIL
* 在圖片上調整色彩平衡、對比度、亮度、清晰度之增強
* 色彩平衡:Color
* 對比度:Contrast
* 亮度:Brightness
* 清晰度:Sharpness
* 線上說明:[連結](https://pillow.readthedocs.io/en/3.1.x/reference/ImageEnhance.html)
```python=
from PIL import ImageEnhance
```
----
### 在圖片上調整色彩平衡
* 參數:0.5可降低色彩平衡
* 參數:1.0原圖不改變
* 參數:1.5可增加色彩平衡
```python=
from PIL import Image, ImageEnhance
im = Image.open("Lenna.png")
im1 = ImageEnhance.Color(im).enhance(0.5)
im1.save("Lenna_Color05.png")
```
|參數=0.5|參數=1.0|參數=1.5
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Color05.png =200x150)|![](https://ocu.tw/class/image/Lenna_Color10.png =200x150)|![](https://ocu.tw/class/image/Lenna_Color15.png =200x150)
----
### 在圖片上調整對比度
* 參數:0.5可降低對比度
* 參數:1.0原圖不改變
* 參數:1.5可增加對比度
```python=
from PIL import Image, ImageEnhance
im = Image.open("Lenna.png")
im1 = ImageEnhance.Contrast(im).enhance(0.5)
im1.save("Lenna_Contrast05.png")
```
|參數=0.5|參數=1.0|參數=1.5
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Contrast05.png =200x150)|![](https://ocu.tw/class/image/Lenna_Contrast10.png =200x150)|![](https://ocu.tw/class/image/Lenna_Contrast15.png =200x150)
----
### 在圖片上調整亮度
* 參數:0.5可降低亮度
* 參數:1.0原圖不改變
* 參數:1.5可增加亮度
```python=
from PIL import Image, ImageEnhance
im = Image.open("Lenna.png")
im1 = ImageEnhance.Brightness(im).enhance(0.5)
im1.save("Lenna_Brightness05.png")
```
|參數=0.5|參數=1.0|參數=1.5
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Brightness05.png =200x150)|![](https://ocu.tw/class/image/Lenna_Brightness10.png =200x150)|![](https://ocu.tw/class/image/Lenna_Brightness15.png =200x150)
----
### 在圖片上調整清晰度
* 參數:0.2可降低清晰度
* 參數:1.0原圖不改變
* 參數:5.0可增加清晰度
```python=
from PIL import Image, ImageEnhance
im = Image.open("Lenna.png")
im1 = ImageEnhance.Sharpness(im).enhance(0.2)
im1.save("Lenna_Sharpness50.png")
```
|參數=0.2|參數=1.0|參數=5.0
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Sharpness02.png =200x150)|![](https://ocu.tw/class/image/Lenna_Brightness10.png =200x150)|![](https://ocu.tw/class/image/Lenna_Sharpness50.png =200x150)
---
## 使用 ImageFilter in PIL
```python=
from PIL import ImageFilter
```
----
### 使用常用濾鏡
```python=
from PIL import Image, ImageFilter
image = Image.open("Lenna.png")
output = image.filter(ImageFilter.BLUR)
output.show()
```
* 常用濾鏡如下:
* ImageFilter.BLUR:模糊
* ImageFilter.CONTOUR:輪廓
* ImageFilter.EDGE_ENHANCE:邊緣增強
* ImageFilter.EMBOSS:壓花
* ImageFilter.FIND_EDGES:找邊
* ImageFilter.SMOOTH:平滑
* ImageFilter.SHARPEN:銳利化
----
|原始圖|濾鏡:模糊化|
|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna.png =250x250)|![](https://ocu.tw/class/image/Lenna_Blur.png =250x250)|
|濾鏡:壓花|濾鏡:銳利化|
|![](https://ocu.tw/class/image/Lenna_Emboss.png =250x250)|![](https://ocu.tw/class/image/Lenna_SHARPEN.png =250x250)|
----
### 使用進階濾鏡:BoxBlur
* 參數:radius = 1~9
```python=
from PIL import Image, ImageFilter
image = Image.open("Lenna.png")
output = image.filter(ImageFilter.BoxBlur(3))
output.save("Lenna_BoxBlur3.png")
```
|radius=1|radius=3|radius=5
|:-:|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_BoxBlur1.png =200x150)|![](https://ocu.tw/class/image/Lenna_BoxBlur3.png =200x150)|![](https://ocu.tw/class/image/Lenna_BoxBlur5.png =200x150)
----
### 使用進階濾鏡:GaussianBlur
* 參數:radius = 1~9 (default=2)
```python=
from PIL import Image, ImageFilter
image = Image.open("Lenna.png")
output = image.filter(ImageFilter.GaussianBlur())
output.save("Lenna_GaussianBlur.png")
```
|radius=2|radius=4
|:-:|:-:
|![](https://ocu.tw/class/image/Lenna_GaussianBlur.png =200x150)|![](https://ocu.tw/class/image/Lenna_GaussianBlur4.png =200x150)
----
### GaussianBlur vs BoxBlur
![](https://ocu.tw/class/image/GaussianBlurVSBoxBlur.jpg =1000x600)
----
## 使用Excel實作濾鏡效果
* 製作參考圖檔:[下載](https://ocu.tw/class/image/letter.png)
* 使用以下程式轉換成CSV檔:letter.csv
* 為了方便理解將圖檔模式轉換成灰階
```python=
import numpy
from PIL import Image
im1 = Image.open("letter.png")
im2 = im1.convert("L")
numpy.savetxt("letter.csv", im2, fmt="%d", delimiter=",")
```
|原始圖|變暗濾鏡
|:-:|:-:
|![](https://ocu.tw/class/image/letter_origin.png =150x120)|![](https://ocu.tw/class/image/letter_div2_origin.png =150x120)
----
### CSV內容值以Excel開啟
* 灰階內容為0~255
* 黑色為 0
* 白色為 255
![](https://i.imgur.com/apyf0S8.png)
----
### 經過變暗濾鏡轉換後
* 變暗濾鏡:將內容值除以2
* 黑色為 0
* 白色為 127
![](https://i.imgur.com/TxegnPX.png)
----
### 經過平均值濾鏡轉換後
* 平均值濾鏡:該位置四周3x3的遮罩求平均值
* B2 = AVERAGE(A1:C3)
* F9 = AVERAGE(E8:G10)
![](https://i.imgur.com/4JrWA0b.png)
----
### 將CSV檔轉換成圖檔
* Excel檔:[下載](https://ocu.tw/class/image/letter.xls)
```python=
from numpy import genfromtxt
from PIL import Image
ar = genfromtxt("letter_mean.csv", delimiter=",")
image1 = Image.fromarray(ar)
image2 = image1.convert("P")
image2.save("letter_mean.png")
```
|原始圖|變暗濾鏡
|:-:|:-:
|![](https://ocu.tw/class/image/letter_origin.png =150x120)|![](https://ocu.tw/class/image/letter_mean_origin.png =150x120)
---
# 使用 NumPy
* Numpy 是 Python 的一個重要模組
* 主要用於資料處理上
* Numpy 底層以 C 和 Fortran 語言實作
* 當 Python 處理龐大資料時,list 效能不理想
* Numpy 具備平行處理能力,方便處理大型陣列
* Python 其它相關套件奠基在 Numpy 的基礎上
![](https://i.imgur.com/LctUwRJ.png =500x200)
----
## NumPy 基礎操作
* 基本結構為N-Dimesion Array(同質多維度陣列)
* 維度(ndim):即 N 值
* 形狀(shape):各維度的大小(回傳tuple)
* 數值類型(dtype):資料的型別
![](https://i.imgur.com/a8Dnbio.png)
----
### 讀出基本屬性及改變屬性
```python=
import numpy as np # 載入 numpy 模組
np1 = np.array([1, 2, 3])
np2 = np.array([4, 5, 6])
print(np1 + np2) # 陣列相加 [5 7 9]
print(np1.ndim, np1.shape, np1.dtype) # 顯示相關資訊
# 1 (3,) int32 => 1維陣列, 3個元素, 資料型別
np3 = np.array([[1, 2, 3],[4, 5, 6],[7,8,9],[10,11,12]])
print(np3.ndim, np3.shape, np3.dtype) # 2 (4, 3) int32
np3 = np3.reshape([3,4])
np3 = np3.astype("float64")
print(np3)
"""
[[ 1. 2. 3. 4.]
[ 5. 6. 7. 8.]
[ 9. 10. 11. 12.]]
"""
print(np3.ndim, np3.shape, np3.dtype) # 2 (3, 4) float64
```
----
### 從 CSV 讀入陣列
* 開啟 Excel,於工作表中輸入欲讀入之資料
* 另存成 CSV 檔(如 2DArray.csv)
```python=
import numpy as np # 載入 numpy 模組
np1 = np.genfromtxt("2DArray.csv",delimiter=",")
print(np1)
# [[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
# [11. 12. 13. 14. 15. 16. 17. 18. 19. 20.]
# [21. 22. 23. 24. 25. 26. 27. 28. 29. 30.]]
print(np1.ndim, np1.shape, np1.dtype) # 顯示相關資訊
# 2 (3, 10) float64
np2 = np.genfromtxt("2DArray.csv",delimiter=",",dtype="int32")
print(np2)
# [[ 1 2 3 4 5 6 7 8 9 10]
# [11 12 13 14 15 16 17 18 19 20]
# [21 22 23 24 25 26 27 28 29 30]]
print(np2.ndim, np2.shape, np2.dtype) # 顯示相關資訊
# 2 (3, 10) int32
```
----
### 建立相同值陣列
```python=
import numpy as np
np1 = np.zeros([2, 3])
print(np1)
# [[0. 0. 0.]
# [0. 0. 0.]]
np2 = np.ones([2, 3])
print(np2)
# [[1. 1. 1.]
# [1. 1. 1.]]
np3 = np2 * 3 + 5
print(np3)
# [[8. 8. 8.]
# [8. 8. 8.]]
```
----
### 讀取部分陣列
```python=
import numpy as np
np1 = np.array([[1, 2, 3],[4, 5, 6],[7,8,9]])
print(np1)
# [[ 1 2 3]
# [ 4 5 6]
# [ 7 8 9]]
print(np1[1,2]) # 6
print(np1[1:3,1:3])
# [[5 6]
# [8 9]]
print(np1[:2,:3])
# [[1 2 3]
# [4 5 6]]
print(np1[(0,2),(0,2)])
# [1 9]
```
----
### 數學、羅輯、統計運算
```python=
import numpy as np
np1 = np.array([[1, 2, 3],[4, 5, 6]])
print(np1)
# [[ 1 2 3]
# [ 4 5 6]
print(np1 * 10)
# [[10 20 30]
# [40 50 60]]
print(np1 > 3)
# [[False False False]
# [ True True True]]
print(np1.sum()) # 21
print(np1.sum(axis=0)) # [5 7 9]
print(np1.sum(axis=1)) # [ 6 15]
```
---
# 使用 Matplotlib
* Python 2D 繪圖工具
* 以簡單幾行程式即可以完成各種圖形
* 製作影像圖、直方圖、條形圖、散點圖等
* 使用在科學家、工程師、數學家等人員
* 線上說明文件:[連結](https://matplotlib.org/)
|影像圖|直方圖|條形圖|散點圖|
| --- | --- | --- | --- |
|![](https://ocu.tw/class/image/image.png =200x150)|![](https://ocu.tw/class/image/histogram.png =200x150)|![](https://ocu.tw/class/image/barchart.png =200x150)|![](https://ocu.tw/class/image/scatter.png =200x150)|
----
### 使用 pyplot in Matplotlib
* 載入用法
* 線上說明:[連結](https://matplotlib.org/3.1.1/api/pyplot_summary.html)
```python=
from matplotlib import pyplot
```
----
#### 畫出一個 y = x^2 的一元二次方程式圖形
* 使用點連成線,基本單位:1
* pyplot.plot():使用繪圖工具
* pyplot.show():顯示繪圖結果
```python=
from matplotlib import pyplot #載入matplotlib.pyplot
x = [-4,-3,-2,-1,0,1,2,3,4]
y = [16,9,4,1,0,1,4,9,16]
pyplot.plot(x,y)
pyplot.show()
```
![](https://ocu.tw/class/image/pyplot_x2.png =350x250)
----
#### 畫出一個 y = x^2 的一元二次方程式圖形
* 使用點連成線,基本單位:0.1
* 使用 numpy moudle
```python=
from matplotlib import pyplot
import numpy
x = numpy.arange(-4,4.1,0.1)
y = numpy.power(x,2)
pyplot.plot(x,y)
pyplot.show()
```
![](https://ocu.tw/class/image/pyplot_x2c.png =350x250)
----
#### 使用 pyplot 顯示圖片檔
* pyplot.imread():讀出圖片檔案或網址
* pyplot.imshow():在繪圖區顯示圖片
* pyplot.axis():顯示(on)/隱藏(off)座標軸
```python=
from matplotlib import pyplot
from matplotlib import image
im = image.imread("Lenna.png")
pyplot.imshow(im)
#pyplot.axis("off")
pyplot.show()
```
|有座標軸|去除座標軸|
|:-:|:-:|
|![](https://ocu.tw/class/image/Lenna_Pyplot.png =250x150)|![](https://ocu.tw/class/image/Lenna_Pyplot_NoAxis.png =250x150)|
----
#### 使用 pyplot 顯示 pillow Image
```python=
from matplotlib import pyplot
from PIL import Image
image1 = Image.open("Lenna.png")
pyplot.imshow(image1)
pyplot.show()
```
----
#### 使用 pyplot 顯示2張圖片檔
* pyplot.subplot(row,col,index):設定子繪圖區
* pyplot.title():設定繪圖標題
```python=
from matplotlib import pyplot
from matplotlib import image
im1 = image.imread("Lenna.png")
im2 = image.imread("Back.png")
pyplot.subplot(1,2,1).imshow(im1)
#pyplot.title("Lenna.png")
pyplot.subplot(1,2,2).imshow(im2)
#pyplot.title("Back.png")
pyplot.show()
```
![](https://ocu.tw/class/image/Lenna_Pyplot_2Image.png =400x250)
---
# 使用 OpenCV
* Open Source Computer Vision
* Linux / Android / iOS / Windows
* 安裝 OpenCV with Python in Windows
* 必備套件:NumPy
* 選備套件:MatplotLib
```python=
pip install numpy
pip install opencv-python
pip install matplotlib
```
* 載入 OpenCV:[教學網址](https://opencv-python-tutroals.readthedocs.io/en/latest/index.html)
```python=
import cv2
```
----
## 使用 OpenCV 讀取圖片並顯示
* imread(filename):讀取圖片為物件
* imwrite(filename, img):寫入檔名
* imshow(title, img):顯示圖片物件
* waitkKey():暫停視窗,按ESC離開
* destroyAllWindows():釋放視窗記憶體
```python=
import cv2
image1 = cv2.imread("Lenna.png")
cv2.imwrite("LennaColorImage.jpg", image1)
cv2.imshow("Read Image by CV2", image1)
cv2.waitKey()
cv2.destroyAllWindows()
```
----
### 讀取圖片並以灰階顯示,並設定壓縮率
* imread(filename, flags=None):加入參數
* imwrite(filename, img, params=None):加入參數
```python=
import cv2
image1 = cv2.imread("Lenna.png", cv2.IMREAD_GRAYSCALE)
# cv2.IMWRITE_JPEG_QUALITY default is 95
cv2.imwrite("Lenna_Gray.jpg", image1)
cv2.imwrite("Lenna_JpegQ80.jpg", image1
, [cv2.IMWRITE_JPEG_QUALITY, 80])
cv2.imwrite("Lenna_JpegQ50.jpg", image1
, [cv2.IMWRITE_JPEG_QUALITY, 50])
```
----
### 讀取圖片並由 Matplotlib 顯示
```python=
import cv2
from matplotlib import pyplot as plot
image1 = cv2.imread("Lenna.png")
image2 = cv2.imread("Lenna.png", cv2.IMREAD_GRAYSCALE)
image1 = image1[:,:,::-1]
#image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
plot.subplot(1,2,1).imshow(image1)
plot.subplot(1,2,2).imshow(image2, cmap="gray")
plot.show()
```
![](https://i.imgur.com/nbfK0po.png)
----
### 開新圖片並由 CV2 繪製直線
* cv2.line(img,pt1,pt2,color,thickness)
```python=
import numpy as np
import cv2
from matplotlib import pyplot as plot
image = np.zeros((512,512,3), np.uint8)
image = cv2.line(image,(0,0),(511,511),(255,0,0),3)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/lec9PvC.png =400x300)
----
### 開新圖片並由 CV2 繪製長方形、圓形
* cv2.rectange(img,pt1,pt2,color,thickness)
* cv2.circle(img,center,radius,color,thickness)
```python=
import numpy as np
import cv2
from matplotlib import pyplot as plot
image = np.zeros((512,512,3), np.uint8)
image = cv2.rectangle(image,(384,10),(510,128),(0,255,0),5)
image = cv2.circle(image,(447,70),50,(0,0,255),-1)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/J7nxvn8.png =400x300)
----
### 開新圖片並由 CV2 繪製橢圓形
* cv2.ellipse(img,center,axes,angle,StartAngle,EndAngle,color,thickness)
```python=
import numpy as np
import cv2
from matplotlib import pyplot as plot
image = np.zeros((512,512,3), np.uint8)
image = cv2.ellipse(image,(256,128),(100,50),45,0,270,(255,255,0),-1)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/teiRx7Y.png)
----
### 開新圖片並由 CV2 繪製多邊形
* cv2.polylines(img,pts,isClosed,color,thickness)
```python=
import numpy as np
import cv2
from matplotlib import pyplot as plot
image = np.zeros((512,512,3), np.uint8)
pts = np.array([[50,25],[120,130],[170,120],[150,80]], np.int32)
pts = pts.reshape((-1,1,2))
image = cv2.polylines(image,[pts],True,(0,255,255),3)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/LX1e79H.png)
----
### 開新圖片並由 CV2 繪製文字
* cv2.putText(img,text,org,fontFace,fontScale,color,thickness,lineType)
```python=
import numpy as np
import cv2
from matplotlib import pyplot as plot
image = np.zeros((512,512,3), np.uint8)
font = cv2.FONT_HERSHEY_PLAIN
cv2.putText(image,"OpenCV",(10,100),cv2.FONT_HERSHEY_SIMPLEX,4,(255,255,255),2,cv2.LINE_AA)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/qjOE592.png)
----
### 以滑鼠座標繪製圖形一
```python=
import cv2
import numpy as np
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
cv2.circle(img,(x,y),50,(255,0,255),-1)
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
```
![](https://i.imgur.com/071TvFi.png =400x300)
----
### 以滑鼠座標繪製圖形二
```python=
import cv2
import numpy as np
drawing = False
mode = True
ix,iy = -1,-1
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing,mode
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
if mode == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if mode == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == ord('m'):
mode = not mode
elif k == 27:
break
cv2.destroyAllWindows()
```
![](https://i.imgur.com/oxFnF4u.png =400x300)
----
### 運用 Numpy 運算做影像處理
```python=
import cv2
from matplotlib import pyplot as plot
image = cv2.imread("Lenna.png")
image = image[:,:,::-1]
print(image[99,99]) # RGB值
print(image[99,99,0]) # R值
print(image.item(99,99,1)) # G值
image[100,100] = [255,255,255] # 白色
image[100,100,0] = 255 # R值改為255
image[0:50,50:100] = [0,255,255] # 黃色
image[101:150,101:150] = image[201:250,201:250]
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/hpjOHmt.png =400x300)
----
### 運用 CV2 做影像疊合
* blend = a * image1 + b * image2 + gama
```python=
import cv2
image1 = cv2.imread("Lenna.png")
image2 = cv2.imread("Back.png")
blend = cv2.addWeighted(image1,0.7,image2,0.3,0)
cv2.imshow('Blending Image',blend)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
![](https://i.imgur.com/chyc3gA.png =350x350)
----
### 使用 CV2 製作影像灰度分割
* Gradient Image:[下載](https://ocu.tw/class/image/gradient.png)
```python=
import cv2
import numpy as np
from matplotlib import pyplot as plot
img = cv2.imread("gradient.png",cv2.IMREAD_GRAYSCALE)
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plot.subplot(2,3,i+1),plot.imshow(images[i],'gray')
plot.title(titles[i])
plot.xticks([]),plot.yticks([])
plot.show()
```
![](https://i.imgur.com/V3sMdP5.png =300x200)
----
### Threshold Function
* cv2.threshold(src,thresh,maxval,type)
![](https://ocu.tw/class/image/GradientFunction.png)
----
### 使用 CV2 製作影像灰度分割一
```python=
import cv2
import numpy as np
from matplotlib import pyplot as plot
img = cv2.imread("Lenna.png",cv2.IMREAD_GRAYSCALE)
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plot.subplot(2,3,i+1),plot.imshow(images[i],'gray')
plot.title(titles[i])
plot.xticks([]),plot.yticks([])
plot.show()
```
![](https://i.imgur.com/fgiDSWH.png =300x200)
----
### 使用 CV2 製作影像灰度分割二
* Sudoku Image:[下載](https://ocu.tw/class/image/sudoku-original.jpg)
```python=
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('sudoku-original.jpg',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)', 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
```
![](https://i.imgur.com/tInXikj.png)
---
## RGBA 顏色的組成
* R:紅色(暗)(0~255)(亮)
* G:綠色(暗)(0~255)(亮)
* B:藍色(暗)(0~255)(亮)
* A:不透明(透明)(0~255)(不透明)
![](https://i.imgur.com/DXTtpcq.png =500x350)
----
### 開啟具透明的PNG檔(錯誤)
* 僑光科大PNG檔:[下載](https://ocu.tw/class/image/ocu.png)
* 具透明的PNG檔:Alpha通道不全為255
* cv2.imread預設參數為IMREAD_COLOR(RGB)
* 因忽略Alpha通道,具透明區塊會顯示錯誤顏色
```python=
import cv2
from matplotlib import pyplot as plot
image = cv2.imread("ocu.png") # BGR少了A
image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/rSE2Hac.png =500x500)
----
### 開啟具透明的PNG檔(正確)
* cv2.imread參數為IMREAD_UNCHANGED
* OpenCV將會保留Alpha通道,即BGRA
* 保留Alpha通道,具透明區塊會顯示正確顏色
```python=
import cv2
from matplotlib import pyplot as plot
image = cv2.imread("ocu.png",cv2.IMREAD_UNCHANGED) # BGRA
image = cv2.cvtColor(image,cv2.COLOR_BGRA2RGBA) #RGBA
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/f9x87ao.png =500x500)
----
### 開啟具透明的PNG檔(背景色為黑色)
* A = 0 ~ 255
* alpha = A / 255.:alpha = 0 ~ 1
```python=
import cv2
from matplotlib import pyplot as plot
image = cv2.imread("ocu.png",cv2.IMREAD_UNCHANGED)
for i in range(3):
alpha = image[:,:,3] / 255. # alpha = A / 255
image[:,:,i] = image[:,:,i] * alpha # BGR = BGR * alpha
image = cv2.cvtColor(image,cv2.COLOR_BGRA2RGB)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/f9x87ao.png =500x500)
----
### 開啟具透明的PNG檔(背景色為單色)
* 假如背景色BGR為黃色(0,255,255)
* 計算公式:front * alpha + back * (1 - alpha)
```python=
import cv2
from matplotlib import pyplot as plot
front = cv2.imread("ocu.png",cv2.IMREAD_UNCHANGED)
back = (0,255,255) # Yellow
for i in range(3): # front * alpha + back * (1 - alpha)
alpha = front[:,:,3] / 255. # alpha = A / 255
front[:,:,i] = front[:,:,i] * alpha + back[i] * (1 - alpha)
front = cv2.cvtColor(front,cv2.COLOR_BGRA2RGB)
plot.imshow(front)
plot.show()
----
### 開啟具透明的PNG檔(背景為圖片)
* 假如背景色BGR為黃色(0,255,255)
* 計算公式:front * alpha + back * (1 - alpha)
```python=
import cv2
from matplotlib import pyplot as plot
image = cv2.imread("Lenna.png")
front = cv2.imread("ocu.png",cv2.IMREAD_UNCHANGED)
height,width,channel=front.shape
back = image[:height,:width] # 擷取與Logo一樣大小的區域
for i in range(3): # front * alpha + back * (1 - alpha)
alpha = front[:,:,3] / 255.
back[:,:,i] = front[:,:,i] * alpha + back[:,:,i] * (1 - alpha)
image = cv2.cvtColor(image,cv2.COLOR_BGRA2RGB)
plot.imshow(image)
plot.show()
```
![](https://i.imgur.com/GBf8gsZ.png)
---
### 使用CV2做平滑
```python=
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('opencv-logo.png')
kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()
```
![](https://i.imgur.com/BkldVI1.png)
----
### 擷取某顏色範圍(RGB)
* 顏色漸層圖:[下載](https://ocu.tw/class/image/hue.png)
```python=
import cv2
import numpy as np
from matplotlib import pyplot as plot
image = cv2.imread("hue.png") # hue.png
image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGB)
lower_blue = np.array([0,0,155]) # 定義在 RGB 中的藍色
upper_blue = np.array([100,100,255])
mask = cv2.inRange(image, lower_blue, upper_blue) # 產生範圍中的 Mask
result = cv2.bitwise_and(image, image, mask= mask)
plot.subplot(131).imshow(image)
plot.subplot(132).imshow(mask,"gray")
plot.subplot(133).imshow(result)
plot.show()
```
----
![](https://i.imgur.com/iGcdAU9.png)
----
### HSV Color Space
* 影像判識中,除 Gray 外,常用的模式
* HSV定義:
* H(Hue):色相(0~360)
* S(Saturatio):飽合度(不飽合)(0~1)(飽合)
* V(Value):明度(暗)(0~1)(亮)
* OpenCV轉換:因為U8(0~255)
* H:色相(0~180) (H = H / 2)
* S:飽合度(0~255)(S = S * 255)
* V:明度(暗)(0~255)(V = V * 255)
----
### Photoshop 中的檢色器
![](https://i.imgur.com/v9rBnxQ.png)
----
### 擷取某顏色範圍(HSV)
* 顏色漸層圖:[下載](https://ocu.tw/class/image/hue.png)
```python=
import cv2
import numpy as np
from matplotlib import pyplot as plot
image = cv2.imread("hue.png") # hue.png
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_blue = np.array([110,50,50]) # 定義在 RGB 中的藍色
upper_blue = np.array([130,255,255])
mask = cv2.inRange(image, lower_blue, upper_blue) # 產生範圍中的 Mask
result = cv2.bitwise_and(image, image, mask= mask)
image = cv2.cvtColor(image, cv2.COLOR_HSV2RGB)
result = cv2.cvtColor(result, cv2.COLOR_HSV2RGB)
plot.subplot(131).imshow(image)
plot.subplot(132).imshow(mask,"gray")
plot.subplot(133).imshow(result)
plot.show()
```
----
### 擷取影片中藍色影像
```python=
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(1):
_, frame = cap.read() # 讀出每個影格
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 轉換 BGR 到 HSV
lower_blue = np.array([110,50,50]) # 定義在 HSV 中的藍色
upper_blue = np.array([130,255,255])
mask = cv2.inRange(hsv, lower_blue, upper_blue) # 產生範圍中的 Mask
res = cv2.bitwise_and(frame,frame, mask= mask) # 對原始影像做 Bitwise-AND mask
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
```
---
# 使用 Tkinter 套件
* 用來開發使用者圖形界面(GUI)的套件
* 它的全名是 Tool Kit Interface
* 跨平台的GUI程式,Linux、Windows、MAC等
```python=
import tkinter as tk
```
----
## 使用 TKInter 產生視窗元件
* Tk():Tk類別的自建構式
* mainloop():產生無窮迴圈,擷取事件並處理
```python=
import tkinter as tk
win = tk.Tk()
win.mainloop()
```
![](https://i.imgur.com/uRW4N2U.png)
----
## 使用 TKInter 的其他方法
```python=
import tkinter as tk
window = tk.Tk()
window.title("我的程式")
window.geometry("640x480")
window.maxsize(800, 600) # 最大視窗尺寸
window.minsize(400, 300) # 最小視窗尺寸
window.mainloop()
```
![](https://i.imgur.com/hl6zdcc.png)
----
## 使用 Button 元件
```python=
import tkinter as tk
window = tk.Tk()
window.minsize(400, 300)
button1 = tk.Button(window, text="確定", width=10, height=2)
button1.config(fg="yellow", bg="blue")
button1.pack()
button2 = tk.Button(window, text="確定", width=10, height=2)
button2.config(text="取消", width=20)
button2.pack(side = tk.BOTTOM)
window.mainloop()
```
![](https://i.imgur.com/5k0IiCN.png)
----
## 使用 Label 元件
```python=
import tkinter as tk
window = tk.Tk()
window.minsize(400, 300)
label1 = tk.Label(window, text="我的程式", width=10, justify=tk.CENTER)
label1.pack()
label2 = tk.Label(window, text="請輸入名稱:", padx=10, pady=10, justify=tk.LEFT)
label2.pack(side = tk.TOP)
label3 = tk.Label(window, text="請輸入年齡:", padx=10, pady=10, justify=tk.LEFT)
label3.pack(side = tk.TOP)
window.mainloop()
```
![](https://i.imgur.com/mSfhODp.png)
----
## 使用 Button Click 事件呼叫
```python=
import tkinter as tk
def button_click():
label1.config(text="已經被我改了")
window = tk.Tk()
window.minsize(400, 300)
label1 = tk.Label(window, text="我的程式")
button1 = tk.Button(window, text="確定", command=button_click)
label1.pack()
button1.pack()
window.mainloop()
```
![](https://i.imgur.com/Pe851ey.png)
----
## 使用 Entry 輸入文字
```python=
import tkinter as tk
def button_click():
window.title(text1.get())
window = tk.Tk()
window.minsize(200, 150)
tk.Label(window, text="請輸入標題:").pack(side=tk.LEFT)
text1 = tk.StringVar()
tk.Entry(window, textvariable=text1).pack(side=tk.LEFT)
tk.Button(window, text="確定", command=button_click).pack(side=tk.BOTTOM)
window.mainloop()
```
![](https://i.imgur.com/x4Mx8or.png)
----
## 使用 messagebox 元件
```python=
import tkinter as tk
from tkinter import messagebox as msgbox
def button_click():
msgbox.showinfo("訊息", "您輸入的名稱是" + text1.get())
window = tk.Tk()
window.minsize(200, 150)
tk.Label(window, text="請輸入名稱:").pack(side=tk.LEFT)
text1 = tk.StringVar()
tk.Entry(window, textvariable=text1).pack(side=tk.LEFT)
tk.Button(window, text="確定", command=button_click).pack(side=tk.BOTTOM)
window.mainloop()
```
![](https://i.imgur.com/WGM65YH.png)
----
## 使用 Checkbutton
```python=
import tkinter as tk
from tkinter import messagebox as msgbox
def showMsg():
result = ""
for i in checkvalue:
if checkvalue[i].get() == True:
result = result + dessert[i] + "\t"
msgbox.showinfo("核取結果", result)
window = tk.Tk()
window.minsize(200, 150)
label1 = tk.Label(window, text = "請核取您喜歡的甜點:").pack()
dessert = {0 : "馬卡龍", 1 : "舒芙蕾", 2 : "草莓塔", 3 : "蘋果派"}
checkvalue = {}
for i in range(len(dessert)):
checkvalue[i] = tk.BooleanVar()
tk.Checkbutton(window, variable = checkvalue[i], text = dessert[i]).pack()
tk.Button(window, text = "確定", command = showMsg).pack()
window.mainloop()
```
![](https://i.imgur.com/Zm1Qr0K.png)
----
## 使用 RadioButton
```python=
import tkinter as tk
from tkinter import messagebox as msgbox
def showMsg():
i = radiovalue.get()
msgbox.showinfo("選取結果", dessert[i])
window = tk.Tk()
label1 = tk.Label(window, text = "請選取您最喜歡的甜點:").pack()
dessert = {0 : "馬卡龍", 1 : "舒芙蕾", 2 : "草莓塔", 3 : "蘋果派"}
radiovalue = tk.IntVar()
radiovalue.set(0)
for i in range(len(dessert)):
tk.Radiobutton(window, text = dessert[i], variable = radiovalue, value = i).pack()
tk.Button(window, text = "確定", command = showMsg).pack()
window.mainloop()
```
![](https://i.imgur.com/eieiw2n.png)
----
## 使用 Menu 元件
```python=
import tkinter as tk
from tkinter import messagebox as msgbox
def newFile():
msgbox.showinfo("開新檔案", "在此撰寫開新檔案的敘述")
def openFile():
msgbox.showinfo("開啟舊檔", "在此撰寫開啟舊檔的敘述")
def about():
msgbox.showinfo("關於我們", "在此撰寫關於我們的敘述")
window = tk.Tk()
menu = tk.Menu(window)
window["menu"] = menu
filemenu = tk.Menu(menu)
menu.add_cascade(label = "檔案", menu = filemenu)
filemenu.add_command(label = "開新檔案...", command = newFile)
filemenu.add_command(label="開啟舊檔...", command = openFile)
filemenu.add_separator()
filemenu.add_command(label="離開", command = window.destroy)
helpmenu = tk.Menu(menu)
menu.add_cascade(label = "說明", menu = helpmenu)
helpmenu.add_command(label="關於我們...", command = about)
window.mainloop()
```
![](https://i.imgur.com/A4oPt3A.png)
----
## 使用 tkinter.Photoimage 載入圖片
* 支援PNG(不含JPG),適合載入固定不變的圖片
```python=
import tkinter as tk
window = tk.Tk()
window.minsize(600, 600)
image1 = tk.PhotoImage(file="Lenna.png")
tk.Label(window, image=image1).pack()
window.mainloop()
```
![](https://i.imgur.com/Db4PusP.png)
----
## 使用 ImageTk.PhotoImage 載入圖片
* 支援PNG、JPG等,適合載入固定不變的圖片
```python=
import tkinter as tk
from PIL import ImageTk
window = tk.Tk()
window.minsize(600, 600)
image1 = ImageTk.PhotoImage(file="Lenna.jpg")
tk.Label(window, image=image1).pack()
window.mainloop()
```
----
## 使用 Image + ImageTk 載入圖片
* 可利用 Image 來修改圖片
```python=
import tkinter as tk
from PIL import Image, ImageTk
window = tk.Tk()
window.minsize(600, 600)
image1 = Image.open("Lenna.png").convert("L")
imagetk1 = ImageTk.PhotoImage(image1)
tk.Label(window, image=imagetk1).pack()
window.mainloop()
```
----
## 使用 ImageTk + Button 修改影像
```python=
import tkinter as tk
from PIL import Image, ImageTk
def image_gray():
global image1, imagetk1, label1
imagetk1 = ImageTk.PhotoImage(image1.convert("L"))
label1.config(image=imagetk1)
window = tk.Tk()
window.minsize(600, 600)
image1 = Image.open("Lenna.png")
imagetk1 = ImageTk.PhotoImage(image1)
tk.Button(window, text="灰階處理", command=image_gray).pack()
label1 = tk.Label(window, image=imagetk1)
label1.pack()
window.mainloop()
```
----
## 使用 tkinter.askopenfilename 讀取檔案
```python=
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import filedialog as fd
def image_load():
global image1, imagetk1, label1
image1 = Image.open(fd.askopenfilename())
imagetk1 = ImageTk.PhotoImage(image1)
label1.config(image = imagetk1)
window = tk.Tk()
window.minsize(800, 600)
tk.Button(window, text="載入圖片", command=image_load).pack()
label1 = tk.Label(window)
label1.pack()
window.mainloop()
```
![](https://i.imgur.com/ZkAUJw3.png)
----
## 使用 askopenfilename + gray 影像處理
```python=
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import filedialog as fd
def image_load():
global image1, imagetk1, label1
image1 = Image.open(fd.askopenfilename())
imagetk1 = ImageTk.PhotoImage(image1)
label1.config(image = imagetk1)
def image_gray():
global image1, imagetk1, label1
image1 = image1.convert("L")
imagetk1 = ImageTk.PhotoImage(image1)
label1.config(image = imagetk1)
window = tk.Tk()
window.minsize(800, 600)
tk.Button(window, text="載入圖片", command=image_load).pack()
tk.Button(window, text="灰階處理", command=image_gray).pack()
label1 = tk.Label(window)
label1.pack()
window.mainloop()
```
----
## 使用 Menu + Image + ImageTk 影像處理
```python=
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import filedialog as fd
def image_load():
global image1, imagetk1, label1
image1 = Image.open(fd.askopenfilename())
imagetk1 = ImageTk.PhotoImage(image1)
label1.config(image = imagetk1)
def image_gray():
global image1, imagetk1, label1
image1 = image1.convert("L")
imagetk1 = ImageTk.PhotoImage(image1)
label1.config(image = imagetk1)
window = tk.Tk()
window.minsize(800, 600)
menu = tk.Menu(window)
window["menu"] = menu
menu.add_cascade(label="開啟檔案", command = image_load)
menu.add_cascade(label="灰階處理", command = image_gray)
menu.add_cascade(label="離開", command = window.destroy)
label1 = tk.Label(window)
label1.pack()
window.mainloop()
```
![](https://i.imgur.com/CarLxP9.png)
----
### 使用 fd.filedialog.askdirectory
* askdirectory:開啟選擇資料夾視窗
```python=
import tkinter as tk
from tkinter import filedialog as fd
def opendir_click():
dirname = fd.askdirectory()
label1.config(text = dirname)
window = tk.Tk()
window.minsize(800, 400)
label1 = tk.Label(window, font = ("Courier", 30))
label1.pack()
tk.Button(window, text = "選擇目錄", font = ("Courier", 30), command = opendir_click).pack()
window.mainloop()
```
----
### 列出某目錄下所有檔案
```python=
import tkinter as tk
import os
from os.path import isdir, isfile, join
from tkinter import filedialog as fd
def opendir_click():
dirname = fd.askdirectory()
label1.config(text = dirname)
filenames = os.listdir(dirname)
for fname in filenames:
if isfile(join(dirname, fname)):
print(join(dirname, fname))
window = tk.Tk()
window.minsize(800, 400)
label1 = tk.Label(window, font = ("Courier", 30))
label1.pack()
tk.Button(window, text = "選擇目錄", font = ("Courier", 30), command = opendir_click).pack()
window.mainloop()
```
----
### 使用 fd.filedialog.asksaveasfilename
```python=
import tkinter as tk
from tkinter import filedialog as fd
def saveas_click():
filename = fd.asksaveasfilename(filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
label1.config(text = filename)
window = tk.Tk()
window.minsize(800, 400)
label1 = tk.Label(window, font = ("Courier", 30))
label1.pack()
tk.Button(window, text = "另存新檔", font = ("Courier", 30), command = saveas_click).pack()
window.mainloop()
```
{"metaMigratedAt":"2023-06-14T23:57:48.679Z","metaMigratedFrom":"Content","title":"數位影像處理","breaks":true,"contributors":"[{\"id\":\"9eed60a5-6546-4dfd-8445-07f81bcfde52\",\"add\":62623,\"del\":17266}]","description":"僑光科技大學 資訊科技系2019/09/09 ~ 2020/01/11資科二忠、二孝:星期四第5、6、7節授課老師:高吉隆電子信箱:kevinkao888@gmail.com專業教室使用規則"}