Skip to content

Commit

Permalink
Merge pull request #12 from gbeckers/develop
Browse files Browse the repository at this point in the history
Parameters project merged for release preparations
  • Loading branch information
carienmol committed Sep 7, 2023
2 parents 03c2a88 + 3f8fc97 commit 8c001a5
Show file tree
Hide file tree
Showing 33 changed files with 2,798 additions and 961 deletions.
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Python 3 versions may also work.
3) Create new environment for Birdwatcher (name is up to you, in example
here 'mybirdwatcher'). We install Jupter lab and ffmpeg at the same time::

$ conda create -n mybirdwatcher python=3.9 jupyterlab ffmpeg
$ conda create -n mybirdwatcher python=3.9 jupyterlab ffmpeg git

4) Switch to this new environment:

Expand All @@ -73,7 +73,9 @@ The following dependencies are automatically taken care of when you
install Birdwatcher from GitHub using the pip method above:

- numpy
- pandas
- matplotlib
- seaborn
- darr
- opencv-python
- opencv-contrib-python
Expand Down
9 changes: 0 additions & 9 deletions birdwatcher/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
from .video import *
from .movementdetection import *
from .backgroundsubtraction import *
from .coordinatearrays import *
from .frames import *
from .utils import *
try:
import pypylon
from .recording import *
except ImportError:
pass

#from .plotting import *
from ._version import get_versions
__version__ = get_versions()['version']
del get_versions


from .tests import test
16 changes: 8 additions & 8 deletions birdwatcher/backgroundsubtraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class BackgroundSubtractorMOG2(BaseBackgroundSubtractor):
Parameters
----------
History : int, default=5
History : int, default=3
Length of the history.
ComplexityReductionThreshold : float, default=0.5
This parameter defines the number of samples needed to accept to prove
Expand All @@ -155,11 +155,11 @@ class BackgroundSubtractorMOG2(BaseBackgroundSubtractor):
The number of gaussian components in the background model.
VarInit : int, default=15
The initial variance of each gaussian component.
VarMin : int, default=4
VarMin : int, default=10
The minimum variance of each gaussian component.
VarMax : int, default=75
The maximum variance of each gaussian component.
VarThreshold : int, default=10
VarThreshold : int, default=70
The variance threshold for the pixel-model match. The main threshold
on the squared Mahalanobis distance to decide if the sample is well
described by the background model or not. Related to Cthr from the
Expand All @@ -180,24 +180,24 @@ class BackgroundSubtractorMOG2(BaseBackgroundSubtractor):
The shadow threshold is a threshold defining how much darker the
shadow can be. 0.5 means that if a pixel is more than twice darker
then it is not shadow.
ShadowValue : int, default=127
ShadowValue : int, default=0
Shadow value is the value used to mark shadows in the foreground mask.
Value 0 in the mask always means background, 255 means foreground.
"""

_setparams = {'History': 5,
_setparams = {'History': 3,
'ComplexityReductionThreshold': 0.05,
'BackgroundRatio': 0.1,
'NMixtures': 7,
'VarInit': 15,
'VarMin': 4,
'VarMin': 10,
'VarMax': 75,
'VarThreshold': 10,
'VarThreshold': 70,
'VarThresholdGen': 9,
'DetectShadows': False,
'ShadowThreshold': 0.5,
'ShadowValue': 127}
'ShadowValue': 0}

_bgsubtractorcreatefunc = cv.createBackgroundSubtractorMOG2

Expand Down
25 changes: 14 additions & 11 deletions birdwatcher/coordinatearrays.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
"""This module provides objects and functions for coordinate data.
Coordinates are pixel positions (x,y). This is a convenient way of storing
output from image algorithms that determine whether or not a pixel is
categorized as something (e.g. crossing some threshold). Since the number of
pixels per frame may be different, we use disk-based ragged arrays from the
python library Darr to store them. This is not the most disk-space efficient
way of doing this, but it is fast and the data can easily be read in any
computing platform. Coordinate files can be archived in compressed form
(lzma) when data is large.
Coordinates are pixel positions (x,y) in a Frame. Coordinate Arrays are a
convenient way of storing output from image algorithms that determine if a
pixel is categorized as something (e.g. crossing some threshold).
Information is memory-mapped from disk, because data can easily become very
large and will not fit in RAM memory. Since the number of pixels per frame may
be variable, we use Ragged Arrays from the python library Darr to store them.
This is not the most disk-space efficient way of doing this (no compression),
but it is fast and the data can easily be read in any scientific computing
environement (Python, Matlab, R, Mathematica, etc.) Coordinate files can be
archived in compressed form (lzma) to save disk space.
"""

import os
from contextlib import contextmanager
from pathlib import Path
import shutil
import numpy as np
import tarfile
from contextlib import contextmanager
from pathlib import Path

import numpy as np
from darr import RaggedArray, delete_raggedarray, create_raggedarray

from ._version import get_versions
from .utils import tempdir
from .frames import frameiterator


__all__ = ['CoordinateArrays', 'open_archivedcoordinatedata',
'create_coordarray']

Expand Down
18 changes: 10 additions & 8 deletions birdwatcher/ffmpeg.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import subprocess
import numpy as np
import json
import subprocess
from pathlib import Path

import numpy as np

from .utils import peek_iterable


__all__ = ['arraytovideo']


Expand Down Expand Up @@ -104,7 +106,7 @@ def videofileinfo(filepath, ffprobepath='ffprobe'):

## FIXME inform before raising StopIteration that file has no frames
def iterread_videofile(filepath, startat=None, nframes=None, color=True,
ffmpegpath='ffmpeg', loglevel= 'quiet'):
ffmpegpath='ffmpeg', loglevel='quiet'):
"""
Parameters
----------
Expand Down Expand Up @@ -199,11 +201,11 @@ def get_frame(filepath, framenumber, color=True, ffmpegpath='ffmpeg',
dtype=np.uint8).reshape(frameshape)


def get_frameat(filepath, time, color=True, ffmpegpath='ffmpeg', loglevel=
'quiet'):
return next(iterread_videofile(filepath, startat=time, nframes=1, \
color=color, ffmpegpath=ffmpegpath),
loglevel=loglevel)
def get_frameat(filepath, time, color=True, ffmpegpath='ffmpeg',
loglevel='quiet'):
return next(iterread_videofile(filepath, startat=time, nframes=1,
color=color, ffmpegpath=ffmpegpath,
loglevel=loglevel))


# FIXME do not assume things on audio (i.e. number of channels) and make more versatile
Expand Down
54 changes: 28 additions & 26 deletions birdwatcher/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
"""

import numpy as np
import cv2 as cv
from functools import wraps
from pathlib import Path

import numpy as np
import cv2 as cv

from .utils import peek_iterable


__all__ = ['Frames', 'FramesColor', 'FramesGray', 'FramesNoise', 'framecolor',
'framegray', 'framenoise']


def frameiterator(func):
@wraps(func)
def wrapper(*args, **kwargs):
Expand Down Expand Up @@ -778,7 +781,7 @@ def calc_meanframe(self, dtype=None):
(0, 0, 0), dtype='float64')
for i, frame in enumerate(self._frames):
meanframe += frame
meanframe /= i
meanframe /= (i+1)
if dtype is None:
dtype = self._dtype
return meanframe.astype(dtype)
Expand Down Expand Up @@ -815,17 +818,18 @@ class FramesColor(Frames):
"""

def __init__(self, nframes, width, height, color=(0, 0, 0), dtype='uint8'):
def __init__(self, nframes, height, width, color=(0, 0, 0),
dtype='uint8'):
"""Creates an iterator that yields color frames.
Parameters
----------
nframes : int
Number of frames to be produced.
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
color : tuple of ints, optional
Fill value of frame (r, g, b). The default (0, 0, 0) color is
black.
Expand All @@ -837,7 +841,7 @@ def __init__(self, nframes, width, height, color=(0, 0, 0), dtype='uint8'):
Iterator of numpy ndarrays
"""
frame = framecolor(width=width, height=height, color=color,
frame = framecolor(height=height, width=width, color=color,
dtype=dtype)
frames = (frame.copy() for _ in range(nframes))
super().__init__(frames=frames)
Expand All @@ -850,17 +854,17 @@ class FramesGray(Frames):
"""

def __init__(self, nframes, width, height, value=0, dtype='uint8'):
def __init__(self, nframes, height, width, value=0, dtype='uint8'):
"""Creates an iterator that yields gray frames.
Parameters
----------
nframes : int
Number of frames to be produced.
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
value : int, optional
Fill value of frame. The default (0) is black.
dtype : numpy dtype, default='uint8'
Expand All @@ -872,9 +876,8 @@ def __init__(self, nframes, width, height, value=0, dtype='uint8'):
"""

frame = framegray(width=width, height=height, value=value,
frame = framegray(height=height, width=width, value=value,
dtype=dtype)

frames = (frame.copy() for _ in range(nframes))
super().__init__(frames=frames)

Expand All @@ -886,17 +889,17 @@ class FramesNoise(Frames):
"""

def __init__(self, nframes,width, height, dtype='uint8'):
def __init__(self, nframes, height, width, dtype='uint8'):
"""Creates an iterator that yields gray frames.
Parameters
----------
nframes : int
Number of frames to be produced.
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
dtype : numpy dtype, default='uint8'
Dtype of frame.
Expand All @@ -911,15 +914,15 @@ def __init__(self, nframes,width, height, dtype='uint8'):
super().__init__(frames=frames)


def framegray(width, height, value=0, dtype='uint8'):
def framegray(height, width, value=0, dtype='uint8'):
"""Creates a gray frame.
Parameters
----------
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
value : int, optional
Fill value of frame. The default (0) is black.
dtype : numpy dtype, default='uint8'
Expand All @@ -933,15 +936,15 @@ def framegray(width, height, value=0, dtype='uint8'):
return np.ones((height, width), dtype=dtype) * value


def framecolor(width, height, color=(0, 0, 0), dtype='uint8'):
def framecolor(height, width, color=(0, 0, 0), dtype='uint8'):
"""Creates a color frame.
Parameters
----------
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
color : tuple of ints, optional
Fill value of frame (r, g, b). The default (0, 0, 0) color is black.
dtype : numpy dtype, default='uint8'
Expand All @@ -952,19 +955,18 @@ def framecolor(width, height, color=(0, 0, 0), dtype='uint8'):
numpy ndarray
"""
return np.ones((height, width, 3), dtype=dtype) * np.asanyarray(color,
dtype=dtype)
return np.ones((height, width, 3), dtype=dtype) * np.asanyarray(color, dtype=dtype)


def framenoise(width, height, dtype='uint8'):
def framenoise(height, width, dtype='uint8'):
"""Creates a noise frame.
Parameters
----------
width : int
Width of frame.
height : int
Height of frame.
width : int
Width of frame.
dtype : numpy dtype, default='uint8'
Dtype of frame.
Expand Down
Loading

0 comments on commit 8c001a5

Please sign in to comment.