/// <summary> /// For a given color, gives information about a blob within the given bitmap that is that color /// </summary> /// <param name="bitmap">The bitmap containing the blob</param> /// <param name="color">The color of the blob</param> /// <returns>An ImageBlobInfo object containing information of the blob of the given color</returns> public ImageBlobInfo(WriteableBitmap bitmap, Color color) { Color = color; Area = 0; Centroid = new Point(0, 0); SecondRowMoment = 0; SecondMixedMoment = 0; SecondColumnMoment = 0; MaxInertia = 0; MinInertia = 0; Circularity = 0; Perimeter = 0; int stride = (bitmap.PixelWidth * bitmap.Format.BitsPerPixel + 7) / 8; int height = bitmap.PixelHeight; int width = bitmap.PixelWidth; byte[] pixelArray = new byte[bitmap.PixelHeight * stride]; bitmap.CopyPixels(pixelArray, stride, 0); // Calculate Area and Centroid double r = 0; double c = 0; for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { int index = row * stride + 4 * column; var pixel = Pixel.GetPixel(pixelArray, index); if (pixel.Color == color) { Area++; r += row; // add the row number c += column; // add the column number } } } Centroid = new Point(c / Area, r / Area); // Calculate Second-Order Moments for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { int index = row * stride + 4 * column; var pixel = Pixel.GetPixel(pixelArray, index); if (pixel.Color == color) { SecondRowMoment += Math.Pow(row - Centroid.Y, 2); SecondMixedMoment += (row - Centroid.Y) * (column - Centroid.X); SecondColumnMoment += Math.Pow(column - Centroid.X, 2); } } } SecondRowMoment /= Area; SecondMixedMoment /= Area; SecondColumnMoment /= Area; // Calculate Inertia var theta = (2 * SecondMixedMoment) / (SecondColumnMoment - SecondRowMoment); theta = Math.Atan(theta); theta /= 2; var otherTheta = theta + Math.PI / 2; var inertiaOne = Math.Pow(Math.Sin(theta), 2) * SecondColumnMoment + Math.Sin(theta) * Math.Cos(theta) * SecondMixedMoment + Math.Pow(Math.Cos(theta), 2) * SecondRowMoment; var inertiaTwo = Math.Pow(Math.Sin(otherTheta), 2) * SecondColumnMoment + Math.Sin(otherTheta) * Math.Cos(otherTheta) * SecondMixedMoment + Math.Pow(Math.Cos(otherTheta), 2) * SecondRowMoment; MaxInertia = Math.Max(inertiaOne, inertiaTwo); MinInertia = Math.Min(inertiaOne, inertiaTwo); // Calculate Circulator double mean = 0; for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { int index = row * stride + 4 * column; var pixel = Pixel.GetPixel(pixelArray, index); if (pixel.Color == color) { mean += Math.Sqrt(Math.Pow(column - Centroid.X, 2) + Math.Pow(row - Centroid.Y, 2)); } } } mean /= Area; double standardDeviation = 0; for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { int index = row * stride + 4 * column; var pixel = Pixel.GetPixel(pixelArray, index); if (pixel.Color == color) { var value = Math.Sqrt(Math.Pow(column - Centroid.X, 2) + Math.Pow(row - Centroid.Y, 2)); value -= mean; value = Math.Pow(value, 2); standardDeviation += value; } } } standardDeviation /= Area; standardDeviation = Math.Sqrt(standardDeviation); Circularity = mean / standardDeviation; // Calculate Perimeter var filteredBitmap = BitmapColorer.EraseAllButCertainColor(bitmap, color); var perimeterBitmap = BitmapOperations.GetPerimeterBitmap(filteredBitmap); byte[] perimeterPixelArray = new byte[perimeterBitmap.PixelHeight * stride]; perimeterBitmap.CopyPixels(perimeterPixelArray, stride, 0); var perimeterPixelList = BitmapOperations.BuildPerimeterPath(perimeterPixelArray, bitmap.PixelHeight, bitmap.PixelWidth, stride); for (int i = 0; i < perimeterPixelList.Count; i++) { int comparisonIndex = i + 1; if (i == perimeterPixelList.Count - 1) { comparisonIndex = 0; } // If the current pixel and the next pixel are vertical or horizontal neighbors if (perimeterPixelList[i] == perimeterPixelList[comparisonIndex] + stride || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] - stride || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] - 4 || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] + 4) { Perimeter++; } // If the current pixel and the next pixel are diagonal neighbors else if (perimeterPixelList[i] == perimeterPixelList[comparisonIndex] + stride + 4 || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] + stride - 4 || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] - stride + 4 || perimeterPixelList[i] == perimeterPixelList[comparisonIndex] - stride - 4) { Perimeter += 1.4; } } }