2016年11月30日 星期三

Raspberry Pi 的 Webcam 影像移位重疊問題


早幾天,我用 Python 編寫了一個偵測移動物的程式。它是在 Raspberry Pi 上配合 Webcam 執行。當發現移動物時,除了把影像儲存外,還會經電郵傳送給我,好讓我知道家中的情況。可是,我經常收到錯誤的警報。原因是 Webcam 傳到 Python 的影像中,有時會上一幀跟下一幀移位重疊。由於動作偵測是以畫面變化來作準,移位重疊令到這個準則成立,觸發誤報。起初以為是 Python 對於處理記憶體物件是以指針來處理,程式工作得慢而導致下一幀已寫進記憶體,可是改用 .copy() 後問題依舊,似乎是鏡頭或是驅動程式的問題。有待解決。
##----------------------------------------------------------------------------------------
##  Motion Detector for Webcam
##----------------------------------------------------------------------------------------
##  Written by Pacess HO
##  Platform : Python3 + OpenCV3
##  Date : 2016.Nov.25
##  Copyright 2016 Pacess Studio.  All rights reserved.
##----------------------------------------------------------------------------------------

import smtplib
import imutils
import time
import cv2

# from msvcrt import getch
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

##----------------------------------------------------------------------------------------
##  Main program start
print("\nMotion Detector for Webcam\n\n")
print("Warming up, please wait...\n")
camera = cv2.VideoCapture(0)
time.sleep(2)

##  Loop over the frames of the video
skipCount = 0
lastStatus = False
thisStatus = False
masterFrame = None
print("Start capturing...\n")
while True:

   ##----------------------------------------------------------------------------------------
   ##  Grab the current frame
   grabbed = camera.grab()
 
   ##  If the frame could not be grabbed, then we have reached the end of the video
   if not grabbed:
      continue
 
   if skipCount > 0:
      skipCount = skipCount-1
      if skipCount == 0:
         print("Count down completed, resume capture.")
      continue

   ##----------------------------------------------------------------------------------------
   ##  Convert frame to grayscale, and blur it
   flag, frame = camera.retrieve()
   if not flag:
      continue

   currentFrame = frame.copy()
   gray = cv2.cvtColor(currentFrame, cv2.COLOR_BGR2GRAY)
   gray = cv2.GaussianBlur(gray, (21, 21), 0)
 
   # if the first frame is None, initialize it
   if masterFrame is None:
      masterFrame = gray
      continue

   ##----------------------------------------------------------------------------------------
   ##  Compute the absolute difference between the current frame and
   frameDelta = cv2.absdiff(masterFrame, gray)
   threshold = cv2.threshold(frameDelta, 68, 255, cv2.THRESH_BINARY)[1]

   threshold = cv2.dilate(threshold, None, iterations=2)
   (_, contourArray, _) = cv2.findContours(threshold.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 

   ##  Loop over the contours
   lastStatus = thisStatus
   thisStatus = False
   for contour in contourArray:

      ##  If the contour is too small, ignore it
      difference = cv2.contourArea(contour)
      if difference < 10000:
         continue
 
      thisStatus = True

      ##  Compute the bounding box for the contour
      (x, y, w, h) = cv2.boundingRect(contour)
      cv2.rectangle(currentFrame, (x, y), (x+w, y+h), (0, 255, 0), 2)

   ##----------------------------------------------------------------------------------------
   ##  Save frame and send email alert
   if thisStatus == True:

      masterFrame = gray

      ##  Save frame
      id = time.strftime("%Y%m%d%H%M%S")
      filename = "./capture/capture_"+str(id)+".jpg"
      cv2.imwrite(filename, currentFrame)
      print("Capture: "+filename)

      ##  Sending email notification
      email = MIMEMultipart()
      email["Subject"] = "Raspberry Pi Motion Alert"
      email["From"] = "pacess@pacess.com"
      email["To"] = "pacess@pacess.com"

      with open(filename, "rb") as filePointer:
         image = MIMEImage(filePointer.read())
      email.attach(image)

      smtp = smtplib.SMTP("smtp.mail.yahoo.com", 587)
      smtp.ehlo()
      smtp.starttls()
      smtp.login("pacess@yahoo.com", "12345678")
      smtp.sendmail(email["From"], email["To"], email.as_string())
      smtp.quit()

      ##  Skip some frames to prevent sending too much image
      print("Email sent, wait for a while...")
      skipCount = 100

##----------------------------------------------------------------------------------------
##  Cleanup the camera and close any open windows
camera.release()
cv2.destroyAllWindows()

沒有留言: