/// <summary> /// Process image looking for interest points. /// </summary> /// /// <param name="image">Source image data to process.</param> /// /// <returns>Returns list of found features points.</returns> /// /// <exception cref="UnsupportedImageFormatException"> /// The source image has incorrect pixel format. /// </exception> /// public List <double[]> ProcessImage(UnmanagedImage image) { // check image format if ( (image.PixelFormat != PixelFormat.Format8bppIndexed) && (image.PixelFormat != PixelFormat.Format24bppRgb) && (image.PixelFormat != PixelFormat.Format32bppRgb) && (image.PixelFormat != PixelFormat.Format32bppArgb) ) { throw new UnsupportedImageFormatException("Unsupported pixel format of the source image."); } // make sure we have grayscale image UnmanagedImage grayImage = null; if (image.PixelFormat == PixelFormat.Format8bppIndexed) { grayImage = image; } else { // create temporary grayscale image grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image); } // get source image size int width = grayImage.Width; int height = grayImage.Height; matrix = new GrayLevelCooccurrenceMatrix(distance, CooccurrenceDegree.Degree0, true, autoGray); if (cellSize > 0) { int cellCountX = (int)Math.Floor(width / (double)cellSize); int cellCountY = (int)Math.Floor(height / (double)cellSize); features = new HaralickDescriptorDictionary[cellCountX, cellCountY]; // For each cell for (int i = 0; i < cellCountX; i++) { for (int j = 0; j < cellCountY; j++) { var featureDict = new HaralickDescriptorDictionary(); Rectangle region = new Rectangle( i * cellSize, j * cellSize, cellSize, cellSize); foreach (CooccurrenceDegree degree in degrees) { matrix.Degree = degree; double[,] glcm = matrix.Compute(grayImage, region); featureDict[degree] = new HaralickDescriptor(glcm); } features[i, j] = featureDict; } } } else { features = new HaralickDescriptorDictionary[1, 1]; features[0, 0] = new HaralickDescriptorDictionary(); foreach (CooccurrenceDegree degree in degrees) { matrix.Degree = degree; double[,] glcm = matrix.Compute(grayImage); features[0, 0][degree] = new HaralickDescriptor(glcm); } } // Free some resources which wont be needed anymore if (image.PixelFormat != PixelFormat.Format8bppIndexed) { grayImage.Dispose(); } var blocks = new List <double[]>(); switch (mode) { case HaralickMode.Average: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Average(featureCount)); } break; case HaralickMode.AverageWithRange: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.AverageWithRange(featureCount)); } break; case HaralickMode.Combine: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Combine(featureCount)); } break; case HaralickMode.NormalizedAverage: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Normalize(featureCount)); } break; } if (normalize) { var sum = new double[featureCount]; foreach (double[] block in blocks) { for (int i = 0; i < sum.Length; i++) { sum[i] += block[i]; } } foreach (double[] block in blocks) { for (int i = 0; i < sum.Length; i++) { block[i] /= sum[i]; } } } return(blocks); }
/// <summary> /// This method should be implemented by inheriting classes to implement the /// actual feature extraction, transforming the input image into a list of features. /// </summary> /// protected override IEnumerable <FeatureDescriptor> InnerTransform(UnmanagedImage image) { // TODO: Improve memory usage of this method by // caching into class variables whenever possible // make sure we have grayscale image UnmanagedImage grayImage = null; if (image.PixelFormat == PixelFormat.Format8bppIndexed) { grayImage = image; } else { // create temporary grayscale image grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image); } // get source image size int width = grayImage.Width; int height = grayImage.Height; this.matrix = new GrayLevelCooccurrenceMatrix(distance, CooccurrenceDegree.Degree0, normalize: true, autoGray: autoGray); if (cellSize > 0) { int cellCountX = (int)Math.Floor(width / (double)cellSize); int cellCountY = (int)Math.Floor(height / (double)cellSize); this.features = new HaralickDescriptorDictionary[cellCountX, cellCountY]; // For each cell for (int i = 0; i < cellCountX; i++) { for (int j = 0; j < cellCountY; j++) { var dict = new HaralickDescriptorDictionary(); var region = new Rectangle(i * cellSize, j * cellSize, cellSize, cellSize); foreach (CooccurrenceDegree degree in degrees) { matrix.Degree = degree; double[,] glcm = matrix.Compute(grayImage, region); dict[degree] = new HaralickDescriptor(glcm); } this.features[i, j] = dict; } } } else { var dict = new HaralickDescriptorDictionary(); foreach (CooccurrenceDegree degree in degrees) { matrix.Degree = degree; double[,] glcm = matrix.Compute(grayImage); dict[degree] = new HaralickDescriptor(glcm); } this.features = new HaralickDescriptorDictionary[, ] { { dict } }; } // Free some resources which wont be needed anymore if (image.PixelFormat != PixelFormat.Format8bppIndexed) { grayImage.Dispose(); } var blocks = new List <FeatureDescriptor>(); switch (mode) { case HaralickMode.Average: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Average(featureCount)); } break; case HaralickMode.AverageWithRange: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.AverageWithRange(featureCount)); } break; case HaralickMode.Combine: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Combine(featureCount)); } break; case HaralickMode.NormalizedAverage: foreach (HaralickDescriptorDictionary feature in features) { blocks.Add(feature.Normalize(featureCount)); } break; } if (normalize) { // TODO: Remove this block and instead propose a general architecture // for applying normalizations to descriptor blocks foreach (FeatureDescriptor block in blocks) { block.Descriptor.Divide(block.Descriptor.Euclidean() + epsilon, result: block.Descriptor); } } return(blocks); }