使用opencv从侧视图检测马的眨眼

Detecting blinks of a horse from side view with opencv

这是一个理论问题,而不是要求具体的代码问题。

我已经使用 Haar Cascades 完成了一些面部标志检测,但这次我手上有不同类型的视频。这是马眼的侧视图(摄像头安装在头部的一侧),所以基本上我看到的是一只巨大的眼睛。我尝试使用 Haar Cascades,但没用,因为我的视频中没有检测到人脸。

我想知道在这匹马身上检测眼睛和眨眼的最佳方法是什么?我是否尝试自定义 dlib 面部标记检测器?我没有找到太多关于动物地标的信息。

提前致谢! :)

在第一帧围绕眼睛绘制边界框后,我使用对象跟踪器继续定位眼睛。

我创建了一个设置宽度和高度的边界框,因为我们可以粗略地假设眼睛相对于相机没有变大或变小。在为跟踪器绘制边界框时,我们不仅要包括眼睛,否则它会在眨眼时失去对物体的追踪。

我在每帧中寻找边界区域的饱和度是否低于阈值,以检查它们是否眨眼。蓝色框是跟踪器返回的边界框,绿色框是我正在裁剪并检查饱和度的区域。

这是视频过程中的饱和度图表

你可以清楚地看到他们眨眼的区域

这是结果的 gif(经过大量压缩以达到 2mb 的限制)

import cv2
import numpy as np
import math

# tuplifies things for opencv
def tup(p):
    return (int(p[0]), int(p[1]));

# returns the center of the box
def getCenter(box):
    x = box[0];
    y = box[1];
    x += box[2] / 2.0;
    y += box[3] / 2.0;
    return [x,y];

# rescales image by percent
def rescale(img, scale):
    h,w = img.shape[:2];
    h = int(h*scale);
    w = int(w*scale);
    return cv2.resize(img, (w,h));

# load video
cap = cv2.VideoCapture("blinking.mov");
scale = 0.5;

# font stuff
font = cv2.FONT_HERSHEY_SIMPLEX;
org = (50, 50);
fontScale = 1;
font_color = (255, 255, 0);
thickness = 2;

# set up tracker
tracker = cv2.TrackerCSRT_create(); # I'm using OpenCV 3.4
backup = cv2.TrackerCSRT_create();

# grab the first frame
_, frame = cap.read();
frame = rescale(frame, scale);

# init tracker
box = cv2.selectROI(frame, False);
tracker.init(frame, box);
backup.init(frame, box);
cv2.destroyAllWindows();

# set center bounds
width = 75;
height = 60;

# save numbers
file_index = 0;

# blink counter
blinks = 0;
blink_thresh = 35;
blink_trigger = True;

# show video
done = False;
while not done:
    # get frame
    ret, frame = cap.read();
    if not ret:
        break;
    frame = rescale(frame, scale);

    # choose a color space
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV);
    h,s,v = cv2.split(hsv);
    channel = s;

    # grab tracking box
    ret, box = tracker.update(frame);
    if ret:
        # get the center
        center = getCenter(box);
        x, y = center;

        # make box on center
        tl = [x - width, y - height];
        br = [x + width, y + height];
        tl = tup(tl);
        br = tup(br);

        # get top left and bottom right
        p1 = [box[0], box[1]];
        p2 = [p1[0] + box[2], p1[1] + box[3]];
        p1 = tup(p1);
        p2 = tup(p2);

        # draw a roi around the image
        cv2.rectangle(frame, p1, p2, (255,0,0), 3);
        cv2.rectangle(frame, tl, br, (0,255,0), 3);
        cv2.circle(frame, tup(center), 6, (0,0,255), -1);

        # get the channel average in the box
        slc = channel[tl[1]:br[1], tl[0]:br[0]];
        ave = np.mean(slc);

        # if it dips below a set value, then trigger a blink
        if ave < blink_thresh:
            if blink_trigger:
                blinks += 1;
                blink_trigger = False;
        else:
            blink_trigger = True;

        # draw blink count
        frame = cv2.putText(frame, "Blinks: " + str(blinks), org, font, fontScale, 
                            font_color, thickness, cv2.LINE_AA);

    # show
    cv2.imshow("Frame", frame);
    key = cv2.waitKey(1);

    # check keypress
    done = key == ord('q');