皆さんお久しぶりです。TMNT(たみねた)と申します。
今回は学校からお借り頂いた、raspi3↓
image

とエレコムのwebカメラ↓
image

を用いてちょっとした画像処理をしてみました。
ライブラリはOpencv3.0以降のもので
開発言語はpython2.0です。
正直、pythonもまだまだ初心者なので色々と手こずりました。
(インデントとか… 間違えてセミコロン打っちゃったりとか…
で、今回は人を検出し通過人数をカウントしてくるソフトを開発したいなぁと思いました。


・人を検出する方法。
人を検出すると言っても色々な検出方法があって
haar-like特徴を用いた顔検出、上半身検出
、下半身検出や…
HOG + SVMを用いた人物検出(人型検出)
などと色々あります。

普通に考えれば今回はカメラの前を通り過ぎる人達を検出出来ればいいのでHOGを使うのが最善と思われるかもしれません。ですがこれにはいくつかのデメリットがあります。
それは、

・遠距離(人がフレーム内に収まる距離)でないと検出されない。

・割と処理が重い。

・検出フレームが余分に大き過ぎる事がある。

以上の3つのデメリットです。

後程、追跡処理についても述べますがあまり検出フレームが余分に大きすぎると人以外の部分も生成されるヒストグラム特徴量に大きな影響を与えてしまい、結果として追跡精度が落ちてしまいます。

一方、haar-like特徴を用いた顔検出の場合は

・正面顔が検出出来れば近距離、遠距離でもok

・処理が軽い

・検出精度が高い

という利点があります。
以上の観点から今回はhaar-like特徴による顔検出を利用します。

しかしこのままでは常に正面顔が写るとは限りませんし、また断続的に検出する場合が多いのでカウント済みの顔のフレーム座標を把握できなくなり、精度低下に繋がりかねません。

このような不具合が発生しないためには、これに合わせて物体追跡処理を加えてやる事が必要です。

今回利用したものはMeanshift法です。

これにより、一度でも顔検出が成功すれば理論上フレーム内での追尾が可能になります。


以下に今回作成したプログラムを置いておきます…

※インデントが消えてしまっているので早いうちになんとかします!

import numpy as np
import cv2
track_window=[None]*10
roi_hist=[None]*10
count=0
check=0
xg_t=[0]*10
yg_t=[0]*10
ch=[0]*10

def detect(img,cascade,minsize):
rects = cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=minsize,flags=cv2.CASCADE_SCALE_IMAGE)
if len(rects) == 0:
return []
rects[:,2:] += rects[:,:2]
return rects
def draw_rects(img, rects, color):
for x1, y1, x2, y2 in rects:
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)

if __name__ == '__main__':
import sys, getopt
print(__doc__)

args, video_src = getopt.getopt(sys.argv[1:], '', ['cascade=', 'nested-cascade='])
try:
video_src = video_src[0]
except:
video_src = 0
args = dict(args)
cascade_fn = args.get('--cascade', "haarcascades/haarcascade_frontalface_alt.xml")
cascade = cv2.CascadeClassifier(cascade_fn)

cap = cv2.VideoCapture(0)
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(True):
ret, frame = cap.read()


if ret == True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.equalizeHist(gray)
rects = detect(gray, cascade,(50,50))
vis = frame.copy()
draw_rects(vis, rects, (0, 255, 0))
for x1, y1, x2, y2 in rects:
xg=(x1+x2)/2
yg=(y1+y2)/2


flag=0
if ((30x2)and(450>y2)and (30 for j in xrange(0,10):
if track_window[j]:
(x,y,w,h)=track_window[j]
if((xx+w)and(450>y+h)and (30 flag=1
track_window[j] = (x1, y1, x2-x1, y2-y1)

roi = frame[y1:y2, x1:x2]

hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

img_mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))

roi_hist[j] = cv2.calcHist([hsv_roi], [0], img_mask, [180], [0,180])

cv2.normalize(roi_hist[j], roi_hist[j], 0, 255, cv2.NORM_MINMAX)
break

if flag!=1:
check+=1
print (check)
for count in xrange(0,10):
if track_window[count]==None:
track_window[count] = (x1, y1, x2-x1, y2-y1)
ch[count]=str(check)+"_HUMAN"
roi = frame[y1:y2, x1:x2]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

img_mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))

roi_hist[count] = cv2.calcHist([hsv_roi], [0], img_mask, [180], [0,180])

cv2.normalize(roi_hist[count], roi_hist[count], 0, 255, cv2.NORM_MINMAX)
break
for i in xrange(0, 10):
if track_window[i]:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

dst = cv2.calcBackProject([hsv],[0],roi_hist[i],[0,180], 1)


ret, track_window[i] = cv2.meanShift(dst, track_window[i], term_crit)

x,y,w,h = track_window[i]
xg_t[i]=(x+x+w)/2
yg_t[i]=(y+y+h)/2
if not ((30x+w)and(450>y+h)and (30 track_window[i]=None

for i2 in xrange(0,10):
if track_window[i2]:
(xl,yl,wl,hl) = track_window[i2]
if(xl track_window[i2]=None

if track_window[i]:
x,y,w,h = track_window[i]
cv2.rectangle(vis, (x,y), (w+x, h+y), 255, 2)
cv2.putText(vis,ch[i],(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255))
cv2.imshow('SHOW MEANSHIFT IMAGE', vis)


k = cv2.waitKey(1)
if k == ord('q'):
break
else:
break





以下のように動きました。

image


緑枠がhaar-like特徴で顔検出したフレームで
青枠がmeanshift法により計算された追跡フレームです。

このように顔検出がされない時でも、、

image


しっかり追跡してくれていますね。

動画も載せて置きます…
https://youtu.be/mNlxeC30llo

問題点は横顔が検出出来ない事です。
公開されているカスケード分類器の中には横顔や上半身、下半身のものもありますが、残念ながらどれも精度がイマイチです…

時間があったら横顔のカスケード分類器作ってみようかなと思います。