圖形辨識筆記-OPEN CV

更新於 2024/11/07閱讀時間約 61 分鐘

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()
為什麼會看到廣告
avatar-img
40會員
130內容數
獨立遊戲開發紀錄
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
吳佳鑫的沙龍 的其他內容
「娜斯佳,可以知道虎嘯山誌的紙本資料放在哪嗎?」我想著若可以知道原始的紙本資料放在哪,那或許就可以知道更完整的資訊了。 「妳可以試著去圖書館找找吧!」娜斯佳背對著我說。 「只是妳可能只會看到一般給市民看的資料,這種有內幕的資訊…」 「可能要到市府內的檔案管理局才有辦法看到。」娜斯佳頓了一頓說。
「學姊~」當我下課,正要上車時,一道聲音從我後方而來。 「好久不見了。」我笑著看向前方奔跑過來的彤羽。 「我就知道學姊會來上課。」彤羽來到我跟前說道。 「我以為,妳會直接去子璇家。」 「本來是想這樣,可是想想我還是先回我住的那,整理好,再來這跟妳一起過去。」 「所以妳自己一個人回來?」 「彤羽?」
「妳們已經見過他了?」當我進房,將背包放置矮櫃沙發下時,開口問道。 「見過了,人應該還可以,就腦子不怎麼精靈。」子璇靠在沙發上無奈的說著。 「要我說,是呆呆的。」蕾雅補充的說道。 「怎了?」 「太年輕,雖然對答上沒什麼問題,但暗示他去當間諜監視劉莊主,暗示了老半天,一直聽不懂。」蕾雅回著我的話。
飯後蕾雅帶娜斯佳回房,而我則將我了解的人員資料,敘說給子璇知道。 「還蠻多人的...」子璇看著名單說著。 「妳覺得.. 我若跟高山族人說,他們可以自由選擇,看是要繼續留在家裡,或是離開生活,我爸他們會不會接受?」 「嗯...我想妳應該要先了解,他們當時被管束的理由,免得一自由就又搞出一堆事。」
隨後,我們便離開會客室,返回子璇的房間。路上,子璇也先請李老將虎嘯山資料傳過來。而我則聯繫小葉,麻煩他處理彤羽所需的東西。 「欸...原來我爸跟二叔、三叔他們是在那時加入那什麼黑派,替嚴老辦事的呀!」子璇像是發現什麼新鮮事般的說著。 「陳言是妳爸的名字?」 鈴~鈴鈴~ 「駭客朋友?」子璇疑惑問道。
「我爸說,待會律師會來找我。」回到已經重新修護裝潢的子璇房間後,她通完電話的第一句話。 「律師?」我回道,此時蕾雅則很自然的坐在沙發凳上看著子璇。 「嗯。說是關於虎嘯山的事,我二叔也同意,所以讓律師來找我。」 「什麼意思?我不太懂,虎嘯山重啟祭壇,怎會需要律師來?」我問著。 叩叩~ 「喔...」
「娜斯佳,可以知道虎嘯山誌的紙本資料放在哪嗎?」我想著若可以知道原始的紙本資料放在哪,那或許就可以知道更完整的資訊了。 「妳可以試著去圖書館找找吧!」娜斯佳背對著我說。 「只是妳可能只會看到一般給市民看的資料,這種有內幕的資訊…」 「可能要到市府內的檔案管理局才有辦法看到。」娜斯佳頓了一頓說。
「學姊~」當我下課,正要上車時,一道聲音從我後方而來。 「好久不見了。」我笑著看向前方奔跑過來的彤羽。 「我就知道學姊會來上課。」彤羽來到我跟前說道。 「我以為,妳會直接去子璇家。」 「本來是想這樣,可是想想我還是先回我住的那,整理好,再來這跟妳一起過去。」 「所以妳自己一個人回來?」 「彤羽?」
「妳們已經見過他了?」當我進房,將背包放置矮櫃沙發下時,開口問道。 「見過了,人應該還可以,就腦子不怎麼精靈。」子璇靠在沙發上無奈的說著。 「要我說,是呆呆的。」蕾雅補充的說道。 「怎了?」 「太年輕,雖然對答上沒什麼問題,但暗示他去當間諜監視劉莊主,暗示了老半天,一直聽不懂。」蕾雅回著我的話。
飯後蕾雅帶娜斯佳回房,而我則將我了解的人員資料,敘說給子璇知道。 「還蠻多人的...」子璇看著名單說著。 「妳覺得.. 我若跟高山族人說,他們可以自由選擇,看是要繼續留在家裡,或是離開生活,我爸他們會不會接受?」 「嗯...我想妳應該要先了解,他們當時被管束的理由,免得一自由就又搞出一堆事。」
隨後,我們便離開會客室,返回子璇的房間。路上,子璇也先請李老將虎嘯山資料傳過來。而我則聯繫小葉,麻煩他處理彤羽所需的東西。 「欸...原來我爸跟二叔、三叔他們是在那時加入那什麼黑派,替嚴老辦事的呀!」子璇像是發現什麼新鮮事般的說著。 「陳言是妳爸的名字?」 鈴~鈴鈴~ 「駭客朋友?」子璇疑惑問道。
「我爸說,待會律師會來找我。」回到已經重新修護裝潢的子璇房間後,她通完電話的第一句話。 「律師?」我回道,此時蕾雅則很自然的坐在沙發凳上看著子璇。 「嗯。說是關於虎嘯山的事,我二叔也同意,所以讓律師來找我。」 「什麼意思?我不太懂,虎嘯山重啟祭壇,怎會需要律師來?」我問著。 叩叩~ 「喔...」
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
在這個人人都是自媒體的時代,知識型自媒體創作者面臨的最大挑戰是如何在海量內容中脫穎而出。然而,生成式 AI 帶來了新契機:它能在靈感枯竭時提供多樣的想法,並借助多種 AI 工具,快速生成圖像,顯著縮短創作流程。本課程將引領你探索如何利用 ChatGPT 結合圖像生成式 AI,提升圖文創作的豐富度
Thumbnail
如果你不擅長直接編寫原始碼的話,要製作網頁、電子報,一定會非常辛苦,本文介紹的開源網頁神器 GrapesJS 絕對是你在找的工具,強大又免費的特性,可以用 Chrome App 的方式安裝,其它編輯器比起來又簡單很多;還有自訂原始碼……
Thumbnail
EasyOCR是一個能夠幫助你對圖片中的文字進行辨識的工具,透過進階分析,可以應用在文件掃描、自動化數據輸入、發票掃描等領域。本章節將介紹如何安裝、引用模型、進行文字辨識、以及辨識結果的分析。透過學習,你可以建立屬於自己的文字辨識系統。
Thumbnail
FENDI為2024高級訂服製建構出人類的「未來主義」。 全新「Scatola」(「盒子」之意)廓形開頭到收尾,流動精準的幾何打版工藝創造出嶄新量體:高撚雙紗真絲卻描述了輕盈硬挺的效果。 Super Kid Mohair 幼馬海毛布料、上等羊絨和小羊駝紗線的羅紋針織一吋一釐貼
Thumbnail
扭曲變形或 P 圖的照片看鬼有差嗎? . 網友問的。 . 請參照下圖原圖和扭曲的圖片, 網友問這樣的兩張圖片讓我看鬼的話, 會有差嗎? 普通看不到的人可能以為沒差, 但答案其實是很可能會有差, 假設鬼是在很接近兔兔身邊的話啦, 那鬼的樣子就會 [ 連帶被扭曲變形 ], 所以呢,如果你拍照的時候, 使
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
在這個人人都是自媒體的時代,知識型自媒體創作者面臨的最大挑戰是如何在海量內容中脫穎而出。然而,生成式 AI 帶來了新契機:它能在靈感枯竭時提供多樣的想法,並借助多種 AI 工具,快速生成圖像,顯著縮短創作流程。本課程將引領你探索如何利用 ChatGPT 結合圖像生成式 AI,提升圖文創作的豐富度
Thumbnail
如果你不擅長直接編寫原始碼的話,要製作網頁、電子報,一定會非常辛苦,本文介紹的開源網頁神器 GrapesJS 絕對是你在找的工具,強大又免費的特性,可以用 Chrome App 的方式安裝,其它編輯器比起來又簡單很多;還有自訂原始碼……
Thumbnail
EasyOCR是一個能夠幫助你對圖片中的文字進行辨識的工具,透過進階分析,可以應用在文件掃描、自動化數據輸入、發票掃描等領域。本章節將介紹如何安裝、引用模型、進行文字辨識、以及辨識結果的分析。透過學習,你可以建立屬於自己的文字辨識系統。
Thumbnail
FENDI為2024高級訂服製建構出人類的「未來主義」。 全新「Scatola」(「盒子」之意)廓形開頭到收尾,流動精準的幾何打版工藝創造出嶄新量體:高撚雙紗真絲卻描述了輕盈硬挺的效果。 Super Kid Mohair 幼馬海毛布料、上等羊絨和小羊駝紗線的羅紋針織一吋一釐貼
Thumbnail
扭曲變形或 P 圖的照片看鬼有差嗎? . 網友問的。 . 請參照下圖原圖和扭曲的圖片, 網友問這樣的兩張圖片讓我看鬼的話, 會有差嗎? 普通看不到的人可能以為沒差, 但答案其實是很可能會有差, 假設鬼是在很接近兔兔身邊的話啦, 那鬼的樣子就會 [ 連帶被扭曲變形 ], 所以呢,如果你拍照的時候, 使