forked from amkovkov/GranuSightSoftware2
feat: some analysis changes
This commit is contained in:
@@ -20,14 +20,14 @@ public partial class AnalyzerService
|
|||||||
public const int SEPARATOR_ROI_THRESHOLD = 5;
|
public const int SEPARATOR_ROI_THRESHOLD = 5;
|
||||||
|
|
||||||
public const float CONTOUR_MAX_SIZE = 100;
|
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_MAX_AREA = (float)Math.PI * (CONTOUR_MAX_SIZE * CONTOUR_MAX_SIZE) / 5.0f;
|
||||||
public const float CONTOUR_MIN_SIZE = 12;
|
public const float CONTOUR_MIN_SIZE = 10;
|
||||||
public const float CONTOUR_MIN_AREA = (float)Math.PI * (CONTOUR_MIN_SIZE * CONTOUR_MIN_SIZE) / 4.0f / 4.0f;
|
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_X = 519;
|
||||||
public const int SEPARATOR_Y = 17;
|
public const int SEPARATOR_Y = 17;
|
||||||
public const int SEPARATOR_WIDTH = 2989;
|
public const int SEPARATOR_WIDTH = 2969;
|
||||||
public const int SEPARATOR_HEIGHT = 2985;
|
public const int SEPARATOR_HEIGHT = 2965;
|
||||||
|
|
||||||
private class ClusterizationData
|
private class ClusterizationData
|
||||||
{
|
{
|
||||||
@@ -143,6 +143,8 @@ public partial class AnalyzerService
|
|||||||
Features = r.Values
|
Features = r.Values
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
trainDataSet = trainDataSet.Shuffle().ToList();
|
||||||
|
_logger.LogInformation("Записей для обучения: {}", trainDataSet.Count());
|
||||||
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
|
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@@ -160,10 +162,9 @@ public partial class AnalyzerService
|
|||||||
if (_ml is null)
|
if (_ml is null)
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
_logger.LogInformation("Создание конвейера");
|
_logger.LogInformation("Создание конвейера");
|
||||||
var pipeline = _ml.Regression.Trainers.Sdca();
|
|
||||||
|
var pipeline = _ml.Regression.Trainers.FastTree();
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -184,13 +185,14 @@ public partial class AnalyzerService
|
|||||||
Features = r.Values
|
Features = r.Values
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
trainDataSet = trainDataSet.Shuffle().ToList();
|
||||||
|
_logger.LogInformation("Записей для обучения: {}", trainDataSet.Count());
|
||||||
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
|
var trainData = _ml.Data.LoadFromEnumerable(trainDataSet);
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
_logger.LogInformation("Обучение модели");
|
_logger.LogInformation("Обучение модели");
|
||||||
var model = pipeline.Fit(trainData);
|
var model = pipeline.Fit(trainData);
|
||||||
|
|
||||||
_regressionEngine = _ml.Model.CreatePredictionEngine<RegressionData, RegressionPrediction>(model);
|
_regressionEngine = _ml.Model.CreatePredictionEngine<RegressionData, RegressionPrediction>(model);
|
||||||
|
|
||||||
model.Dispose();
|
model.Dispose();
|
||||||
@@ -230,7 +232,7 @@ public partial class AnalyzerService
|
|||||||
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
|
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
|
||||||
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
|
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
|
||||||
{
|
{
|
||||||
if (mask.At<byte>(row, col) != 255)
|
if (mask.At<byte>(row, col) == 0)
|
||||||
continue;
|
continue;
|
||||||
var vector = new float[FEATURES_LENGTH];
|
var vector = new float[FEATURES_LENGTH];
|
||||||
foreach (var (i, image) in images.Enumerate())
|
foreach (var (i, image) in images.Enumerate())
|
||||||
@@ -250,17 +252,72 @@ public partial class AnalyzerService
|
|||||||
});
|
});
|
||||||
if (prediction is null)
|
if (prediction is null)
|
||||||
continue;
|
continue;
|
||||||
result.Set<float>(row, col, prediction.Value);
|
if (prediction.Value >= 0)
|
||||||
values[row * ANALYSIS_IMAGE_SIZE + col] = prediction.Value;
|
{
|
||||||
|
result.Set(row, col, prediction.Value);
|
||||||
|
values[row * ANALYSIS_IMAGE_SIZE + col] = prediction.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldMean = Cv2.Mean(result, mask).Val0;
|
// foreach (var contour in contours)
|
||||||
values.Sort();
|
// {
|
||||||
float p999 = values[(int)(values.Length * 0.999)];
|
// var contourMask = new Mat(new Size(ANALYSIS_IMAGE_SIZE, ANALYSIS_IMAGE_SIZE), MatType.CV_8UC1, new Scalar(0));
|
||||||
Cv2.Threshold(result, result, p999, p999, ThresholdTypes.Trunc);
|
// var contourDistance = new Mat(new Size(ANALYSIS_IMAGE_SIZE, ANALYSIS_IMAGE_SIZE), MatType.CV_32FC1, new Scalar(0));
|
||||||
var newMean = Cv2.Mean(result, mask).Val0;
|
// Cv2.DrawContours(contourMask, [contour], -1, new Scalar(255), -1);
|
||||||
var scale = oldMean / newMean;
|
// Cv2.DistanceTransform(contourMask, contourDistance, DistanceTypes.L2, DistanceTransformMasks.Mask5);
|
||||||
Cv2.ConvertScaleAbs(result, result, scale);
|
// 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);
|
||||||
|
|
||||||
return (avgImage, roi, mask, contours, result, separatorType, sampleBrand);
|
return (avgImage, roi, mask, contours, result, separatorType, sampleBrand);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -335,7 +392,7 @@ public partial class AnalyzerService
|
|||||||
continue;
|
continue;
|
||||||
if (!keyMap.ContainsKey(prediction.Label))
|
if (!keyMap.ContainsKey(prediction.Label))
|
||||||
keyMap.Add(prediction.Label, ++lastKey);
|
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)
|
foreach (var image in images)
|
||||||
@@ -344,7 +401,6 @@ public partial class AnalyzerService
|
|||||||
var valueMap = keyMap.ToDictionary(kv => kv.Value, kv => kv.Key);
|
var valueMap = keyMap.ToDictionary(kv => kv.Value, kv => kv.Key);
|
||||||
var frequency = new Dictionary<string, int>();
|
var frequency = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
|
||||||
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
|
for (var row = 0; row < ANALYSIS_IMAGE_SIZE; row++)
|
||||||
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
|
for (var col = 0; col < ANALYSIS_IMAGE_SIZE; col++)
|
||||||
{
|
{
|
||||||
@@ -358,13 +414,13 @@ public partial class AnalyzerService
|
|||||||
if (valueMap[value].StartsWith(separatorPrefix))
|
if (valueMap[value].StartsWith(separatorPrefix))
|
||||||
result.Set<ushort>(row, col, 0);
|
result.Set<ushort>(row, col, 0);
|
||||||
if (valueMap[value].StartsWith(samplePrefix))
|
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);
|
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.Close, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
|
||||||
Cv2.MorphologyEx(result, result, MorphTypes.Open, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)), iterations: 2);
|
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.Dilate(result, result, Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3)));
|
||||||
Cv2.GaussianBlur(result, result, new Size(5, 5), 1);
|
Cv2.GaussianBlur(result, result, new Size(5, 5), 1);
|
||||||
Cv2.Threshold(result, result, 127, 255, ThresholdTypes.Binary);
|
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));
|
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);
|
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 (
|
return (
|
||||||
avgImage,
|
avgImage,
|
||||||
roi.Value,
|
roi.Value,
|
||||||
@@ -524,9 +583,9 @@ public partial class AnalyzerService
|
|||||||
// }
|
// }
|
||||||
private static bool ContourIsValid(Point[] contour)
|
private static bool ContourIsValid(Point[] contour)
|
||||||
{
|
{
|
||||||
var bbox = Cv2.BoundingRect(contour);
|
var bbox = Cv2.MinAreaRect(contour);
|
||||||
var area = Cv2.ContourArea(contour);
|
var area = Cv2.ContourArea(contour);
|
||||||
return CONTOUR_MIN_SIZE < bbox.Width && CONTOUR_MIN_SIZE < bbox.Height && CONTOUR_MIN_AREA < area &&
|
return CONTOUR_MIN_SIZE < bbox.Size.Width && CONTOUR_MIN_SIZE < bbox.Size.Height && CONTOUR_MIN_AREA < area &&
|
||||||
CONTOUR_MAX_SIZE > bbox.Width && CONTOUR_MAX_SIZE > bbox.Height && CONTOUR_MAX_AREA > area;
|
CONTOUR_MAX_SIZE > bbox.Size.Width && CONTOUR_MAX_SIZE > bbox.Size.Height && CONTOUR_MAX_AREA > area;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user