Dispenser/DispenserUI/ViewModels/Components/CameraViewerVM.cs

586 lines
15 KiB
C#
Raw Permalink Normal View History

2024-08-16 07:20:09 +00:00
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using DispenserCommon.Aop;
using DispenserCommon.Events;
using DispenserCommon.Scheduler;
using DispenserCore.Service;
using DispenserHal.Camera.Factory;
using DispenserHal.Camera.VO;
using DispenserUI.Utils;
using DispenserUI.ViewModels.Recorder;
using DispenserUI.Views.Controls;
using DispenserUI.Views.Windows;
using Masuit.Tools;
using ReactiveUI;
using Serilog;
using SharpAvi;
using Bitmap = System.Drawing.Bitmap;
using Point = Avalonia.Point;
namespace DispenserUI.ViewModels.Components;
/// <summary>
/// 相机查看器
/// </summary>
[GlobalTry]
public class CameraViewerVM : ViewModelBase
{
private readonly SystemParamsService _systemParamsService = new();
private const string TaskName = "CameraViewer#UpdateDisplay";
private bool _blank = true;
private Bitmap _captureImage;
private bool _capturingPicture;
private bool _capturingVideo;
private bool _recordingVideo;
private bool _isFullScreen;
private bool _showingCrosshair;
private bool _showingGrid;
private double _acquisitionFps;
private int _imageCount;
private int _displayImageCount;
private string _bandwidth;
private string _originSize;
private string _pixelCoordinate;
private Point _centerPixelPoint = new(2048, 1500);
private PixelFormat _pixelFormat;
private int _receivedPerSecond;
private int _tuningStep = 1;
private Stretch _stretch = Stretch.Uniform;
private readonly Stretch[] _stretchs =
[
Stretch.None, Stretch.Uniform, Stretch.UniformToFill
];
private int _stretchIndex = 1;
private string _scaleRatio = "100%";
private ImageViewer _imageViewer;
private CameraViewerWindow _window;
private ScreenRecorder? _recorder;
private double _cameraPositionX;
private double _cameraPositionY;
public CameraViewerVM()
{
ToCapturePhoto = ReactiveCommand.CreateFromTask(CapturePhoto);
ToggleCaptureVideo = ReactiveCommand.Create(CaptureVideo);
ToToggleRecordingVideo = ReactiveCommand.Create(ToggleRecordingVideo);
ToggleShowCrosshair = ReactiveCommand.Create(ShowCrosshair);
ToggleShowGrid = ReactiveCommand.Create(ShowGrid);
ToChangeStretch = ReactiveCommand.Create(ChangeStretch);
ToSavePhoto = ReactiveCommand.Create(SavePhoto);
}
public bool Blank
{
get => _blank;
private set => this.RaiseAndSetIfChanged(ref _blank, value);
}
public bool CapturingVideo
{
get => _capturingVideo;
set => this.RaiseAndSetIfChanged(ref _capturingVideo, value);
}
public bool CapturingPicture
{
get => _capturingPicture;
set => this.RaiseAndSetIfChanged(ref _capturingPicture, value);
}
public Bitmap CaptureImage
{
get => _captureImage;
set => this.RaiseAndSetIfChanged(ref _captureImage, value);
}
public bool ShowingCrosshair
{
get => _showingCrosshair;
set => this.RaiseAndSetIfChanged(ref _showingCrosshair, value);
}
public bool ShowingGrid
{
get => _showingGrid;
set => this.RaiseAndSetIfChanged(ref _showingGrid, value);
}
public double AcquisitionFps
{
get => _acquisitionFps;
set => this.RaiseAndSetIfChanged(ref _acquisitionFps, value);
}
public int ImageCount
{
get => _imageCount;
set => this.RaiseAndSetIfChanged(ref _imageCount, value);
}
public int DisplayImageCount
{
get => _displayImageCount;
set => this.RaiseAndSetIfChanged(ref _displayImageCount, value);
}
public string Bandwidth
{
get => _bandwidth;
set => this.RaiseAndSetIfChanged(ref _bandwidth, value);
}
public string OriginSize
{
get => _originSize;
set => this.RaiseAndSetIfChanged(ref _originSize, value);
}
public string PixelCoordinate
{
get => _pixelCoordinate;
set => this.RaiseAndSetIfChanged(ref _pixelCoordinate, value);
}
public Point CenterPixelPoint
{
get => _centerPixelPoint;
set => this.RaiseAndSetIfChanged(ref _centerPixelPoint, value);
}
public int TuningStep
{
get => _tuningStep;
set => this.RaiseAndSetIfChanged(ref _tuningStep, value);
}
public bool IsFullScreen
{
get => _isFullScreen;
set => this.RaiseAndSetIfChanged(ref _isFullScreen, value);
}
public Stretch Stretch
{
get => _stretch;
set => this.RaiseAndSetIfChanged(ref _stretch, value);
}
public int StretchIndex
{
get => _stretchIndex;
set => this.RaiseAndSetIfChanged(ref _stretchIndex, value);
}
public bool RecordingVideo
{
get => _recordingVideo;
set => this.RaiseAndSetIfChanged(ref _recordingVideo, value);
}
public string ScaleRatio
{
get => _scaleRatio;
set => this.RaiseAndSetIfChanged(ref _scaleRatio, value);
}
public double CameraPositionX
{
get => _cameraPositionX;
set => this.RaiseAndSetIfChanged(ref _cameraPositionX, value);
}
public double CameraPositionY
{
get => _cameraPositionY;
set => this.RaiseAndSetIfChanged(ref _cameraPositionY, value);
}
public ReactiveCommand<Unit, Unit> ToCapturePhoto { get; }
public ReactiveCommand<Unit, Unit> ToSavePhoto { get; }
public ReactiveCommand<Unit, Unit> ToggleCaptureVideo { get; }
public ReactiveCommand<Unit, Unit> ToToggleRecordingVideo { get; }
public ReactiveCommand<Unit, Unit> ToChangeStretch { get; }
public ReactiveCommand<Unit, Unit> ToggleShowCrosshair { get; }
public ReactiveCommand<Unit, Unit> ToggleShowGrid { get; }
public ReactiveCommand<Unit, Unit> ToMoveToWaferHeight { get; }
public ReactiveCommand<Unit, Unit> ToMoveToCrosshairCenter { get; }
public ReactiveCommand<Unit, Unit> ToMoveToSubstrateHeight { get; }
public void InitViewer(ImageViewer imageViewer)
{
_imageViewer = imageViewer;
}
public void InitWindow(CameraViewerWindow window)
{
_window = window;
}
/// <summary>
/// 切换图片拉伸模式
/// </summary>
private void ChangeStretch()
{
StretchIndex = ++StretchIndex % _stretchs.Length;
Stretch = _stretchs[StretchIndex];
}
/// <summary>
/// 保存截图
/// </summary>
private void SavePhoto()
{
Task.Run(() =>
{
try
{
var systemParams = _systemParamsService.GetSystemParams();
var dir = systemParams.CameraViewerStoragePath ??
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
var viewBox = _imageViewer.GetViewer();
var pixelSize = new PixelSize((int)viewBox.Bounds.Width, (int)viewBox.Bounds.Height);
var dpi = new Vector(96, 96);
var renderTargetBitmap = new RenderTargetBitmap(pixelSize, dpi);
var context = renderTargetBitmap.CreateDrawingContext();
Dispatcher.UIThread.Invoke(() => { viewBox.Render(context); });
dir = Path.Combine(dir, "Dispenser", $"{DateTime.Now:yyyyMMdd}");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
using (var stream = File.OpenWrite(Path.Combine(dir, $"{DateTime.Now:yyyyMMddHHmmss}.bmp")))
{
renderTargetBitmap.Save(stream);
}
Dispatcher.UIThread.InvokeAsync(() => { new CameraViewerNotifyWindow("图片保存成功", dir).Show(); });
}
catch (Exception e)
{
Log.Error(e, "保存图片失败");
}
});
}
/// <summary>
/// 采集单张图片
/// </summary>
[Operation("采集单张图片")]
private async Task CapturePhoto()
{
var camera = CameraManager.GetCamera()!;
if (CapturingVideo)
{
// 如果正在采集则提示停止
var confirm = await ConfirmDialog.ToConfirm("正在采集中,是否停止采集?");
if (!confirm) return;
// // 先停止采集
camera.StopCaptureVideo();
}
// 采集单张图片
camera.TokePhoto();
if (camera.CapturingVideo)
{
camera.StartCaptureVideo();
}
CapturingPicture = true;
ShowingCrosshair = false;
}
/// <summary>
/// 连续采集
/// </summary>
[Operation("开启/停止连续采集")]
private void CaptureVideo()
{
ImageCount = 0;
DisplayImageCount = 0;
_receivedPerSecond = 0;
var camera = CameraManager.GetCamera()!;
if (CapturingVideo)
{
// 先停止采集
camera.StopCaptureVideo();
// 开启定时任务去更新显示信息
JobScheduler.RemoveTask(TaskName);
}
else
{
// 开始连续采集
camera.StartCaptureVideo();
// 开启定时任务去更新显示信息
JobScheduler.AddTask(TaskName, UpdateDisplayInfo, 1000);
}
CapturingVideo = !CapturingVideo;
ShowingCrosshair = false;
}
public void StopCaptureVideo()
{
CapturingVideo = false;
var camera = CameraManager.GetCamera()!;
// 先停止采集
camera.StopCaptureVideo();
// 开启定时任务去更新显示信息
JobScheduler.RemoveTask(TaskName);
ShowingCrosshair = false;
}
/// <summary>
/// 接受图像回调
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
private async Task CameraCallBack(EventType type, CameraData data)
{
await Task.Run(() =>
{
if (!CapturingPicture && !CapturingVideo) return;
var image = data.Image;
if (image.IsNullOrEmpty()) return;
_pixelFormat = image.PixelFormat;
_receivedPerSecond++;
// 更新接收到的图片数
ImageCount = CapturingVideo ? ImageCount + 1 : 1;
// 照片尺寸
OriginSize = $"{image.Width}*{image.Height}";
// 更新展示的图片数
DisplayImageCount++;
Dispatcher.UIThread.Invoke(() =>
{
if (Blank) Blank = false;
CaptureImage = image;
if (CapturingPicture) CapturingPicture = false;
});
});
}
// 定时更新显示信息
private void UpdateDisplayInfo()
{
if (OriginSize.IsNullOrEmpty()) return;
var pixelFormatSize = Image.GetPixelFormatSize(_pixelFormat) / 8;
var size = OriginSize.Split("*");
// 开启定时任务每隔1秒更新一次
Dispatcher.UIThread.InvokeAsync(() =>
{
Bandwidth = GetByteSizeString(
int.Parse(size[0]) * int.Parse(size[1]) * pixelFormatSize * _receivedPerSecond);
AcquisitionFps = _receivedPerSecond;
_receivedPerSecond = 0;
});
}
/// <summary>
/// 动态就算带宽
/// </summary>
/// <param name="sizeInBytes"></param>
/// <returns></returns>
static string GetByteSizeString(long sizeInBytes)
{
string[] sizeSuffixes = ["Bps", "Kps", "Mps", "Gps"];
if (sizeInBytes == 0)
return "0" + sizeSuffixes[0];
var magnitude = (int)Math.Log(sizeInBytes, 1024);
var adjustedSize = sizeInBytes / Math.Pow(1024, magnitude);
return $"{adjustedSize:0.##} {sizeSuffixes[magnitude]}";
}
/// <summary>
/// 控制是否显示十字辅助现
/// </summary>
private void ShowCrosshair()
{
ShowingCrosshair = !ShowingCrosshair;
}
/// <summary>
/// 屏幕录屏
/// </summary>
private void ToggleRecordingVideo()
{
if (RecordingVideo)
{
Task.Run(StopRecordingVideo);
}
else
{
Task.Run(StartRecordingVideo);
}
}
/// <summary>
/// 开始录屏
/// </summary>
[Operation("开始录屏")]
private void StartRecordingVideo()
{
if (RecordingVideo) return;
RecordingVideo = true;
var systemParams = _systemParamsService.GetSystemParams();
var dir = systemParams.CameraViewerStoragePath ??
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
dir = Path.Combine(dir, "Dispenser", $"{DateTime.Now:yyyyMMdd}");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var lastFileName = Path.Combine(dir, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".avi");
// ReSharper disable once InconsistentNaming
FourCC MJPEG_IMAGE_SHARP = "IMG#";
_recorder = new ScreenRecorder(lastFileName, MJPEG_IMAGE_SHARP, 10, 100);
OnWindowResize();
_recorder.Start();
}
// 停止录屏
[Operation("停止录屏")]
public void StopRecordingVideo()
{
if (!RecordingVideo || _recorder == null) return;
RecordingVideo = false;
var dir = _recorder.GetDirectory();
_recorder.Stop();
Dispatcher.UIThread.InvokeAsync(() => { new CameraViewerNotifyWindow("录屏保存成功", dir!).Show(); });
}
public void OnWindowResize()
{
// 支取中间的90%画面内容
if (_recorder != null)
{
var container = _imageViewer.GetContainer();
var width = container.Bounds.Width - 50;
var height = container.Bounds.Height - 50;
var left = _window.Position.X + 20;
var top = _window.Position.Y + 60;
_recorder.Width = (int)width;
_recorder.Height = (int)height;
_recorder.Left = left;
_recorder.Top = top;
}
}
public void UpdateScaleRatio(double ratio)
{
if (ratio < 1)
{
ScaleRatio = ((1 - ratio) * -100).ToString("0.##") + "%";
}
else
{
ScaleRatio = (ratio * 100).ToString("0.##") + "%";
}
}
/// <summary>
/// 控制是否显示网格线
/// </summary>
private void ShowGrid()
{
}
/// <summary>
/// 移除监听
/// </summary>
public void DisableCameraListener()
{
EventBus<CameraData>.RemoveEventHandler(EventType.CameraData, CameraCallBack);
JobScheduler.RemoveTask("CameraViewer#WatchCameraPosition");
}
private void TriggerTakePhoto()
{
try
{
var camera = CameraManager.GetCamera()!;
camera.TokePhoto();
if (camera.CapturingVideo)
{
camera.StartCaptureVideo();
}
}
catch (Exception e)
{
Log.Error(e, "坐标验证采集照片失败: ");
}
}
}