using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace ImageProcessing { public class BitmapFilter { static public float GetMedian( float[] array) { float[] tempArray = array; int count = tempArray.Length; Array.Sort(tempArray); float medianValue = 0; if (count % 2 == 0) { // count is even, need to get the middle two elements, add them together, then divide by 2 float middleElement1 = tempArray[(count / 2) - 1]; float middleElement2 = tempArray[(count / 2)]; medianValue = (middleElement1 + middleElement2) / 2; } else { // count is odd, simply get the middle element. medianValue = tempArray[(count / 2)]; } return medianValue; } static public float[] ComputeLocalMean(float[] gray, int width, int height) { int kernelWidth = 5; float[] tab = new float[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { tab[x + y * width] = ComputeMean(x, y, gray, width, height, kernelWidth); } } return tab; } private static float ComputeMean(int x, int y, float[] gray, int width, int height, int kernelWidth) { float mean = 0; float count = 0; for (int yy = Math.Max(0, y - kernelWidth); yy < Math.Min(y + kernelWidth, height); yy++) { for (int xx = Math.Max(0, x - kernelWidth); xx < Math.Min(x + kernelWidth, width); xx++) { mean += gray[xx + yy * width]; count++; } } mean /= count; return mean; } static public float[] HorizontalThreshold(float[] gray, int width, int height) { float[] tab = new float[width * height]; float[] line = new float[width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { line[x] = gray[x + y * width]; } float med = GetMedian(line); for (int x = 0; x < width; x++) { float diff = gray[x + y * width] - med; if (diff < 0) diff = 0; else diff = 1; tab[x + y * width] = diff; } } return tab; } static public bool Invert(Bitmap b) { // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; int nWidth = b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < nWidth; ++x) { p[0] = (byte)(255 - p[0]); ++p; } p += nOffset; } } b.UnlockBits(bmData); return true; } static public Bitmap GenerateBitmap(float[] gray, int width, int height) { Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { p[0] = p[1] = p[2] = (byte)(gray[y * b.Width + x]*255); p += 3; } p += nOffset; } } b.UnlockBits(bmData); return b; } static public float GenericScaleF(float input, float i1, float o1, float i2, float o2) { if (i2 == i1) return ((o2 + o1) / 2.0f); float a = (o2 - o1) / (i2 - i1); float b = o1 - a * i1; return (a * input + b); } static public ColorRGB[] ColorRGBBitmap(Bitmap b) { ColorRGB[] tab = new ColorRGB[b.Width * b.Height]; Bitmap des = new Bitmap(b); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { tab[y * b.Width + x].R = p[2]; tab[y * b.Width + x].G = p[1]; tab[y * b.Width + x].B = p[0]; p += 3; } p += nOffset; } } b.UnlockBits(bmData); return tab; } static public ColorHSL[] ColorLSHBitmap(Bitmap b) { ColorHSL[] tab = new ColorHSL[b.Width * b.Height]; Bitmap des = new Bitmap(b); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; byte red, green, blue; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { blue = p[0]; green = p[1]; red = p[2]; tab[y * b.Width + x] = ColorTools.RGB2HSL( red, green, blue); p += 3; } p += nOffset; } } b.UnlockBits(bmData); return tab; } static public Bitmap GenerateBitmapFromHSL(ColorHSL[] col, int width, int height) { Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { ColorHSL hsl = col[y * b.Width + x]; Color c = ColorTools.HSL2RGB(hsl.H,hsl.S,hsl.L,1); p[0] = c.B; p[1] = c.G; p[2] = c.R; p += 3; } p += nOffset; } } b.UnlockBits(bmData); return b; } static public float[] GrayScaleFloat(Bitmap b) { float[] tab = new float[b.Width * b.Height]; Bitmap des = new Bitmap(b); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; byte red, green, blue; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { blue = p[0]; green = p[1]; red = p[2]; tab[y *b.Width + x] = (float)(.299 * red + .587 * green + .114 * blue)/255.0f; p += 3; } p += nOffset; } } b.UnlockBits(bmData); return tab; } static public Bitmap GrayScaleBitmap(Bitmap b) { Bitmap des = new Bitmap(b); // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); BitmapData bmDataDest = des.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; System.IntPtr Scan0Dest = bmDataDest.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; byte* pDest = (byte*)(void*)Scan0Dest; int nOffset = stride - b.Width * 3; byte red, green, blue; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { blue = p[0]; green = p[1]; red = p[2]; pDest[0] = pDest[1] = pDest[2] = (byte)(.299 * red + .587 * green + .114 * blue); p += 3; pDest += 3; } p += nOffset; pDest += nOffset; } } b.UnlockBits(bmData); des.UnlockBits(bmDataDest); return des; } static public float[] Normalize(float[] grayf, out float min, out float max, out float mean) { mean = 0; min = float.MaxValue; max = 0; float[] res = new float[grayf.Length]; for (int i = 0; i < grayf.Length; i++) { if (grayf[i] > max) max = grayf[i]; if (grayf[i] < min) min = grayf[i]; mean += grayf[i]; } mean /= grayf.Length; for (int i = 0; i < grayf.Length; i++) { res[i] = GenericScaleF( grayf[i], min,0,max,1); } return res; } static public bool Brightness(Bitmap b, int nBrightness) { if (nBrightness < -255 || nBrightness > 255) return false; // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; int nVal = 0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; int nWidth = b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < nWidth; ++x) { nVal = (int)(p[0] + nBrightness); if (nVal < 0) nVal = 0; if (nVal > 255) nVal = 255; p[0] = (byte)nVal; ++p; } p += nOffset; } } b.UnlockBits(bmData); return true; } static public bool Contrast(Bitmap b, sbyte nContrast) { if (nContrast < -100) return false; if (nContrast > 100) return false; double pixel = 0, contrast = (100.0 + nContrast) / 100.0; contrast *= contrast; int red, green, blue; // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { blue = p[0]; green = p[1]; red = p[2]; pixel = red / 255.0; pixel -= 0.5; pixel *= contrast; pixel += 0.5; pixel *= 255; if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; p[2] = (byte)pixel; pixel = green / 255.0; pixel -= 0.5; pixel *= contrast; pixel += 0.5; pixel *= 255; if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; p[1] = (byte)pixel; pixel = blue / 255.0; pixel -= 0.5; pixel *= contrast; pixel += 0.5; pixel *= 255; if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; p[0] = (byte)pixel; p += 3; } p += nOffset; } } b.UnlockBits(bmData); return true; } static public bool Gamma(Bitmap b, double red, double green, double blue) { if (red < .2 || red > 5) return false; if (green < .2 || green > 5) return false; if (blue < .2 || blue > 5) return false; byte[] redGamma = new byte[256]; byte[] greenGamma = new byte[256]; byte[] blueGamma = new byte[256]; for (int i = 0; i < 256; ++i) { redGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / red)) + 0.5)); greenGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / green)) + 0.5)); blueGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / blue)) + 0.5)); } // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { p[2] = redGamma[p[2]]; p[1] = greenGamma[p[1]]; p[0] = blueGamma[p[0]]; p += 3; } p += nOffset; } } b.UnlockBits(bmData); return true; } static public bool Color(Bitmap b, int red, int green, int blue) { if (red < -255 || red > 255) return false; if (green < -255 || green > 255) return false; if (blue < -255 || blue > 255) return false; // GDI+ still lies to us - the return format is BGR, NOT RGB. BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte* p = (byte*)(void*)Scan0; int nOffset = stride - b.Width * 3; int nPixel; for (int y = 0; y < b.Height; ++y) { for (int x = 0; x < b.Width; ++x) { nPixel = p[2] + red; nPixel = Math.Max(nPixel, 0); p[2] = (byte)Math.Min(255, nPixel); nPixel = p[1] + green; nPixel = Math.Max(nPixel, 0); p[1] = (byte)Math.Min(255, nPixel); nPixel = p[0] + blue; nPixel = Math.Max(nPixel, 0); p[0] = (byte)Math.Min(255, nPixel); p += 3; } p += nOffset; } } b.UnlockBits(bmData); return true; } } }