using System; using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; namespace XP.Common.Controls.ImageHistogram { /// /// 直方图后台计算引擎 | Histogram background computation engine /// 负责在后台线程中执行灰度值遍历和统计计算 /// internal sealed class HistogramEngine : IDisposable { /// /// 单帧计算超时时间(毫秒)| Single frame computation timeout (ms) /// private const int ComputeTimeoutMs = 5000; private CancellationTokenSource? _timeoutCts; private readonly object _lock = new(); private bool _disposed; /// /// 从 Image<Rgba32> 计算灰度直方图 | Compute histogram from Image<Rgba32> /// 使用 ITU-R BT.601 亮度公式:Gray = 0.299R + 0.587G + 0.114B /// /// 输入图像 | Input image /// 取消令牌 | Cancellation token /// 256 长度的频次数组,失败返回 null | 256-length frequency array, null on failure public Task ComputeAsync(Image image, CancellationToken ct) { if (image == null) return Task.FromResult(null); // 创建超时令牌 | Create timeout token var linkedCts = CreateLinkedTimeoutToken(ct); var linkedToken = linkedCts.Token; return Task.Run(() => { try { var width = image.Width; var height = image.Height; var histogram = new long[256]; // 遍历像素,使用亮度公式计算灰度值 | Iterate pixels, compute grayscale using luminance formula for (int y = 0; y < height; y++) { linkedToken.ThrowIfCancellationRequested(); for (int x = 0; x < width; x++) { var pixel = image[x, y]; // ITU-R BT.601 亮度公式 | ITU-R BT.601 luminance formula var gray = (int)(0.299 * pixel.R + 0.587 * pixel.G + 0.114 * pixel.B); // 钳位到 0-255 范围 | Clamp to 0-255 range gray = Math.Clamp(gray, 0, 255); histogram[gray]++; } } return (long[]?)histogram; } catch (OperationCanceledException) { // 超时或取消,返回 null | Timeout or cancelled, return null return null; } catch { // 所有异常内部捕获,不向外抛出 | Catch all exceptions internally return null; } finally { linkedCts.Dispose(); } }, linkedToken); } /// /// 从原始字节数组计算灰度直方图 | Compute histogram from raw byte array /// /// 原始像素数据 | Raw pixel data /// 图像宽度 | Image width /// 图像高度 | Image height /// 位深度(8 或 16)| Bit depth (8 or 16) /// 取消令牌 | Cancellation token /// 频次数组(8位:256长度,16位:65536长度),失败返回 null | Frequency array, null on failure public Task ComputeAsync(byte[] rawData, int width, int height, int bitDepth, CancellationToken ct) { // 参数有效性验证 | Parameter validation if (rawData == null || width <= 0 || height <= 0) return Task.FromResult(null); if (bitDepth != 8 && bitDepth != 16) return Task.FromResult(null); int expectedLength = bitDepth == 8 ? width * height : width * height * 2; if (rawData.Length != expectedLength) return Task.FromResult(null); // 创建超时令牌 | Create timeout token var linkedCts = CreateLinkedTimeoutToken(ct); var linkedToken = linkedCts.Token; return Task.Run(() => { try { if (bitDepth == 8) { return ComputeHistogram8Bit(rawData, width, height, linkedToken); } else { return ComputeHistogram16Bit(rawData, width, height, linkedToken); } } catch (OperationCanceledException) { return null; } catch { return null; } finally { linkedCts.Dispose(); } }, linkedToken); } /// /// 计算 8 位灰度直方图 | Compute 8-bit grayscale histogram /// private static long[]? ComputeHistogram8Bit(byte[] rawData, int width, int height, CancellationToken ct) { var histogram = new long[256]; int totalPixels = width * height; for (int i = 0; i < totalPixels; i++) { if (i % 65536 == 0) ct.ThrowIfCancellationRequested(); histogram[rawData[i]]++; } return histogram; } /// /// 计算 16 位灰度直方图 | Compute 16-bit grayscale histogram /// private static long[]? ComputeHistogram16Bit(byte[] rawData, int width, int height, CancellationToken ct) { var histogram = new long[65536]; int totalPixels = width * height; for (int i = 0; i < totalPixels; i++) { if (i % 65536 == 0) ct.ThrowIfCancellationRequested(); // 小端序读取 16 位值 | Read 16-bit value in little-endian int offset = i * 2; ushort value = (ushort)(rawData[offset] | (rawData[offset + 1] << 8)); histogram[value]++; } return histogram; } /// /// 创建带超时的链接取消令牌 | Create linked cancellation token with timeout /// private CancellationTokenSource CreateLinkedTimeoutToken(CancellationToken externalToken) { var timeoutCts = new CancellationTokenSource(ComputeTimeoutMs); var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeoutCts.Token); lock (_lock) { _timeoutCts?.Dispose(); _timeoutCts = timeoutCts; } return linkedCts; } /// /// 释放资源 | Dispose resources /// public void Dispose() { if (_disposed) return; _disposed = true; lock (_lock) { _timeoutCts?.Cancel(); _timeoutCts?.Dispose(); _timeoutCts = null; } } } }