# Python 結合 OpenCV 計算太陽能板數量之實作 ###### tags: `Python` `OpenCV` ## 設計理念 利用Python執行OpenCV,首先將照片導入Python後,經由OpenCV對照片進行處理,再利用OpenCv進行形狀算,完成這次的計算太陽能板數量。 ## Module Block ![](https://i.imgur.com/hKY1XB1.jpg) ### 匯入照片及灰階 要注意的是,假如.py檔和照片的位置在不同層時,需要打路徑,且路徑不得有中文,否則Python會找不到照片位置。 ``` image = cv2.imread("filed.jpg") #讀取你要的照片檔案(位置) ``` ![](https://i.imgur.com/g1Y3w6d.jpg) 對照片進行灰階,其主要的原因是:OpenCV只使用灰階的圖,才能進行梯度的運算。 灰階的原理網路上就一堆,這邊就不在多論述。 ``` gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #對照片進行灰階 ``` ![](https://i.imgur.com/PHphBIB.jpg) ### 二值化(負片) 在OpenCV裡有幾個二值化的功能可以使用 + BINARY + BINARY_INV + TOZERO + TOZERO_INV ``` black = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)[1] #將照片進行負片處理 ``` ![](https://i.imgur.com/oxqxTsC.png) 在下方會演示不同功能所調教出來的照片 我們可以看到在照片進行二值化之後照片有明顯的對比,但在後面的框選邊框時還是會有雜訊,這時再增加一段指令。 ``` strong = cv2.adaptiveThreshold(black, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 10) #加強負片效果 ``` ![](https://i.imgur.com/GNusLW3.png) 可以看到照片的對比度有明顯的增加,且在之後的辨識中可以提高辨識度。 但在後續的辨識中,這樣還是不夠的,其原因是雜訊還是太多,所以再增加**侵蝕與膨脹**的指令。 ``` element = cv2.getStructuringElement(cv2.MORPH_OPEN,(10,10)) dst = cv2.morphologyEx(strong,cv2.MORPH_CLOSE,element) ``` ![](https://i.imgur.com/AEXbIDQ.png) 所謂的侵蝕與膨脹,只要的目的是要讓訊號消除,試想一下,當我們今天有一張圖片周圍有許多的雜訊小點,先經由膨脹將圖增大把周圍的小點吃掉,在透過侵蝕將原先的圖還原,就達成了消除雜訊。 而這次則是需要將圖像目標分明,所以要使用相反的方式,先侵蝕把每個目標分清楚之後再進行膨脹。 ### 尋找邊框 利用contours的指令先找出所有的輪廓。 ``` (contours,_) = cv2.findContours(dst, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) #尋找邊框 ``` ### 尋找四邊形 這段程式的功能是用來計算輪廓近似,可以透過該段程式來獲得圖形近似於幾個頂點。 使用方法是先利用arcLength()計算出該圖形的周長,再利用approxPolyDP()來近似圖形有幾個點。 ``` peri = cv2.arcLength(cont,True) #計算物件的周長 approx = cv2.approxPolyDP(cont,0.05*peri,True) #利用周長計算出圖形有幾個點 ``` ### 繪製四邊形及標示 利用if()判斷式來篩選四邊形,並將圖形繪製後標示數量。 ``` if int(len(approx)) == 4: #當點有四個(長方形)時做下列的運算 cv2.drawContours(image,[cont],-1,(240,0,159),1) #繪製邊框 a=a+1 #計算總共多少塊模板 cv2.putText(image,str(a),(x,y+10),cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,255,255),1) #利用座標將數量之值放置在圖片左上方 ``` ![](https://i.imgur.com/HtZMvP2.jpg) ## Code ```python= import cv2 #導入OpenCV image = cv2.imread("filed.jpg") #讀取你要的照片檔案(位置) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #對照片進行灰階 #glass = cv2.GaussianBlur(image,(5,5),0) #對照片進行5x5的高斯模糊化 #若將照片高斯模糊化會影響之後負片轉換時雜訊增加,影響模板計算的值,在此不使用 black = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)[1] #將照片進行負片處理 strong = cv2.adaptiveThreshold(black, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 10) #加強負片效果 element = cv2.getStructuringElement(cv2.MORPH_OPEN,(10,10)) dst = cv2.morphologyEx(strong,cv2.MORPH_CLOSE,element) #將照片中的物品腐蝕之後再生長,增加照片板塊的辨識度 (contours,_) = cv2.findContours(dst, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) #尋找邊框 a=0 #模板數量 for cont in contours: peri = cv2.arcLength(cont,True) #計算物件的周長 approx = cv2.approxPolyDP(cont,0.05*peri,True) #利用周長計算出圖形有幾個點 (x,y,w,h)=cv2.boundingRect(approx) #尋找點的座標 if int(len(approx)) == 4: #當點有四個(長方形)時做下列的運算 cv2.drawContours(image,[cont],-1,(240,0,159),1) #繪製邊框 a=a+1 #計算總共多少塊模板 cv2.putText(image,str(a),(x,y+10),cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,255,255),1) #利用座標將數量之值放置在圖片左上方 cv2.imshow("dst",dst) #顯示負片的圖片效果 cv2.imshow("image",image) #顯示原圖 print(a) #顯示模板數量 ``` ## 問題與討論 + 為什麼不使用GaussianBlur? + OpenCV有這麼多功能要怎麼知道哪一個才是我要的? + 完成圖有框出許多不是太陽能板的圖形? ### 為什麼不使用GaussianBlur? 在Code裡我有描述為何不使用高斯模糊,首先我們要先知道當照片經過高斯模糊之後,我們得到了什麼,我們失去了什麼,在照片經過高斯模糊之後我得到**減少圖像雜訊**以及**降低細節層次**,讓程式在執行時加快速,雖然對於一般照片來說,進行高斯模糊確實會又減少圖像雜訊的功能,但對於我們這種比較密集的照片來說,失去了照片的辨識度,所以在此不使用高斯模糊。 ![](https://i.imgur.com/20QZ2ZT.jpg) ### OpenCv有這麼多功能要怎麼知道哪一個才是我要的? 我的答案是:一個一個慢慢試。 首先要知道每一張圖的特徵都不太一樣同樣是辨識東西,即使兩個東西看起來長得很像,但經過數學式運算之後可能不逕相同,更別提其他東西了,我在做這個,在做這張太陽能的照片時,其實就遇到許多問題,也是得要慢慢嘗試之後才會知道哪一個功能才是我要的,這是一條沒有捷徑的路,然而需要非常精準的判別模板片數,或許還有很長一段路要走。 ![](https://i.imgur.com/fF2WbIn.jpg) ![](https://i.imgur.com/mzJlQZi.jpg) ### 完成圖有框出許多不是太陽能板的圖形? 就像我上述講的,能有許多可判別的細節在轉換的過程隻被吃掉或是消失,能有許多空間可以進步以及改進,以及照片的規範,在何種角度及大小能更精準的判斷,都是未來能增加的方向。 ![](https://i.imgur.com/OhNaF4N.png) ### 新增參考資料 ***校正圖片水平***:https://blog.gtwang.org/programming/python-opencv-auto-crop-and-rotate-scanned-image-tutorial/ ## newcase ``` python= import cv2 #導入OpenCV import numpy as np global maxlabel image = cv2.imread("P/kkk.jpg") #讀取你要的照片檔案(位置) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #對照片進行灰階 #glass = cv2.GaussianBlur(image,(5,5),0) #對照片進行5x5的高斯模糊化 #若將照片高斯模糊化會影響之後負片轉換時雜訊增加,所以在此不使用 black = cv2.threshold(gray, 125, 255, cv2.THRESH_BINARY)[1] #將照片進行負片處理 strong = cv2.adaptiveThreshold(black, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 10) #加強負片效果 element = cv2.getStructuringElement(cv2.MORPH_OPEN,(10,10)) dst = cv2.morphologyEx(strong,cv2.MORPH_CLOSE,element) #將照片中的物品腐蝕之後再生長,增加照片板塊的辨識度 (contours,_) = cv2.findContours(dst, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) #尋找邊框 a=0 #模板數量 perilist=[]; for cont in contours: peri = cv2.arcLength(cont,True) #計算物件的周長 INTperi=int(peri) #轉換型態 perilist.append(INTperi) #將計算的周長增加至列舉裡 maxlabel = max(perilist,key = perilist.count) #計算眾數 for cont in contours: peri = cv2.arcLength(cont,True) #再計算一次 INTperi=int(peri) perilist.append(INTperi) approx = cv2.approxPolyDP(cont,0.06*peri,True) #利用周常計算出圖形有幾個邊 (x,y,w,h)=cv2.boundingRect(approx) #尋找邊的座標 if INTperi < (maxlabel*1.1) and INTperi > (maxlabel*0.9) and int(len(approx)) == 4 : #當圖形的邊有四個(長方形)時且在眾數範圍內做下列的運算 cv2.drawContours(image,[cont],-1,(240,0,159),1) #繪製邊框 a=a+1 #計算總共多少塊模板 cv2.putText(image,str(a),(x,y+10),cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,255,255),1) #利用座標將數量之值放置在圖片左上方 cv2.imshow("dst",dst) #顯示負片的圖片效果 cv2.imshow("image",image) #顯示原圖 print(a) #顯示模板數量 ``` 程式:http://www.planetb.ca/projects/syntaxHighlighter/popup.php