检测连续图像中非/最少变化像素的最快方法

6

我想要找到视频流中静态像素。这样我就可以检测我的视频流上的logo和其他不动的物品。我脚本的想法如下:

  • 在一个名为previous的列表中收集一些等大小和灰度大小的帧。
  • 如果收集了一定数量的帧,则调用函数np.std
  • 该函数循环遍历新图像的所有x-y-坐标
  • 基于所有帧对应坐标的灰度值,计算所有坐标的灰度值的标准差。

我的脚本:

import math
import cv2
import numpy as np


video = cv2.VideoCapture(0)
previous = []
n_of_frames = 200

while True:
   ret, frame = video.read()
   if ret:
      cropped_img = frame[0:150, 0:500]
      gray = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2GRAY)
      if len(previous) == n_of_frames:
         stdev_gray = np.std(previous, axis=2)
         previous = previous[1:]
         previous.append(gray)
      else:
         previous.append(gray)

      cv2.imshow('frame', frame)

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

video.release()
cv2.destroyAllWindows()

这个过程相当缓慢,我很好奇是否有更快的方法来完成。我可以考虑使用Cython等工具。非常感谢您提前的帮助!


如果我假设你以25帧每秒进行捕捉,那么您对200个帧的平均意味着您正在查找8秒内的变化。 如果是这样,您肯定不需要每秒对每一帧进行25次计算吧? 您可能只需获取并分析每10或25张图像吗? 其次,在尝试通过优化加快速度之前,测量您所看到的东西-因此请尝试掌握捕捉本身需要多长时间,然后再分析您自己的分析...然后进行优化。 - Mark Setchell
最快是指什么时间窗口? - Martin
2个回答

5
一种方法是使用cv2.bitwise_and()逐帧比较。其想法是,前一帧中的像素必须存在于当前帧中才能成为非变化像素。通过遍历帧列表,场景中的所有特征必须在前一帧和当前帧中出现,才能被视为非移动项。因此,如果我们按顺序迭代每个帧,则最后一次迭代将具有来自所有先前帧的共享特征。
使用每秒捕获一次的这组帧

enter image description here

我们将每个帧转换为灰度图像,然后cv2.bitwise_and()与前一个帧和当前帧。每个连续迭代的不变像素以灰色突出显示,而变化的像素则为黑色。最后一次迭代应该展示所有帧之间共享的像素。

enter image description here

如果您还对每个帧进行二值化,则会得到更明显的结果。

enter image description here

import cv2
import glob

images = [cv2.imread(image, 0) for image in glob.glob("*.png")]

result = cv2.bitwise_and(images[0], images[1])
for image in images[2:]:
    result = cv2.bitwise_and(result, image)

cv2.imshow('result', result)
cv2.waitKey(0)

你的方法给出了我一直以来所期望和想象中的完美输出!!谢谢。 - HJA24
这种方法无法检测闪烁在白色和黑色之间的背景上的黑色标志。我认为方差/偏差方法更可靠。 - tstanisl
你好 @nathancy,你怎么在视频中使用你的代码迭代连续的视频帧?谢谢。 - user3925023

2

可以从总和和平方和计算方差和标准差。

VAR X = EX^2 - (EX)^2

请参见链接 https://en.wikipedia.org/wiki/Variance#Definition

可以通过添加新图像并减去 n_of_frames 之前捕获的图像来逐步更新总和和平方和。接下来计算方差并取平方根以获得标准偏差。请注意,计算时间不取决于帧数。

请参见代码

import math
import cv2
import numpy as np


video = cv2.VideoCapture(0)
previous = []
n_of_frames = 200

sum_of_frames = 0
sumsq_of_frames = 0

while True:
   ret, frame = video.read()
   if ret:
      cropped_img = frame[0:150, 0:500]
      gray = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2GRAY)
      gray = gray.astype('f4')
      if len(previous) == n_of_frames:
         stdev_gray = np.sqrt(sumsq_of_frames / n_of_frames - np.square(sum_of_frames / n_of_frames))
         cv2.imshow('stdev_gray', stdev_gray * (1/255))
         sum_of_frames -= previous[0]
         sumsq_of_frames -=np.square(previous[0])
         previous.pop(0)
      previous.append(gray)
      sum_of_frames = sum_of_frames + gray
      sumsq_of_frames = sumsq_of_frames + np.square(gray)

      #cv2.imshow('frame', frame)

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

video.release()
cv2.destroyAllWindows()

结果看起来非常棒。

谢谢,我喜欢你的方法。然而,被接受的答案让我能够非常简单地使用cv2.findContours - HJA24
抱歉 @HJA24,您能否分享一下使用 bitwise_and 方法进行迭代的示例代码?谢谢。 - user3925023

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