using System; using System.Collections.Generic; using System.Text; using Swan; namespace Unosquare.RaspberryIO.Camera { /// <summary> /// Defines a wrapper for the raspistill program and its settings (command-line arguments). /// </summary> /// <seealso cref="CameraSettingsBase" /> public class CameraStillSettings : CameraSettingsBase { private Int32 _rotate; /// <inheritdoc /> public override String CommandName => "raspistill"; /// <summary> /// Gets or sets a value indicating whether the preview window (if enabled) uses native capture resolution /// This may slow down preview FPS. /// </summary> public Boolean CaptureDisplayPreviewAtResolution { get; set; } = false; /// <summary> /// Gets or sets the encoding format the hardware will use for the output. /// </summary> public CameraImageEncodingFormat CaptureEncoding { get; set; } = CameraImageEncodingFormat.Jpg; /// <summary> /// Gets or sets the quality for JPEG only encoding mode. /// Value ranges from 0 to 100. /// </summary> public Int32 CaptureJpegQuality { get; set; } = 90; /// <summary> /// Gets or sets a value indicating whether the JPEG encoder should add raw bayer metadata. /// </summary> public Boolean CaptureJpegIncludeRawBayerMetadata { get; set; } = false; /// <summary> /// JPEG EXIF data /// Keys and values must be already properly escaped. Otherwise the command will fail. /// </summary> public Dictionary<String, String> CaptureJpegExtendedInfo { get; } = new Dictionary<String, String>(); /// <summary> /// Gets or sets a value indicating whether [horizontal flip]. /// </summary> /// <value> /// <c>true</c> if [horizontal flip]; otherwise, <c>false</c>. /// </value> public Boolean HorizontalFlip { get; set; } = false; /// <summary> /// Gets or sets a value indicating whether [vertical flip]. /// </summary> /// <value> /// <c>true</c> if [vertical flip]; otherwise, <c>false</c>. /// </value> public Boolean VerticalFlip { get; set; } = false; /// <summary> /// Gets or sets the rotation. /// </summary> /// <exception cref="ArgumentOutOfRangeException">Valid range 0-359.</exception> public Int32 Rotation { get => this._rotate; set { if(value < 0 || value > 359) { throw new ArgumentOutOfRangeException(nameof(value), "Valid range 0-359"); } this._rotate = value; } } /// <inheritdoc /> public override String CreateProcessArguments() { StringBuilder sb = new StringBuilder(base.CreateProcessArguments()); _ = sb.Append($" -e {this.CaptureEncoding.ToString().ToLowerInvariant()}"); // JPEG Encoder specific arguments if(this.CaptureEncoding == CameraImageEncodingFormat.Jpg) { _ = sb.Append($" -q {this.CaptureJpegQuality.Clamp(0, 100).ToString(Ci)}"); if(this.CaptureJpegIncludeRawBayerMetadata) { _ = sb.Append(" -r"); } // JPEG EXIF data if(this.CaptureJpegExtendedInfo.Count > 0) { foreach(KeyValuePair<String, String> kvp in this.CaptureJpegExtendedInfo) { if(String.IsNullOrWhiteSpace(kvp.Key) || String.IsNullOrWhiteSpace(kvp.Value)) { continue; } _ = sb.Append($" -x \"{kvp.Key.Replace("\"", "'")}={kvp.Value.Replace("\"", "'")}\""); } } } // Display preview settings if(this.CaptureDisplayPreview && this.CaptureDisplayPreviewAtResolution) { _ = sb.Append(" -fp"); } if(this.Rotation != 0) { _ = sb.Append($" -rot {this.Rotation}"); } if(this.HorizontalFlip) { _ = sb.Append(" -hf"); } if(this.VerticalFlip) { _ = sb.Append(" -vf"); } return sb.ToString(); } } }