使用OpenCV和Python在视频中应用热力图

6

我希望编写一段代码,能够在视频中检测到运动的地方应用热力图。我已经编写了一个可以通过轮廓检测运动的代码,但我不知道如何制作热力图。

这是我的代码:

import cv2
import numpy as np

# upload video
cap = cv2.VideoCapture('test_video.mp4')

#reading two frames
ret, frame1 = cap.read()
ret, frame2 = cap.read()

while cap.isOpened():

     # get diference between two frames
     diff = cv2.absdiff(frame1, frame2)

     # convert diference in gray
     gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

     # bluring and treshold
     blur = cv2.GaussianBlur(gray, (5,5), 0)
     _, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)

     dilated = cv2.dilate(thresh, None, iterations = 3)

     # define contours
     contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
     
     # draw contours
     cv2.drawContours(frame1, contours, -1, (255,0,0), 1)

     # show frames
     cv2.imshow('frame', frame1)
     frame1 = frame2
     ret, frame2 = cap.read()

     if cv2.waitKey(60) == 60:
          break

cv2.destroyAllWindows()
cap.release()

我看到了这个链接:用Python和OpenCV构建动态热力图视频。我想复制代码,但有很多东西,比如fourccimage_folderimages都没有定义,所以我尝试另一种方法。
你能帮我吗?基本上,我想将热力图应用于有运动的视频。

你在谷歌上搜索过静态图像的示例吗? - fmw42
是的,但这与我的问题有什么关系? - taga
也许我误解了你的问题。有许多热力图可用。您可以使用cv2.applyColorMap应用它。 - fmw42
抱歉,我没有看到链接。那是我专业范围之外的另一件事情。 - fmw42
1
请注意,您所参考的来自towardsdatascience的代码可在github上找到:https://github.com/robertosannazzaro/motion-heatmap-opencv。您可以git clone它,添加一个“frames”文件夹并尝试该代码以查看其工作原理。您还需要pip安装“progress”模块,结果可能与所示不完全相同(可能是opencv版本不匹配),但它(有点)有效。 - S_Bersier
显示剩余4条评论
1个回答

7

这里有一个想法。你知道如何使用opencv循环遍历视频的帧,对吧?那么,在while循环的每个帧中,将接下来的一帧存储在变量中,并比较当前帧和未来帧之间的差异。

通过两个帧之间的差异,您可以检测移动的轮廓。假设我们用绿色在图像上绘制轮廓。

while循环之前定义一个空白数组作为热力图; 每次循环迭代,向热力图的每个坐标添加一定数量,其中该坐标在帧上是绿色的,并从热力图中去除某些数量,其中该坐标在图像上不是绿色的。

  1. 导入必要的模块:
import cv2
import numpy as np
  1. 定义一个函数来处理两帧之间的差异,使其更容易检测轮廓。这些值可以根据您的需求进行调整:
def process(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_blur = cv2.GaussianBlur(img_gray, (5, 5), 25)
    img_canny = cv2.Canny(img_blur, 5, 50)
    kernel = np.ones((3, 3))
    img_dilate = cv2.dilate(img_canny, kernel, iterations=4)
    img_erode = cv2.erode(img_dilate, kernel, iterations=1)
    return img_erode

定义一个函数,该函数将接收两个帧之间差异的处理图像和您想要在其上绘制轮廓的图像。它将返回在图像上绘制轮廓(绿色)后的图像:
def get_contours(img, img_original):
    img_contours = img_original.copy()
    contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    cv2.drawContours(img_contours, contours, -1, (0, 255, 0), -1) 
    return img_contours
  1. 定义捕获设备并在while循环之前读取两个帧,第一个帧是每次迭代while 循环的当前帧,第二个帧是每次迭代while循环的未来帧。同时,为热图定义空白图像heat_map
cap = cv2.VideoCapture("Bar fight security cam.mp4")

success, img1 = cap.read()
success, img2 = cap.read()
heat_map = np.zeros(img1.shape[:-1])
  • while循环中,找到两帧之间的差异,并使用绘制在其上的差异轮廓获取每个循环的当前帧:
  • while success:
        diff = cv2.absdiff(img1, img2)
        img_contours = get_contours(process(diff), img1)
    
    1. 在先前定义的get_contours函数返回的图像上,对于每个绿色区域,将其所在的heat_map坐标全部加上数字3,对于非绿色区域则减去3。为确保不会出现小于0或大于255的值,请将边界应用于热度图:
        heat_map[np.all(img_contours == [0, 255, 0], 2)] += 3
        heat_map[np.any(img_contours != [0, 255, 0], 2)] -= 3
        heat_map[heat_map < 0] = 0
        heat_map[heat_map > 255] = 255
    

    heat_map数组转换为灰度图像,然后再转换为热度图像:
        img_mapped = cv2.applyColorMap(heat_map.astype('uint8'), cv2.COLORMAP_JET)
    

    最后,显示帧并检索下一次迭代的帧:
        cv2.imshow("Original", img1)
        cv2.imshow("Heat Map", img_mapped)
        
        img1 = img2
        success, img2 = cap.read()
        
        if cv2.waitKey(1) == ord('q'):
            break
    

    总之:

    import cv2
    import numpy as np
    
    def process(img):
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img_blur = cv2.GaussianBlur(img_gray, (5, 5), 25)
        img_canny = cv2.Canny(img_blur, 5, 50)
        kernel = np.ones((3, 3))
        img_dilate = cv2.dilate(img_canny, kernel, iterations=4)
        img_erode = cv2.erode(img_dilate, kernel, iterations=1)
        return img_erode
    
    def get_contours(img, img_original):
        img_contours = img_original.copy()
        contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    
        cv2.drawContours(img_contours, contours, -1, (0, 255, 0), -1) 
        # If you want to omit smaller contours, loop through the detected contours, and only draw them on the image if they are at least a specific area. Don't forget to remove the line above if you choose the below block of code.
        # for cnt in contours: 
        #     if cv2.contourArea(cnt) > 500:
        #         cv2.drawContours(img_contours, [cnt], -1, (0, 255, 0), -1) 
    
        return img_contours
    
    cap = cv2.VideoCapture("Bar fight security cam.mp4")
    
    success, img1 = cap.read()
    success, img2 = cap.read()
    heat_map = np.zeros(img1.shape[:-1])
    
    while success:
        diff = cv2.absdiff(img1, img2)
        img_contours = get_contours(process(diff), img1)
    
        heat_map[np.all(img_contours == [0, 255, 0], 2)] += 3 # The 3 can be tweaked depending on how fast you want the colors to respond
        heat_map[np.any(img_contours != [0, 255, 0], 2)] -= 3
        heat_map[heat_map < 0] = 0
        heat_map[heat_map > 255] = 255
    
        img_mapped = cv2.applyColorMap(heat_map.astype('uint8'), cv2.COLORMAP_JET)
    
    #    img1[heat_map > 160] = img_mapped[heat_map > 160] Use this line to draw the heat map on the original video at a specific temperature range. For this it's where ever the temperature is above 160 (min is 0 and max is 255)
    
        cv2.imshow("Original", img1)
        cv2.imshow("Heat Map", img_mapped)
        
        img1 = img2
        success, img2 = cap.read()
        
        if cv2.waitKey(1) == ord('q'):
            break
    

    非常感谢!我有三个问题...您能否解释一下第6部分,为什么是数字3?并且是否可以在原始视频上应用热图(仅使用橙色和红色颜色)? 第三个问题是,是否可以仅在快速移动或大运动时应用热图,即增加运动阈值? - taga
    基本上,我希望只有在战斗开始时才有橙色/红色。 - taga

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