# 數位影像處理 僑光科技大學 資訊科技系 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專業教室使用規則"}
    6603 views