numpyimage 3.0.1__tar.gz → 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -161,6 +161,7 @@ class VideoStreamer:
161
161
  self.container = av.open(str(self.filename))
162
162
  self.stream = self.container.streams.video[0]
163
163
  self.time_base = self.stream.time_base
164
+ self._frame_iterator = self.container.decode(self.stream)
164
165
  self._shape = None
165
166
  self._first_frame = None
166
167
  self._width = None
@@ -313,6 +314,7 @@ class VideoStreamer:
313
314
  return float(1.0 / self._framerate)
314
315
 
315
316
  def frame_number_to_pts(self, frame_number: int) -> int:
317
+ frame_number = self._normalize_frame_number(frame_number)
316
318
  if self._framerate == 'variable':
317
319
  return self.frames_pts[frame_number]
318
320
  else:
@@ -321,6 +323,22 @@ class VideoStreamer:
321
323
  def frame_number_to_time(self, frame_number: int) -> float:
322
324
  return float(self.frame_number_to_pts(frame_number) * self.time_base)
323
325
 
326
+ def pts_to_frame_number(self, pts: int) -> int:
327
+ if self._framerate == 'variable':
328
+ if pts not in self.frames_pts:
329
+ raise ValueError(f'PTS {pts} not in video index.')
330
+ return self.frames_pts.index(pts)
331
+ else:
332
+ if pts < self.pts0:
333
+ raise ValueError(f'PTS {pts} is before the start of the'
334
+ f' video (PTS {self.pts0}).')
335
+ if pts > self.pts_delta * (self.n_frames - 1) + self.pts0:
336
+ raise ValueError(f'PTS {pts} is after the end of the video (PTS '
337
+ f'{self.pts_delta * (self.n_frames - 1) + self.pts0}).')
338
+ if (pts - self.pts0) % self.pts_delta != 0:
339
+ raise ValueError(f'PTS {pts} is between frames for this video.')
340
+ return (pts - self.pts0) // self.pts_delta
341
+
324
342
  def __getitem__(self, key) -> np.ndarray:
325
343
  if isinstance(key, tuple):
326
344
  frame_idx = key[0]
@@ -349,9 +367,11 @@ class VideoStreamer:
349
367
  to the requested frame number.
350
368
  """
351
369
  target_pts = self.frame_number_to_pts(frame_number)
352
- for frame in self.container.decode(self.stream):
370
+ for frame in self._frame_iterator:
353
371
  if frame.pts is None:
354
- continue # Skip frames without PTS
372
+ if self.verbose:
373
+ print('WARNING: Skipping a frame with no PTS.')
374
+ continue
355
375
  if frame.pts == target_pts:
356
376
  frame = frame.to_ndarray(format='rgb24')
357
377
  self._current_frame_number = frame_number
@@ -359,23 +379,14 @@ class VideoStreamer:
359
379
  if frame.pts > target_pts:
360
380
  raise RuntimeError(f'Frame with PTS {target_pts} not found after'
361
381
  f' seeking – current frame PTS: {frame.pts}')
362
- raise RuntimeError(f'Frame with PTS {target_pts} not found after'
363
- f' seeking – current frame PTS: {frame.pts}')
382
+ raise RuntimeError('Ran out of frames before finding frame with requested'
383
+ f' PTS {target_pts} – current frame PTS: {frame.pts}')
364
384
 
365
385
  if isinstance(frame_number, slice): # Support slicing
366
386
  start, stop, step = frame_number.indices(self.n_frames)
367
387
  return np.array([self._get_frame(i) for i in range(start, stop, step)])
368
388
  with self._lock:
369
- # Support negative indexing
370
- if frame_number < 0 and frame_number + self.n_frames >= 0:
371
- frame_number += self.n_frames
372
- elif frame_number >= self.n_frames:
373
- raise IndexError(f'Frame {frame_number} out of range:'
374
- f' [0, {self.n_frames-1}]')
375
- elif frame_number < 0:
376
- raise IndexError(f'Negative frame {frame_number} out of'
377
- f' range: [-{self.n_frames}, -1]')
378
-
389
+ frame_number = self._normalize_frame_number(frame_number)
379
390
  if (self._current_frame_number is None
380
391
  or frame_number <= self._current_frame_number
381
392
  or frame_number > self._current_frame_number + 100):
@@ -391,6 +402,28 @@ class VideoStreamer:
391
402
  image = np.rot90(image, k=-int(self.rotation) // 90)
392
403
  return image
393
404
 
405
+ def _normalize_frame_number(self, frame_number: int) -> int:
406
+ """
407
+ Support negative indexing by converting negative frame numbers to
408
+ positive ones, e.g. -1 becomes n_frames - 1, -2 becomes n_frames - 2, etc.
409
+ """
410
+ try:
411
+ frame_number = int(frame_number)
412
+ except ValueError:
413
+ raise TypeError(f'Frame number must be castable to int but got "{frame_number}"')
414
+
415
+ if frame_number < -self.n_frames:
416
+ raise IndexError(f'Negative frame {frame_number} not in'
417
+ f' valid range [-{self.n_frames}, -1]')
418
+ elif -self.n_frames <= frame_number and frame_number < 0:
419
+ return frame_number + self.n_frames
420
+ elif 0 <= frame_number and frame_number < self.n_frames:
421
+ return frame_number
422
+ elif self.n_frames <= frame_number:
423
+ raise IndexError(f'Frame {frame_number} not in'
424
+ f' valid range [0, {self.n_frames-1}]')
425
+ raise IndexError(f'Frame {frame_number} not understood')
426
+
394
427
  @property
395
428
  def first_frame(self):
396
429
  if self._first_frame is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'numpyimage'
7
- version = '3.0.1'
7
+ version = '3.1.0'
8
8
  description = 'Load, save, & manipulate image files as numpy arrays'
9
9
  readme.file = 'README.md'
10
10
  readme.content-type = 'text/markdown'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes