圖形辨識筆記-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 顯示就會得到正確的結果了。

BGR 模式

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)
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
吳佳鑫的沙龍
41會員
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
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
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等多種程式語言的接口,方便不同語言的開發者使用。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News