Skip to content

Commit c8e6a8d

Browse files
Updates for Docker build
1 parent a06fd6c commit c8e6a8d

4 files changed

Lines changed: 70 additions & 21 deletions

File tree

.github/workflows/docker-publish.yaml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,24 @@ jobs:
5555
username: ${{ github.actor }}
5656
password: ${{ secrets.GITHUB_TOKEN }}
5757

58-
# Push tagged image (version tag + latest) to registries
59-
- name: Tagged Docker Hub
60-
if: ${{ github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') }}
61-
run: |
62-
VERSION=$(echo ${GITHUB_REF} | sed 's#.*/v##')
63-
echo "IMAGE_TAG=${VERSION}" >> $GITHUB_ENV
64-
65-
# Push bleeding-edge image (main tag) to registries
66-
- name: Bleeding Edge Docker Hub
67-
if: github.ref == 'refs/heads/main'
58+
# Push bleeding-edge image ("<branch name>" tag) to registries
59+
- name: Bleeding Edge Docker Hub (Default Option)
6860
run: |
69-
echo "IMAGE_TAG=main" >> $GITHUB_ENV
61+
echo "IMAGE_TAG=${GITHUB_REF_NAME}" >> $GITHUB_ENV
7062
71-
# Push nightly image (nightly tag) to registries
63+
# Push nightly image ("nightly" tag) to registries
7264
- name: Nightly Docker Hub
7365
if: github.event_name == 'schedule'
7466
run: |
7567
echo "IMAGE_TAG=nightly" >> $GITHUB_ENV
7668
69+
# Push tagged image ("<symantic version>" tag) to registries
70+
- name: Tagged Docker Hub
71+
if: ${{ github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') }}
72+
run: |
73+
VERSION=$(echo ${GITHUB_REF} | sed 's#.*/v##')
74+
echo "IMAGE_TAG=${VERSION}" >> $GITHUB_ENV
75+
7776
# Build images
7877
- name: Build Batbot
7978
run: |

ISSUES.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
Known Issues
33
============
44

5-
N/A
5+
TODO:
6+
- Fix CI/CD docker build
7+
- Add API documentation, tutorials, and examples
8+
- Create example notebooks with Google colab
9+
- Crate discord / discourse community board
10+
- [BatAI] Upload training scripts with MLFlow support, database export with celery

README.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,51 @@ Development Environment
4141
# Run batbot
4242
batbot --help
4343
44+
Spectrogram Extraction
45+
----------------------
46+
47+
Here are the steps for extracting the compressed spectrogram:
48+
49+
- Create the STFT
50+
- Load the original waveform at the original sample rate
51+
- Resample waveform to 250kHz
52+
- Convert to a STFT spectrogram (fft=512, method=blackmanharris, window=256, hop=16)
53+
- Convert complex power STFT to amplitude STFT (dB)
54+
- Normalize the STFT
55+
- Trim STFT to minimum and maximum frequencies (5kHz to 120kHz)
56+
- Subtract the per-freqency median dB (reduce any spectral bias / shift)
57+
- Set global dynamic range to -80 dB from the global maximum amplitude
58+
- Calculate the global median non-minimum dB (greater than -80dB)
59+
- Calculate the median absolute deviation (MAD)
60+
- Autogain the dynamic range to (5 * MAD) below the global amplitude median, if necessary
61+
- Quantize the STFT
62+
- Quantize the floating-point amplitude STFT to a 16-bit integer representation spanning the full dynamic range (65,536 bins)
63+
- Vertically flip the spectrogram (low frequencies on bottom) and convert to a C-contiguous array
64+
- Find Candidate Chirps
65+
- Create a 12ms sliding window with a 3ms stride
66+
- Keep the time windows that show a substantial right-skew across 10% of the frequency range
67+
- Add any user-provided time windows (annotations) to the found candidates windows
68+
- Merge any overlapping time windows into a set of contiguous time ranges
69+
- Tighten the candidate time ranges (and separate as needed) by repeating the same skew-based filter with a smaller sliding window and stride
70+
- Extract Chirp Metrics
71+
- *for each candidate chirp*
72+
- *Start*: First, find the peak amplitude location.
73+
- Step 1 - Normalize the chirp to the full 16-bit range. Calculate a histogram and identify the most common dB and standard deviation. Scale the amplitude values using an inverted PDF, weighting each value by its inverse probability of being noise (values below the most common dB are set to zero)
74+
- Step 2 - Apply a median filter and re-normalize
75+
- Step 3 - Apply a morphological open operation
76+
- Step 4 - Blur the chirp (k=5) and re-normalize
77+
- Step 5 - Find contours using the "marching squares" algorithm and select the one that contains the peak amplitude. Extract the convex hull of the contour and smooth the resulting outline
78+
- Step 6 - Extract a segmentation mask for the contour
79+
- Step 7 - Locate the harmonic (doubling the frequency) and echo (right edge of the contour to the end of the chirp time range) regions. Remove any overlapping noise from the chirp contour.
80+
- Step 8 - Locate the start, end, and characteristic frequency points (peak amplitude) and calculate an optimization cost grid for the contour using the masked amplitudes.
81+
- Step 9 - Solve a minimum distance optimization using A* that also maximizes the amplutide values from start to end points.
82+
- Step 10 - Smooth the contour path, extract the contour's slope, then identify the knee, heel, and other defining attributes.
83+
- *End*: Finally, if any of the above steps fails, or the chirp's attributes do not make semantic sense, then skip the candidate chirp.
84+
- Create Output
85+
- Collect all valid chirps regions and metadata, create a compressed spectrogram
86+
- Write the 16-bit spectrogram as a series of 8-bit JPEGs image chunks (max width per chunk 50k pixels)
87+
- Write the file and chirp metadata to a JSON file.
88+
4489
How to Install
4590
--------------
4691

batbot/spectrogram/__init__.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ def calculate_astar_grid_and_endpoints(
831831
costs = segment.copy()
832832
segmentmask_ = np.logical_not(segmentmask)
833833
costs[segmentmask_] = 0
834-
write_contour_debug_image(costs, index, 7, 'costs', output_path=output_path)
834+
write_contour_debug_image(costs, index, 8, 'costs', output_path=output_path)
835835

836836
ys, xs = np.where(costs > 0)
837837
points = np.stack([ys, xs], axis=1, dtype=np.float32)
@@ -880,7 +880,7 @@ def calculate_astar_grid_and_endpoints(
880880
cv2.circle(canvas, begin[::-1], 5, (0, value, 0), -1)
881881
cv2.circle(canvas, end[::-1], 5, (0, 0, value), -1)
882882

883-
write_contour_debug_image(canvas, index, 7, 'endpoints', output_path=output_path)
883+
write_contour_debug_image(canvas, index, 8, 'endpoints', output_path=output_path)
884884

885885
costs = segment.astype(np.float32)
886886
segmentmask_ = segmentmask.astype(np.float32)
@@ -1261,7 +1261,7 @@ def calculate_harmonic_and_echo_flags(
12611261

12621262
@lp
12631263
def compute_wrapper(
1264-
wav_filepath, annotations=None, output_folder='.', bitdepth=16, debug=False, **kwargs
1264+
wav_filepath, annotations=None, output_folder='.', bitdepth=16, debug=True, **kwargs
12651265
):
12661266
"""
12671267
Compute the spectrograms for a given input WAV and saves them to disk.
@@ -1396,12 +1396,12 @@ def compute_wrapper(
13961396
original, index, segmentmask, harmonic, echo, canvas, output_path=debug_path
13971397
)
13981398

1399-
# Step 8 - Remove harmonic and echo from segmentation
1399+
# Remove harmonic and echo from segmentation
14001400
segment = remove_harmonic_and_echo(
14011401
segment, index, harmonic, echo, threshold, output_path=debug_path
14021402
)
14031403

1404-
# Step 7 - Calculate the A* cost grid and start/end points
1404+
# Step 8 - Calculate the A* cost grid and start/end points
14051405
costs, grid, begin, end, boundary = calculate_astar_grid_and_endpoints(
14061406
segment, index, segmentmask, peak, canvas, output_path=debug_path
14071407
)
@@ -1414,15 +1414,15 @@ def compute_wrapper(
14141414
if not significant:
14151415
continue
14161416

1417-
# Step 8 - Extract optimal path from start to end using the cost grid
1417+
# Step 9 - Extract optimal path from start to end using the cost grid
14181418
path = extract_contour_path(grid, begin, end, canvas, index, output_path=debug_path)
14191419

1420-
# Step 9 - Extract contour keypoints
1420+
# Step 10 - Extract contour keypoints
14211421
path_smoothed, (knee, fc, heel), slopes = extract_contour_keypoints(
14221422
path, canvas, index, peak, output_path=debug_path
14231423
)
14241424

1425-
# Step 10 - Collect chirp metadata
1425+
# Step 11 - Collect chirp metadata
14261426
metadata = {
14271427
'curve.(khz,ms)': [
14281428
(

0 commit comments

Comments
 (0)