mirror of
https://github.com/THU-MIG/yolov10.git
synced 2025-05-24 06:07:03 +08:00
Implementing Resource Release Mechanism for Stream Loading (#4285)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
This commit is contained in:
parent
b73f9b13df
commit
645c715350
@ -34,6 +34,7 @@ class LoadStreams:
|
|||||||
def __init__(self, sources='file.streams', imgsz=640, vid_stride=1):
|
def __init__(self, sources='file.streams', imgsz=640, vid_stride=1):
|
||||||
"""Initialize instance variables and check for consistent input stream shapes."""
|
"""Initialize instance variables and check for consistent input stream shapes."""
|
||||||
torch.backends.cudnn.benchmark = True # faster for fixed-size inference
|
torch.backends.cudnn.benchmark = True # faster for fixed-size inference
|
||||||
|
self.running = True # running flag for Thread
|
||||||
self.mode = 'stream'
|
self.mode = 'stream'
|
||||||
self.imgsz = imgsz
|
self.imgsz = imgsz
|
||||||
self.vid_stride = vid_stride # video frame-rate stride
|
self.vid_stride = vid_stride # video frame-rate stride
|
||||||
@ -41,6 +42,7 @@ class LoadStreams:
|
|||||||
n = len(sources)
|
n = len(sources)
|
||||||
self.sources = [ops.clean_str(x) for x in sources] # clean source names for later
|
self.sources = [ops.clean_str(x) for x in sources] # clean source names for later
|
||||||
self.imgs, self.fps, self.frames, self.threads, self.shape = [[]] * n, [0] * n, [0] * n, [None] * n, [None] * n
|
self.imgs, self.fps, self.frames, self.threads, self.shape = [[]] * n, [0] * n, [0] * n, [None] * n, [None] * n
|
||||||
|
self.caps = [None] * n # video capture objects
|
||||||
for i, s in enumerate(sources): # index, source
|
for i, s in enumerate(sources): # index, source
|
||||||
# Start thread to read frames from video stream
|
# Start thread to read frames from video stream
|
||||||
st = f'{i + 1}/{n}: {s}... '
|
st = f'{i + 1}/{n}: {s}... '
|
||||||
@ -51,21 +53,22 @@ class LoadStreams:
|
|||||||
if s == 0 and (is_colab() or is_kaggle()):
|
if s == 0 and (is_colab() or is_kaggle()):
|
||||||
raise NotImplementedError("'source=0' webcam not supported in Colab and Kaggle notebooks. "
|
raise NotImplementedError("'source=0' webcam not supported in Colab and Kaggle notebooks. "
|
||||||
"Try running 'source=0' in a local environment.")
|
"Try running 'source=0' in a local environment.")
|
||||||
cap = cv2.VideoCapture(s)
|
self.caps[i] = cv2.VideoCapture(s) # store video capture object
|
||||||
if not cap.isOpened():
|
if not self.caps[i].isOpened():
|
||||||
raise ConnectionError(f'{st}Failed to open {s}')
|
raise ConnectionError(f'{st}Failed to open {s}')
|
||||||
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
w = int(self.caps[i].get(cv2.CAP_PROP_FRAME_WIDTH))
|
||||||
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
h = int(self.caps[i].get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||||||
fps = cap.get(cv2.CAP_PROP_FPS) # warning: may return 0 or nan
|
fps = self.caps[i].get(cv2.CAP_PROP_FPS) # warning: may return 0 or nan
|
||||||
self.frames[i] = max(int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), 0) or float('inf') # infinite stream fallback
|
self.frames[i] = max(int(self.caps[i].get(cv2.CAP_PROP_FRAME_COUNT)), 0) or float(
|
||||||
|
'inf') # infinite stream fallback
|
||||||
self.fps[i] = max((fps if math.isfinite(fps) else 0) % 100, 0) or 30 # 30 FPS fallback
|
self.fps[i] = max((fps if math.isfinite(fps) else 0) % 100, 0) or 30 # 30 FPS fallback
|
||||||
|
|
||||||
success, im = cap.read() # guarantee first frame
|
success, im = self.caps[i].read() # guarantee first frame
|
||||||
if not success or im is None:
|
if not success or im is None:
|
||||||
raise ConnectionError(f'{st}Failed to read images from {s}')
|
raise ConnectionError(f'{st}Failed to read images from {s}')
|
||||||
self.imgs[i].append(im)
|
self.imgs[i].append(im)
|
||||||
self.shape[i] = im.shape
|
self.shape[i] = im.shape
|
||||||
self.threads[i] = Thread(target=self.update, args=([i, cap, s]), daemon=True)
|
self.threads[i] = Thread(target=self.update, args=([i, self.caps[i], s]), daemon=True)
|
||||||
LOGGER.info(f'{st}Success ✅ ({self.frames[i]} frames of shape {w}x{h} at {self.fps[i]:.2f} FPS)')
|
LOGGER.info(f'{st}Success ✅ ({self.frames[i]} frames of shape {w}x{h} at {self.fps[i]:.2f} FPS)')
|
||||||
self.threads[i].start()
|
self.threads[i].start()
|
||||||
LOGGER.info('') # newline
|
LOGGER.info('') # newline
|
||||||
@ -76,7 +79,7 @@ class LoadStreams:
|
|||||||
def update(self, i, cap, stream):
|
def update(self, i, cap, stream):
|
||||||
"""Read stream `i` frames in daemon thread."""
|
"""Read stream `i` frames in daemon thread."""
|
||||||
n, f = 0, self.frames[i] # frame number, frame array
|
n, f = 0, self.frames[i] # frame number, frame array
|
||||||
while cap.isOpened() and n < f:
|
while self.running and cap.isOpened() and n < f:
|
||||||
# Only read a new frame if the buffer is empty
|
# Only read a new frame if the buffer is empty
|
||||||
if not self.imgs[i]:
|
if not self.imgs[i]:
|
||||||
n += 1
|
n += 1
|
||||||
@ -92,6 +95,19 @@ class LoadStreams:
|
|||||||
else:
|
else:
|
||||||
time.sleep(0.01) # wait until the buffer is empty
|
time.sleep(0.01) # wait until the buffer is empty
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close stream loader and release resources."""
|
||||||
|
self.running = False # stop flag for Thread
|
||||||
|
for i, thread in enumerate(self.threads):
|
||||||
|
if thread.is_alive():
|
||||||
|
thread.join(timeout=5) # Add timeout
|
||||||
|
for cap in self.caps: # Iterate through the stored VideoCapture objects
|
||||||
|
try:
|
||||||
|
cap.release() # release video capture
|
||||||
|
except Exception as e:
|
||||||
|
LOGGER.warning(f'WARNING ⚠️ Could not release VideoCapture object: {e}')
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"""Iterates through YOLO image feed and re-opens unresponsive streams."""
|
"""Iterates through YOLO image feed and re-opens unresponsive streams."""
|
||||||
self.count = -1
|
self.count = -1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user