體驗了將近3年特別而難得的經驗後,如今又站在新的分岔路上煩惱了。
也許人生就是由希望與絕望交織而成的交響詩篇吧XD
無論發生什麼事,能有機緣體驗一切走一遭,也許是件好事呢
不過照這個話題再寫下去就要變落落長廢話大叔了,總之下個小結吧
小結:小弟待業中 乾....
前情提要分隔線
小弟家裡一直有幾位老住戶,住得很舒服很愜意。
牠們的名字叫做波波↓
(圖一) 小鼠波波@廚房
Key錯圖了,迪●尼請不要吉小弟(´・ω・`)
小鼠波波相關介紹:https://mamilove.com.tw/groupbuy/1245
小鼠波波相關介紹:https://mamilove.com.tw/groupbuy/1245
為了悉心養育小鼠波波,小弟家裡背後付出了極大的代價...
- 原本骯髒不堪雜物滿滿的老家,成為波波愛的小窩和遊樂場(捉迷藏?)
- 家父三不五時會使用的廚房成為波波的美食天堂,波波也用滿滿的油腳印跟米田共回饋給家父(會不會有鼠疫@@)
- 舍妹的吶喊(所以搬走了)
- 放一堆黏鼠板希望亂槍打鳥,導致老家室內景觀無法直視(還會踩到黏鼠板阿乾~~)
《孫子兵法》 孫子曰:「知己知彼,百戰不殆;不知彼而知己,一勝一負;不知彼,不知己,每戰必殆。」 |
雖身為待業廢宅,小弟仍然保持一顆熱血而向上的心。回到老家看到問題,這裡好歹是從小成長的地方,總還是要想想辦法解決啊!所以總之先來研究一下小鼠波波的生態、活動路徑、活躍時間、了解波波性格等等。剛好小弟手邊也有監視器,就先從波波覓食處 【廚房】 先偵測起嘍~
因為近年的家用監視器\攝影機幾乎都有內建移動物體偵測,把偵測靈敏度調最高,監視器就會按設定時段、條件等等自動錄下物體移動的片段。
結果也著實讓人欣慰(如圖一),真的錄到了豪開心啊~!
捕捉畫面成功後,下一步要了解波波的移動路徑、活動範圍、時間等等。剛好家裡有第2台監視器(到底為啥要買這麼多監視器啊),就放在廚房鄰接的 【客廳】 嘍~為了捕捉波波移動路徑,小弟把監視器放在廣角可以拍到整個客廳的位置,設置完後放置一晚期待結果。但是……
(圖二) 希望捕捉小鼠波波動態的監視器視角
一.個.鬼.影.都.沒.拍.到
※沒可能的!?已經設定偵測靈敏度最高還拍不到動靜!?※
原來……因為監視器是有偵測到物體移動才錄影,而波波太小隻偵測不到,既然偵測不到就不會把畫面錄下來了RRRRRRRRRR(崩潰)
這樣一來,如果小弟要把當時波波的畫面也錄下來,就要全時段不間斷錄像,然後錄完就要像大樓管理員一樣盯著監視器畫面不放、看有沒有閒雜人等進出。這樣小弟每天看監視器畫面就飽了啊!(/‵Д′)/~ ╧╧ (再崩潰)
正文分隔線
既然監視器內建的移動物體偵測不敷小弟使用,那不如就自己來搞個偵測更靈敏的版本嘍~小弟最初構想是:用監視器連續錄製特定時段(例如PM6:00~AM7:00),然後再用相關的影片分析軟體來抓畫面的極微小變化。
本來也有研究Tracker,但發現不合用;而且也找不到其他相關的套裝分析軟體。後來稍微上網查了一下,發現最近G. T. Wang大的部落格有寫個很不錯的方法:基於Python掛載OpenCV,可以做為監視器影片偵測、也可應用在雲端監視器實時分析,後續也可以再多加想要的分析等等,彈性度高+可再擴充功能的優點,真的蠻不錯的。詳見連結[1][2]。
資料來源:
[1] Python 與 OpenCV 實作移動偵測程式教學,打造智慧型監視器
https://blog.gtwang.org/programming/opencv-motion-detection-and-tracking-tutorial/
[2] OpenCV 擷取網路攝影機串流影像,處理並寫入影片檔案教學
https://blog.gtwang.org/programming/opencv-webcam-video-capture-and-file-write-tutorial/
然而因為小弟愚笨,也沒寫過Python,光一開始就遇到不知怎麼安裝Python到掛載OpenCV、以及選用合適的Python IDE(整合開發環境)……還好網路資源豐富,隨手一撈就是巨量資源阿哈哈哈~(迷之聲:要認真學似乎還是買書打底為上,google為輔,千萬別亂腦補阿…QQ)。
靠著google大神無比神奇的力量,總算搞定安裝的問題了,安裝方法請參見連結[3][4]。另外小弟最後主要先安裝的IDE是選PyCharm,因為包山包海我洗翻~安裝方法請參見連結[5];至於其他不錯的IDE如Sublime, Vim, Spyder....等等,各自都有獨到之處跟適合的應用背景(ex:簡潔快速\網頁用\數值分析等等),不過小弟也完全沒試過,也沒辦法做啥評論嘍~覺得哪位夫人比較適合自己,可以多看其他網友評論多多了解一下~
安裝方法:
安裝方法:
[4] 最简单方法:windows平台下python安装opencv,即实现import cv2功能
https://blog.csdn.net/lyj_viviani/article/details/59482602
[5] Pycharm及python安裝詳細教程
https://read01.com/zh-tw/EKnxLJ.html#.WsoUype-nb0
經過幾天的努力後,總算搞出一個可以跑的py程式拉~~猴開桑~~(ノ>ω<)ノ
(圖三) 執行Python程式碼下的樣貌
這邊小弟特別說明一下程式中有import(匯入)的幾個Lib(函式庫):
- 從G. T. Wang大的範本來看,程序至少要匯入"cv2", "numpy"這兩種函式庫,目的是要匯入OpenCV
以進一步對影片做處理,例如存儲、模糊化、找區域、上繪圖線…等等(詳見底下"使用迴圈分析所有影片"部分程式碼) - 因為小弟有一堆監視器錄製的影片要分析(至少幾百個以上),只能用些方法列出要分析的影片清單拉。
方法也是上網搜索到的,至少要匯入"os"函式庫(詳見底下"搜尋影片檔案"部分程式碼) - "List"(序列)是因為裡面的程式碼需用到需要儲存一堆檔名跟路徑在videoFile、videoFN等變數裡,
PyCharm就自己偵測到語法問題並在前面自動加入import嘍 - 為了讓程式在print(印出字串)時,在某些特定階段能有些許時間暫停,所以小弟多匯入"time"函式庫。
但……………這功能其實完全可以拿掉இдஇ(爆)
實際使用上,只要把要分析的影片檔丟到 "E:\!video!\input" 底下(可以自行修改程式碼)再跑程式後,就會自動把分析並縮減時間的所有影片存到 "E:\!video!\output" 嘍~偵測的靈敏度、要忽略的監視器畫面中時間數字部分等等,可以在"忽略太小的區域"、"忽略監視器時間區域"部分的程式碼做修改歐~
除了OpenCV分析影片以外,小弟主要還用到了一些函式跟程式寫法,詳見連結[6][7][8]。
資料來源:
[6] 一小時Python入門-part 1
https://hellolynn.hpd.io/2017/01/18/%E4%B8%80%E5%B0%8F%E6%99%82python%E5%85%A5%E9%96%80-part-1/
[7] 在Python中列出目录中的所有文件
https://taizilongxu.gitbooks.io/stackoverflow-about-python/content/39/README.html
[8] python字符串字串查找 find和index方法
http://outofmemory.cn/code-snippet/6682/python-string-find-or-index
以下是小弟拼湊出來的拼裝程式碼拉…
話說小弟完全是大外行,編程習慣也不太好,所以有什麼可以改進的地方還請各位先進多多賜教嘍(,,・ω・,,)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | # 匯入OpenCV, Numpy...等功能 from typing import List import cv2 import numpy as np import os from os import listdir from os.path import isfile, join import time # 搜尋影片檔案 print('讀取相關影片檔:') loadpath: str = 'E:\!video!\input' # 影片來源資料夾 loadfnext: List[str] = ['.av', '.mp4', '.avi'] # 要過濾的影片副檔名,只要OpenCV支援的可以自己加 onlyfiles = [f for f in listdir(loadpath) if isfile(join(loadpath, f))] # 只列表文件,把子目錄濾掉 videoFile: List[str] = [] videoFN: List[str] = [] for i in range(0, len(onlyfiles)): # 從列表文件篩選影片副檔名,選出影片檔 for j in range(0, len(loadfnext)): if onlyfiles[i].rfind(loadfnext[j]) != -1: videoFile.append(loadpath + '\\' + onlyfiles[i]) videoFN.append(onlyfiles[i].replace(loadfnext[j], '')) if len(videoFile) == 0: # 若找不到任何影片檔,就結束程式 print('找不到任何影片,結束程序...') quit() for k in range(0, len(videoFile)): # 顯示找到的影片檔 print(videoFile[k]) print('共找到', len(videoFile), '個影片') # 設定輸出目錄 savepath: str = 'E:\!video!\output' # 影片輸出資料夾 if not os.path.exists(savepath): # 自動建立目錄 os.makedirs(savepath) # 使用迴圈分析所有影片 time.sleep(1) print('\n\n==============================================\n') print('開始分析影片....\n') for i in range(0, len(videoFile)): # 解析影片 cap = cv2.VideoCapture(videoFile[i]) # 開啟影片檔 width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 取得畫面尺寸-寬 height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 取得畫面尺寸-高 print('序列: ', i, '\n影片位址: ', videoFile[i], '\n影片寬: ', int(width), '\n影片高: ', int(height), sep='') fourcc = cv2.VideoWriter_fourcc(*'XVID') # 使用 XVID 編碼 out = cv2.VideoWriter(savepath + '\\' + videoFN[i] + '.avi', fourcc, 16, (int(width), int(height))) # 建VideoWriter物件並輸出影片 area = width * height # 計算畫面面積 ret, frame = cap.read() # 初始化平均畫面 blrlen = 2 # 模糊處理長寬 mrphlen = 2 # 去除雜訊處理長寬 avg = cv2.blur(frame, (blrlen, blrlen)) # 平均畫面模糊處理 avg_float = np.float32(avg) # 平均畫面轉浮點數 frameCnt = 0 # 影片畫格計數器 while cap.isOpened(): ret, frame = cap.read() # 讀取一幅影格 frameCnt = frameCnt + 1 if not ret: # 若讀取至影片結尾,則跳出 break blur = cv2.blur(frame, (blrlen, blrlen)) # 模糊處理 # cv2.imshow('blur', blur) # 解除註解可看畫面 diff = cv2.absdiff(avg, blur) # 計算目前影格與平均影像的差異值 gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # 將圖片轉為灰階 # cv2.imshow('gray', gray) # 解除註解可看畫面 ret, thresh = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY) # 篩選出變動程度大於門檻值的區域 # cv2.imshow('thresh', thresh) # 解除註解可看畫面 kernel = np.ones((mrphlen, mrphlen), np.uint8) # 使用型態轉換函數去除雜訊 thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2) # cv2.imshow('thresh', thresh) # 解除註解可看畫面 cntImg, cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 產生等高線 movecatch = [0, False] for c in cnts: (x, y, w, h) = cv2.boundingRect(c) # 計算等高線的外框範圍 if cv2.contourArea(c) < 30: # 忽略太小的區域 continue elif x + w < 500 and y + h < 55: # 忽略監視器時間區域,米家(470,50),海爾(500,55) continue else: movecatch[0] = movecatch[0] + 1 movecatch[1] = True cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 1) # 篩選出的區域畫出方形外框 cv2.drawContours(frame, cnts, -1, (255, 0, 255), 1) # 畫出等高線(除錯用) # if frameCnt % 5 == 1: # 解除註解可看畫面 # cv2.imshow('frame', frame) # 顯示偵測結果影像 if frameCnt % 500 == 0: print('>> 目前分析到影格[', frameCnt, '] <<', sep='') if movecatch[1]: # 秀出該影格抓到的動態區域數 print('影格=', frameCnt, ', 區塊=', movecatch[0], sep='') out.write(frame) # 若有動靜則寫入影格 if cv2.waitKey(1) & 0xFF == ord('q'): # 若顯示過程鍵盤按下"q"鍵則終止while迴圈 break cv2.accumulateWeighted(blur, avg_float, 0.1) # 更新平均影像 avg = cv2.convertScaleAbs(avg_float) # cv2.imshow('avg', avg) # 解除註解可看畫面 print('\n') time.sleep(0.1) cap.release() # 釋放所有讀取中影片 cv2.destroyAllWindows() # 關閉所有預覽畫面 print('\n\n==============================================\n') print('影片分析完成!\n') |
(動畫一) 分析影片去除不需要的影格後結果展現
最後讓人流淚的結果如以上影片(只是冰山一角阿@@)。
看來,波波們活得挺好的,每天幾乎都有牠們的蹤跡。
但,這只是一個開端而已……小弟跟小鼠波波們之間的戰爭與愛恨情仇,還要繼續演下去…………
◢▆▅▄▃崩╰(〒皿〒)╯潰▃▄▅▇◣
4 則留言:
版主是用哪個型號的攝影機?
小弟我用的是海爾無線監控攝像頭一體機1080p(沒有英文型號),之前在大陸工作時買的
順道一提,不管是用哪種型號的攝影機,都不會影響程序的執行喔~
版主~請問要怎麼把攝影機跟python結合...攝影機接上電源跟USB線後就好嗎?
(這邊只LIVE時候)
這個功能小弟還沒有玩過,不過要live實時偵測除了攝影機接PC,在程式碼上也要做修改喔
例如程式碼最開頭要加上cap = cv2.VideoCapture(1)跟迴圈偵測畫面的程序
詳情可以參考以下這篇網誌:
https://blog.gtwang.org/programming/opencv-webcam-video-capture-and-file-write-tutorial/
張貼留言