243 lines
7.4 KiB
C#
243 lines
7.4 KiB
C#
using Basler.Pylon;
|
||
using System;
|
||
using System.IO;
|
||
using System.Windows;
|
||
using System.Windows.Media.Imaging;
|
||
using System.Windows.Threading;
|
||
|
||
namespace WpfApp1
|
||
{
|
||
/// <summary>
|
||
/// TimedCapture.xaml 的交互逻辑
|
||
/// </summary>
|
||
public partial class TimedCapture : Window
|
||
{
|
||
private readonly Camera _camera;
|
||
private readonly DispatcherTimer _timer = new DispatcherTimer();
|
||
|
||
private string _saveFolder =
|
||
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
|
||
|
||
private int _intervalMs = 1000;
|
||
private bool _isCapturing = false;
|
||
|
||
// 复用一个转换器,避免每次 new
|
||
private readonly PixelDataConverter _converter = new PixelDataConverter
|
||
{
|
||
OutputPixelFormat = PixelType.BGRA8packed
|
||
};
|
||
|
||
public TimedCapture(Camera camera)
|
||
{
|
||
InitializeComponent();
|
||
|
||
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
|
||
|
||
var name = _camera.CameraInfo?[CameraInfoKey.FriendlyName];
|
||
CameraInfoTextBlock.Text = string.IsNullOrWhiteSpace(name)
|
||
? "未知相机"
|
||
: name;
|
||
|
||
FolderTextBox.Text = _saveFolder;
|
||
IntervalTextBox.Text = _intervalMs.ToString();
|
||
|
||
_timer.Tick += OnTimerTick;
|
||
_timer.IsEnabled = false;
|
||
|
||
UpdateUiState();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据 _isCapturing 更新按钮文本和状态栏
|
||
/// </summary>
|
||
private void UpdateUiState(string? extraStatus = null)
|
||
{
|
||
if (_isCapturing)
|
||
{
|
||
ToggleCaptureButton.Content = "停止定时拍照";
|
||
if (extraStatus == null)
|
||
{
|
||
StatusTextBlock.Text = "状态:拍摄中";
|
||
}
|
||
else
|
||
{
|
||
StatusTextBlock.Text = extraStatus;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ToggleCaptureButton.Content = "开始定时拍照";
|
||
if (extraStatus == null)
|
||
{
|
||
StatusTextBlock.Text = "状态:已停止";
|
||
}
|
||
else
|
||
{
|
||
StatusTextBlock.Text = extraStatus;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择保存路径
|
||
/// </summary>
|
||
private void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var selected = FolderHelper.SelectFolder("请选择保存目录");
|
||
if (!string.IsNullOrWhiteSpace(selected))
|
||
{
|
||
_saveFolder = selected;
|
||
FolderTextBox.Text = _saveFolder;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单按钮:开始 / 停止 定时拍照
|
||
/// </summary>
|
||
private void ToggleCaptureButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (!_isCapturing)
|
||
{
|
||
// 当前是“未拍照”状态,点击后尝试开始
|
||
if (!StartCapture())
|
||
{
|
||
// 开始失败,不改变 _isCapturing
|
||
return;
|
||
}
|
||
|
||
_isCapturing = true;
|
||
UpdateUiState();
|
||
}
|
||
else
|
||
{
|
||
// 当前在拍照,点击后停止
|
||
StopCapture();
|
||
_isCapturing = false;
|
||
UpdateUiState();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试开始定时拍照(参数校验、目录检查等)
|
||
/// 返回 true 表示成功启动
|
||
/// </summary>
|
||
private bool StartCapture()
|
||
{
|
||
if (_camera == null || !_camera.IsOpen)
|
||
{
|
||
MessageBox.Show("相机未打开");
|
||
return false;
|
||
}
|
||
|
||
// 读取间隔
|
||
if (!int.TryParse(IntervalTextBox.Text, out var ms) || ms <= 0)
|
||
{
|
||
MessageBox.Show("请输入有效的拍摄间隔(毫秒,>0)");
|
||
return false;
|
||
}
|
||
_intervalMs = ms;
|
||
_timer.Interval = TimeSpan.FromMilliseconds(_intervalMs);
|
||
|
||
// 读取保存路径
|
||
if (string.IsNullOrWhiteSpace(FolderTextBox.Text))
|
||
{
|
||
MessageBox.Show("请选择保存文件夹");
|
||
return false;
|
||
}
|
||
|
||
_saveFolder = FolderTextBox.Text.Trim();
|
||
|
||
if (!Directory.Exists(_saveFolder))
|
||
{
|
||
try
|
||
{
|
||
Directory.CreateDirectory(_saveFolder);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show("创建文件夹失败:" + ex.Message);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
_timer.Start();
|
||
UpdateUiState("状态:拍摄中");
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止定时拍照
|
||
/// </summary>
|
||
private void StopCapture()
|
||
{
|
||
_timer.Stop();
|
||
UpdateUiState("状态:已停止");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时抓拍:每次 Tick 抓一帧并保存
|
||
/// (现在 Tick 在 UI 线程执行,如果将来发现卡顿,可以再改成后台线程抓拍)
|
||
/// </summary>
|
||
private void OnTimerTick(object? sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
using (IGrabResult grabResult = _camera.StreamGrabber.GrabOne(_intervalMs))
|
||
{
|
||
if (grabResult == null || !grabResult.GrabSucceeded)
|
||
{
|
||
UpdateUiState("状态:抓拍失败(无效图像)");
|
||
return;
|
||
}
|
||
|
||
int width = grabResult.Width;
|
||
int height = grabResult.Height;
|
||
int stride = width * 4;
|
||
int bufferSize = stride * height;
|
||
|
||
// 每次使用局部 buffer,避免多线程和尺寸问题
|
||
byte[] buffer = new byte[bufferSize];
|
||
_converter.Convert(buffer, grabResult);
|
||
|
||
var bitmap = BitmapSource.Create(
|
||
width,
|
||
height,
|
||
96,
|
||
96,
|
||
System.Windows.Media.PixelFormats.Bgra32,
|
||
null,
|
||
buffer,
|
||
stride);
|
||
|
||
string filePath = Path.Combine(
|
||
_saveFolder,
|
||
$"Timed_{DateTime.Now:yyyyMMdd_HHmmss_fff}.png");
|
||
|
||
SaveImage(bitmap, filePath);
|
||
|
||
UpdateUiState("状态:已保存 " + System.IO.Path.GetFileName(filePath));
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateUiState("状态:抓拍失败 - " + ex.Message);
|
||
}
|
||
}
|
||
|
||
private static void SaveImage(BitmapSource bitmap, string filePath)
|
||
{
|
||
var encoder = new PngBitmapEncoder();
|
||
encoder.Frames.Add(BitmapFrame.Create(bitmap));
|
||
|
||
using var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
|
||
encoder.Save(stream);
|
||
}
|
||
|
||
protected override void OnClosed(EventArgs e)
|
||
{
|
||
_timer.Stop();
|
||
base.OnClosed(e);
|
||
}
|
||
}
|
||
}
|