基於opencv的家居智能安防機器視覺系統

基於opencv的家居智能安防機器視覺系統(關鍵詞:Windows、樹莓派、python、opencv)

1.寫在前面的話

大學4年很快過去了,因爲疫情原因我們從大四上學期結束之後直接跳到了大學的尾聲:畢業設計、畢業答辯、畢業的環境。回顧整個大學生活中最難忘的就是在光電創新實驗室從事機器視覺課題研究的那段時間,感謝學院各位老師對我的幫助。這套系統爲我的畢業設計內容,自認爲有諸多不足,但是還是斗膽發佈出來讓各位從事視覺方面的同學查看。

2.opencv3和opencv4的區別

在我畢業設計項目進行的時候我選擇了當時現行的opencv3版本,但是到了編寫這篇文章的時候opencv發佈了4.0版本。3和4版本基本一樣,只是會有一點點的區別,注意即可。本篇文章將會從opencv4出發,使用opencv3版本的同學要注意一下,在獲取輪廓的時候findContours函數返回結果由3.x的三個參數變爲兩個參數,不過我在後面也會標出不同的地方。

3.系統的整體結構設計

本文所開發的基於機器視覺的家居智能安防系統結構如圖 1 所示。該系統採
用紅外夜視攝像頭作爲採集家庭信息的傳感器,在有異常現象的情況時會自動報
警,系統會通過 SMTP 協議將會採集當前視頻幀發送到用戶指定郵箱,同時發出
警報聲,讓業主及時查看即時報警畫面採取行動。系統由樹莓派、紅外夜視攝像
頭、物聯網模塊和電源模塊組成。 樹莓派上部署嵌入式 Linux 對採集到的圖像進
行機器視覺處理、並利用樹莓派上搭載的網卡模塊進行聯網通信;再把信息通過
SMTP 協議發往用戶郵箱。 用戶可以通過手機電腦等平臺來查看採集到的圖像信
息從而採取行動。

4.主要工作

1.硬件平臺:包括攝像頭(配套的圖像採集卡或者集成)、鏡頭、嵌入式處理器
(樹莓派或者其他平臺)以及其他配件;
2.在嵌入式 Linux 系統上構建 python 以及 opencv 機器視覺環境;
3.建立視覺注意與運動視覺的關鍵算法,包括運動檢測、實現動作捕捉、抓拍並
存儲照片,同時可以在夜間使用;
4.當有運動物體進入超過一定閾值時可以實現自動報警,啓動蜂鳴器;並將報警
通知通過郵件發送到郵箱;

5.代碼部分

#-*-coding:utf-8-*-
# 導入必要的軟件包
import argparse
import datetime
import imutils
import time
import cv2

import threading
import yagmail

# 創建參數解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size")
args = vars(ap.parse_args())
shot_idx = 0
# 如果video參數爲None,那麼我們從攝像頭讀取數據
if args.get("video", None) is None:
    camera = cv2.VideoCapture(0)#直接打開攝像頭0獲取圖像

# 否則我們讀取一個視頻文件
else:
    camera = cv2.VideoCapture(args["video"])




def shijue() :
    shot_idx = 0
    # 遍歷視頻的每一幀
    # 初始化視頻流的第一幀
    firstFrame = None
    while True:

        # 讀入攝像頭的幀
        (grabbed, frame) = camera.read()
        text = "Stop"
        flat = 0
        # 如果不能抓取到一幀,說明我們到了視頻的結尾
        if not grabbed:
            break
        cv2.imshow('frame',frame)
        # 調整該幀的大小,轉換爲灰階圖像並且對其進行高斯模糊
        frame = imutils.resize(frame, width=500)
        # 對幀進行預處理,先轉灰度圖,再進行高斯濾波。
        # 用高斯濾波進行模糊處理,進行處理的原因:每個輸入的視頻都會因自然震動、光照變化或者攝像頭本身等原因而產生噪聲。對噪聲進行平滑是爲了避免在運動和跟蹤時將其檢測出來。
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (21, 21), 0)
        cv2.imshow('gray', gray)
        # 如果第一幀是None,對其進行初始化

        if firstFrame is None:
            firstFrame = gray#一開始檢測的話首幀會不存在,那麼就把灰度圖作爲首幀
            continue
        # 計算當前幀和第一幀的不同
        # 對於每個從背景之後讀取的幀都會計算其與北京之間的差異,並得到一個差分圖(different map)。
        # 還需要應用閾值來得到一幅黑白圖像,並通過下面代碼來膨脹(dilate)圖像,從而對孔(hole)和缺陷(imperfection)進行歸一化處理
        frameDelta = cv2.absdiff(firstFrame, gray)
        thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
        firstFrame = gray

        # 擴展閥值圖像填充孔洞,然後找到閥值圖像上的輪廓
        thresh = cv2.dilate(thresh, None, iterations=2)
        # 搜索輪廓
        contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL,
                                                      cv2.CHAIN_APPROX_SIMPLE)
        #這裏用的是opencv4,cv2.findContours返回了2個參數,但是用opencv3的話會返回3給參數,你要確保有足夠的變量承接返回值可改成 binary, contours, hierarchy = cv.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
        #返回值:contours:一個列表,每一項都是一個輪廓, 不會存儲輪廓所有的點,只存儲能描述輪廓的點hierarchy:一個ndarray, 元素數量和輪廓數量一樣, 每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,則該值爲負數

        """
            cv.findContours()
                參數:
                    1 要尋找輪廓的圖像 只能傳入二值圖像,不是灰度圖像
                    2 輪廓的檢索模式,有四種:
                        cv2.RETR_EXTERNAL表示只檢測外輪廓
                        cv2.RETR_LIST檢測的輪廓不建立等級關係
                        cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層爲外邊界,
                            裏面的一層爲內孔的邊界信息。
                            如果內孔內還有一個連通物體,這個物體的邊界也在頂層
                        cv2.RETR_TREE建立一個等級樹結構的輪廓
                    3 輪廓的近似辦法
                        cv2.CHAIN_APPROX_NONE存儲所有的輪廓點,
                            相鄰的兩個點的像素位置差不超過1,
                            即max(abs(x1-x2),abs(y2-y1))==1
                        cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,
                            只保留該方向的終點座標,例如一個矩形輪廓只需4個點來保存輪廓信息
                返回值:
                    contours:一個列表,每一項都是一個輪廓, 不會存儲輪廓所有的點,只存儲能描述輪廓的點
                    hierarchy:一個ndarray, 元素數量和輪廓數量一樣, 
                        每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0] ~hierarchy[i][3],
                        分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,則該值爲負數
            """
        # 遍歷輪廓
        for c in contours:
            # 輪廓太小忽略 有可能是斑點噪聲

            if cv2.contourArea(c) < 5000:  # 該爲args["min_area"]
                continue
            # 將輪廓畫出來
            # compute the bounding box for the contour, draw it on the frame,
            # and update the text
            # 計算輪廓的邊界框,在當前幀中畫出該框
            flat = 1  # 設置一個標籤,當有運動的時候爲1
            (x, y, w, h) = cv2.boundingRect(c)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            text = "Moving"
            # 在畫面上顯示運動

        # draw the text and timestamp on the frame
        # 在當前幀上寫文字以及時間戳
        cv2.putText(frame, "Movement State: {}".format(text), (10, 20),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
        cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
                    (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)

        # 顯示當前幀並記錄用戶是否按下按鍵

        cv2.imshow("Thresh", thresh)
        cv2.imshow("Frame Delta", frameDelta)
        cv2.imshow("Security Feed", frame)
        #cv2.imwrite("/home/pi/Desktop/movement_detection/image.jpg", frame)#保存到某個位置,這裏是樹莓派


        if cv2.waitKey(1) & 0xFF == ord('q'):  # 按q保存一張圖片
            # cv2.imwrite("E:\cpy\pictures\\pic.jpg", frame1)
            break

    camera.release()
    cv2.destroyAllWindows()


def qqyouxian(num):#這裏是控制郵箱發送的函數
    yag = yagmail.SMTP(user="*****@qq.com", password="****你的密碼", host="smtp.exmail.qq.com")#這裏應該填入你需要用的郵箱,user=郵箱地址,password=郵箱的密碼,host=郵箱的服務器域名,這裏是qq企業郵
    contents = ["檢測到運動問物體", "/home/pi/Desktop/movement_detection/image.jpg"]#正文部分 隨意,後面的是在樹莓派系統下的抓拍地址,自己可以改一下
    yag.send("[email protected]", "檢測到運動問物體", contents)#目標郵箱
    yag.close()
    
    time.sleep(50)


def main():#設計了多線程並行,郵件發送和機器視覺部分不衝突
    """創建啓動線程"""
    t_sing = threading.Thread(target=shijue)
    t_dance = threading.Thread(target=qqyouxian, args=(6, ))
    t_sing.start()
    t_dance.start()


if __name__ == '__main__':
    main()

6.演示效果

視頻演示:

https://www.bilibili.com/video/BV1e541147dR/

正經:基於樹莓派的家庭安防系統(機器視覺)


移動端演示:


7.總結

時間過得飛快,轉眼就到了畢業的時候了,希望這篇文章可以幫到你。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章