Skip to content

Commit

Permalink
djdd87#36 Added support for image rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
djdd87 committed Jul 16, 2021
1 parent 7f587a7 commit b619826
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 0 deletions.
58 changes: 58 additions & 0 deletions SynoAI/Controllers/CameraController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public async void Get(string id)

// Take the snapshot from Surveillance Station
byte[] snapshot = await GetSnapshot(id);
snapshot = PreProcessSnapshot(camera, snapshot);

// Save the original unprocessed image if required
if (Config.SaveOriginalSnapshot)
Expand Down Expand Up @@ -121,6 +122,63 @@ public async void Get(string id)
}
}

/// <summary>
/// Handles any required preprocessing of the captured image.
/// </summary>
/// <param name="camera">The camera that the snapshot is from.</param>
/// <param name="snapshot">The image data.</param>
/// <returns>A byte array of the image.</returns>
private byte[] PreProcessSnapshot(Camera camera, byte[] snapshot)
{
if (camera.Rotate == 0)
{
return snapshot;
}

Stopwatch stopwatch = Stopwatch.StartNew();

// Load the bitmap & rotate the image
SKBitmap bitmap = SKBitmap.Decode(new MemoryStream(snapshot));
_logger.LogInformation($"{camera.Name}: Rotating image {camera.Rotate} degrees.");
bitmap = Rotate(bitmap, camera.Rotate);

using (SKPixmap pixmap = bitmap.PeekPixels())
using (SKData data = pixmap.Encode(SKEncodedImageFormat.Jpeg, 100))
{
_logger.LogInformation($"{camera.Name}: Image preprocessing complete ({stopwatch.ElapsedMilliseconds}ms).");
return data.ToArray();
}
}

/// <summary>
/// Rotates the image to the specified angle.
/// </summary>
/// <param name="bitmap">The bitmap to rotate.</param>
/// <param name="angle">The angle to rotate to.</param>
/// <returns>The rotated bitmap.</returns>
private SKBitmap Rotate(SKBitmap bitmap, double angle)
{
double radians = Math.PI * angle / 180;
float sine = (float)Math.Abs(Math.Sin(radians));
float cosine = (float)Math.Abs(Math.Cos(radians));
int originalWidth = bitmap.Width;
int originalHeight = bitmap.Height;
int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

SKBitmap rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear();
canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
canvas.RotateDegrees((float)angle);
canvas.Translate(-originalWidth / 2, -originalHeight / 2);
canvas.DrawBitmap(bitmap, new SKPoint());
}

return rotatedBitmap;
}

private async Task SendNotifications(Camera camera, ISnapshotManager snapshotManager, IEnumerable<string> labels)
{
Stopwatch stopwatch = Stopwatch.StartNew();
Expand Down
4 changes: 4 additions & 0 deletions SynoAI/Models/Camera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public class Camera
/// The minimum size the object must be vertically to be considered as a valid result.
/// </summary>
public int? MinSizeY { get; set; }
/// <summary>
/// The number of degrees to rotate the captured image before processing.
/// </summary>
public float Rotate { get; set; }

/// <summary>
/// Gets the minimum size the object must be horizontally to be considered as a valid result from either the current camera, or the main config default if not specified.
Expand Down

0 comments on commit b619826

Please sign in to comment.