2022-11-08|閱讀時間 ‧ 約 60 分鐘

圖形辨識筆記-OPEN CV

OpenCV 讀取圖片
#安裝 OpenCV
pip install opencv-python
pip install opencv-contrib-python

#卸載
pip uninstall opencv-python

import numpy as np #引入 NumPy 
import cv2 #引入 OpenCV

# 讀取圖檔
img = cv2.imread('test.jpg')

以灰階的方式讀取圖檔
#img_gray = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
#cv2.IMREAD_COLOR     為預設值
#cv2.IMREAD_GRAYSCALE 以灰階的格式來讀取圖片。
#cv2.IMREAD_UNCHANGED 讀取圖片中所有的 channels,包含透明度的 channel。

# 讓視窗可以自由縮放大小
cv2.namedWindow('TEST', cv2.WINDOW_NORMAL)

# 顯示圖片
cv2.imshow('TEST', img)

# 按下任意鍵則關閉所有視窗
cv2.waitKey(0)
cv2.destroyAllWindows()

原碼:https://reurl.cc/3354ZL

成果:

OpenCV 本身有提供讀取圖檔的函數可用,讀取圖檔,只要呼叫 cv2.imread 即可將圖片讀取進來,以 cv2.imread 讀進來的資料,會儲存成一個 NumPy 的陣列。
將圖片讀取進來之後,可使用 cv2.imshow 顯示圖片,預設狀況下, cv2.imshow 開啟的視窗會依圖片大小自動調整,若希望可以自由縮放視窗的大小,可用 cv2.namedWindow 將視窗設定為 cv2.WINDOW_NORMAL。
cv2.waitKey 是等待與讀取使用者按下的按鍵,而其參數是等待時間(單位為毫秒),設定為 0 表示持續等待至使用者按下按鍵為止,這樣當按下任意按鍵之後,就會呼叫 cv2.destroyAllWindows 關閉所有 OpenCV 的視窗。若在程式中有許多 OpenCV 視窗,而只要關閉特定視窗時,可用 cv2.destroyWindow 加上視窗名稱,關閉指定視窗。

圖檔格式

cv2.imread 在讀取圖片時,可以在第二個參數指定圖片的格式
# 以灰階的方式讀取圖檔
img_gray = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
cv2.IMREAD_COLOR
此為預設值,這種格式會讀取 RGB 三個 channels 的彩色圖片,而忽略透明度。
cv2.IMREAD_GRAYSCALE
以灰階的格式來讀取圖片。
cv2.IMREAD_UNCHANGED
讀取圖片中所有的 channels,包含透明度。
結果:

寫入圖片檔案

# 寫入圖檔
cv2.imwrite('output.jpg', img)
將 NumPy 陣列中儲存的圖片寫入檔案,可以使用 OpenCV 的 cv2.imwrite
# 寫入不同圖檔格式
cv2.imwrite('output.png', img)
cv2.imwrite('output.tiff', img)
調整圖片品質或壓縮率
# 設定 JPEG 圖片品質為 90(可用值為 0 ~ 100)
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

# 設定 PNG 壓縮層級為 5(可用值為 0 ~ 9)
cv2.imwrite('output.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 5])

Matplotlib

pip install matplotlib
Matplotlib 顯示圖片的方式,只要呼叫 imshow 就可以了,但由於 OpenCV 讀取進來的圖片會以 BGR 的方式儲存三個顏色的 channel,若直接把 OpenCV 讀入的圖片放進 Matplotlib 來顯示,會以預設的BGR處理而出現顏色錯誤問題,因此須將 OpenCV 讀入的 BGR 格式轉為 Matplotlib 用的 RGB 格式,再交給 Matplotlib 顯示就會得到正確的結果了。
BGR 模式
import numpy as np
import cv2
from matplotlib import pyplot as plt

# 使用 OpenCV 讀取圖檔
img_bgr = cv2.imread('test.jpg')

# 將 BGR 圖片轉為 RGB 圖片
img_rgb = img_bgr[:,:,::-1]

# 或是這樣亦可
# img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# 使用 Matplotlib 顯示圖片
plt.imshow(img_bgr)
plt.show()

轉換語法
img_rgb = img_bgr[:,:,::-1]
# 或是這樣亦可
# img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
import numpy as np
import cv2
from matplotlib import pyplot as plt

# 使用 OpenCV 讀取圖檔
img_bgr = cv2.imread('test.jpg')

# 將 BGR 圖片轉為 RGB 圖片
img_rgb = img_bgr[:,:,::-1]

# 或是這樣亦可
# img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# 使用 Matplotlib 顯示圖片
plt.imshow(img_rgb)
plt.show()

當以 OpenCV 讀取灰階的圖片時,由於 channel 只有一個,所以不會有上述色彩問題,直接把 OpenCV 讀入的 NumPy 陣列放進 Matplotlib 的 imshow 中即可顯示,但是 Matplotlib 在顯示一個 channel 的圖片時,會用預設的 colormap 上色。
import numpy as np
import cv2
from matplotlib import pyplot as plt

# 使用 OpenCV 讀取灰階圖檔
img_gray = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)

# 使用 Matplotlib 顯示圖片
plt.imshow(img_gray, cmap = 'gray')
plt.show()

查看圖片屬性
#1920×1080的彩色圖片img.shape(1080, 1920, 3)      
#(圖片高度,圖片寬度,RGB維度)RGB微度彩色為3,灰階為1
圖像的形狀可透過img.shape()來取得,會return行數,列數以及channel數(如果是彩色圖像時)的turple圖像的形狀可以透過img.shape()來取得,會return行數,列數以及channel數(如果是彩色圖像時)的tuple。
#總像素數可以用img.size來查詢。
print('img.size',img.size)

圖片內畫線

cv2.line(img, (60, 20), (400, 200), (0, 0, 255), 5)

#img為要畫線的圖片名稱
#(60, 20)=(x1, y1)
#(400, 200)=(x2, y2)
#(0, 0, 255)為線條顏色(GBR或RGB,取決於是否使用cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
#5 為線條粗細度
#--------------------------------------------------------------
#cv2.line()畫直線
#cv2.circle()畫圓
#cv2.rectangle()畫矩形
#cv2.ellipse()畫橢圓
#cv2.polylines()畫多邊形
import numpy as np #引入 NumPy 
import cv2 #引入 OpenCV

# 讀取圖檔
img = cv2.imread('test.jpg')

# 以灰階的方式讀取圖檔
#img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)

# 讓視窗可以自由縮放大小
cv2.namedWindow('TEST', cv2.WINDOW_NORMAL)


cv2.line(img, (60, 20), (400, 200), (0, 0, 255), 5)

#img為要畫線的圖片名稱
#(60, 20)=(x1, y1)
#(400, 200)=(x2, y2)
#(0, 0, 255)為線條顏色(GBR或RGB,取決於是否使用cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
#5 為線條粗細度
#--------------------------------------------------------------
#cv2.line()畫直線
#cv2.circle()畫圓
#cv2.rectangle()畫矩形
#cv2.ellipse()畫橢圓
#cv2.polylines()畫多邊形

# 顯示圖片
cv2.imshow('TEST', img)

# 寫入圖檔
cv2.imwrite('output.jpg', img)

print('img.shape',img.shape)
print('img.size',img.size)


# 按下任意鍵則關閉所有視窗
cv2.waitKey(0)#持續等待至使用者按下按鍵為止(單位為毫秒)
cv2.destroyAllWindows()       #關閉所有視窗
cv2.destroyWindow('TEST') #關閉 TEST 視窗

裁切圖片
import cv2
# 讀取圖檔
img = cv2.imread("test.jpg")
# 裁切區域的 x 與 y 座標(左上角)
x = 100
y = 100
# 裁切區域的長度與寬度
w = 250
h = 150
# 裁切圖片
crop_img = img[y:y+h, x:x+w]
# 顯示圖片
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)
# 寫入圖檔
cv2.imwrite('crop.jpg', crop_img)

亮度調整

import cv2
import numpy as np
img = cv2.imread('test.jpg')
res = np.uint8(np.clip((1.5 * img + 10), 0, 255))
tmp = np.hstack((img, res))  # 兩張圖片橫向合併(便於對比顯示)
cv2.imshow('image', tmp)
cv2.waitKey(0)

圖片比例調整

import cv2
import numpy as np
img = cv2.imread('test.jpg')
scale_percent = 20 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
# 顯示圖片
cv2.imshow('TEST', resized)
# 按下任意鍵則關閉所有視窗
cv2.waitKey(0)#持續等待至使用者按下按鍵為止(單位為毫秒)
cv2.destroyAllWindows()       #關閉所有視窗
cv2.destroyWindow('TEST') #關閉 TEST 視窗
圖片旋轉
import cv2
import numpy as np
img = cv2.imread('test.jpg')
(h, w, d) = img.shape
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 180, 1.0)
rotated = cv2.warpAffine(img, M, (w, h))
# 顯示圖片
cv2.imshow('TEST', rotated)
#利用 img.shape 顯示高,寬,channels. #M:從中心旋轉180度.
水平垂直翻轉
import cv2
import numpy as np
img = cv2.imread('test.jpg')
image=cv2.flip(img, 1)
# 顯示圖片
cv2.imshow('TEST', image)
#參數2 = 0: 垂直翻轉(沿x軸)
#參數2 > 0: 水平翻轉(沿y軸)
#參數2 < 0: 水平垂直翻轉

模糊

import cv2
import numpy as np
img = cv2.imread('test.jpg')
blurred = cv2.GaussianBlur(img, (51, 51), 0)
# 顯示圖片
cv2.imshow('TEST', blurred)
#img 要模糊化的圖片名稱
#(51, 51)必須為正奇數,數字越高照片越模糊
#0: sigmaX and sigmaY,預設值即可
邊緣檢測
import cv2
import numpy as np
img = cv2.imread('test.jpg',0)
edges = cv2.Canny(img, 30, 70)  # canny邊緣檢測
# 顯示圖片
cv2.imshow('TEST', np.hstack((img, edges)))
#cv2.Canny()進行邊緣檢測,參數2、3表示最低、高閾值

圖形比對

#用模板圖片去尋找圖片中的物件
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

#讀入原圖和模板
img_rgb = cv.imread('mario.jpg') #原圖
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('mario_coin.jpg',0) #比對用圖
w, h = template.shape[::-1]

#標準相關模板匹配
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold) # 匹配程度大於%80的坐標y,x

for pt in zip(*loc[::-1]): # *號表示可選參數
    cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
#cv.rectangle 畫框線
cv.imwrite('res.png',img_rgb)

加入英文字
import numpy as np
import cv2

img = np.zeros((400, 400, 3), np.uint8)
img.fill(90)

# 文字
text = 'Hello, OpenCV!'

# 使用字體
# cv2.putText(影像, 要顯示的文字, 座標, 字型, 字體大小, 顏色, 線條寬度, 線條種類)
cv2.putText(img, text, (10, 40), cv2.FONT_HERSHEY_SIMPLEX,
  1, (0, 255, 255), 1, cv2.LINE_AA)

cv2.imshow('My Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

加入中文字
import cv2
import numpy
from PIL import Image, ImageDraw, ImageFont

img = cv2.imread('mario.jpg') #原圖
def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
    if (isinstance(img, numpy.ndarray)):  #判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontText = ImageFont.truetype(
        "font/simsun.ttc", textSize, encoding="utf-8")
    draw.text((left, top), text, textColor, font=fontText)
    return cv2.cvtColor(numpy.asarray(img), cv2.COLOR_RGB2BGR)

img = cv2ImgAddText(img, "大家好", 140, 60, (255, 255, 0), 20)

cv2.imshow('My Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
視訊鏡頭影像
import cv2
# 選擇攝影機(0 代表第一隻、1 代表第二隻)。
cap = cv2.VideoCapture(0)
while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()
  # 顯示圖片
  cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break
# 釋放攝影機
cap.release()
# 關閉所有 OpenCV 視窗
cv2.destroyAllWindows()
#用cap.isOpened() 檢查攝影機是否有啟動
#用cap.open() 啟動它。
影片相關資訊
import cv2cap = cv2.VideoCapture(1)# 解析 Fourcc 格式資料的函數
def decode_fourcc(v):
 v = int(v)
 return "".join([chr((v >> 8 * i) & 0xFF) for i in range(4)])# 取得影像的尺寸大小
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print("Image Size: %d x %d" % (width, height))# 取得 Codec 名稱
fourcc = cap.get(cv2.CAP_PROP_FOURCC)
codec = decode_fourcc(fourcc)
print("Codec: " + codec)cap.release()
變更影片解析度
import cv2
# 選擇第二隻攝影機(0 代表第一隻、1 代表第二隻)。
cap = cv2.VideoCapture(0)

  # 設定影像的尺寸大小
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 200)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 120)

while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()

  # 顯示圖片
  cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break
# 釋放攝影機
cap.release()
# 關閉所有 OpenCV 視窗
cv2.destroyAllWindows()
#用cap.isOpened() 檢查攝影機是否有啟動
#用cap.open() 啟動它。

寫入影片檔案
import cv2
cap = cv2.VideoCapture(0)
# 設定擷取影像的尺寸大小
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)
# 使用 XVID 編碼
fourcc = cv2.VideoWriter_fourcc(*'XVID')
#影片常見編碼格式:DIVX、XVID、MJPG、X264、WMV1、WMV2
# 建立 VideoWriter 物件,輸出影片至 output.avi
# FPS 值為 20.0,解析度為 640x360
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 360))
while(cap.isOpened()):
  ret, frame = cap.read()
  if ret == True:
    # 寫入影格
    out.write(frame)
    cv2.imshow('frame',frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
      break
  else:
    break
# 釋放所有資源
cap.release()
out.release()
cv2.destroyAllWindows()
讀取影片檔案
import cv2
# 開啟影片檔案
cap = cv2.VideoCapture('output.mp4')

# 以迴圈從影片檔案讀取影格,並顯示出來
while(cap.isOpened()):
  ret, frame = cap.read()
  cv2.imshow('frame',frame)
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break
cap.release()
cv2.destroyAllWindows()

圖片比對+文字顯示

#用模板圖片去尋找圖片中的物件
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image, ImageDraw, ImageFont

#讀入原圖和模板
img_rgb = cv.imread('test2.jpeg') #原圖
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('test_x.jpg',0) #比對用圖
w, h = template.shape[::-1]

#標準相關模板匹配
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold) # 匹配程度大於%80的坐標y,x

for pt in zip(*loc[::-1]): # *號表示可選參數
    cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,0), 2)
#cv.rectangle 畫框線
cv.imwrite('res.png',img_rgb)

# 讓視窗可以自由縮放大小
cv.namedWindow('TEST', cv.WINDOW_NORMAL)

# 顯示圖片
img_ok = cv.imread('res.png')

def cv2ImgAddText(img_ok, text, left, top, textColor=(0, 255, 0), textSize=20):
    if (isinstance(img_ok, np.ndarray)):  #判断是否OpenCV图片类型
        img_ok = Image.fromarray(cv.cvtColor(img_ok, cv.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_ok)
    fontText = ImageFont.truetype(
        "font/simsun.ttc", textSize, encoding="utf-8")
    draw.text((left, top), text, textColor, font=fontText)
    return cv.cvtColor(np.asarray(img_ok), cv.COLOR_RGB2BGR)


img_txt = cv2ImgAddText(img_ok, "東尼。史坦克", pt[0] ,  pt[1]-30 , (255, 0, 0), 20)
cv.imshow('TEST', img_txt)

圖片差異 相似度

import cv2

#均值哈希算法
def aHash(img):
    img=cv2.resize(img,(8,8),interpolation=cv2.INTER_CUBIC)
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #s爲像素和初值爲0,hash_str爲hash值初值爲''
    s=0
    ahash_str=''
    for i in range(8):                  #遍歷累加求像素和
        for j in range(8):
            s=s+gray[i,j]
    avg=s/64                            #求平均灰度
    for i in range(8):                  #灰度大於平均值爲1相反爲0生成圖片的hash值
        for j in range(8):
            if  gray[i,j]>avg:
                ahash_str=ahash_str+'1'
            else:
                ahash_str=ahash_str+'0'
    return ahash_str


#差值感知算法
def dHash(img):
    img=cv2.resize(img,(9,8),interpolation=cv2.INTER_CUBIC)
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    dhash_str=''
    for i in range(8):                #每行前一個像素大於後一個像素爲1,相反爲0,生成哈希
        for j in range(8):
            if gray[i,j]>gray[i,j+1]:
                dhash_str = dhash_str+'1'
            else:
                dhash_str = dhash_str+'0'
    return dhash_str

def cmpHash(hash1,hash2):              #Hash值對比
    n=0
    if len(hash1)!=len(hash2):         #hash長度不同則返回-1代表傳參出錯
        return -1
    for i in range(len(hash1)):        #遍歷判斷
        if hash1[i]!=hash2[i]:         #不相等則n計數+1,n最終爲相似度
            n=n+1
    return n


if __name__ == '__main__':
    img1=cv2.imread('x01.jpg')
    img2=cv2.imread('x02.jpg')
    hash1= aHash(img1)
    hash2= aHash(img2)
    print(hash1)
    print(hash2)
    n=cmpHash(hash1,hash2)
    print('均值哈希算法相似度:',n)

    hash1= dHash(img1)
    hash2= dHash(img2)
    print(hash1)
    print(hash2)
    n=cmpHash(hash1,hash2)
    print('差值哈希算法相似度:',n)

影像追蹤

import cv2
import numpy as np

# 開啟網路攝影機
cap = cv2.VideoCapture(0)

# 設定影像尺寸
width = 1280
height = 960

# 設定擷取影像的尺寸大小
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

# 計算畫面面積
area = width * height

# 初始化平均影像
ret, frame = cap.read()
avg = cv2.blur(frame, (4, 4))
avg_float = np.float32(avg)

while(cap.isOpened()):
    # 讀取一幅影格
    ret, frame = cap.read()

    # 若讀取至影片結尾,則跳出
    if ret == False:
        break

    # 模糊處理
    blur = cv2.blur(frame, (4, 4))

    # 計算目前影格與平均影像的差異值
    diff = cv2.absdiff(avg, blur)

    # 將圖片轉為灰階
    gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

    # 篩選出變動程度大於門檻值的區域
    ret, thresh = cv2.threshold(gray, 25, 255, cv2.THRESH_BINARY)

    # 使用型態轉換函數去除雜訊
    kernel = np.ones((5, 5), np.uint8)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)

    # 產生等高線
    cntimg, cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for c in cntimg:
    # 忽略太小的區域
        if cv2.contourArea(c) < 2500:
            continue

# 偵測到物體,可以自己加上處理的程式碼在這裡…

# 計算等高線的外框範圍
        (x, y, w, h) = cv2.boundingRect(c)

# 畫出外框
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 畫出等高線(除錯用)
        cv2.drawContours(frame, cntimg, -1, (0, 255, 255), 2)

# 顯示偵測結果影像
        cv2.imshow('frame', frame)

        if cv2.waitKey(1) == 27:
         break

# 更新平均影像
    cv2.accumulateWeighted(blur, avg_float, 0.01)
    avg = cv2.convertScaleAbs(avg_float)

cap.release()
cv2.destroyAllWindows()

cv2.absdiff

OpenCV 移動偵測應用時,需取兩張圖片不同點,去感測出哪些東西經過了變動,而這需求可用 cv2.absdiff。
import cv2
import numpy as np

A = np.array([10, 30, 50], dtype='uint8')

B = np.array([50, 30, 10], dtype='uint8')

C = cv2.absdiff(A, B)

print(C)
用OpenCV讀圖片時,圖片在內部暫存格式其實是一個二維陣列,而cv2.absdiff函式的作用是取兩個numpy陣列差絕對值,藉由解析前後兩個陣列的不同之處,做到每秒偵測變動的效果。
import cv2
import numpy as np

cap = cv2.VideoCapture(0)


while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()
  img1 = cap.read()[1]
  img2 = cap.read()[1]

  # 彩色圖轉灰階圖
  gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
  gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

  # 高斯模糊化處理
  blur1 = cv2.GaussianBlur(gray1,(5,5),0)
  blur2 = cv2.GaussianBlur(gray2,(5,5),0)

  #計算兩張灰階影像的差異值
  result = cv2.absdiff(blur1, blur2)

  #顯示
  cv2.imshow("test",result)


  # 顯示圖片
  #cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break

# 釋放攝影機
cap.release()
cv2.destroyAllWindows()

透過上方程式,利用攝影機將
img1 = cap.read()[1]
img2 = cap.read()[1]
兩張影格進行拍照,經由移動造成的色階差,使函式判定兩張圖片在少許部份存在差值,經過計算後重新賦值,差值越大顏色越亮,也就達到只留移動軌跡的效果,達到移動捕捉的功能。
若要讓結果更加清楚,則使用二值化函式cv2.threshold把result重新賦值一遍
ret, th = cv2.threshold(result, 15, 255, cv2.THRESH_BINARY)
此函式能將灰階圖片依照門檻值參數劃分成黑白二色,將色階大於15的元素賦值255,色階小於15則賦值0。最後,再使用膨脹函數cv2.dilate把二值化後的圖片進行輪廓加強處理。
import cv2
import numpy as np

cap = cv2.VideoCapture(0)


while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()
  img1 = cap.read()[1]
  img2 = cap.read()[1]

  # 彩色圖轉灰階圖
  gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
  gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

  # 高斯模糊化處理
  blur1 = cv2.GaussianBlur(gray1,(5,5),0)
  blur2 = cv2.GaussianBlur(gray2,(5,5),0)

  #計算兩張灰階影像的差異值
  result = cv2.absdiff(blur1, blur2)

  #加強輪廓
  ret, th = cv2.threshold(result, 15, 255, cv2.THRESH_BINARY)
  dilated = cv2.dilate(th, None, iterations=1)

  #顯示
  cv2.imshow("test",dilated)


  # 顯示圖片
  #cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break

# 釋放攝影機
cap.release()
cv2.destroyAllWindows()
cv2.erode是一個影像侵蝕函數,只能用於經過二值化運算的灰階圖片,可起到圖片去噪、細化影像和消除毛刺的作用。
erosion = cv2.erode(th, (3,3), iterations = 1)
第一個參數為二值化的影像,所以直接帶入變數th。
第二個參數為卷積kernel,影像侵蝕的原理是透過卷積核心沿著圖片滾動並計算元素值,如果卷積核心範圍內的元素值都是1(即白色),那麼重新賦予的元素值就保持原來的值。反之如果核心範圍內的元素值不全為1,重新賦予的元素值為0(即黑色),這表示卷積核心經過的所有像素如果不是全白都會被腐蝕或侵蝕掉(變為0)。
卷積核心的大小通常設定為奇數,如上面程式碼設定的3x3,也可以等差設定上去,如5x5、7x7、9x9,核心越大侵蝕的範圍也會越大。
第三個參數為迭代次數,通常預設為1,不用特別去動它。
import cv2
import numpy as np

cap = cv2.VideoCapture(0)


while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()
  img1 = cap.read()[1]
  img2 = cap.read()[1]

  # 彩色圖轉灰階圖
  gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
  gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

  # 高斯模糊化處理
  blur1 = cv2.GaussianBlur(gray1,(5,5),0)
  blur2 = cv2.GaussianBlur(gray2,(5,5),0)

  #計算兩張灰階影像的差異值
  result = cv2.absdiff(blur1, blur2)

  #加強輪廓 二值化處理
  ret, th = cv2.threshold(result, 15, 255, cv2.THRESH_BINARY)
  dilated = cv2.dilate(th, None, iterations=1) #膨脹
  erosion = cv2.erode(th, (3,3), iterations = 1) #影像侵蝕函數

  #顯示
  cv2.imshow("test",erosion)


  # 顯示圖片
  #cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break

# 釋放攝影機
cap.release()
cv2.destroyAllWindows()

cv2.dilate 第一個參數為二值化的影像,所以直接帶入變數th。
第二個參數是卷積核心大小!與cv2.erod的差別在於核心範圍的元素值不全是1,重新賦予的值就全是0(侵蝕),cv2.dilate核心範圍元素值出現1,重新賦予的值就全是1(膨脹)。
第三個參數也和cv2.erode 是迭代次數,不需改動。
影像膨脹,通常配合影像侵蝕使用,先用侵蝕函數將圖片中的輪廓變細,同時去除多餘毛點,再用影像膨脹函數將輪廓恢復正常大小,使圖片看上去更加清晰。
因此,在移動偵測時,先將二值化後圖片用cv2.erode侵蝕,再用cv2.dilate把輪廓膨脹,同時做到去除毛點和輪廓清晰化。
import cv2
import numpy as np

cap = cv2.VideoCapture(0)


while(True):
  # 從攝影機擷取一張影像
  ret, frame = cap.read()
  img1 = cap.read()[1]
  img2 = cap.read()[1]

  # 彩色圖轉灰階圖
  gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
  gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

  # 高斯模糊化處理
  blur1 = cv2.GaussianBlur(gray1,(5,5),0)
  blur2 = cv2.GaussianBlur(gray2,(5,5),0)

  #計算兩張灰階影像的差異值
  result = cv2.absdiff(blur1, blur2)

  #加強輪廓 二值化處理
  ret, th = cv2.threshold(result, 15, 255, cv2.THRESH_BINARY)
  #dilated = cv2.dilate(th, None, iterations=1) #膨脹
  erosion = cv2.erode(th, (3,3), iterations = 1) #影像侵蝕函數
  dilated = cv2.dilate(erosion, (3,3), iterations=1) #膨脹
  
  #顯示
  cv2.imshow("test",dilated)


  # 顯示圖片
  #cv2.imshow('frame', frame)
  # 若按下 q 鍵則離開迴圈
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break

# 釋放攝影機
cap.release()
cv2.destroyAllWindows()

移動偵測

import cv2
import numpy as np

#CAP = cv2.VideoCapture('input.avi')
CAP = cv2.VideoCapture(0)

FR_W = int( CAP.get(cv2.CAP_PROP_FRAME_WIDTH))

FR_H =int( CAP.get( cv2.CAP_PROP_FRAME_HEIGHT))

FRC = cv2.VideoWriter_fourcc('X','V','I','D')

OP = cv2.VideoWriter("output.avi", FRC, 5.0, (1280,720))

ret, F1 = CAP.read()
ret, F2 = CAP.read()
print(F1.shape)
while CAP.isOpened():
    if ret==False:
        print(ret)
        break
    DF = cv2.absdiff(F1, F2)
    Gray_Scale = cv2.cvtColor(DF, cv2.COLOR_BGR2GRAY)
    BL = cv2.GaussianBlur(Gray_Scale, (5,5), 0)
    _, thresh = cv2.threshold(BL, 20, 255, cv2.THRESH_BINARY)
    DL = cv2.dilate(thresh, None, iterations=3)
    CTS, _ = cv2.findContours(DL, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    for CT in CTS:
        (x, y, w, h) = cv2.boundingRect(CT)

        if cv2.contourArea(CT) < 900:
            continue
        cv2.rectangle(F1, (x, y), (x+w, y+h), (0, 255, 0), 2)
        cv2.putText(F1, "Status: {}".format('Movement'), (10, 20), cv2.FONT_HERSHEY_SIMPLEX,
                    1, (0, 0, 255), 3)


    IMG = cv2.resize(F1, (1280,720))
    OP.write(IMG)
    cv2.imshow("feed", F1)
    F1 = F2
    ret, F2 = CAP.read()

    if cv2.waitKey(40) == 27:
        break

cv2.destroyAllWindows()
CAP.release()
OP.release()

DF 變數為使用 absdiff() 函式。absdiff() 幫助找到幀之間的絕對差異, F1 和 F2。
Gray_Scale = cv2.cvtColor(DF, cv2.COLOR_BGR2GRAY)
cvtColor() 方法將差異轉換為灰度模式。第一個引數是 DF。
第二個引數是 COLOR_BGR2GRAY,將幀顏色 BGR 轉換為灰度模式,用以找到輪廓,所以將彩色模式轉為灰模式找到輪廓。
BL = cv2.GaussianBlur(Gray_Scale, (5,5), 0)
GaussianBlur() 方法來模糊灰度幀。第一個是 Gray_Scale,第二個引數是核心大小 5x5,第三個引數是 Sigma X 值。
_, thresh = cv2.threshold(BL, 20, 255, cv2.THRESH_BINARY)
threshold() 方法來確定閾值。它返回兩個物件;定義 _,因為不需要第一個變數,然後第二個變數是 thresh。
DL = cv2.dilate(thresh, None, iterations=3)
擴大閾值影象以填充所有;用來幫助找到更好的輪廓。
dilate() 方法,第一個引數是定義的閾值,第二個引數是核心大小,但此處將其傳遞給 None。
第三個引數是迭代次數為 3。如果不起作用,可以增加或減少迭代次數。
for CT in CTS:
   (x, y, w, h) = cv2.boundingRect(CT)

   if cv2.contourArea(CT) < 900:
       continue
   cv2.rectangle(F1, (x, y), (x + w, y + h), (0, 255, 0), 2)
繪製矩形,使用 for 迴圈遍歷所有輪廓。
CTS 是一個列表,所以第一步是使用 boundingRect() 方法儲存輪廓的所有座標。接著,找出輪廓區域,如果這個區域小於某個值,將不會繪製矩形。
在 for 迴圈中,定義如果輪廓面積小於 700,將繼續迭代;否則,繪製矩形。
cv2.rectangle() 方法,這裡的第一個引數是源,即 F1;第二個引數是點 1 (x,y)。第三個引數是點 2,下一個引數是作為顏色值的元組,下一個引數是厚度。
cv2.putText(F1, "Status: {}".format('Movement'), (10, 20), cv2.FONT_HERSHEY_SIMPLEX,
                   1, (0, 0, 255), 3)
若有移動,則在影片上方放置文字。使用 cv2.putText() 方法;此方法第一個引數為來源,也就是將 F1上面放置文字,文字內容則為第二個引數,第三個引數是要放置此文字的原點。第四個引數是字型 FONT_HERSHEY_SIMPLEX。第五個引數是字型比例。第六個是字型的顏色,最後一個引數是文字的粗細。
OP.write(IMG)
cv2.imshow("feed", F1)
F1 = F2
ret, F2 = CAP.read()
迴圈外,將輸出影象以儲存輸出,然後顯示 F1,即應用輪廓後的結果。在下一行中,將正在讀取變數 F2 中的新幀,在讀取新幀之前,先將 F2 的值分配給 F1。這樣,就能顯示並找到兩個框架之間的差異。

Video To Image

import cv2

outputFile = './output/'

vc = cv2.VideoCapture(0)
c = 1
if vc.isOpened():
    rval, frame = vc.read()
else:
    print('openerror!')
    rval = False
    
timeF = 100  #視頻幀計數間隔次數

while rval:
    print(1)
    #print(c)
    rval, frame = vc.read()
    if c % timeF == 0:
        print(2)
        cv2.imwrite(outputFile + str(int(c / timeF)) + '.jpg', frame)
    c += 1
 
    cv2.waitKey(1)
vc.release()

Real Time 圖形比對(純粹OpenCV)

import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image, ImageDraw, ImageFont

outputFile = './output/'

vc = cv2.VideoCapture(0)
c = 1
if vc.isOpened():
    rval, frame = vc.read()
else:
    print('openerror!')
    rval = False
    
timeF = 1  #視頻幀計數間隔次數

while rval:
    print(1)
    #print(c)
    rval, frame = vc.read()
    if c % timeF == 0:
        print(2)
        cv2.imwrite(outputFile + str(int(c / timeF)) + '.jpg', frame)
    c += 1
    if int(c / timeF)>5:
        c=1

    
    #讀入原圖和模板
    img_rgb = cv2.imread('./output/1.jpg') #原圖
    img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
    template = cv2.imread('oo.jpg',0) #比對用圖
    w, h = template.shape[::-1]

    #標準相關模板匹配
    res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
    threshold = 0.8
    loc = np.where( res >= threshold) # 匹配程度大於%80的坐標y,x

    pt=[0,0]
    for pt in zip(*loc[::-1]): # *號表示可選參數
        cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,0), 2)
        xo=1
        
    #cv.rectangle 畫框線
    cv2.imwrite('res.png',img_rgb)

    # 讓視窗可以自由縮放大小
    cv2.namedWindow('TEST', cv2.WINDOW_NORMAL)

    # 顯示圖片
    img_ok = cv2.imread('res.png')


    def cv2ImgAddText(img_ok, text, left, top, textColor=(0, 255, 0), textSize=20):
        if (isinstance(img_ok, np.ndarray)):  #判断是否OpenCV图片类型
            img_ok = Image.fromarray(cv2.cvtColor(img_ok, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img_ok)
        fontText = ImageFont.truetype(
            "font/simsun.ttc", textSize, encoding="utf-8")
        draw.text((left, top), text, textColor, font=fontText)
        return cv2.cvtColor(np.asarray(img_ok), cv2.COLOR_RGB2BGR)
    
    
    if(pt[0]==0):
        img_txt = cv2ImgAddText(img_ok, "x", pt[0] ,  pt[1] , (255, 0, 0), 20)
    else:
        img_txt = cv2ImgAddText(img_ok, "Joker", pt[0] ,  pt[1]-30 , (255, 0, 0), 20)
    cv2.imshow('TEST', img_txt)

      
    cv2.waitKey(1)
vc.release()

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.