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 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);
|
||||
values[row * ANALYSIS_IMAGE_SIZE + col] = prediction.Value;
|
||||
if (prediction.Value >= 0)
|
||||
{
|
||||
result.Set(row, col, prediction.Value);
|
||||
values[row * ANALYSIS_IMAGE_SIZE + col] = prediction.Value;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// 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);
|
||||
|
||||
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,
|
||||
@@ -504,7 +563,7 @@ public partial class AnalyzerService
|
||||
// Mat avgImageClone = new Mat();
|
||||
// Mat mask = new Mat();
|
||||
// Rect? roi = null;
|
||||
|
||||
|
||||
// avgImage.CopyTo(avgImageClone);
|
||||
|
||||
// avgImageClone = avgImageClone.CvtColor(ColorConversionCodes.BGR2GRAY);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user