直方图
其实就是各个色素的比例,一个图片有很多像素,一个像素里有RGB可能是234,3,5 也就是红的234加一,绿的3加一,蓝的5加一,
所以先初始化256个。每个有就加一个,我写了个类来保存
public class HistogrmData { //2 red,1 green, 0 blue private readonly Dictionary<int, int>[] _histogrms; public HistogrmData() { _histogrms = new Dictionary<int, int>[4]; for (int i = 0; i < 4; i++) { _histogrms[i]= new Dictionary<int, int>(256); for (int j = 0; j < 256; j++) { _histogrms[i].Add(j, 0); } } } public int RedMaxValue { get;private set; } public int GreenMaxValue { get; private set; } public int BlueMaxValue { get; private set; } public int LightMaxValue { get; private set; } public void SetRedPixel(int pixel) { _histogrms[2][pixel]++; if (_histogrms[2][pixel] > RedMaxValue) RedMaxValue = _histogrms[2][pixel]; } public void SetGreenPixel(int pixel) { _histogrms[1][pixel]++; if (_histogrms[1][pixel] > GreenMaxValue) GreenMaxValue = _histogrms[1][pixel]; } public void SetBluePixel(int pixel) { _histogrms[0][pixel]++; if (_histogrms[0][pixel] > BlueMaxValue) BlueMaxValue = _histogrms[0][pixel]; } public void SetLightPixel(int pixel) { _histogrms[3][pixel]++; if (_histogrms[3][pixel] > LightMaxValue) LightMaxValue = _histogrms[3][pixel]; } public Dictionary<int, int> RedValue { get { return _histogrms[2]; } } public Dictionary<int, int> GreenValue { get { return _histogrms[1]; } } public Dictionary<int, int> BlueValue { get { return _histogrms[0]; } } public Dictionary<int, int> LightValue { get { return _histogrms[3]; } } }
不过我因为重构过类所以那个写起来还是挺轻松
public static class BitmapHelper { public static Bitmap CopyBitmap(Bitmap source) { return CopyBitmapPart(source, new Rectangle(0, 0, source.Width, source.Height)); } public static Bitmap CopyBitmapPart(Bitmap source, Rectangle part) { Bitmap bmp = new Bitmap(part.Width, part.Height); Graphics g = Graphics.FromImage(bmp); g.DrawImage(source, 0, 0, part, GraphicsUnit.Pixel); g.Dispose(); return bmp; } internal delegate void ColorDelegate(ref int red,ref int green,ref int blue); internal static Bitmap LoopPixel(Bitmap b, ColorDelegate colorDelegate) { int width = b.Width; int height = b.Height; BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, b.PixelFormat); //Bytes Per Pixel int BPP = 4; int bluePixel; int greenPixel; int redPixel; unsafe { byte* p = (byte*)data.Scan0; int offset = data.Stride - width * BPP; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { bluePixel = p[0]; greenPixel = p[1]; redPixel = p[2]; colorDelegate(ref redPixel, ref greenPixel, ref bluePixel); if (redPixel < 0) redPixel = 0; if (redPixel > 255) redPixel = 255; p[2] = (byte)redPixel; if (greenPixel < 0) greenPixel = 0; if (greenPixel > 255) greenPixel = 255; p[1] = (byte)greenPixel; if (bluePixel < 0) bluePixel = 0; if (bluePixel > 255) bluePixel = 255; p[2] = (byte)bluePixel; p += BPP; } p += offset; } b.UnlockBits(data); return b; } } public static HistogrmData GetHistogrmData(Bitmap b) { var histogrmData = new HistogrmData(); ColorDelegate colorDelegate = (ref int red,ref int green,ref int blue) => { int lightPixel = (int) (red*0.299 + green*0.587 + blue*0.114); histogrmData.SetRedPixel(red); histogrmData.SetGreenPixel(green); histogrmData.SetBluePixel(blue); histogrmData.SetLightPixel(lightPixel); }; LoopPixel(b, colorDelegate); return histogrmData; } }
第一个红的不太明显,所以他的东西要加Log放大一下
if(IsUselog) height = (float)Math.Log((HistogrmData[i]/(double)MaxValue*100+1),101); else height = HistogrmData[i] / (float)MaxValue;
这里的Math.Log(percent*100+1,101)中加一是为了LOG函数的正确性。
用LOG扩大后的样子,红的明显清晰了,不过其他的就相对太臃肿了。
整个控件其实就是重载下OnPaint,在构造函数加句this.SetStyle(ControlStyles.OptimizedDoubleBuffer |ControlStyles.AllPaintingInWmPaint, true);作双缓冲。
protected override void OnPaint(PaintEventArgs e) { if (HistogrmData == null) return; Graphics graphics = e.Graphics; Pen pen = new Pen(Color.Black); for (int i = 0; i < 255; i++) { float height; if(IsUselog) height = (float)Math.Log((HistogrmData[i]/(double)MaxValue*100+1),101); else height = HistogrmData[i] / (float)MaxValue; height = Height - height*Height; float width = i/255f*Width; graphics.DrawLine(pen, width, Height, width, height); } pen.Dispose(); }