在移动摄像机中检测移动物体(监视安装在无人机上的一个区域)

Kir*_*ari 0 opencv python-3.x opencv3.0

def run(self):
    while True:
        _ret, frame = self.cam.read()
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        vis = frame.copy()

        if len(self.tracks) > 0:
            img0, img1 = self.prev_gray, frame_gray
            p0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)
            p1, _st, _err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
            p0r, _st, _err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
            d = abs(p0-p0r).reshape(-1, 2).max(-1)
            good = d < 1
            new_tracks = []
            for i in range(len(p1)):
                A.append(math.sqrt((p1[i][0][0])**2 + (p1[i][0][1])**2))
            counts,bins,bars = plt.hist(A)

            for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):
                if not good_flag:
                    continue
                tr.append((x, y))
                if len(tr) > self.track_len:
                    del tr[0]
                new_tracks.append(tr)
                cv2.circle(vis, (x, y), 2, (0, 255, 0), -1)
            self.tracks = new_tracks
            cv2.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))
            draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))

        if self.frame_idx % self.detect_interval == 0:
            mask = np.zeros_like(frame_gray)
            mask[:] = 255
            for x, y in [np.int32(tr[-1]) for tr in self.tracks]:
                cv2.circle(mask, (x, y), 5, 0, -1)
            p = cv2.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params)
            if p is not None:
                for x, y in np.float32(p).reshape(-1, 2):
                    self.tracks.append([(x, y)])


        self.frame_idx += 1
        self.prev_gray = frame_gray
        cv2.imshow('lk_track', vis)

        ch = cv2.waitKey(1)
        if ch == 27:
            break
Run Code Online (Sandbox Code Playgroud)

我正在使用来自opencv样本的lk_track.py尝试检测移动物体。我正在尝试使用光流矢量大小的直方图来查找相机运动,然后计算相似值的平均值,该平均值应与相机运动成正比。我已经计算出向量的幅度并将其保存在列表A中。有人可以建议如何从中找到最高相似值,并仅计算那些值的平均值吗?

alk*_*asm 5

我创建了一个玩具问题来模拟通过光流对图像进行二值化处理的方法。这是对该问题的简化视图,但总体思路很好。我将把问题分成几个部分,并为它们提供功能。如果您直接使用视频,那么当然会需要很多其他代码,而我只是硬编码了很多需要转换为参数的值。

第一个功能仅用于生成图像序列。图像在场景中移动,并且对象在序列中移动。图像序列只是简单地平移场景,对象在序列中显得静止不动,但这意味着对象实际上实际上是在与相机相反的方向上移动。

import numpy as np
import cv2


def gen_seq():
    """Generate motion sequence with an object"""

    scene = cv2.GaussianBlur(np.uint8(255*np.random.rand(400, 500)), (21, 21), 3)

    h, w = 400, 400
    step = 4
    obj_mask = np.zeros((h, w), np.bool)
    obj_h, obj_w = 50, 50
    obj_x, obj_y = 175, 175
    obj_mask[obj_y:obj_y+obj_h, obj_x:obj_x+obj_w] = True
    obj_data = np.uint8(255*np.random.rand(obj_h, obj_w)).ravel()
    imgs = []
    for i in range(0, 1+w//step, step):
        img = scene[:, i:i+w].copy()
        img[obj_mask] = obj_data
        imgs.append(img)

    return imgs

# generate image sequence
imgs = gen_seq()

# display images
for img in imgs:
    cv2.imshow('Image', img)
    k = cv2.waitKey(100) & 0xFF
    if k == ord('q'):
        break
cv2.destroyWindow('Image')
Run Code Online (Sandbox Code Playgroud)

因此,这是可视化的基本图像序列。我只是使用了一个随机场景,进行了平移,并在中间添加了一个随机对象。

与对象生成的图像序列

大!现在我们需要计算每帧之间的流量。我在这里使用了密集流,但是对于实际图像,稀疏流会更健壮。

def find_flows(imgs):
    """Finds the dense optical flows"""

    optflow_params = [0.5, 3, 15, 3, 5, 1.2, 0]
    prev = imgs[0]
    flows = []
    for img in imgs[1:]:
        flow = cv2.calcOpticalFlowFarneback(prev, img, None, *optflow_params)
        flows.append(flow)
        prev = img

    return flows

# find optical flows between images
flows = find_flows(imgs)

# display flows
h, w = imgs[0].shape[:2]
hsv = np.zeros((h, w, 3), dtype=np.uint8)
hsv[..., 1] = 255

for flow in flows:
    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    hsv[..., 0] = ang*180/np.pi/2
    hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    cv2.imshow('Flow', rgb)
    k = cv2.waitKey(100) & 0xFF
    if k == ord('q'):
        break
cv2.destroyWindow('Flow')
Run Code Online (Sandbox Code Playgroud)

在这里,我根据流的角度和大小为流着色。角度将确定颜色,而大小将确定颜色的强度/亮度。这与OpenCV教程关于密集光流使用的观点相同。

光流

然后,我们需要对该流进行二值化处理,以便根据像素的移动方式获得两组不同的像素。在稀疏情况下,效果相同,只是您将获得两组不同的功能

def label_flows(flows):
    """Binarizes the flows by direction and magnitude"""

    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    flags = cv2.KMEANS_RANDOM_CENTERS
    h, w = flows[0].shape[:2]

    labeled_flows = []
    for flow in flows:
        flow = flow.reshape(h*w, -1)
        comp, labels, centers = cv2.kmeans(flow, 2, None, criteria, 10, flags)
        n = np.sum(labels == 1)
        camera_motion_label = np.argmax([labels.size-n, n])
        labeled = np.uint8(255*(labels.reshape(h, w) == camera_motion_label))
        labeled_flows.append(labeled)
    return labeled_flows

# binarize the flows
labeled_flows = label_flows(flows)

# display binarized flows
for labeled_flow in labeled_flows:
    cv2.imshow('Labeled Flow', labeled_flow)
    k = cv2.waitKey(100) & 0xFF
    if k == ord('q'):
        break
cv2.destroyWindow('Labeled Flow')
Run Code Online (Sandbox Code Playgroud)

这里令人烦恼的是,标签将被随机设置,即,每个帧的标签将不同。如果可视化二进制图像,它将在黑白之间随机切换。我只使用二进制标签,0和1,所以我做了什么被认为是分配给更多的像素是“摄像机运动标签”,然后我设定的标签标签是白中产生的图像,以及其他标签为黑色,则摄像机运动标签在每帧中始终相同。为了处理视频Feed,这可能需要更加复杂。

二值流

但是在这里,我们有了一个二元化的流,其中的颜色只是显示了两个不同的流向量集。

现在,如果要在此流程中找到目标,我们可以反转图像并找到二进制图像的连接组件。反转会使摄像机运动成为背景标签(0)。然后,每个黑色斑点将变为白色并被标记,我们可以找到与最大成分有关的斑点,在这种情况下,最大成分将成为目标。这将在目标周围提供一个遮罩,我们可以在原始图像上绘制该遮罩的轮廓以查看被检测到的目标。在找到连接的组件之前,我还将剪裁图像的边框,以便忽略密集流的边缘效果。

def find_target_in_labeled_flow(labeled_flow):

    labeled_flow = cv2.bitwise_not(labeled_flow)
    bw = 10
    h, w = labeled_flow.shape[:2]
    border_cut = labeled_flow[bw:h-bw, bw:w-bw]
    conncomp, stats = cv2.connectedComponentsWithStats(border_cut, connectivity=8)[1:3]
    target_label = np.argmax(stats[1:, cv2.CC_STAT_AREA]) + 1
    img = np.zeros_like(labeled_flow)
    img[bw:h-bw, bw:w-bw] = 255*(conncomp == target_label)
    return img

for labeled_flow, img in zip(labeled_flows, imgs[:-1]):
    target_mask = find_target_in_labeled_flow(labeled_flow)
    display_img = cv2.merge([img, img, img])
    contours = cv2.findContours(target_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[1]
    display_img = cv2.drawContours(display_img, contours, -1, (0, 255, 0), 2)

    cv2.imshow('Detected Target', display_img)
    k = cv2.waitKey(100) & 0xFF
    if k == ord('q'):
        break
Run Code Online (Sandbox Code Playgroud)

当然,这可能会得到一些清理,而对于稀疏流,您将不会完全这样做。您可以只在跟踪点周围定义一个感兴趣的区域。

检测目标

现在,仍有许多工作要做。您的流量是二值化的...您可能会假设最经常出现的标签是相机运动(就像我一样)是安全的。但是,您必须确保另一个标签是您要跟踪的对象。您必须在流之间跟踪它,以便在它停止移动时知道相机在移动中的位置。当执行k -means步骤时,您将需要确保k -means 的中心相距“足够远”,以便您知道对象是否在移动。

从视频的起始帧开始,基本步骤是:

  1. 如果两个中心“闭合”,则可以假定您的对象不在场景中或不在场景中移动。
  2. 一旦中心足够分开,您将找到要跟踪的对象。跟踪对象的位置。
  3. 在跟踪对象期间,请验证位置是否在预测附近。您可以使用前一帧的光流速矢量来预测新帧中每个像素/特征的位置,因此请确保您的预测与跟踪结果一致。
  4. 如果对象停止移动,则k均值的中心应接近。跟踪对象位置周围的光流矢量,并跟踪它们,以预测对象一旦重新移动就再次位于何处,并再次使用此预测验证检测到的位置。

我以前从未使用过这些方法,因此不确定它们是否强大。HOOF或“定向光流直方图”的典型方法比这要先进得多(请参阅此处的重要文章)。不仅仅是二进制化,其思想是使用每帧的直方图作为概率分布,并且可以使用时间序列分析工具来分析这种概率分布随时间变化的方式,我认为这为该方法提供了更强大的框架。