1. 程式人生 > 其它 >opencv筆記(一)

opencv筆記(一)

opencv筆記(一)

目錄

環境搭建

  • python3環境
  • pip install numpy matplotlib opencv-python

基礎用法

常用物件

  • 視窗
  • 攝像裝置
  • 視訊寫入
import cv2

# 建立fourcc物件
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# 建立VideoWriter物件,引數為1.檔案路徑、fourcc、幀率、解析度
vw = cv2.VideoWriter('d.mp4', fourcc, 24, (640, 480))
# 建立視窗物件
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
# 建立攝像裝置物件,引數為裝置序列號
cap = cv2.VideoCapture(0)

while cap.isOpened():
    ok, frame = cap.read()
    if ok:
        # 螢幕顯示
        cv2.imshow('video', frame)
        # 儲存
        vw.write(frame)
        key = cv2.waitKey(6)
        if key & 0xFF == ord('q'):
            break
    else:
        break

# 釋放資源
cap.release()
vw.release()
cv2.destroyAllWindows()
  • 滑鼠回撥函式
import cv2
import numpy as np


# 定義回撥函式體,引數:1.事件、2.寬、3.高、4.滑鼠鍵或者組合鍵、5.設定滑鼠回撥函式時傳入的userdata
def mouse_callback(event, x, y, flag, userdata):
    print(event, x, y, flag, userdata)


# 建立視窗物件
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 1280, 720)
# 設定滑鼠回撥函式,引數:1.視窗名、2.回撥函式、3.userdata值
cv2.setMouseCallback('video', mouse_callback, 'userdata')

img = np.zeros((720, 1280, 3), np.uint8)

while 1:
    # 螢幕顯示
    cv2.imshow('video', img)
    key = cv2.waitKey(6)
    if key & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
  • TrackBar
import cv2
import numpy as np


def callback(userdata):
    print(userdata)


cv2.namedWindow('trackbar', cv2.WINDOW_NORMAL)
cv2.resizeWindow('trackbar', 640, 320)

# 建立trackbar
cv2.createTrackbar('R', 'trackbar', 0, 255, callback)
cv2.createTrackbar('G', 'trackbar', 0, 255, callback)
cv2.createTrackbar('B', 'trackbar', 0, 255, callback)

img = np.zeros((320, 640, 3), np.uint8)

while 1:
    # 螢幕顯示
    r = cv2.getTrackbarPos('R', 'trackbar')
    g = cv2.getTrackbarPos('G', 'trackbar')
    b = cv2.getTrackbarPos('B', 'trackbar')
    img[:] = [b, g, r]
    cv2.imshow('trackbar', img)
    key = cv2.waitKey(6)
    if key & 0xFF == ord('q') or cv2.getWindowProperty('trackbar', cv2.WND_PROP_VISIBLE) < 1.0:
        break

cv2.destroyAllWindows()

色彩空間

  • BGR
  • RGB
  • HSV:色相+飽和度+明度
  • HSL:色相+飽和度+亮度
  • YUV
import cv2


def callback(userdata):
    print(userdata)


cv2.namedWindow('color', cv2.WINDOW_NORMAL)

img = cv2.imread('1.png')

color_spaces = [cv2.COLOR_BGR2BGRA, cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV_FULL, cv2.COLOR_BGR2RGBA, cv2.COLOR_BGR2YUV]

cv2.createTrackbar('trans', 'color', 0, len(color_spaces)-1, callback)

while 1:
    v = cv2.getTrackbarPos('trans', 'color')
    # 色彩空間轉換
    new_img = cv2.cvtColor(img, color_spaces[v])
    cv2.imshow('color', new_img)
    key = cv2.waitKey(6)
    if key & 0xFF == ord('q') or cv2.getWindowProperty('color', cv2.WND_PROP_VISIBLE) < 1.0:
        break

cv2.destroyAllWindows()

Numpy基礎

建立矩陣

  • 通過array:np.array[[1, 2, 3], [4, 5, 6]]
  • 通過zeros:np.zeros((320, 640, 3), np.uint8)
  • 通過ones:np.ones((320, 640, 3), np.uint8)
  • 通過full:np.full((8, 8, 3), 255, np.uint8)
  • 通過identity:np.identity(4)
  • 通過eye:np.eye(3, 5, k=2)

矩陣取值與賦值

  • img[y, x]:[1, 2, 3]
  • img[y, x, channel]:1

ROI

  • img[y1:y2, x1:x2]
  • img[:, :]
import cv2
import numpy as np

cv2.namedWindow('color', cv2.WINDOW_NORMAL)

img = np.full((320, 640, 3), 0, np.uint8)
img[100:200, 100:200] = [0, 0, 255]
img[:, 300] = [255, 0, 0]

cv2.imshow('color', img)
key = cv2.waitKey(0)
if key & 0xFF == ord('q') or cv2.getWindowProperty('color', cv2.WND_PROP_VISIBLE) < 1.0:
    cv2.destroyAllWindows()

Mat

結構

  • header:dims維度、rows行數、cols列數、depth畫素位深、channels通道數、size矩陣大小、type
  • data:矩陣詳細資料

拷貝

當顯式呼叫copy()生成新矩陣時會拷貝header和data,否則只拷貝header。

import cv2
import numpy as np

cv2.namedWindow('color', cv2.WINDOW_NORMAL)

img = np.full((320, 640, 3), 0, np.uint8)

img2 = img[:]
img2[:] = [0, 0, 255]

img3 = img.copy()
img3[100:200, 100:200] = [255, 0, 0]

cv2.imshow('color', img)
cv2.imshow('color2', img2)
cv2.imshow('color3', img3)
key = cv2.waitKey(0)
if key & 0xFF == ord('q') or cv2.getWindowProperty('color', cv2.WND_PROP_VISIBLE) < 1.0:
    cv2.destroyAllWindows()

屬性訪問

  • img.shape:高、寬、通道數
  • img.size:高x寬x通道數
  • img.dtype:位深,例如uint8

通道的分離與合併

  • split(mat)
  • merge((chan1, chan2))
import cv2
import numpy as np

img = np.zeros((320, 640, 3), np.uint8)

b, g, r = cv2.split(img)  # split將同時拷貝header與data
b[100:200, 100:200] = 255
g[100:200, 100:200] = 255
r[0:100, 0:100] = 255
img2 = cv2.merge((b, g, r))  # merge將同時拷貝header與data
img2[200:300, 200:300] = [0, 0, 255]

cv2.imshow('color', img)
cv2.imshow('color0', img2)
cv2.imshow('color1', b)
cv2.imshow('color2', g)
cv2.imshow('color3', r)

key = cv2.waitKey(0)
if key & 0xFF == ord('q'):
    cv2.destroyAllWindows()

圖形繪製

  • 線:line
  • 矩形:rectangle
  • 圓:circle
  • 橢圓:ellipse
  • 多邊形:
  • 字型:
import cv2
import numpy as np

# 此處座標引數為y, x順序
img = np.zeros((320, 640, 3), np.uint8)

# 此處座標相關引數全部是(x, y)順序,引數:影象、起始點、終止點、顏色、線寬、線形
cv2.line(img, (10, 20), (100, 200), (255, 0, 0), 5)

#矩形
cv2.rectangle(img, (80, 80), (200, 200), (0, 255, 0))

# 圓
cv2.circle(img, (140, 140), 60, (0, 0, 255))

# 橢圓 引數:影象、圓心、x和y半徑、順時針旋轉角度、繪製起始角度、繪製終止角度、顏色、厚
cv2.ellipse(img, (140, 140), (60, 30), 90, 0, 360, (0, 0, 255), -1)

# 多邊形 引數:影象、點、是否閉合、顏色
points = np.array([(300, 5), (132, 109), (480, 109)], np.int32)
cv2.polylines(img, [points], True, (0, 0, 255))

# 填充的多邊形 引數:影象、點、顏色
points_fill = np.array([(300, 10), (150, 100), (450, 100)], np.int32)
cv2.fillPoly(img, [points_fill], (255, 0, 0))

# 文字 引數:影象、文字內容、起始點、字型、字號、顏色
cv2.putText(img, 'test', (200, 300), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255))

cv2.imshow('line', img)

key = cv2.waitKey(0)
if key & 0xFF == ord('q'):
    cv2.destroyAllWindows()

基本影象運算與處理

加法運算

import cv2
import numpy as np

# 加法運算
# 必須確保兩張影象大小通道一致
im1 = cv2.imread('x.png')
# im2 = cv2.imread('y.png')
im2 = np.ones((1080, 1920, 3), np.uint8) * 100

cv2.imshow('add', cv2.add(im1, im2))
cv2.waitKey(0)

減法運算

import cv2
import numpy as np

# 減法運算
# 必須確保兩張影象大小通道一致
# 注意受np.uint8型別約束,加法運算逆運算後可能得到與原圖有偏差的結果
im1 = cv2.imread('x.png')
im2 = np.ones(im1.shape, np.uint8) * 10

cv2.imshow('ori', im1)
cv2.imshow('subtract', cv2.subtract(cv2.add(im1, im2), im2))
cv2.waitKey(0)

乘除運算

import cv2
import numpy as np

# 減法運算
# 必須確保兩張影象大小通道一致
# 注意受np.uint8型別約束,加法運算逆運算後可能得到與原圖有偏差的結果
im1 = cv2.imread('x.png')
im2 = np.ones(im1.shape, np.uint8) * 10

cv2.imshow('ori', im1)
cv2.imshow('multiply', cv2.multiply(im1, im2))
cv2.imshow('divide', cv2.divide(im1, im2))
cv2.waitKey(0)

影象溶合

即帶權重的影象疊加

import cv2


im1 = cv2.imread('x.png')
im2 = cv2.imread('y.png')

# 引數:影象一、影象一權重、影象二、影象二權重、靜態權重
cv2.imshow('addWeighted', cv2.addWeighted(im1, 0.6, im2, 0.4, 0))
cv2.waitKey(0)

影象位運算

  • 異或

import cv2
import numpy as np

im1 = np.zeros((200, 200), np.uint8)
im2 = np.zeros((200, 200), np.uint8)

im1[20:120, 20:120] = 255
im2[80:180, 80:180] = 255

cv2.imshow('not', cv2.bitwise_not(im1))
cv2.imshow('and', cv2.bitwise_and(im1, im2))
cv2.imshow('or', cv2.bitwise_or(im1, im2))
cv2.imshow('xor', cv2.bitwise_xor(im1, im2))
cv2.waitKey(0)

影象的變換

影象縮放

import cv2

# 影象縮放
"""
引數:源、縮放後大小、X軸縮放因子、Y軸縮放因子、插值演算法,同時指定大小和縮放因子優先取大小
插值演算法:INTER_NEAREST(臨近插值,速度快,效果差)、INTER_LINEAR(雙線性插值,參考周圍4點)
INTER_CUBIC(三次插值,參考16點)、INTER_AREA(效果最好)
"""
img = cv2.imread('1.png')
res = cv2.resize(img, (1920, 1080))
res2 = cv2.resize(img, None, fx=0.6, fy=0.6)

cv2.imshow('resize1', res)
cv2.imshow('resize2', res2)
cv2.waitKeyEx(0)

影象翻轉

import cv2

# 影象翻轉
# 引數:影象、翻轉標誌(0代表上下翻轉, >0代表左右翻轉, <0代表上下+左右)
img = cv2.imread('1.png')

cv2.imshow('flip', cv2.flip(img, -1))
cv2.waitKeyEx(0)

影象旋轉

import cv2

# 影象旋轉
# 引數:影象、旋轉標誌(ROTATE_90_CLOCKWISE, ROTATE180, ROTATE_90_COUNTERCLOCKWISE)
img = cv2.imread('1.png')

cv2.imshow('rotate', cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE))
cv2.waitKeyEx(0)

仿射變換

即影象旋轉、縮放、平移的總稱

import cv2
import numpy as np

img = cv2.imread('1.png')
# 仿射變換
# 引數:影象、變換矩陣、輸出大小、flag插值演算法、mode邊界外推法標誌、value填充邊界的值
# X軸平移100
M = np.float32([[1, 0, 100], [0, 1, 0]])

h, w, _ = img.shape
cv2.imshow('warpAffine', cv2.warpAffine(img, M, (w, h)))

# 通過API獲取變換矩陣
# 引數:中心點、角度(逆時針)、縮放比例
# 變換不改變影象大小,如果要修改影象大小需要修改dsize
M = cv2.getRotationMatrix2D((100, 100), 30, 1.0)
cv2.imshow('warpAffine2', cv2.warpAffine(img, M, (w, h)))

# 通過API獲取變換矩陣-三點定位
# 引數:原三點、目的三點
ori = np.float32([[100, 200], [300, 200], [100, 400]])
new = np.float32([[150, 300], [200, 400], [50, 350]])
M = cv2.getAffineTransform(ori, new)
cv2.imshow('warpAffine3', cv2.warpAffine(img, M, (w, h)))

cv2.waitKeyEx(0)

透視變換

import cv2
import numpy as np

img = cv2.imread('3.jpg')

# 獲取變換矩陣
# 引數:原四點、目標四點
src = np.float32([[100, 1100], [2100, 1100], [0, 4000], [2500, 3900]])
des = np.float32([[0, 0], [2300, 0], [0, 3000], [2300, 3000]])
M = cv2.getPerspectiveTransform(src, des)

# 透視變換
# 引數:影象、變換矩陣、輸出大小...
warp_perspective = cv2.warpPerspective(img, M, (2300, 3000))

cv2.imshow('warpPerspective', warp_perspective)

cv2.waitKeyEx(0)

影象濾波

一幅影象通過濾波器得到另一幅影象。

卷積

濾波器又稱卷積核,濾波的過程稱為卷積。

  • 卷積核的大小:

    一般為奇數(原因包括增加padding和確保錨點居中),例如3x3、5x5等,在深度學習中,卷積核越大,感受野越大,資訊越多,提取的特徵越好,同時計算量也越大

  • 錨點:卷積核中心點

  • 邊界擴充:

    當卷積核大於1且不進行邊界擴充,輸出尺寸將相應縮小

    當卷積核一標準方式進行邊界擴充,則輸出資料的空間尺寸將與輸入相等

    N = (W - F + 2P) / S + 1

    N即輸出影象大小,W源大小,F卷積核大小,P擴充尺寸,S步長大小

  • 步長

低通濾波:低於某個閾值的值可以通過,可以去除噪音或者平滑影象

高通濾波:高於某個閾值的值可以通過,可以幫助查詢影象邊緣

常用濾波器

低通濾波
濾波器名 作用或優點 缺點 備註
*方盒濾波(boxFilter) / / normalize=True時為均值濾波,使用較少
均值濾波(blur) 平滑影象,減少銳化 失去紋理 /
高斯濾波(GaussianBlur) 塗抹噪點 邊緣模糊 越靠近中心,權重越高,錨點權重約0.148
中值濾波(medianBlur) 消除胡椒噪音 對其他噪音效果不佳,邊緣模糊 取卷積陣列的中值作為卷積結果
雙邊濾波(bilateralFilter) 保留邊緣同時對邊緣內區域進行平滑處理 胡椒噪音效果不佳
高通濾波
運算元 作用或優點 缺點 備註
索貝爾(Sobel) 抗噪 同時求一個以上方向邊緣時效果極差 卷積核大小為-1時即沙爾
*沙爾(Scharr) 細小邊緣檢測 只能同時求一個方向上的邊緣 使用較少
拉普拉斯(Laplacian) 同時求多方向邊緣 噪音敏感 使用前需要先做降噪處理
邊緣檢測-Canny

內部實現步驟

  1. 使用5x5高斯濾波消除噪聲
  2. 從四個方向計算影象梯度:0°/45°/90°/135°
  3. 取區域性極大值
  4. 閾值計算:高於最大值即是邊緣,低於最小值即不是邊緣,中間值如與以確定邊緣有連續關係則是邊緣,否則不是邊緣
import cv2
import numpy as np

img = cv2.imread('8.png')

# 影象卷積
# 引數:影象、位深,卷積核,錨點,指定偏差,邊界型別
# kernel = np.ones((5, 5), np.float32) / 25
# des = cv2.filter2D(img, -1, kernel)

# 均值濾波,引數:源、卷積核大小...
# blur_des = cv2.blur(img, (5, 5))

# 高斯濾波,引數:源、卷積核(大小)、sigma(值越大越模糊,未指定時參考卷積核大小)
# gauss_des = cv2.GaussianBlur(img, (5, 5), sigmaX=3)

# 中值濾波,引數:源、卷積核大小
# median_des = cv2.medianBlur(img, 5)

# 雙邊濾波,引數:源、filter大小、顏色sigma、sigma空間
# bil_des = cv2.bilateralFilter(img, 6, 20, 50)

# 索貝爾運算元,引數:源、輸出位深、對X求導、對Y求導、核大小...
# sobel_des = cv2.add(cv2.Sobel(img, cv2.CV_64F, 1, 0, 5), cv2.Sobel(img, cv2.CV_64F, 0, 1, 5))

# 拉普拉斯運算元,引數:源、輸出位深、核大小
# lap_des = cv2.Laplacian(img, cv2.CV_64F, ksize=5)

# Canny,引數:源、最小值、最大值
canny_des = cv2.Canny(img, 100, 200)

cv2.imshow('ori', img)
# cv2.imshow('des', des)
# cv2.imshow('blur_des', blur_des)
# cv2.imshow('gauss_des', gauss_des)
# cv2.imshow('median_des', median_des)
# cv2.imshow('bil_des', bil_des)
# cv2.imshow('sobel_des', sobel_des)
# cv2.imshow('lap_des', lap_des)
cv2.imshow('canny_des', canny_des)

cv2.waitKeyEx(0)

形態學

影象二值化

全域性二值化

將影象的每個畫素變成兩種值

import cv2
import numpy as np

ori_img = cv2.imread('8.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)

# 全域性二值化,引數:源、閾值、用於替換超過最大值的值、型別
# 型別包含THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV
_, des = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

# 全域性二值化(自適應閾值),引數:源、用於替換超過最大值的值、自適應方法、型別、自適應區域、從計算出的平均值或者加權平均值中減去的常量
# 自適應方法:ADAPTIVE_THRESH_MEAN_C(計算鄰近區域的平均值)、ADAPTIVE_THRESH_GAUSSIAN_C(高斯視窗加權平均值)
# 型別包含THRESH_BINARY、THRESH_BINARY_INV
auto_des = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 0)

cv2.imshow('ori', ori_img)
cv2.imshow('des', des)
cv2.imshow('auto_des', auto_des)

cv2.waitKeyEx(0)

腐蝕

import cv2
import numpy as np

ori_img = cv2.imread('8.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, des = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

# 手動構建卷積核
# kernel = np.ones((3, 3), np.uint8)

# 獲取卷積核,引數:卷積核型別、卷積核大小
# 卷積核型別:MORPH_RECT(全1)、MORPH_ELLIPSE(橢圓核,四角為0)、MORPH_CROSS(十字核,橫豎為1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))


# 腐蝕,引數:源、卷積核、腐蝕次數
erode_des = cv2.erode(des, kernel)

cv2.imshow('ori', ori_img)
cv2.imshow('erode_des', erode_des)

cv2.waitKeyEx(0)

膨脹

import cv2
import numpy as np

ori_img = cv2.imread('8.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, des = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

# 手動構建卷積核
# kernel = np.ones((3, 3), np.uint8)

# 獲取卷積核,引數:卷積核型別、卷積核大小
# 卷積核型別:MORPH_RECT(全1)、MORPH_ELLIPSE(橢圓核,四角為0)、MORPH_CROSS(十字核,橫豎為1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))


# 腐蝕,引數:源、卷積核、腐蝕次數
erode_des = cv2.erode(des, kernel)

# 膨脹,引數:源、卷積核、腐蝕次數
dilate_des = cv2.dilate(des, kernel)

cv2.imshow('ori', ori_img)
cv2.imshow('erode_des', erode_des)
cv2.imshow('dilate_des', dilate_des)

cv2.waitKeyEx(0)

開運算

先做腐蝕後做膨脹

用於消除背景中的小圖形,保留大圖形,可以理解為消除主要目標外的背景中的噪點

閉運算

先做膨脹後做腐蝕

用於消除大圖形中的小圖形,可以理解為消除主要目標中的噪點

梯度運算

梯度=原圖-腐蝕後的圖,用於得到目標圖形的邊緣,一定條件下,卷積核越小,得到的邊緣越清晰而細小,卷積核越大,得到的邊緣越模糊

頂帽運算

頂帽=原圖-開運算,用於得到主要目標圖形外的小圖形,可以理解為獲取背景中的噪點

黑帽運算

黑帽=原圖-閉運算,用於得到主要圖形中的小圖形,可以理解為獲取目標圖形中的噪點

import cv2

ori_img = cv2.imread('8.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))

# 開運算,引數:源、巨集型別(MORPH_OPEN)、卷積核
morph_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

# 閉運算,引數:源、巨集型別(MORPH_CLOSE)、卷積核
morph_close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

# 梯度運算,引數:源、巨集型別(MORPH_GRADIENT)、卷積核
morph_gr = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)

# 頂帽運算,引數:源、巨集型別(MORPH_TOPHAT)、卷積核
morph_top = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

# 黑帽運算,引數:源、巨集型別(MORPH_BLACKHAT)、卷積核
morph_black = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)

cv2.imshow('ori', ori_img)
cv2.imshow('morph_open', morph_open)
cv2.imshow('morph_close', morph_close)
cv2.imshow('morph_gr', morph_gr)
cv2.imshow('morph_top', morph_top)
cv2.imshow('morph_black', morph_black)


cv2.waitKeyEx(0)
運算名 步驟(方法) 作用
開運算 先做腐蝕後做膨脹,MORPH_OPEN 消除背景中的噪點
閉運算 先做膨脹後做腐蝕,MORPH_CLOSE 消除主要目標中的噪點
梯度運算 原圖-腐蝕後的圖,MORPH_GRADIENT 得到目標圖形的邊緣
頂帽運算 原圖-開運算,MORPH_TOPHAT 獲取背景中的噪點
黑帽運算 原圖-閉運算,MORPH_BLACKHAT 獲取目標圖形中的噪點

輪廓查詢

影象輪廓即具有相同顏色或強度的連續點的曲線。

影象輪廓的作用

  1. 圖形分析
  2. 物體的識別與檢測

查詢輪廓注意

  • 需要先對影象進行二值化或Canny操作
  • 畫輪廓會修改輸入影象(需要拷貝)
  • 通常會將背景設定為黑色,目標設定為白色
import cv2

ori_img = cv2.imread('9.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

# 查詢輪廓,引數:源、組織已找到輪廓的模式、邊框點儲存模式,返回值:輪廓結果列表、層級
# 組織已找到輪廓的模式:RETR_EXTERNAL=0(只檢測外輪廓)、RETR_LIST=1(檢測到的輪廓不建立層級關係)、RETR_CCOMP=2(最多兩層)、RETR_TREE=3(樹形組織)
# 邊框點儲存模式:CHAIN_APPROX_NONE(儲存所有輪廓上的點)、CHAIN_APPROX_SIMPLE(只儲存角點)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 繪製輪廓,引數:源、輪廓(點)、需要繪製的輪廓的索引(-1表示所有)、顏色、線寬(-1表示全部填充)
contours_res = cv2.drawContours(ori_img, contours, -1, (0, 255, 0), 1)

cv2.imshow('binary', binary)
cv2.imshow('contours_res', contours_res)


cv2.waitKeyEx(0)

輪廓的面積和周長

import cv2

ori_img = cv2.imread('9.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours_res = cv2.drawContours(ori_img, contours, -1, (0, 255, 0), 1)

# 計算輪廓面積,引數:單一輪廓
area = cv2.contourArea(contours[0])

# 計算輪廓周長,引數:單一輪廓、是否閉合
length = cv2.arcLength(contours[0], True)

cv2.imshow('binary', binary)
cv2.imshow('contours_res', contours_res)


cv2.waitKeyEx(0)

多邊形逼近與凸包

import cv2


def draw(img, points):
    for i in range(len(points)):
        if i == len(points) -1:
            next_one = points[0][0]
        else:
            next_one = points[i + 1][0]
        cv2.line(img, points[i][0], next_one, (0, 0, 255), 3)
        i += 1


ori_img = cv2.imread('10.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours_res = cv2.drawContours(ori_img, contours, -1, (0, 255, 0), 1)

# 多邊形逼近,引數:輪廓(點)、精度(越小越貼合)、是否閉合
approx_res = cv2.approxPolyDP(contours[1], 20, True)
draw(ori_img, approx_res)
# 凸包,引數:輪廓(點)、順逆時針
convex_res = cv2.convexHull(contours[2])
draw(ori_img, convex_res)

cv2.imshow('ori_img', ori_img)
# cv2.imshow('contours_res', contours_res)

cv2.waitKeyEx(0)

外接矩形

import cv2
import numpy as np

ori_img = cv2.imread('66.png')
img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 最小外接矩形,引數:輪廓(點),返回值:RotatedRect(包含起始點(x,y)、寬高(w,h)、角度(angle))
min_r = cv2.minAreaRect(contours[0])
min_box = np.int0(cv2.boxPoints(min_r))
cv2.drawContours(ori_img, [min_box], 0, (0, 255, 0), 3)
# 最大外界矩形,引數:輪廓(點),返回值:Rect(包含起始點(x,y)、寬高(w,h))
x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(ori_img, (x, y), (x + w, y + h), (0, 0, 255), 3)

cv2.imshow('ori_img', ori_img)

cv2.waitKeyEx(0)