---
# System prepended metadata

title: Python 結合 OpenCV 計算太陽能板數量之實作
tags: [OpenCV, Python]

---

# 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


