2019年8月8日 星期四

[影像處理] openCV 圖像中物體輪廓偵測 Contour detection

圖像中物理的輪廓偵測 Contour detection

Satoshi Suzuki在1985年提出的Topological structural analysis of digitized binary images by border following 在使用cv2內建的findContours()函數找尋輪廓時,輸入的圖片必須二值圖(可以使用cv2.threshold()函數),物件的輪廓可以用來找圖片中的物體,取得ROI(region of interest),用在物件的追蹤(object tracking).
語法: cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
  1. 第一個參數是尋找輪廓的圖像
  2. 第二個參數表示輪廓的檢索模式,有四種(本文介紹的都是新的cv2接口):
    cv2.RETR_EXTERNAL表示只檢測外輪廓
    cv2.RETR_LIST檢測的輪廓不建立等級關係
    cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。
    cv2.RETR_TREE建立一個等級樹結構的輪廓。
  3. 第三個參數method為輪廓的近似辦法
    cv2.CHAIN_APPROX_NONE存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
    cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點坐標,例如一個矩形輪廓只需4個點來保存輪廓信息
    cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

#先產生一個圖像
import cv2
import numpy as np
from pylab import *
from scipy import ndimage
img = np.zeros((200, 200), dtype=np.uint8)
img[50:150, 50:150] = 255

In [4]:
imshow(img)

Out[4]:
<matplotlib.image.AxesImage at 0x181bed8ac8>
In [5]:
#使用threshold過濾器將pixel強度大於127的改為255,將pixel強度小於127的改為0
ret, thresh = cv2.threshold(img, 127, 255, 0)

In [6]:
# 顯示threshold過濾後的影像
imshow(thresh)

Out[6]:
<matplotlib.image.AxesImage at 0x181f2afe10>

In [7]:
#使用cv2.findContours()找尋輪廓
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#把影像變成彩色
color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
#在原先的影像上疊上綠色輪廓,粗細為2
img = cv2.drawContours(color, contours, -1, (0,255,0), 2)
cv2.imshow("contours", color)
cv2.waitKey()
cv2.destroyAllWindows()
Screenshot%202019-08-08%2007.15.11.png
In [1]:
import cv2
import numpy as np
#使用pyrDown()對圖像做down sampling
img = cv2.pyrDown(cv2.imread("contour.jpg", cv2.IMREAD_UNCHANGED))
'''
Opencv中可以通過函數cv2.pyrDown()和cv2.pyrUp()來構建金字塔。
函數cv2.pyrDown()是從一個高分辨率圖像變成低分辨率圖像的。


cv2.pyrDown()函數接受3個參數:
tmp: 當前圖像,初始化為原圖像 src 。
dst: 目的圖像( 顯示圖像,為輸入圖像的一半)
Size( tmp.cols/2, tmp.rows/2 ) :目的圖像大小, 既然我們是向下採樣
默認情況下直接輸入需要操作的圖像就可以,他會把圖像按縮小1/4的來處理
'''
#設定threshod過濾器將pixel強度大於127的改為255
ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
#使用cv2.findContours()找尋輪廓
image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print('numbers of contour:',len(contours))

numbers of contour: 3


In [2]:
for c in contours:
  # 把找到的contour用矩形圍起來
  x,y,w,h = cv2.boundingRect(c)
    #把contour標成綠色
  cv2.rectangle(img, (x,y), (x+w, y+h), (0, 255, 0), 2)


  # 找出圍繞contour的最小面積矩形
  rect = cv2.minAreaRect(c)
  # 計算最小矩形的四個座標點
  box = cv2.boxPoints(rect)
  # 把四個座標變成整數值
  box = np.int0(box)
  # 把最小矩形標示成紅色
  cv2.drawContours(img, [box], 0, (0,0, 255), 3)
  
  # 找到圍繞contour的最小圓形,並且回傳圓心座標和半徑
  (x,y),radius = cv2.minEnclosingCircle(c)
  # 把座標變成整數值
  center = (int(x),int(y))
  radius = int(radius)
  # 用藍色表示最小圓形
  img = cv2.circle(img,center,radius,(255,0,0),2)


# 用洋紅色標出所有找到的輪廓
cv2.drawContours(img, contours, -1, (255, 0, 255), 1)
cv2.imshow("contours", img)


cv2.waitKey()
cv2.destroyAllWindows()
Screenshot%202019-08-09%2000.44.28.png

找多邊形的方法:
step 1. 找尋輪廓(contour)
step 2. 使用Douglas–Peucker algorithm演算法找出多邊形:

  1. 在曲線首尾兩點A,B之間連線一條直線AB,該直線為曲線的弦;
  2. 得到曲線上離該直線段距離最大的點C,計算其與AB的距離d;
  3. 比較該距離與預先給定的閾值threshold的大小,如果小於threshold,則該直線段作為曲線的近似,該段曲線處理完畢。
  4. 如果距離大於閾值,則用C將曲線分為兩段AC和BC,並分別對兩段取信進行1~3的處理。
  5. 當所有曲線都處理完畢時,依次連線各個分割點形成的折線,即可以作為曲線的近似。 當threshold設的越小,找出的分割點連線越近似曲線
In [2]:
import cv2
import numpy as np
from pylab import *
from scipy import ndimage


#使用pyrDown()對圖像做down sampling
img = cv2.pyrDown(cv2.imread("contour.jpg", cv2.IMREAD_UNCHANGED))


#設定threshod過濾器將pixel強度大於127的改為255
ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
#初始化一張與原圖大小相同的黑圖,並把它變成彩色(3個channels)
black = cv2.cvtColor(np.zeros((img.shape[1], img.shape[0]), dtype=np.uint8), cv2.COLOR_GRAY2BGR)
#使用cv2.findContours()找尋輪廓
image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

In [6]:
for cnt in contours:
    # 設定epsilon值
  epsilon = 0.01 * cv2.arcLength(cnt,True)
    # 找輪廓的近似
  approx = cv2.approxPolyDP(cnt,epsilon,True)
    # 用內建的convexHull()函數找尋輪廓的convex
    '''
    convex的定義:隨機在convex內部取兩個點,兩點連線永遠都在convex內部
    '''
  hull = cv2.convexHull(cnt)  
  cv2.drawContours(black, [cnt], -1, (0, 255, 0), 2) #綠色
  cv2.drawContours(black, [approx], -1, (255, 255, 0), 2) #青色
  cv2.drawContours(black, [hull], -1, (0, 0, 255), 2) #紅色


cv2.imshow("hull", black)
cv2.waitKey()
cv2.destroyAllWindows()
Screenshot%202019-08-08%2007.39.40.png

三倍槓桿和一倍槓桿的長期定期定額報酬率分析

  以下是中國,美國股票債卷的三倍槓桿和一倍槓桿ETF分析.可以發現,三倍槓桿在下跌時期的跌幅遠比一倍槓桿的多 .且從時間軸來看,三倍槓桿由於下跌力道較強,因此會把之前的漲幅都吃掉,所以對於長期上身的市場,例如美國科技股,由於上升時間遠比下跌時間長,所以持有TQQQ的長期回報率會...