Dispenser/DispenserUI/ViewModels/Components/CameraViewerVM.cs

586 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, "坐标验证采集照片失败: ");
}
}
}