Skip to content

Commit

Permalink
Added loading and saving annotation projects
Browse files Browse the repository at this point in the history
  • Loading branch information
mbsantiago committed Jul 15, 2023
1 parent b84c774 commit 9567c51
Show file tree
Hide file tree
Showing 16 changed files with 895 additions and 150 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ watch: ## Run tests on every change.

.PHONY: clean
clean: ## Clean unused files.
@find ./ -name '*.pyc' -exec rm -f \;
@find ./ -name '__pycache__' -exec rm -rf {} \;
@find ./ -name 'Thumbs.db' -exec rm -f {} \;
@find ./ -name '*~' -exec rm -f {} \;
@find src/ -name '*.pyc' -exec rm -f \;
@find src/ -name '__pycache__' -exec rm -rf {} \;
@find src/ -name 'Thumbs.db' -exec rm -f {} \;
@find src/ -name '*~' -exec rm -f {} \;
@rm -rf .cache
@rm -rf .pytest_cache
@rm -rf .mypy_cache
Expand Down
2 changes: 1 addition & 1 deletion src/soundevent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
ModelRun,
MultiLineString,
MultiPoint,
Sequence,
MultiPolygon,
Note,
Point,
Expand All @@ -27,6 +26,7 @@
PredictedTag,
ProcessedClip,
Recording,
Sequence,
SoundEvent,
Tag,
Time,
Expand Down
2 changes: 1 addition & 1 deletion src/soundevent/audio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Soundevent functions for handling audio files and arrays."""

from .io import load_clip, load_recording
from .media_info import get_media_info, MediaInfo
from .media_info import MediaInfo, get_media_info
from .spectrograms import compute_spectrogram

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion src/soundevent/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
from soundevent.data.clips import Clip
from soundevent.data.dataset import Dataset
from soundevent.data.features import Feature
from soundevent.data.sequences import Sequence
from soundevent.data.geometries import (
MAX_FREQUENCY,
BoundingBox,
Expand All @@ -60,6 +59,7 @@
from soundevent.data.predicted_tags import PredictedTag
from soundevent.data.processed_clip import ProcessedClip
from soundevent.data.recordings import Recording
from soundevent.data.sequences import Sequence
from soundevent.data.sound_events import SoundEvent
from soundevent.data.tags import Tag

Expand Down
9 changes: 6 additions & 3 deletions src/soundevent/data/annotation_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@
class AnnotationProject(BaseModel):
"""Annotation collection."""

id: UUID = Field(default_factory=uuid4)
id: UUID = Field(default_factory=uuid4, repr=False)
"""Unique identifier for the annotation collection."""

name: str
"""Name of the annotation project."""

description: Optional[str] = None
description: Optional[str] = Field(None, repr=False)
"""Description of the annotation collection."""

tasks: List[AnnotationTask] = Field(default_factory=list)
tasks: List[AnnotationTask] = Field(default_factory=list, repr=False)
"""List of annotation tasks in the project."""

instructions: Optional[str] = Field(default=None, repr=False)
"""Instructions for annotators."""
12 changes: 12 additions & 0 deletions src/soundevent/data/annotation_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
research projects.
"""
import datetime
from typing import List, Optional
from uuid import UUID, uuid4

from pydantic import BaseModel, Field

Expand All @@ -60,6 +62,9 @@
class AnnotationTask(BaseModel):
"""Annotation task."""

id: UUID = Field(default_factory=uuid4)
"""Unique identifier for the annotation task."""

clip: Clip
"""The annotated clip."""

Expand All @@ -69,6 +74,9 @@ class AnnotationTask(BaseModel):
completed_by: Optional[str] = None
"""The user who completed the annotation task."""

completed_on: Optional[datetime.datetime] = None
"""The date and time when the annotation task was completed."""

notes: List[Note] = Field(default_factory=list)
"""Notes associated with the annotation task."""

Expand All @@ -77,3 +85,7 @@ class AnnotationTask(BaseModel):

completed: bool = False
"""Whether the annotation task has been completed."""

def __hash__(self):
"""Compute the hash value for the annotation task."""
return hash(self.id)
6 changes: 5 additions & 1 deletion src/soundevent/data/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,14 @@ class Annotation(BaseModel):
tags: List[Tag] = Field(default_factory=list)
"""User provided tags to the annotated sound event."""

created_on: datetime.datetime = Field(
created_on: Optional[datetime.datetime] = Field(
default_factory=datetime.datetime.now
)
"""The time at which the annotation was created.
Important for tracking the progress of an annotation task.
"""

def __hash__(self):
"""Compute the hash of the annotation."""
return hash(self.id)
10 changes: 7 additions & 3 deletions src/soundevent/data/clips.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
class Clip(BaseModel):
"""Clip model."""

uuid: UUID = Field(default_factory=uuid4)
"""The unique identifier of the clip."""

recording: Recording
"""The recording that the clip belongs to."""

Expand All @@ -66,9 +69,6 @@ class Clip(BaseModel):
end_time: float
"""The end time of the clip in seconds."""

uuid: UUID = Field(default_factory=uuid4)
"""The unique identifier of the clip."""

tags: List[Tag] = Field(default_factory=list)
"""List of tags associated with the clip."""

Expand All @@ -81,3 +81,7 @@ def validate_times(cls, values):
if values["start_time"] > values["end_time"]:
raise ValueError("start_time must be less than end_time")
return values

def __hash__(self):
"""Hash clip object."""
return hash((self.recording, self.start_time, self.end_time))
35 changes: 18 additions & 17 deletions src/soundevent/data/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
"""
import sys
from abc import ABC, abstractmethod
from typing import List, Tuple
from typing import List, Tuple, Union

from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, field_validator
from pydantic import BaseModel, Field, PrivateAttr, field_validator
from shapely import geometry
from shapely.geometry.base import BaseGeometry

Expand Down Expand Up @@ -112,14 +112,16 @@ class Geometry(BaseModel, ABC):
geometry object is always in sync with the Shapely geometry object.
"""

model_config = ConfigDict(
arbitrary_types_allowed=True,
type: str = Field(
description="the type of geometry used to locate the sound event.",
frozen=True,
include=True,
)

type: GeometryType = Field(
...,
description="the type of geometry used to locate the sound event.",
coordinates: Union[float, List, Tuple] = Field(
description="the coordinates of the geometry.",
frozen=True,
include=True,
)

_geom: BaseGeometry = PrivateAttr()
Expand Down Expand Up @@ -182,7 +184,7 @@ class TimeStamp(Geometry):
time interval.
"""

type: Literal["TimeStamp"] = "TimeStamp"
type: str = "TimeStamp"

coordinates: Time = Field(
...,
Expand Down Expand Up @@ -216,10 +218,9 @@ class TimeInterval(Geometry):
not have a clear frequency range.
"""

type: Literal["TimeInterval"] = "TimeInterval"
type: str = "TimeInterval"

coordinates: Tuple[Time, Time] = Field(
...,
description="The time interval of the sound event.",
)
"""The time interval of the sound event.
Expand Down Expand Up @@ -267,7 +268,7 @@ class Point(Geometry):
time and frequency.
"""

type: Literal["Point"] = "Point"
type: str = "Point"

coordinates: Tuple[Time, Frequency] = Field(
...,
Expand Down Expand Up @@ -297,7 +298,7 @@ class LineString(Geometry):
trajectory.
"""

type: Literal["LineString"] = "LineString"
type: str = "LineString"

coordinates: List[Tuple[Time, Frequency]] = Field(
...,
Expand Down Expand Up @@ -345,7 +346,7 @@ class Polygon(Geometry):
frequency trajectory and that are bounded by a clear start and end time.
"""

type: Literal["Polygon"] = "Polygon"
type: str = "Polygon"

coordinates: List[List[Tuple[Time, Frequency]]] = Field(
...,
Expand Down Expand Up @@ -384,7 +385,7 @@ class BoundingBox(Geometry):
range and start and stop times.
"""

type: Literal["BoundingBox"] = "BoundingBox"
type: str = "BoundingBox"

coordinates: Tuple[Time, Frequency, Time, Frequency] = Field(
...,
Expand Down Expand Up @@ -431,7 +432,7 @@ class MultiPoint(Geometry):
together form a sound event.
"""

type: Literal["MultiPoint"] = "MultiPoint"
type: str = "MultiPoint"

coordinates: List[Tuple[Time, Frequency]] = Field(
...,
Expand Down Expand Up @@ -460,7 +461,7 @@ class MultiLineString(Geometry):
harmonics.
"""

type: Literal["MultiLineString"] = "MultiLineString"
type: str = "MultiLineString"

coordinates: List[List[Tuple[Time, Frequency]]] = Field(
...,
Expand Down Expand Up @@ -520,7 +521,7 @@ class MultiPolygon(Geometry):
polygons.
"""

type: Literal["MultiPolygon"] = "MultiPolygon"
type: str = "MultiPolygon"

coordinates: List[List[List[Tuple[Time, Frequency]]]] = Field(
...,
Expand Down
4 changes: 2 additions & 2 deletions src/soundevent/data/recordings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@

import datetime
import os
from uuid import UUID, uuid4
from pathlib import Path
from typing import List, Optional
from uuid import UUID, uuid4

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -125,7 +125,7 @@ class Recording(BaseModel):

def __hash__(self):
"""Hash function."""
return hash(self.hash)
return hash(self.id)

@classmethod
def from_file(
Expand Down
8 changes: 6 additions & 2 deletions src/soundevent/data/sound_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
of the sound events.
"""

from typing import List, Optional
from uuid import UUID, uuid4
from typing import List

from pydantic import BaseModel, Field

Expand All @@ -60,11 +60,15 @@ class SoundEvent(BaseModel):
recording: Recording
"""The recording containing the sound event."""

geometry: Geometry
geometry: Optional[Geometry]
"""The geometry locating the sound event within the recording."""

tags: List[Tag] = Field(default_factory=list)
"""The tags associated with the sound event."""

features: List[Feature] = Field(default_factory=list)
"""The features associated with the sound event."""

def __hash__(self):
"""Compute the hash of the sound event."""
return hash(self.uuid)
1 change: 0 additions & 1 deletion src/soundevent/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from soundevent.io.datasets import load_dataset, save_dataset


__all__ = [
"load_dataset",
"save_dataset",
Expand Down
Loading

0 comments on commit 9567c51

Please sign in to comment.