检测接近的物体

4

我阅读了this的博客文章,他使用激光和网络摄像头来估算纸板距离网络摄像头的距离。

我有另一个想法。我不想计算从网络摄像头到物体的距离。

我想检查物体是否靠近网络摄像头。根据我的想法,算法将是:

  1. 在网络摄像头中检测物体。
  2. 如果物体靠近网络摄像头,它在视频中会变得越来越大。
  3. 使用这些数据进行进一步的计算。

由于我想检测随机物体,因此我使用findContours()方法在视频中找到轮廓。使用这种方法,我至少可以获得视频中物体的轮廓。源代码如下:

import numpy as np
import cv2

vid=cv2.VideoCapture(0)
ans, instant=vid.read()
average=np.float32(instant)
cv2.accumulateWeighted(instant, average, 0.01)
background=cv2.convertScaleAbs(average)

while(1):
    _,f=vid.read()
    imgray=cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    ret, thresh=cv2.threshold(imgray,127,255,0)
    diff=cv2.absdiff(f, background)
    cv2.imshow("input", f)
    cv2.imshow("Difference", diff)
    if cv2.waitKey(5)==27:
        break
cv2.destroyAllWindows()

输出结果为:

enter image description here

我卡在这里了。我已经将轮廓存储在一个数组中。当大小增加时,我该怎么办?我该如何继续进行?


2
只是为了激发你的好奇心,甚至有一种声学方法可以实现这一点 ;) - berak
2个回答

4
一个问题是需要识别和区分视频中的移动物体和其他内容。一种方法是让相机“学习”没有物体的背景,然后不断将其输入与该背景进行比较。获取背景的一种方法是使用运行平均值。
任何大于小阈值的差异都意味着有移动物体。如果您不断显示这种差异,基本上就有了运动跟踪器。对象的大小只是所有非零(经过阈值处理)像素或它们的边界矩形的总和。您可以跟踪此大小并使用它来猜测对象是否靠近或远离。形态学操作可以帮助将轮廓组合成一个连贯的对象。
由于它将跟踪任何运动,如果有两个对象,它们将被一起计算。在这里,您可以使用轮廓来查找和跟踪单个对象,例如使用轮廓边界或质心。您还可以通过颜色可能将它们分开。
以下是使用此策略的一些结果(灰色斑点是我的手):

My hand moving towards the camera

My hand moving away from the camera

它实际上做得相当不错,猜测我的手移动的方向。

代码:

import cv2
import numpy as np

AVERAGE_ALPHA      = 0.2         # 0-1 where 0 never adapts, and 1 instantly adapts
MOVEMENT_THRESHOLD = 30          # Lower values pick up more movement
REDUCED_SIZE       = (400, 600)
MORPH_KERNEL       = np.ones((10, 10), np.uint8)

def reduce_image(input_image):
    """Make the image easier to deal with."""
    reduced = cv2.resize(input_image, REDUCED_SIZE) 
    reduced = cv2.cvtColor(reduced, cv2.COLOR_BGR2GRAY)
    return reduced

# Initialise
vid = cv2.VideoCapture(0)
average = None

old_sizes = np.zeros(20)
size_update_index = 0

while (True):
    got_frame, frame = vid.read()

    if got_frame:
        # Reduce image
        reduced = reduce_image(frame)
        if average is None: average = np.float32(reduced)

        # Get background
        cv2.accumulateWeighted(reduced, average, AVERAGE_ALPHA) 
        background = cv2.convertScaleAbs(average)

        # Get thresholded difference image
        movement     = cv2.absdiff(reduced, background)
        _, threshold = cv2.threshold(movement, MOVEMENT_THRESHOLD, 255, cv2.THRESH_BINARY)

        # Apply morphology to help find object
        dilated = cv2.dilate(threshold, MORPH_KERNEL, iterations=10)
        closed  = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, MORPH_KERNEL)

        # Get contours
        contours, _ = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        cv2.drawContours(closed, contours, -1, (150, 150, 150), -1)

        # Find biggest bounding rectangle
        areas = [cv2.contourArea(c) for c in contours]
        if (areas != list()):
            max_index = np.argmax(areas)
            max_cont  = contours[max_index]

            x, y, w, h = cv2.boundingRect(max_cont)
            cv2.rectangle(closed, (x, y), (x+w, y+h), (255, 255, 255), 5)

            # Guess movement direction
            size = w*h
            if size > old_sizes.mean():
                print "Towards"
            else:
                print "Away"

            # Update object size 
            old_sizes[size_update_index] = size
            size_update_index += 1
            if (size_update_index) >= len(old_sizes): size_update_index = 0

        # Display image
        cv2.imshow('RaptorVision', closed)

显然,在识别、选择和跟踪对象方面还需要更多的工作(如果背景中有其他移动物体,目前的效果非常糟糕)。还有许多参数可以变化和调整(设置的参数适用于我的系统)。不过这些都由你来决定。
一些链接: 背景提取 运动跟踪 如果您想在背景去除方面更加高科技,请看这里: Wallflower

PS 我原本打算让背景定期更新,所以它应该在你的循环内部。这个想法是任何静态的东西都会逐渐消失。 - Pokey McPokerson
在循环中计算只会给我一个黑色的图像,什么都没有。虽然最初它显示了差异,但程序确实需要更新背景图像。 - praxmon
你完全复制了我的代码吗?抱歉我不知道,这对我起作用... 尝试调整参数... - Pokey McPokerson
是的,我刚刚复制并粘贴了它。我会尝试这样做。 - praxmon
没有,我的系统改变了。我会重新安装并再次尝试。 - praxmon
显示剩余11条评论

1
Detect the object in the webcam feed.
If the object is approaching the webcam it'll grow larger and larger in the video feed.
Use this data for further calculations.

好主意。 如果您想使用轮廓检测方法,可以按照以下方式进行:

  1. 您有一系列图像 I1、I2、... In
  2. 对每个图像进行轮廓检测。C1、C2、...、Cn(轮廓是 OpenCV 中的一组点)
  3. 在图像 i 和 i+1 上取足够大的采样:S_i \leq C_i,i \in 1...n
  4. 检查采样中所有点在 i+1 上的最近点。然后为所有点生成轨迹。
  5. 检查这些轨迹的点是否大多向外(有点棘手;)
  6. 如果它们在足够多帧中呈现向外的状态,则您的轮廓变大了。

或者,您可以尝试修剪不属于正确轮廓的点,并使用覆盖矩形进行操作。这种方式很容易检查大小,但我不知道选择“正确”点有多容易。


  1. 确实有些棘手!一个问题是如果对象比背景大得多。在这种情况下,轮廓可能会围绕着一块背景形成,随着对象变大而缩小。
- Pokey McPokerson
@PokeyMcPokerson:如果一个轮廓接触到了边界的30%-60%,你可以将其反转。 - Sebastian Schmitz
如果对象不适合屏幕怎么办?那么它也会触及边界。 - Pokey McPokerson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接