圖形辨識筆記-OPEN CV

更新於 發佈於 閱讀時間約 60 分鐘

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

成果:

raw-image

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,包含透明度。

結果:

raw-image

寫入圖片檔案

# 寫入圖檔
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 顯示就會得到正確的結果了。

raw-image
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)
raw-image
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()
raw-image

查看圖片屬性

#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()
raw-image

透過上方程式,利用攝影機將

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()
raw-image

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()
raw-image

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()
raw-image

移動偵測

raw-image
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)

raw-image
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()


留言
avatar-img
留言分享你的想法!
avatar-img
吳佳鑫的沙龍
42會員
136內容數
獨立遊戲開發紀錄
吳佳鑫的沙龍的其他內容
2025/04/12
🗓 Steam 上市日期:2025/4/28 | 🖤 加入願望清單:https://store.steampowered.com/app/3634400/Elara/ 「他們說我太敏感,說我不合群。 但我只是——不想學會沉默。」 —Elara Quinn 你是否曾經在工作時,感
Thumbnail
2025/04/12
🗓 Steam 上市日期:2025/4/28 | 🖤 加入願望清單:https://store.steampowered.com/app/3634400/Elara/ 「他們說我太敏感,說我不合群。 但我只是——不想學會沉默。」 —Elara Quinn 你是否曾經在工作時,感
Thumbnail
2025/03/05
在靈異頻發的醫院夜班,小護士林筱筱意外發現她的冷酷上司——蘇醫生,竟然早已死亡五年,從此她與這位神秘的「鬼醫生」攜手處理靈異事件,並在驚魂不斷的夜班中逐漸建立起一段超越生死的羈絆。
Thumbnail
2025/03/05
在靈異頻發的醫院夜班,小護士林筱筱意外發現她的冷酷上司——蘇醫生,竟然早已死亡五年,從此她與這位神秘的「鬼醫生」攜手處理靈異事件,並在驚魂不斷的夜班中逐漸建立起一段超越生死的羈絆。
Thumbnail
2025/02/24
這是一篇懸疑驚悚小說,描述女主角江語珊調查一起離奇命案的故事。死者林曦是一位心理學教授,死於自家公寓,現場佈滿鏡子,死者嘴角帶著詭異的微笑。語珊在調查過程中發現,此案與一種名為「鏡像侵蝕」的心理現象有關,並逐漸被捲入其中。
Thumbnail
2025/02/24
這是一篇懸疑驚悚小說,描述女主角江語珊調查一起離奇命案的故事。死者林曦是一位心理學教授,死於自家公寓,現場佈滿鏡子,死者嘴角帶著詭異的微笑。語珊在調查過程中發現,此案與一種名為「鏡像侵蝕」的心理現象有關,並逐漸被捲入其中。
Thumbnail
看更多
你可能也想看
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
上一篇提到利用cv2.inRangex,建立遮罩來過濾出紅球。這次我們稍微更動一下程式碼,將紅球變顏色。 [OpenCV][Python]利用cv2.inRange搭配cv2.bitwise_and過濾紅球 結果圖 將紅球改變顏色成藍球
Thumbnail
上一篇提到利用cv2.inRangex,建立遮罩來過濾出紅球。這次我們稍微更動一下程式碼,將紅球變顏色。 [OpenCV][Python]利用cv2.inRange搭配cv2.bitwise_and過濾紅球 結果圖 將紅球改變顏色成藍球
Thumbnail
用小畫家隨意畫三個圈分別用紅藍綠,我們利用cv2.inRange與搭配cv2.bitwise_and,將紅球過濾出來吧。 程式範例 因為OpenCV中cv2.imread讀取圖檔預設讀取是為[B,G,R]的格式,所以設置紅色範圍要注意設定在R的範圍內。
Thumbnail
用小畫家隨意畫三個圈分別用紅藍綠,我們利用cv2.inRange與搭配cv2.bitwise_and,將紅球過濾出來吧。 程式範例 因為OpenCV中cv2.imread讀取圖檔預設讀取是為[B,G,R]的格式,所以設置紅色範圍要注意設定在R的範圍內。
Thumbnail
使用cv2.imread讀取圖片時,如果路徑有包含到中文,就會報錯。 本文將提供另外一個方式cv2.imdecode,路徑有包含到中文時仍可以正常讀取圖片。 測試範例 import cv2 img = cv2.imread('D:/CRABpy/write/圖檔/chars_01.png'
Thumbnail
使用cv2.imread讀取圖片時,如果路徑有包含到中文,就會報錯。 本文將提供另外一個方式cv2.imdecode,路徑有包含到中文時仍可以正常讀取圖片。 測試範例 import cv2 img = cv2.imread('D:/CRABpy/write/圖檔/chars_01.png'
Thumbnail
當我們在進行影像處理時, 在Python的世界最常聽到的就是OpenCV, 而我們在處理影片時也會想要僅針對某時間段的影片進行處理, 今天我們就來教您如何透過OpenCV來讀取特定的時間區段。 在進入主題之前, 有一些基本概念務必先行建立, 一個影片是由多張圖片組成的, 因此最小單元為一張圖
Thumbnail
當我們在進行影像處理時, 在Python的世界最常聽到的就是OpenCV, 而我們在處理影片時也會想要僅針對某時間段的影片進行處理, 今天我們就來教您如何透過OpenCV來讀取特定的時間區段。 在進入主題之前, 有一些基本概念務必先行建立, 一個影片是由多張圖片組成的, 因此最小單元為一張圖
Thumbnail
涉及圖像處理和計算機視覺時,色彩空間轉換是一個常見操作,應用如下: 降維: 將一張彩色圖像轉換為灰度圖像可以減少數據的維度,簡化處理過程,同時在某些情況下保留重要的視覺信息。 突顯特徵: 在某些情況下,某些色彩通道可能包含冗餘或不必要的信息,通過轉換到其他色彩空間,可以更好地突顯圖像中的重要特徵
Thumbnail
涉及圖像處理和計算機視覺時,色彩空間轉換是一個常見操作,應用如下: 降維: 將一張彩色圖像轉換為灰度圖像可以減少數據的維度,簡化處理過程,同時在某些情況下保留重要的視覺信息。 突顯特徵: 在某些情況下,某些色彩通道可能包含冗餘或不必要的信息,通過轉換到其他色彩空間,可以更好地突顯圖像中的重要特徵
Thumbnail
本文將介紹影像的基本操作包括:影像的讀取、顯示、保存,以及一些常見的操作如裁剪、旋轉、縮放等。 語法介紹 讀取影像: cv2.imread函數的參數是影像的檔案路徑。讀取後的影像以NumPy的ndarray形式表示。
Thumbnail
本文將介紹影像的基本操作包括:影像的讀取、顯示、保存,以及一些常見的操作如裁剪、旋轉、縮放等。 語法介紹 讀取影像: cv2.imread函數的參數是影像的檔案路徑。讀取後的影像以NumPy的ndarray形式表示。
Thumbnail
OpenCV(Open Source Computer Vision Library)是一個開源的計算機視覺和影像處理庫,它提供了豐富的功能和工具,可用於開發各種視覺應用程式。 OpenCV最初是用C++編寫的,但它也提供了Python、Java等多種程式語言的接口,方便不同語言的開發者使用。
Thumbnail
OpenCV(Open Source Computer Vision Library)是一個開源的計算機視覺和影像處理庫,它提供了豐富的功能和工具,可用於開發各種視覺應用程式。 OpenCV最初是用C++編寫的,但它也提供了Python、Java等多種程式語言的接口,方便不同語言的開發者使用。
Thumbnail
#安裝 OpenCV 相關套件 pip install opencv-python pip install opencv-contrib-python pip install matplotlib
Thumbnail
#安裝 OpenCV 相關套件 pip install opencv-python pip install opencv-contrib-python pip install matplotlib
Thumbnail
取得掌紋 圖片比對 後續思維... 透過特徵比對+CNN,以YOLO 或 TensorFlow+Keras進行會比較正確。 單靠OpneCV至此是極限,要判斷出各條線,沒模型,則要以numpy陣列加上相似度(哈希算法等)去做,整體上不好進行... 待續...
Thumbnail
取得掌紋 圖片比對 後續思維... 透過特徵比對+CNN,以YOLO 或 TensorFlow+Keras進行會比較正確。 單靠OpneCV至此是極限,要判斷出各條線,沒模型,則要以numpy陣列加上相似度(哈希算法等)去做,整體上不好進行... 待續...
Thumbnail
OpenCV 讀取圖片 原碼:https://reurl.cc/3354ZL 成果: OpenCV 本身有提供讀取圖檔的函數可用,讀取圖檔,只要呼叫 cv2.imread 即可將圖片讀取進來,以 cv2.imread 讀進來的資料,會儲存成一個 NumPy 的陣列。 將圖片讀取進來之後,可使用 c
Thumbnail
OpenCV 讀取圖片 原碼:https://reurl.cc/3354ZL 成果: OpenCV 本身有提供讀取圖檔的函數可用,讀取圖檔,只要呼叫 cv2.imread 即可將圖片讀取進來,以 cv2.imread 讀進來的資料,會儲存成一個 NumPy 的陣列。 將圖片讀取進來之後,可使用 c
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News