246 lines
8.6 KiB
C#
246 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Telerik.Windows.Controls;
|
|
using Telerik.Windows.Controls.ChartView;
|
|
|
|
namespace XP.Common.Controls.ImageHistogram
|
|
{
|
|
/// <summary>
|
|
/// RadChartView 渲染适配器 | RadChartView rendering adapter
|
|
/// 负责将直方图频次数据渲染到 Telerik RadCartesianChart 控件
|
|
/// </summary>
|
|
internal sealed class ChartRenderer
|
|
{
|
|
private readonly RadCartesianChart _chart;
|
|
private readonly BarSeries _barSeries;
|
|
private readonly LinearAxis _xAxis;
|
|
|
|
/// <summary>
|
|
/// 16 位数据聚合因子 | 16-bit data aggregation factor
|
|
/// </summary>
|
|
private const int AggregationFactor = 256;
|
|
|
|
/// <summary>
|
|
/// 构造函数,接收 RadCartesianChart 实例 | Constructor, receives RadCartesianChart instance
|
|
/// </summary>
|
|
/// <param name="chart">图表控件实例 | Chart control instance</param>
|
|
/// <param name="barSeries">柱状图系列 | Bar series</param>
|
|
/// <param name="xAxis">X 轴 | X axis</param>
|
|
public ChartRenderer(RadCartesianChart chart, BarSeries barSeries, LinearAxis xAxis)
|
|
{
|
|
_chart = chart ?? throw new ArgumentNullException(nameof(chart));
|
|
_barSeries = barSeries ?? throw new ArgumentNullException(nameof(barSeries));
|
|
_xAxis = xAxis ?? throw new ArgumentNullException(nameof(xAxis));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新直方图数据 | Update histogram data
|
|
/// </summary>
|
|
/// <param name="histogram">频次数组(256 或 65536 长度)| Frequency array (256 or 65536 length)</param>
|
|
/// <param name="isLogarithmic">是否使用对数 Y 轴 | Whether to use logarithmic Y axis</param>
|
|
public void UpdateData(long[] histogram, bool isLogarithmic)
|
|
{
|
|
if (histogram == null || histogram.Length == 0)
|
|
return;
|
|
|
|
// 确定是否需要聚合(16 位数据)| Determine if aggregation needed (16-bit data)
|
|
long[] displayData;
|
|
int xAxisMax;
|
|
|
|
if (histogram.Length == 65536)
|
|
{
|
|
// 16 位数据聚合为 256 个柱体 | Aggregate 16-bit data to 256 bars
|
|
displayData = Aggregate16BitHistogram(histogram);
|
|
xAxisMax = 65535;
|
|
}
|
|
else
|
|
{
|
|
// 8 位数据直接显示 | Display 8-bit data directly
|
|
displayData = histogram;
|
|
xAxisMax = 255;
|
|
}
|
|
|
|
// 设置 X 轴范围 | Set X axis range
|
|
_xAxis.Minimum = 0;
|
|
_xAxis.Maximum = xAxisMax;
|
|
|
|
// 构建数据点 | Build data points
|
|
var dataPoints = new List<HistogramDataPoint>();
|
|
|
|
if (isLogarithmic)
|
|
{
|
|
// 对数模式:频次为 0 的不绘制 | Logarithmic mode: skip zero frequency
|
|
for (int i = 0; i < displayData.Length; i++)
|
|
{
|
|
if (displayData[i] > 0)
|
|
{
|
|
double xValue = histogram.Length == 65536
|
|
? i * AggregationFactor + AggregationFactor / 2.0
|
|
: i;
|
|
dataPoints.Add(new HistogramDataPoint
|
|
{
|
|
GrayLevel = xValue,
|
|
Frequency = displayData[i]
|
|
});
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 线性模式:所有灰度级别都绘制 | Linear mode: draw all gray levels
|
|
for (int i = 0; i < displayData.Length; i++)
|
|
{
|
|
double xValue = histogram.Length == 65536
|
|
? i * AggregationFactor + AggregationFactor / 2.0
|
|
: i;
|
|
dataPoints.Add(new HistogramDataPoint
|
|
{
|
|
GrayLevel = xValue,
|
|
Frequency = displayData[i]
|
|
});
|
|
}
|
|
}
|
|
|
|
// 更新图表数据 | Update chart data
|
|
_barSeries.ItemsSource = dataPoints;
|
|
|
|
// 设置 Y 轴范围 | Set Y axis range
|
|
UpdateYAxis(displayData, isLogarithmic);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清空图表,恢复初始状态 | Clear chart, restore initial state
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
_barSeries.ItemsSource = null;
|
|
|
|
// X 轴范围重置为 0-255 | Reset X axis range to 0-255
|
|
_xAxis.Minimum = 0;
|
|
_xAxis.Maximum = 255;
|
|
|
|
// Y 轴范围重置为 0-1 | Reset Y axis range to 0-1
|
|
SetYAxisRange(0, 1, isLogarithmic: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前数据点数量 | Get current data point count
|
|
/// </summary>
|
|
public int DataPointCount
|
|
{
|
|
get
|
|
{
|
|
if (_barSeries.ItemsSource is ICollection<HistogramDataPoint> collection)
|
|
return collection.Count;
|
|
if (_barSeries.ItemsSource is IEnumerable<HistogramDataPoint> enumerable)
|
|
return enumerable.Count();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 65536 长度的频次数组聚合为 256 个柱体 | Aggregate 65536-length array to 256 bars
|
|
/// </summary>
|
|
private static long[] Aggregate16BitHistogram(long[] histogram)
|
|
{
|
|
var aggregated = new long[256];
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
long sum = 0;
|
|
int startIndex = i * AggregationFactor;
|
|
for (int j = 0; j < AggregationFactor; j++)
|
|
{
|
|
sum += histogram[startIndex + j];
|
|
}
|
|
aggregated[i] = sum;
|
|
}
|
|
return aggregated;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新 Y 轴范围 | Update Y axis range
|
|
/// </summary>
|
|
private void UpdateYAxis(long[] displayData, bool isLogarithmic)
|
|
{
|
|
long maxValue = 0;
|
|
for (int i = 0; i < displayData.Length; i++)
|
|
{
|
|
if (displayData[i] > maxValue)
|
|
maxValue = displayData[i];
|
|
}
|
|
|
|
if (maxValue == 0)
|
|
maxValue = 1;
|
|
|
|
SetYAxisRange(0, maxValue, isLogarithmic);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置 Y 轴范围和刻度类型 | Set Y axis range and scale type
|
|
/// </summary>
|
|
private void SetYAxisRange(double minimum, double maximum, bool isLogarithmic)
|
|
{
|
|
// 获取或创建 Y 轴 | Get or create Y axis
|
|
var verticalAxis = _chart.VerticalAxis;
|
|
|
|
if (isLogarithmic)
|
|
{
|
|
// 对数刻度 | Logarithmic scale
|
|
if (verticalAxis is LogarithmicAxis logAxis)
|
|
{
|
|
logAxis.Minimum = 1;
|
|
logAxis.Maximum = maximum;
|
|
logAxis.LogarithmBase = 10;
|
|
}
|
|
else
|
|
{
|
|
// 需要切换为对数轴 | Need to switch to logarithmic axis
|
|
var newLogAxis = new LogarithmicAxis
|
|
{
|
|
Minimum = 1,
|
|
Maximum = maximum,
|
|
LogarithmBase = 10
|
|
};
|
|
_chart.VerticalAxis = newLogAxis;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 线性刻度 | Linear scale
|
|
if (verticalAxis is LinearAxis linearAxis)
|
|
{
|
|
linearAxis.Minimum = minimum;
|
|
linearAxis.Maximum = maximum;
|
|
}
|
|
else
|
|
{
|
|
// 需要切换为线性轴 | Need to switch to linear axis
|
|
var newLinearAxis = new LinearAxis
|
|
{
|
|
Minimum = minimum,
|
|
Maximum = maximum
|
|
};
|
|
_chart.VerticalAxis = newLinearAxis;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 直方图数据点模型 | Histogram data point model
|
|
/// </summary>
|
|
internal class HistogramDataPoint
|
|
{
|
|
/// <summary>
|
|
/// 灰度级别(X 轴值)| Gray level (X axis value)
|
|
/// </summary>
|
|
public double GrayLevel { get; set; }
|
|
|
|
/// <summary>
|
|
/// 像素频次(Y 轴值)| Pixel frequency (Y axis value)
|
|
/// </summary>
|
|
public long Frequency { get; set; }
|
|
}
|
|
}
|