处理第二个闪电时取消按钮无法终止操作的技术问题
闪电处理流程的异常问题排查
逻辑规则
- 点击
btnStartProcessing_Click按钮启动处理,再次点击则停止处理 - 若在闪电处理过程中点击停止按钮,需等待当前闪电处理完成后再终止整个流程
问题现象
该逻辑在处理第一个、第三个及后续闪电时均可正常工作,但处理第二个闪电时出现异常:点击取消按钮后,第二个闪电处理完成后,系统并未终止操作,而是继续处理后续闪电。仅第二个闪电处理存在该问题。
相关代码
基于C#、OpenCVSharp开发的实现代码,各模块相互关联:
引用命名空间
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.UI.WebControls; using System.Windows.Forms; using OpenCvSharp; using OpenCvSharp.Extensions;
Form1类成员变量
private CancellationTokenSource cancellationTokenSource; private TaskCompletionSource<bool> lightningProcessingTaskCompletion = new TaskCompletionSource<bool>(); private VideoCapture videoCapture; private int totalFrames; private int processedFrames; private bool processingFrames; private readonly int framesToCaptureAfterLightning = 4; private string selectedLightningsFolderPath; private string selectedVideoFilePath; private bool paused = false; private bool lightningDetected = false; private List<string> lightningDirectories; private int currentLightningIndex; private List<string> lightningImages; private int currentImageIndex; private Logger logger;
btnStartProcessing_Click事件
private async void btnStartProcessing_Click(object sender, EventArgs e) { btnVideoFileFolder.Enabled = false; btnSaveLightnings.Enabled = false; if (!processingFrames) { // Get the current date and time with the desired format string startTime = DateTime.Now.ToString("dd/MM/yyyy HH:mm"); // Log the process start time logger.Log($"Process Started At: {startTime}", Color.Green); logger.Log("", Color.White); // Empty line processingFrames = true; btnStartProcessing.Text = "STOP PROCESSING"; customProgressBar1.UpdateProgress(0); videoCapture = new VideoCapture(@"d:\lightnings1.mp4"); totalFrames = (int)videoCapture.Get(VideoCaptureProperties.FrameCount); processedFrames = 0; // Create a new cancellationTokenSource for the current processing cancellationTokenSource = new CancellationTokenSource(); try { await ProcessVideoFramesAsync(cancellationTokenSource.Token); } catch (OperationCanceledException) { // Handle cancellation } finally { // Always release resources and reset cancellationTokenSource?.Dispose(); videoCapture?.Release(); processingFrames = false; btnStartProcessing.Text = "START PROCESSING"; // Enable the buttons and check conditions EnableButtonsAndCheckConditions(); if (!lightningDetected) { logger.Log("Process Cancelled At: " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"), Color.Red); logger.Log("", Color.White); // Empty line } } } else { if (!lightningDetected) { paused = false; // Reset pause state if stopping if (cancellationTokenSource != null) { cancellationTokenSource.Cancel(); await Task.Delay(100); // Allow current lightning capture to complete before stopping } } else { await ProcessingLightning(); } } }
ProcessingLightning方法
private async Task ProcessingLightning() { // Simulate the lightning processing. Replace this with your actual logic. for (int i = 0; i < 3; i++) // Simulate 3 steps of processing { await Task.Delay(1000); // Simulate a 1-second processing step if (cancellationTokenSource != null) { // Check if the main cancellation token is requested (indicating a full cancellation) if (cancellationTokenSource.IsCancellationRequested) { // Perform any cleanup or cancel logic here paused = false; // Reset pause state if stopping videoCapture?.Release(); processingFrames = false; btnStartProcessing.Text = "START PROCESSING"; // Signal that the lightning processing is canceled lightningProcessingTaskCompletion.TrySetResult(false); logger.Log("Process Cancelled At: " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"), Color.Red); logger.Log("", Color.White); // Empty line return; // Exit the method } else if (!lightningDetected) { // Check if lightningDetected is false (assuming this is when you want to cancel the current lightning) // Perform any cleanup or cancel logic here for the current lightning paused = false; // Reset pause state if stopping cancellationTokenSource.Cancel(); cancellationTokenSource.Dispose(); cancellationTokenSource = null; await Task.Delay(100); // Allow current lightning capture to complete before stopping videoCapture?.Release(); processingFrames = false; btnStartProcessing.Text = "START PROCESSING"; // Signal that the lightning processing is canceled lightningProcessingTaskCompletion.TrySetResult(false); logger.Log("Process Cancelled At: " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"), Color.Red); logger.Log("", Color.White); // Empty line return; // Exit the method } else { // Log the lightning processing canceled or ended with an empty line before and after /*logger.Log("", Color.White); // Empty line before logger.Log("Process " + (lightningDetected ? "Ended" : "Cancelled") + " At: " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"), lightningDetected ? Color.Green : Color.Red); logger.Log("", Color.White); // Empty line after*/ } } } // Signal that the lightning processing is complete lightningProcessingTaskCompletion.TrySetResult(true); }
ProcessVideoFramesAsync方法
private async Task ProcessVideoFramesAsync(CancellationToken cancellationToken) { int framesCaptured = 0; // Track the number of frames captured after lightning string currentLightningFolderPath = null; int currentLightningStartFrame = 0; int folderNameCounter = 0; await Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { using (Mat frame = new Mat()) { videoCapture.Read(frame); if (frame.Empty()) { break; } lightningDetected = LightningDetector.DetectLightning(frame); // Use the new class if (lightningDetected) { if (currentLightningFolderPath == null) { currentLightningStartFrame = processedFrames + 1; currentLightningFolderPath = Path.Combine(selectedLightningsFolderPath, $"Lightning{processedFrames + 1:D3}"); Directory.CreateDirectory(currentLightningFolderPath); } while (framesCaptured < framesToCaptureAfterLightning && !cancellationToken.IsCancellationRequested) { using (Mat capturedFrame = new Mat()) { videoCapture.Read(capturedFrame); if (capturedFrame.Empty()) { break; } string frameFileName = $"Frame{framesCaptured + 1:D2}.png"; // Example filename await SaveLightningImageAsync(capturedFrame, currentLightningFolderPath, frameFileName); DisplayFrameInPictureBox(capturedFrame); } framesCaptured++; await Task.Delay(1000, cancellationToken); // Optional delay between captured frames } } else if (currentLightningFolderPath != null) { int currentLightningEndFrame = currentLightningStartFrame + framesCaptured - 1; string frameRange = $"[{currentLightningStartFrame:D4}-{currentLightningEndFrame:D4}]"; string lightningFolderName = $"Lightning{folderNameCounter.ToString("D3")}_{frameRange}"; Directory.Move(currentLightningFolderPath, Path.Combine(selectedLightningsFolderPath, lightningFolderName)); currentLightningFolderPath = null; framesCaptured = 0; // Reset the counter folderNameCounter++; logger.Log("Lightning Detected: " + lightningFolderName, Color.Yellow); // Empty line } processedFrames++; float progressValue = (float)processedFrames / (totalFrames - 1) * 100; customProgressBar1.UpdateProgress((int)progressValue); await Task.Yield(); // Allow UI updates before continuing to the next frame } } // Finish capturing the current lightning before completing if (currentLightningFolderPath != null) { // Wait for lightning processing to complete before finishing await lightningProcessingTaskCompletion.Task; } customProgressBar1.UpdateProgress(100); // Enable the buttons and check conditions EnableButtonsAndCheckConditions(); }); }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

