Базовый файл:

Файл различий (после сравнения выполнено) см. маленькую красную рамку вокруг данных графика:

Эта работа направлена на то, чтобы избежать проблем, подобных описанным ниже, но после почти двух недель разработки и настройки кода у меня начинают заканчиваться идеи. Код, который я сейчас использую, приведен ниже со следующими параметрами:
1. baselinePath -> This is simply the path to the baseline image
2. currentImageScreenshot - The current screenshot to compare against baseline
3. testName -> string containing a test name for debugging and logging purposes
4. imageConfig -> Object containing threshold values
public static void CompareImagesForDifferences(string baselineImagePath, Screenshot currentImageScreenshot, string testName, ImageComparisonConfig imageConfig)
{
string currentImagePath = SaveCurrentImage(currentImageScreenshot, testName, imageConfig);
Mat baselineImage = LoadImage(baselineImagePath);
Mat currentImage = LoadImage(currentImagePath);
ResizeImage(baselineImage, currentImage);
Mat baselineGray = ConvertToGrayscale(baselineImage);
Mat currentGray = ConvertToGrayscale(currentImage);
double ssimScore = ComputeSSIM(baselineGray, currentGray, out Mat ssimMap);
if (ssimScore >= double.Parse(imageConfig.GetSSIMThresholdSetting()))
{
// Images are identical
Logger.Info("Images are similar. No significant differences detected.");
return;
}
if (isSignificantChangesBetweenImages(baselineImage, currentImage, ssimMap, imageConfig, out Mat filledImage))
{
string diffImagePath = $@"{imageConfig.GetFailuresPath()}\\{testName}_Diff.png";
SaveDiffImage(filledImage, testName, imageConfig, diffImagePath, baselineImagePath);
}
}
В коде ниже кроется проблема. Здесь рассчитываются различия. Метод принимает 2 объекта Mat, содержащие базовое и новое изображение, а также ssimMap и конфигурацию.
private static bool isSignificantChangesBetweenImages(Mat baselineImage, Mat currentImage, Mat ssimMap, ImageComparisonConfig imageConfig, out Mat filledImage)
{
filledImage = currentImage.Clone();
Mat diff = new Mat();
ssimMap.ConvertTo(diff, MatType.CV_8UC1, 255);
Mat thresh = new Mat();
Cv2.Threshold(diff, thresh, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);
Point[][] contourDifferencePoints;
HierarchyIndex[] hierarchyIndex;
Cv2.FindContours(thresh, out contourDifferencePoints, out hierarchyIndex, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
return DrawSignificantChanges(baselineImage, contourDifferencePoints, imageConfig, filledImage);
}
Приведенный ниже метод затем нарисует точки контура (если таковые имеются). Я считаю, что что-то внутри этого кода создает ложные срабатывания. Я добавил сюда некоторый код, чтобы игнорировать любые изменения вокруг границы изображения, поскольку в этом конкретном приложении вокруг границы изменений обычно не происходит. В настоящее время для imageConfig.GetPixelToleranceSetting() установлено значение 10. Следующим моим предположением было бы увеличить это число до 15 или 20
private static bool DrawSignificantChanges(Mat baselineImage, Point[][] contours, ImageComparisonConfig imageConfig, Mat filledImage, double minAreaRatio = 0.0001, double maxAreaRatio = 0.1)
{
bool hasSignificantChanges = false;
double totalImageArea = baselineImage.Width * baselineImage.Height;
double minArea = totalImageArea * minAreaRatio;
double maxArea = totalImageArea * maxAreaRatio;
foreach (var contour in contours)
{
double area = Cv2.ContourArea(contour);
if (area < minArea || area > maxArea) continue;
Rect boundingRect = Cv2.BoundingRect(contour);
// Ignore changes near the image border
int borderThreshold = 5;
if (boundingRect.X = baselineImage.Height - borderThreshold)
{
continue;
}
// Check if the difference is significant enough
using (Mat roi = new Mat(baselineImage, boundingRect))
{
Scalar mean = Cv2.Mean(roi);
if (mean.Val0 < int.Parse(imageConfig.GetPixelToleranceSetting())) // Adjust this threshold as needed
{
continue;
}
}
// Draw Rectangle shape in red around the differences
Cv2.Rectangle(filledImage, boundingRect, new Scalar(0, 0, 255), 2);
hasSignificantChanges = true;
}
return hasSignificantChanges;
}
Наконец, метод ComputeSSIM (я взял его из сообщения, написанного на Python и преобразованного в CSharp)
public static double StructuralSimilarityIndex(Mat img1, Mat img2, out Mat diff)
{
const double K1 = 0.01;
const double K2 = 0.03;
const double L = 255;
// Constants for SSIM calculation
double c1 = Math.Pow(K1 * L, 2);
double c2 = Math.Pow(K2 * L, 2);
// Convert images to floating point
using var f1 = new Mat();
using var f2 = new Mat();
img1.ConvertTo(f1, MatType.CV_32F);
img2.ConvertTo(f2, MatType.CV_32F);
// Compute means
using var mu1 = new Mat();
using var mu2 = new Mat();
Cv2.GaussianBlur(f1, mu1, new Size(11, 11), 1.5);
Cv2.GaussianBlur(f2, mu2, new Size(11, 11), 1.5);
// Compute squares
using var mu1Sq = mu1.Mul(mu1);
using var mu2Sq = mu2.Mul(mu2);
using var mu1Mu2 = mu1.Mul(mu2);
// Compute variances and covariance
using var temp1 = new Mat();
using var temp2 = new Mat();
using var sigma1Sq = new Mat();
using var sigma2Sq = new Mat();
using var sigma12 = new Mat();
Cv2.GaussianBlur(f1.Mul(f1), temp1, new Size(11, 11), 1.5);
Cv2.GaussianBlur(f2.Mul(f2), temp2, new Size(11, 11), 1.5);
Cv2.Subtract(temp1, mu1Sq, sigma1Sq);
Cv2.Subtract(temp2, mu2Sq, sigma2Sq);
Cv2.GaussianBlur(f1.Mul(f2), sigma12, new Size(11, 11), 1.5);
Cv2.Subtract(sigma12, mu1Mu2, sigma12);
// Compute SSIM
using var numerator1 = new Mat();
using var numerator2 = new Mat();
using var denominator1 = new Mat();
using var denominator2 = new Mat();
Cv2.Multiply(mu1Mu2, 2, numerator1);
Cv2.Add(numerator1, Scalar.All(c1), numerator1);
Cv2.Multiply(sigma12, 2, numerator2);
Cv2.Add(numerator2, Scalar.All(c2), numerator2);
Cv2.Add(mu1Sq, mu2Sq, denominator1);
Cv2.Add(denominator1, Scalar.All(c1), denominator1);
Cv2.Add(sigma1Sq, sigma2Sq, denominator2);
Cv2.Add(denominator2, Scalar.All(c2), denominator2);
using var ssimMap = new Mat();
using var temp = new Mat();
Cv2.Multiply(numerator1, numerator2, temp);
Cv2.Multiply(denominator1, denominator2, ssimMap);
Cv2.Divide(temp, ssimMap, ssimMap);
// Calculate mean SSIM
var mssim = Cv2.Mean(ssimMap);
// Calculate difference map for visualization
diff = new Mat();
Cv2.Absdiff(img1, img2, diff);
Cv2.Normalize(diff, diff, 0, 255, NormTypes.MinMax);
diff.ConvertTo(diff, MatType.CV_8UC1);
return mssim.Val0;
}
Подробнее здесь: https://stackoverflow.com/questions/791 ... -sharp-net
Мобильная версия