/// <summary> /// Applies color filter to image. /// (Safer if source image was compressed, may be additional bytes per row) /// </summary> /// <param name="imageBytes">Color image bytes 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> /// <param name="bmpData">Image dimension info</param> public static void ApplyFilterDirectRGB(byte[] imageBytes, byte r, byte g, byte b, BitmapData bmpData) { if (!bmpData.IsColor()) { throw new ArgumentException("Image is not color, RGB filter cannot be applied."); } int stride = bmpData.Stride; int stridePadding = bmpData.GetStridePaddingLength(); int width = stride - stridePadding; int height = bmpData.Height; int limit = bmpData.GetSafeArrayLimitForImage(imageBytes); // May also have alpha byte int pixelDepth = bmpData.GetPixelDepth(); // Apply mask for each color pixel for (int y = 0; y < height; 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. // I.e. not divisible by 3 int offset = y * stride; for (int x = 0; x < width; x += pixelDepth) { int i = offset + x; if (i < limit) { // Red (LSB) imageBytes[i + 2] &= r; // Green imageBytes[i + 1] &= g; // Blue (MSB) imageBytes[i] &= b; } } } }
/// <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> /// <param name="imageBytes">Image bytes</param> /// <param name="bitmapData">Pixel depth</param> /// <param name="threshold">Threshold value</param> private static void ApplyDirect(byte[] imageBytes, BitmapData bitmapData, byte threshold) { if (bitmapData.IsColor()) { int pixelDepth = bitmapData.GetPixelDepth(); int stride = bitmapData.Stride; int width = bitmapData.GetStrideWithoutPadding(); int height = bitmapData.Height; int limit = bitmapData.GetSafeArrayLimitForImage(imageBytes); // Exclude alpha/transparency byte when averaging int thresholdByteLength = Math.Min(pixelDepth, Constants.PixelDepthRGB); int pixelSum; bool belowThreshold; // Find distribution of pixels at each level for (int y = 0; y < height; 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 * stride; for (int x = 0; x < width; x += pixelDepth) { int i = offset + x; if (i < limit) { pixelSum = 0; // Sum each pixel component for (int j = 0; j < thresholdByteLength && i + j < imageBytes.Length; j++) { pixelSum += imageBytes[i + j]; } // Compare average to threshold belowThreshold = (pixelSum / thresholdByteLength) < threshold; // Apply threshold for (int j = 0; j < thresholdByteLength && i + j < imageBytes.Length; j++) { imageBytes[i + j] = belowThreshold ? byte.MinValue : byte.MaxValue; } } else { break; } } } } else { // Apply threshold value to image for (int i = 0; i < imageBytes.Length; i++) { imageBytes[i] = imageBytes[i] < threshold ? byte.MinValue : byte.MaxValue; } } }
/// <summary> /// Stretches image contrast to specified min/max values. /// </summary> /// <param name="imageBytes">Image bytes (Grayscale/RGB)</param> /// <param name="bmpData">Info on image properties</param> /// <param name="min">Minimum contrast value</param> /// <param name="max">Maximum contrast value</param> public static void StretchDirect(byte[] imageBytes, BitmapData bmpData, byte min, byte max) { int pixelDepth = bmpData.GetPixelDepth(); bool isColor = bmpData.IsColor(); // Initialize with opposite values byte highest = byte.MinValue; byte lowest = byte.MaxValue; byte val; // Bitmap converted from jpeg can potentially have array with extra odd byte. int limit = bmpData.GetSafeArrayLimitForImage(imageBytes); ////////////////////////////////////// // First find current contrast range ////////////////////////////////////// for (int i = 0; i < limit; i += pixelDepth) { if (isColor) { val = (byte)((imageBytes[i] + imageBytes[i + 1] + imageBytes[i + 2]) / 3); } else { val = imageBytes[i]; } // Check contrast ranges if (val < lowest) { lowest = val; } if (val > highest) { highest = val; } } // Constant for loop double maxMinusMin = max - min; // Exclude alpha/transparency byte int pixelDepthWithoutAlpha = Math.Min(pixelDepth, Constants.PixelDepthRGB); ////////////////////////////////////// // Now contrast stretch image ////////////////////////////////////// for (int i = 0; i < limit; i += pixelDepth) { for (int j = 0; j < pixelDepthWithoutAlpha; j++) { // Contrast-stretch value val = (byte)(((imageBytes[i + j] - lowest) * (maxMinusMin / (highest - lowest))) + min); // Check limits if (val < lowest) { val = min; } else if (val > highest) { val = max; } imageBytes[i + j] = val; } } }