程序作品(7)——家庭監(jiān)控系統(tǒng)

籌劃
在我家的后院中,經常會有諸如鳥、兔子一類的小動物出沒(據鄰居說還會有狐貍)。但是這些小動物出現的時間一般非常短,往窗外看的時候正好看到它們的幾率非常小,用相機記錄下來更是非常困難。
因此,我打算制作一個家庭監(jiān)控系統(tǒng)來監(jiān)控后院中出現的小動物,這個監(jiān)控系統(tǒng)應當周期性記錄后院的畫面,并且可以作為服務器將記錄下的圖片按照用戶的需求發(fā)送到他的電腦上,在收集到一定數量的圖片后還可以快速篩出可能含有小動物的圖片。
1. 周期性拍攝圖片并響應客戶端的請求
1.1?周期性拍攝圖片
想要長期監(jiān)控后院中的情景,我就必須讓一臺能夠拍攝圖片的計算機一直在窗邊保持開機并運行我寫的拍攝和服務器程序。此外,為了控制這個項目的成本,我必須選用家中的閑置設備,而且這臺設備的能耗不能太高。
這里我選用樹莓派4b單片機。這臺單片機搭載了基于Linux系統(tǒng)的Raspberry Pi OS,可以運行python程序,可以外接攝像頭,并且它的能耗也相對較低。
在軟件方面,控制計算機拍照時我選用了python的picamera庫。這個庫可以調用樹莓派自帶的攝像頭來拍攝和存儲照片,并且可以將攝像頭的畫面實時顯示在屏幕上以供預覽。定時拍照的程序非常容易實現,這里不過多贅述。
1.2?作為服務器響應客戶機請求
雖然我之前寫過了很多服務器有關的程序,但是這個項目對服務器的需求和以前的項目有些許不一樣。主要有兩個不一樣的地方:
1.2.1 傳輸大文件
在剛剛接觸網絡通信的時候,我就曾嘗試使用最基本的socket語句傳輸一個圖片。但是每次都只能接收到這個圖片開頭的一個kb,后面的信息就在傳輸的過程中“丟失”了。后來在寫坦克動蕩的重制版的時候,我才知道了粘包問題,也知道了必須接受多次才能完整接受到發(fā)出的信息。但是對于這個項目,坦克動蕩中的收發(fā)函數還需要做一些改動。
在坦克動蕩重制版中,我只考慮了粘包問題中的一次不能完整接受一條信息的情況。但是實際上,當發(fā)送的文件足夠大的時候,還有可能出現一個信息被切成多個小段發(fā)送的情況。在這種情況下,我們就需要使用循環(huán)來保證程序完整地發(fā)送了信息。具體步驟如下:
?將要發(fā)送的信息存在一個二進制字符串里,并用socket的send函數發(fā)送
send函數會返回一個值,這個值表示待發(fā)送的字符串實際發(fā)送出的長度。為了防止由于文件過大而沒有完整發(fā)送文件,我們需要判斷這個返回值是否等于字符串的長度,如果不等于,則需要接著發(fā)送沒有發(fā)完的部分。
1.2.2 Linux系統(tǒng)下的多線程
由于一個服務器往往要同時服務多臺客戶機,而且一臺客戶機所造成的報錯(如客戶機突然斷開連接等)不能影響到其它客戶機,因此在服務器中使用多線程是非常重要的。
在Windows系統(tǒng)下,多線程支持往待運行的函數中傳入各種參數,其中包括socket庫用于表示客戶機連接的參數。在這種情況下,我們可以直接在主程序中循環(huán)等待客戶機連接,一旦接收到一個連接,就用多線程運行一個處理客戶機連接的函數,并把客戶機的連接信息傳進去。
但是由于某些原因,Linux系統(tǒng)的多線程不能傳入socket表示客戶機連接的參數。這就導致上述方法不能在樹莓派上使用。在詢問了chatGPT進行了大量的信息搜集后,我采用了如下辦法:
python中有一個叫做queue的庫,可以創(chuàng)建可被多個線程訪問的隊列
在主程序里創(chuàng)建一個空隊列用于在線程之間傳遞信息,并定義如下函數:
accept():用于接受客戶機的連接并將連接信息存到隊列中;
handle_client():這個函數開始的時候不斷檢測隊列中是否有元素,一旦檢測到隊列中有元素(即有客戶機被連接),就將該客戶機從隊列中推出來由自己處理,并開一個新的線程將自己再運行一遍
在啟動服務器程序的時候,先開兩個線程分別運行accept和handle_client函數,然后在主程序中控制拍照。
這樣一來我們不僅將服務器程序和拍照程序結合到了一個程序中,也完美解決了Linux系統(tǒng)多線程不支持傳遞連接信息參數的問題。
2.?篩查圖片
在圖片識別這方面,python有非常多的第三方庫可以很快做到這一點,但是老是調用第三方庫太沒意思了,因此我自己寫了一個非常簡陋的篩查圖片中的小動物的程序。這個程序的思路大致是這樣的:
由于我的監(jiān)控系統(tǒng)拍攝照片的間隔時間非常短(15秒一張),因此相鄰兩張圖片的差別不會特別大。利用這個特點,我可以對比相鄰兩張圖片,如果它們之間有一個區(qū)域差別非常大,那么就可以初步確定這個區(qū)域有東西出現。
當然,我的篩查程序還沒有經過訓練,一些重要的參數,如區(qū)域的大小、區(qū)域之間差別的大小等還需要調整。
除了識別的準確度以外,識別的速度也是一個非常重要的指標。在第一次對后院進行拍攝的時候,我將拍攝程序運行了兩三天,就得到了超過一萬兩千張照片。如果篩查程序的效率不高,那么識別這一萬多張照片所需要的時間將會非常多。
鑒于python底下的運行效率,我本來計劃用C++寫識別程序,但是我對C++的文件操作還不是很熟悉,不能夠在一個程序中讀取多個文件。因此被迫轉用python。
但是在詢問了chatGPT查詢了大量資料之后,我了解到了numpy庫。這個庫可以利用C語言對python程序中的特定簡單運算進行加速,而且經過嘗試,效果非常好。
最終,我將拍到的前七千張照片輸入篩查程序進行篩查,這個程序篩查出了17張有小動物的圖片中的11張,準確率還有進步的空間。
一些展望&flag
很明顯,這個項目的潛力是非常大的。篩查程序的精度可以提高是一方面,另一方面我可以將這個程序做成apk的格式放到安卓手機上運行,甚至上傳到應用市場上,這樣家中的閑置手機就可以被用作監(jiān)控,發(fā)揮它們的作用。但是由于apk格式和安卓系統(tǒng)的限制,python中只有很少數庫可以被打包成apk格式。因此想要登錄安卓系統(tǒng),我必須要學習非常多的新的庫的使用方法,并且更深入地了解安卓系統(tǒng)。這是一個不小的挑戰(zhàn)。
一些拍到的小動物




完結撒花?。╞ushi
注:
封面的圖不是用樹莓派拍的
我已經將這個項目的所有源代碼上傳到百度網盤,點擊“閱讀原文”即可下載。鏈接:https://pan.baidu.com/s/16I5r3pi7kNqTaM-vkkRzzQ?pwd=ejv1提取碼:ejv1
這個項目拍到的前七千張照片已經被我做成延時視頻上傳到b站Jerrycjk賬號下。感興趣的讀者可以去觀看。