Skip to content

Commit

Permalink
Add methods to draw events on spectrograms
Browse files Browse the repository at this point in the history
Issue #297
  • Loading branch information
towsey committed Apr 24, 2020
1 parent 1b83c46 commit 20b29f0
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 4 deletions.
19 changes: 17 additions & 2 deletions src/AudioAnalysisTools/Events/SpectralEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,24 @@ public override void Draw(IImageProcessingContext graphics, EventRenderingOption
// draw a border around this event
var border = options.Converters.GetPixelRectangle(this);
graphics.NoAA().DrawBorderInset(options.Border, border);
}

public void Draw(IImageProcessingContext graphics, EventRenderingOptions options, double scoreMax)
{
this.Draw(graphics, options);

//draw the score bar to indicate relative score
var topBin = options.Converters.HertzToPixels(this.HighFrequencyHertz);
var bottomBin = (int)Math.Round(options.Converters.HertzToPixels(this.LowFrequencyHertz));
var eventPixelHeight = bottomBin - topBin + 1;
var eventPixelStart = (int)Math.Round(options.Converters.SecondsToPixels(this.EventStartSeconds));
int scoreHt = (int)Math.Round(eventPixelHeight * this.Score / scoreMax);

var scorePen = new Pen(Color.LimeGreen, 1);

graphics.NoAA().DrawLine(scorePen, eventPixelStart, bottomBin - scoreHt, eventPixelStart, bottomBin);

// draw event title
// TODO
graphics.DrawTextSafe(this.Name, Acoustics.Shared.ImageSharp.Drawing.Tahoma6, Color.Gray, new PointF(eventPixelStart, topBin - 4));
}
}
}
96 changes: 94 additions & 2 deletions src/AudioAnalysisTools/StandardSpectrograms/SpectrogramTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ namespace AudioAnalysisTools.StandardSpectrograms
using System;
using System.Collections.Generic;
using Acoustics.Shared.Contracts;
using AnalysisBase.ResultBases;
using AudioAnalysisTools.DSP;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.Events.Drawing;
using AudioAnalysisTools.LongDurationSpectrograms;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using TowseyLibrary;

public static class SpectrogramTools
Expand All @@ -36,6 +40,91 @@ public static class SpectrogramTools
return m;
}

/// <summary>
/// NOTE: TODO TODO THIS METHOD HAS BEEN DUPLICATED BELOW WITH ALTERED SIGNATURE.
/// NEED TO FIX THIS.
/// This method draws a spectrogram with other useful information attached.
/// </summary>
/// <param name="sonogram">of BaseSonogram class.</param>
/// <param name="events">a list of acoustic events.</param>
/// <param name="plots">a list of plots relevant to the spectrogram scores.</param>
/// <param name="hits">not often used - can be null.</param>
public static Image<Rgb24> GetSonogramPlusCharts(
BaseSonogram sonogram,
List<EventCommon> events,
List<Plot> plots,
double[,] hits)
{
var spectrogram = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false);
Contract.RequiresNotNull(spectrogram, nameof(spectrogram));

var height = spectrogram.Height;
var width = spectrogram.Width;
var frameSize = sonogram.Configuration.WindowSize;
var segmentDuration = sonogram.Duration;

// ####################################################### THIS NEXT LINE NEEDS TO BE FIXED TO TAKE ANY START TIME.
var segmentStartTime = TimeSpan.Zero;

// init with linear frequency scale and draw freq grid lines on image
int hertzInterval = 1000;
if (height < 200)
{
hertzInterval = 2000;
}

var nyquist = sonogram.NyquistFrequency;
var freqScale = new FrequencyScale(nyquist, frameSize, hertzInterval);
FrequencyScale.DrawFrequencyLinesOnImage(spectrogram, freqScale.GridLineLocations, includeLabels: true);

// draw event outlines onto spectrogram.
if (events != null && events.Count > 0)
{
// ############################################################### FIX THIS SCORE MAX.
double maxScore = 10;
foreach (SpectralEvent ev in events)
{
var options = new EventRenderingOptions(new UnitConverters(segmentStartTime.TotalSeconds, segmentDuration.TotalSeconds, nyquist, width, height));
spectrogram.Mutate(x => ev.Draw(x, options, maxScore));
}
}

// now add in hits to the spectrogram image.
if (hits != null)
{
spectrogram = Image_MultiTrack.OverlayScoresAsRedTransparency(spectrogram, hits);
}

int pixelWidth = spectrogram.Width;
var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram("TITLE", pixelWidth);
var timeTrack = ImageTrack.DrawTimeTrack(sonogram.Duration, pixelWidth);

var imageList = new List<Image<Rgb24>>
{
titleBar,
timeTrack,
spectrogram,
timeTrack,
};

if (plots != null)
{
foreach (var plot in plots)
{
// Next line assumes plot data normalised in 0,1
var plotImage = plot.DrawAnnotatedPlot(ImageTrack.DefaultHeight);

// the following draws same plot without the title.
//var plotImage = ImageTrack.DrawScoreArrayTrack(plot.data, plot.threshold, pixelWidth);
imageList.Add(plotImage);
}
}

var compositeImage = ImageTools.CombineImagesVertically(imageList);

return compositeImage;
}

/// <summary>
/// This method draws a spectrogram with other useful information attached.
/// </summary>
Expand All @@ -53,7 +142,9 @@ public static Image<Rgb24> GetSonogramPlusCharts(
Contract.RequiresNotNull(spectrogram, nameof(spectrogram));

var height = spectrogram.Height;
var width = spectrogram.Width;
var frameSize = sonogram.Configuration.WindowSize;
var segmentDuration = sonogram.Duration;

// init with linear frequency scale and draw freq grid lines on image
int hertzInterval = 1000;
Expand All @@ -62,15 +153,16 @@ public static Image<Rgb24> GetSonogramPlusCharts(
hertzInterval = 2000;
}

var freqScale = new FrequencyScale(sonogram.NyquistFrequency, frameSize, hertzInterval);
var nyquist = sonogram.NyquistFrequency;
var freqScale = new FrequencyScale(nyquist, frameSize, hertzInterval);
FrequencyScale.DrawFrequencyLinesOnImage(spectrogram, freqScale.GridLineLocations, includeLabels: true);

// draw event outlines onto spectrogram.
if (events != null && events.Count > 0)
{
// set colour for the events
foreach (AcousticEvent ev in events)
{
// next three lines are for drawing the old AcousticEvent class.
ev.BorderColour = AcousticEvent.DefaultBorderColor;
ev.ScoreColour = AcousticEvent.DefaultScoreColor;
ev.DrawEvent(spectrogram, sonogram.FramesPerSecond, sonogram.FBinWidth, height);
Expand Down

0 comments on commit 20b29f0

Please sign in to comment.