Scanning directory and processing multiple files with cv2.VideoCapture()

Hi all,

I’m totally stuck on this, I just can’t find what’s wrong. Not sure if this is OpenCV specific or just my limited python skills. But I’m hoping someone can help me out.

What I would like to do is:

  • scan a directory for (mp4) video files
  • make a list of the found files
  • process each file with cv2.VideoCapture()

This is my code:

# file operations
import os
# Motion detection
import cv2


# Scan target directory
# os.scandir returns an iterator, make it a list to be re-usable

# Target directory is in the same directory as the script
targetDirectory = "videofiles"
dircontent = list(os.scandir(targetDirectory))

# Search for mp4 files and make a list [ [filename, path], [filename, path], ... ]
mp4Files = [[f.name[:-4], os.path.abspath(f.name)] for f in dircontent if f.name.lower().endswith('.mp4')]

for videofile in mp4Files:

    print(videofile[1], " -- ", type(videofile[1]))
    # This prints: E:\Projects\Programming\Python\OpenCV\_CaptureSaveROI\Motiondetection_Batch_Heatmap\Burglary.mp4  --  <class 'str'>

    # Open file to process
    # Replace backslahes with forward slashes
    videofilepath = videofile[1].replace("\\","/")
    print(videofilepath)
    # This prints: E:/Projects/Programming/Python/OpenCV/_CaptureSaveROI/Motiondetection_Batch_Heatmap/Burglary.mp4
    cap = cv2.VideoCapture(videofilepath)

    # the following doesn't give an error:
    # cap = cv2.VideoCapture("E:/Projects/Programming/Python/OpenCV/_CaptureSaveROI/Motiondetection_Batch_Heatmap/videofiles/Burglary.mp4")

    print(cap)
    # This prints: <VideoCapture 018CB4C0>

    if (cap.isOpened() == False):
        print("Error opening the video file")
    else:
        ret, frame1 = cap.read()
        heatmap = frame1.copy() # <<<<<<< ERROR

In the last if/else:
the error I get when not using the if/else :

heatmap = frame1.copy()  
AttributeError: 'NoneType' object has no attribute 'copy'

When using the if/else, cap.isOpened() returns false and I get the “Error opening the video file” message

Thanks for any help in advance!

You know what your variable ret is for? Checking that read succeeds. If it doesn’t, would there be a frame?

Well, ret returns false and nonetype indicates there is no frame to perform copy on. The question is:

This works:

  • cap = cv2.VideoCapture(“E:/Projects/…/Burglary.mp4”) returns a valid cap object

This does not work

  • cap = cv2.VideoCapture(videofilepath) where videofilepath is the string coming from a list

The strings look the same to me and I have no clue what’s going on.

Thanks

there may be non - video files in your folder
(hidden thumbnails, dot files, DS_Store, etc).
use python’s glob to filter filetypes,
and check cap.isOpened() before proceeding

but they aren’t equal in the python == sense, are they?

have your script print those strings. show both strings to us.

also, you don’t need to rewrite backslashes to forward slashes.

Ok, I did a test using glob and guess what, it works:

# file operations
import glob
# Motion detection
import cv2


# Scan target directory
# os.scandir returns an iterator, make it a list to be re-usable

# Target directory is in the same directory as the script
targetDirectory = "videofiles"
mp4files = glob.glob(targetDirectory + '/*.mp4')

for element in mp4files:
    print(element)
# The above prints all found paths as videofiles\filename.mp4

for videofile in mp4files:

    print(videofile, " -- ", type(videofile))
    # This prints: videofiles\Burglary.mp4  --  <class 'str'>

    cap = cv2.VideoCapture(videofile)

    print(cap)
    # This prints: <VideoCapture 018CB4C0>

    ret, frame1 = cap.read()

    if(ret):
        heatmap = frame1.copy()  # no error
        cv2.imshow('heatmap', heatmap)
    else:
        print('error')

So it works, glob however returns relative paths.

@crackwitz: I tested the following

targetDirectory = "videofiles"
dircontent = list(os.scandir(targetDirectory))

# Search for mp4 files and make a list [ [filename, path], [filename, path], ... ]
mp4Files = [[f.name[:-4], os.path.abspath(f.name)] for f in dircontent if f.name.lower().endswith('.mp4')]

for videofile in mp4Files:

    print(videofile[1], " -- ", type(videofile[1]))
    # This prints: E:\Projects\Programming\Python\OpenCV\_CaptureSaveROI\Motiondetection_Batch_Heatmap\Burglary.mp4  --  <class 'str'>

    pathAsString = 'E:/Projects/Programming/Python/OpenCV/_CaptureSaveROI/Motiondetection_Batch_Heatmap/videofiles/Burglary.mp4'

    print(pathAsString)
    # This prints: E:/Projects/Programming/Python/OpenCV/_CaptureSaveROI/Motiondetection_Batch_Heatmap/videofiles/Burglary.mp4

    print(videofile[1] == pathAsString)
    # This prints: False

    print('types:')
    print(type(videofile[1]))       # This prints: <class 'str'>
    print(type(pathAsString))       # This prints: <class 'str'>

    cap = cv2.VideoCapture(videofile[1])

    # the following lines don't give an error:
    # cap = cv2.VideoCapture('E:/Projects/Programming/Python/OpenCV/_CaptureSaveROI/Motiondetection_Batch_Heatmap/videofiles/Burglary.mp4')
    # cap = cv2.VideoCapture(pathAsString)

    print(cap)
    # This prints: <VideoCapture 018CB4C0>

    ret, frame1 = cap.read()
    print(ret)                  # This prints: False
    heatmap = frame1.copy()     # <<<<<<< ERROR: 'NoneType' object has no attribute 'copy'

videofile[1] == pathAsString returns false because they are two different objects right? What I don’t understand is that they are both strings with the same content but when fed into cv2.VideoCapture() one works and the other doesn’t.

I don’t know if it’s relevant but I’m using pycharm on windows:

uh no. that means they are not equal by value. that means their “content” differs.

videoFile[1] contains backward slashes
pathAsString has forward slashes

so, not the same string

Man… I’ve been staring at this too long, I missed the actual difference in the paths (Motiondetection_Batch_Heatmap\Burglary.mp4 != Motiondetection_Batch_Heatmap/videofiles/Burglary.mp4)

Just changed:

mp4Files = [[f.name[:-4], os.path.abspath(f.name)] for f in dircontent if f.name.lower().endswith('.mp4')]

To:

mp4Files = [[f.name[:-4], os.path.abspath(f)] for f in dircontent if f.name.lower().endswith('.mp4')]

So to conclude, this works as expected:

# file operations
import os
# Motion detection
import cv2


# Scan target directory
# os.scandir returns an iterator, make it a list to be re-usable

# Target directory is in the same directory as the script
targetDirectory = "videofiles"
dircontent = list(os.scandir(targetDirectory))

# Search for mp4 files and make a list [ [filename, path], [filename, path], ... ]
mp4Files = [[f.name[:-4], os.path.abspath(f)] for f in dircontent if f.name.lower().endswith('.mp4')]


for videofile in mp4Files:

    cap = cv2.VideoCapture(videofile[1])

    ret, frame1 = cap.read()
    heatmap = frame1.copy()