feat: some analysis changes

This commit is contained in:
2026-06-08 07:37:38 +03:00
parent f1a840b23b
commit 226d35f5d5
@@ -20,14 +20,14 @@ public partial class AnalyzerService
public const int SEPARATOR_ROI_THRESHOLD = 5;
public const float CONTOUR_MAX_SIZE = 100;
public const float CONTOUR_MAX_AREA = (float)Math.PI * (CONTOUR_MAX_SIZE * CONTOUR_MAX_SIZE) / 4.0f;
public const float CONTOUR_MIN_SIZE = 12;
public const float CONTOUR_MIN_AREA = (float)Math.PI * (CONTOUR_MIN_SIZE * CONTOUR_MIN_SIZE) / 4.0f / 4.0f;
public const float CONTOUR_MAX_AREA = (float)Math.PI * (CONTOUR_MAX_SIZE * CONTOUR_MAX_SIZE) / 5.0f;
public const float CONTOUR_MIN_SIZE = 10;
public const float CONTOUR_MIN_AREA = (float)Math.PI * (CONTOUR_MIN_SIZE * CONTOUR_MIN_SIZE) / 5.0f / 5.0f;
public const int SEPARATOR_X = 519;
public const int SEPARATOR_Y = 17;
public const int SEPARATOR_WIDTH = 2989;
public const int SEPARATOR_HEIGHT = 2985;
public const int SEPARATOR_WIDTH = 2969;
public const int SEPARATOR_HEIGHT = 2965;
private class ClusterizationData
{
@@ -143,6 +143,8 @@ public partial class AnalyzerService
Features = r.Values
})
.ToList();
trainDataSet = trainDataSet.Shuffle().ToList();
_logger.LogInformation("Записей для обучения: {}", trainDataSet.Count());
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
cancellationToken.ThrowIfCancellationRequested();
@@ -160,10 +162,9 @@ public partial class AnalyzerService
if (_ml is null)
throw new InvalidOperationException();
cancellationToken.ThrowIfCancellationRequested();
_logger.LogInformation("Создание конвейера");
var pipeline = _ml.Regression.Trainers.Sdca();
var pipeline = _ml.Regression.Trainers.FastTree();
cancellationToken.ThrowIfCancellationRequested();
@@ -184,13 +185,14 @@ public partial class AnalyzerService
Features = r.Values
})
.ToList();
trainDataSet = trainDataSet.Shuffle().ToList();
_logger.LogInformation("Записей для обучения: {}", trainDataSet.Count());
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
cancellationToken.ThrowIfCancellationRequested();
_logger.LogInformation("Обучение модели");
var model = pipeline.Fit(trainData);
_regressionEngine = _ml.Model.CreatePredictionEngine<RegressionData, RegressionPrediction>(model);
model.Dispose();
@@ -230,7 +232,7 @@ public partial class AnalyzerService
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
{
if (mask.At<byte>(row, col) != 255)
if (mask.At<byte>(row, col) == 0)
continue;
var vector = new float[FEATURES_LENGTH];
foreach (var (i, image) in images.Enumerate())
@@ -250,17 +252,72 @@ public partial class AnalyzerService
});
if (prediction is null)
continue;
result.Set<float>(row, col, prediction.Value);
if (prediction.Value >= 0)
{
result.Set(row, col, prediction.Value);
values[row * ANALYSIS_IMAGE_SIZE + col] = prediction.Value;
}
}
// foreach (var contour in contours)
// {
// var contourMask = new Mat(new Size(ANALYSIS_IMAGE_SIZE, ANALYSIS_IMAGE_SIZE), MatType.CV_8UC1, new Scalar(0));
// var contourDistance = new Mat(new Size(ANALYSIS_IMAGE_SIZE, ANALYSIS_IMAGE_SIZE), MatType.CV_32FC1, new Scalar(0));
// Cv2.DrawContours(contourMask, [contour], -1, new Scalar(255), -1);
// Cv2.DistanceTransform(contourMask, contourDistance, DistanceTypes.L2, DistanceTransformMasks.Mask5);
// Cv2.Pow(contourDistance, 2, contourDistance);
// Cv2.MinMaxLoc(contourDistance, out double minVal, out double maxVal);
// contourDistance.ConvertTo(contourDistance, MatType.CV_32FC1, 1 / (maxVal - minVal), 1 * minVal / (maxVal - minVal));
// // Cv2.ConvertScaleAbs(contourDistance, contourDistance, -1, 1);
// Cv2.BitwiseNot(contourMask, contourMask);
// contourDistance.SetTo(new Scalar(1), contourMask);
// Cv2.Multiply(result, contourDistance, result);
// contourMask.Dispose();
// contourDistance.Dispose();
// }
// var maskDistance = new Mat();
// var resultDistance = new Mat();
// var temp = new Mat();
// Cv2.DistanceTransform(mask, maskDistance, DistanceTypes.L2, DistanceTransformMasks.Mask5);
// Cv2.MinMaxLoc(result, out double minValue, out double maxValue, out _, out _, mask);
// result.ConvertTo(temp, MatType.CV_8UC1, 255 / (maxValue - 0), 255 * 0 / (maxValue - 0));
// Cv2.DistanceTransform(temp, resultDistance, DistanceTypes.L2, DistanceTransformMasks.Mask5);
// Cv2.Multiply(maskDistance, resultDistance, temp);
// Cv2.Threshold(temp, temp, 0, 255, ThresholdTypes.Binary);
// for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
// for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
// if (mask.At<byte>(row, col) == 0)
// {
// result.Set(row, col, 0);
// mask.Set(row, col, 0);
// }
// maskDistance.Dispose();
// resultDistance.Dispose();
// temp.Dispose();
// var oldMean = Cv2.Mean(result, mask).Val0;
// float p001 = values[values.Count(v => v == 0) + 1];
// float p999 = values[(int)(values.Length * 0.999)];
// Cv2.Threshold(result, result, p999, p999, ThresholdTypes.Trunc);
// var zeroMask = result.LessThanOrEqual(0);
// result.SetTo(p001, zeroMask);
// zeroMask.Dispose();
// var newMean = Cv2.Mean(result, mask).Val0;
// var scale = oldMean / newMean;
// Cv2.ConvertScaleAbs(result, result, scale);
// Добавляем эрозию к результатм и маске, удаляющую края, и пересчитываем контура
// поскольку края грунул подсвечиваются близлежайщими гранулами и сепаратором,
// их нельзя считаль представительными
Cv2.Erode(result, result, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
Cv2.Erode(mask, mask, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
Cv2.FindContours(mask, out var newContours, out _, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
var oldMean = Cv2.Mean(result, mask).Val0;
values.Sort();
float p999 = values[(int)(values.Length * 0.999)];
Cv2.Threshold(result, result, p999, p999, ThresholdTypes.Trunc);
var newMean = Cv2.Mean(result, mask).Val0;
var scale = oldMean / newMean;
Cv2.ConvertScaleAbs(result, result, scale);
return (avgImage, roi, mask, contours, result, separatorType, sampleBrand);
}
catch (Exception ex)
@@ -335,7 +392,7 @@ public partial class AnalyzerService
continue;
if (!keyMap.ContainsKey(prediction.Label))
keyMap.Add(prediction.Label, ++lastKey);
result.Set<ushort>(row, col, keyMap[prediction.Label]);
result.Set(row, col, keyMap[prediction.Label]);
}
foreach (var image in images)
@@ -344,7 +401,6 @@ public partial class AnalyzerService
var valueMap = keyMap.ToDictionary(kv => kv.Value, kv => kv.Key);
var frequency = new Dictionary<string, int>();
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
{
@@ -358,13 +414,13 @@ public partial class AnalyzerService
if (valueMap[value].StartsWith(separatorPrefix))
result.Set<ushort>(row, col, 0);
if (valueMap[value].StartsWith(samplePrefix))
result.Set<ushort>(row, col, ushort.MaxValue);
result.Set(row, col, ushort.MaxValue);
}
result.ConvertTo(result, MatType.CV_8UC1, 1 / 255.0);
Cv2.MorphologyEx(result, result, MorphTypes.Close, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)), iterations: 2);
Cv2.MorphologyEx(result, result, MorphTypes.Open, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)), iterations: 2);
Cv2.MorphologyEx(result, result, MorphTypes.Close, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
Cv2.MorphologyEx(result, result, MorphTypes.Open, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
Cv2.Dilate(result, result, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
Cv2.GaussianBlur(result, result, new Size(5, 5), 1);
Cv2.Threshold(result, result, 127, 255, ThresholdTypes.Binary);
@@ -374,6 +430,9 @@ public partial class AnalyzerService
result = new Mat(new Size(ANALYSIS_IMAGE_SIZE, ANALYSIS_IMAGE_SIZE), MatType.CV_8UC1, new Scalar(0));
Cv2.DrawContours(result, contours.Where(ContourIsValid), -1, new Scalar(255), -1);
_logger.LogInformation("Найдено гранул: {}", contours.Where(ContourIsValid).Count());
_logger.LogInformation("Детекторованные обекты:\n\t{}", string.Join("\n\t", frequency.Select(f => $"{f.Key}: {f.Value}")));
return (
avgImage,
roi.Value,
@@ -524,9 +583,9 @@ public partial class AnalyzerService
// }
private static bool ContourIsValid(Point[] contour)
{
var bbox = Cv2.BoundingRect(contour);
var bbox = Cv2.MinAreaRect(contour);
var area = Cv2.ContourArea(contour);
return CONTOUR_MIN_SIZE < bbox.Width && CONTOUR_MIN_SIZE < bbox.Height && CONTOUR_MIN_AREA < area &&
CONTOUR_MAX_SIZE > bbox.Width && CONTOUR_MAX_SIZE > bbox.Height && CONTOUR_MAX_AREA > area;
return CONTOUR_MIN_SIZE < bbox.Size.Width && CONTOUR_MIN_SIZE < bbox.Size.Height && CONTOUR_MIN_AREA < area &&
CONTOUR_MAX_SIZE > bbox.Size.Width && CONTOUR_MAX_SIZE > bbox.Size.Height && CONTOUR_MAX_AREA > area;
}
}