/// <summary> /// Any pixel values below threshold will be changed to 0. /// Any pixel values above (or equal to) threshold will be changed to 1. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="threshold">Binary threshold</param> /// <returns>New image as binary</returns> public static T AsImage <T>(T image, byte threshold) where T : Image { Bitmap binaryBitmap = AsBitmap(image, threshold); // Convert to original format return((T)ImageFormatting.ToFormat(binaryBitmap, image.RawFormat)); }
/// <summary> /// Applies localized/adaptive region thresholding to image using Chow & Kaneko method. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="horizontalRegions">Number of horizonal regions to apply</param> /// <param name="verticalRegions">Number of veritical regions to apply</param> /// <returns>New image with localized threshold applied</returns> public static T ApplyChowKanekoMethod <T>(T image, int horizontalRegions, int verticalRegions) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ApplyChowKanekoMethodDirect(ref bitmap, horizontalRegions, verticalRegions); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Any pixel values outside the threshold will be changed to min/max. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="minValue">Minimum threshold value</param> /// <param name="maxValue">Maximum threshold value</param> /// <returns>New image with threshold applied</returns> public static T ApplyMinMax <T>(T image, byte minValue, byte maxValue) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ApplyMinMaxDirect(ref bitmap, minValue, maxValue); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Applies thresholding to image using Otsu's Method. /// Returned image will consist of black (0) and white (255) values only. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <returns>New image with threshold applied</returns> public static T ApplyOtsuMethod <T>(T image) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ApplyOtsuMethodDirect(ref bitmap); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Any pixel values below threshold will be changed to 0 (black). /// Any pixel values above (or equal to) threshold will be changed to 255 (white). /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="threshold">Threshold value</param> /// <returns>New image with threshold applied</returns> public static T Apply <T>(T image, byte threshold) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ApplyDirect(ref bitmap, threshold); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Histogram Equalization will enhance general contrast /// by distributing grey levels wider and more evenly. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to equalize</param> /// <returns>Equalized image</returns> public static T HistogramEqualization <T>(T image) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); HistogramEqualizationDirect(ref bitmap); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Stretches image contrast to specified min/max values. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="min">Minimum contrast value</param> /// <param name="max">Maximum contrast value</param> /// <returns>Contrast-stretched image</returns> public static T Stretch <T>(T image, byte min, byte max) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); StretchDirect(ref bitmap, min, max); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Inverts image to negative. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to convert</param> /// <returns>Negative image</returns> public static T ToNegative <T>(T image) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ToNegativeDirect(ref bitmap); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Applies color filter to image. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to process</param> /// <param name="r">Red component to apply</param> /// <param name="g">Green component to apply</param> /// <param name="b">Blue component to apply</param> /// <returns>New image with filter applied</returns> public static T ApplyFilterRGB <T>(T image, byte r, byte g, byte b) where T : Image { Bitmap bitmap = ImageFormatting.ToBitmap(image); ApplyFilterDirectRGB(ref bitmap, r, g, b); return((T)ImageFormatting.Convert(bitmap, image.RawFormat)); }
/// <summary> /// Converts the image to a bitmap, and gets the bytes. /// </summary> /// <param name="image">Image to get bytes from</param> /// <param name="bitmapData">Data relating to bitmap</param> /// <returns>Image bytes (without header info)</returns> public static byte[] FromImage(Image image, out BitmapData bitmapData) { if (image is Bitmap bmp) { return(GetBitmapBytes(bmp, out bitmapData)); } else { using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { return(GetBitmapBytes(bitmap, out bitmapData)); } } }
/// <summary> /// Get the average pixel value between min and max. /// </summary> /// <param name="image">Image to process</param> /// <param name="min">Minimum byte value</param> /// <param name="max">Maximum byte value</param> /// <returns>Average pixel intensity</returns> public static byte GetAverageValue(Image image, byte min, byte max) { if (image is Bitmap bmp) { return(GetAverageBitmapValue(bmp, min, max)); } else { using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { return(GetAverageBitmapValue(bitmap, min, max)); } } }
/// <summary> /// Gets the minimum & maximum pixel value within an image. /// For color images, min/max pixel avg is returned. /// </summary> /// <param name="image">Image to search</param> /// <param name="findMin">Determines whether to return min value</param> /// <param name="findMax">Determines whether to return max value</param> /// <returns>min, max bytes</returns> private static Tuple <byte, byte> GetMinMaxValue(Image image, bool findMin, bool findMax) { if (image is Bitmap bmp) { return(GetMinMaxBitmapValue(bmp, findMin, findMax)); } else { using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { return(GetMinMaxBitmapValue(bitmap, findMin, findMax)); } } }
/// <summary> /// Applies multiple convolution kernels to source image, then combines the results. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to apply kernel to</param> /// <param name="convolutionTypes">Convolutions to apply</param> /// <returns>Image with all kernels combined</returns> public static T ApplyKernelsThenCombine <T>(T image, params ConvolutionType[] convolutionTypes) where T : Image { if (image is Bitmap bmp) { return((T)(Image)ApplyKernelsThenCombine(bmp, convolutionTypes)); } else { // Convert to bitmap for image processing using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { using (Bitmap combinedBitmap = ApplyKernelsThenCombine(bitmap, convolutionTypes)) { // Return processed image to original format return((T)ImageFormatting.ToFormat(combinedBitmap, image.RawFormat)); } } } }
/// <summary> /// Applies convolution matrix/kernel to image. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to apply kernel to</param> /// <param name="kernelMatrix">Kernel matrix</param> /// <returns>Image with kernel applied</returns> public static T ApplyKernel <T>(T image, int[,] kernelMatrix) where T : Image { // Skip conversion if (image is Bitmap bmp) { return((T)(Image)ApplyKernel(bmp, kernelMatrix)); } else { // Convert to bitmap for image processing using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { // Apply filter to bitmap using (Bitmap bitmapWithFilter = ApplyKernel(bitmap, kernelMatrix)) { // Covert processed image to original format return((T)ImageFormatting.ToFormat(bitmapWithFilter, image.RawFormat)); } } } }
/// <summary> /// Crops an image based on the crop region. /// </summary> /// <typeparam name="T">Image type to process and return</typeparam> /// <param name="image">Image to crop</param> /// <param name="cropRegion">Region within image to crop</param> /// <returns>Cropped image</returns> public static T ByRegion <T>(T image, Rectangle cropRegion) where T : Image { // Check if already a bitmap if (image is Bitmap bmp) { return((T)(Image)CropBitmap(bmp, cropRegion)); } else { // Convert to bitmap using (Bitmap bitmap = ImageFormatting.ToBitmap(image)) { // Crop as bitmap using (Image croppedBitmap = CropBitmap(bitmap, cropRegion)) { // Restore original image format return((T)ImageFormatting.ToFormat(croppedBitmap, image.RawFormat)); } } } }
/// <summary> /// Combines multiple images together. /// (Pixel values combined via bitwise or, not averaged) /// </summary> /// <param name="images">Images to combine</param> /// <returns>New combined image as bitmap</returns> public static Bitmap All<T>(IEnumerable<T> images) where T : Image { // Check have at least 1 image if (!images.Any()) { return null; } // Use first image as starting point Bitmap combinedImage = ImageFormatting.ToBitmap(images.ElementAt(0)); // Get bytes for image byte[] rgbValues1 = ImageEdit.Begin(combinedImage, out BitmapData bmpData1); try { // Get image 1 data int pixelDepth1 = bmpData1.GetPixelDepth(); int pixelDepthWithoutAlpha = Math.Min(pixelDepth1, Constants.PixelDepthRGB); int stride1 = bmpData1.Stride; int width1 = bmpData1.GetStrideWithoutPadding(); int height1 = bmpData1.Height; // Add additional images to first foreach (Image image in images.Skip(1)) { // Only reading this image byte[] rgbValues2 = ImageBytes.FromImage(image, out BitmapData bmpData2); // Check both images are color or B&W if (pixelDepth1 == bmpData2.GetPixelDepth()) { // Protect against different sized images int limit = Math.Min(bmpData1.GetSafeArrayLimitForImage(rgbValues1), bmpData2.GetSafeArrayLimitForImage(rgbValues2)); int minHeight = Math.Min(height1, bmpData2.Height); int minStride = Math.Min(stride1, bmpData2.Stride); int minWidth = Math.Min(width1, bmpData2.GetStrideWithoutPadding()); for (int y = 0; y < minHeight; y++) { // Images may have extra bytes per row to pad for CPU addressing. // so need to ensure we traverse to the correct byte when moving between rows. int offset = y * minStride; for (int x = 0; x < minWidth; x += pixelDepth1) { int i = offset + x; if (i < limit) { for (int j = 0; j < pixelDepthWithoutAlpha; j++) { // Combine images rgbValues1[i + j] |= rgbValues2[i + j]; } } else { break; } } } } else { throw new ArgumentException("Not all images have the same color depth"); } } } finally { // Release combined image ImageEdit.End(combinedImage, bmpData1, rgbValues1); } return combinedImage; }