目前在做图片主题色提取相关的事情,所以搜索到的了 LockBitmap 这个类,但是发现其中取出的颜色有些问题,明明没有的颜色,却出现在主题色里面,经过多方面排除,之后确定是 LockBitmap 的问题,这个类相关的文章已经被转发了很多次,基本上到处都是,但是只有少数人提出问题(如 https://www.cnblogs.com/xiashengwang/p/4225848.html 的评论里面),也没给出相关的解决方案,所以只能自己改了
修正版如下:(修改了 50/95/137 行)
1 public class LockBitmap : IDisposable 2 { 3 Bitmap source = null; 4 IntPtr Iptr = IntPtr.Zero; 5 BitmapData bitmapData = null; 6 7 public byte[] Pixels { get; set; } 8 public int Depth { get; private set; } 9 public int Width { get; private set; } 10 public int Height { get; private set; } 11 12 public LockBitmap(Bitmap source) 13 { 14 this.source = source; 15 LockBits(); 16 } 17 18 /// <summary> 19 /// Lock bitmap data 20 /// </summary> 21 public void LockBits() 22 { 23 try 24 { 25 // Get width and height of bitmap 26 Width = source.Width; 27 Height = source.Height; 28 29 // get total locked pixels count 30 //int PixelCount = Width * Height; 31 32 // Create rectangle to lock 33 System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, Width, Height); 34 35 // get source bitmap pixel format size 36 Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat); 37 38 // Check if bpp (Bits Per Pixel) is 8, 24, or 32 39 if (Depth != 8 && Depth != 24 && Depth != 32) 40 { 41 throw new ArgumentException("Only 8, 24 and 32 bpp images are supported."); 42 } 43 44 // Lock bitmap and return bitmap data 45 bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, 46 source.PixelFormat); 47 48 // create byte array to copy pixel values 49 int step = Depth / 8; 50 Pixels = new byte[bitmapData.Stride * Height]; 51 Iptr = bitmapData.Scan0; 52 53 // Copy data from pointer to array 54 Marshal.Copy(Iptr, Pixels, 0, Pixels.Length); 55 } 56 catch (Exception ex) 57 { 58 throw ex; 59 } 60 } 61 62 /// <summary> 63 /// Unlock bitmap data 64 /// </summary> 65 public void UnlockBits() 66 { 67 try 68 { 69 // Copy data from byte array to pointer 70 Marshal.Copy(Pixels, 0, Iptr, Pixels.Length); 71 72 // Unlock bitmap data 73 source.UnlockBits(bitmapData); 74 } 75 catch (Exception ex) 76 { 77 throw ex; 78 } 79 } 80 81 /// <summary> 82 /// Get the color of the specified pixel 83 /// </summary> 84 /// <param name="x"></param> 85 /// <param name="y"></param> 86 /// <returns></returns> 87 public Color GetPixel(int x, int y) 88 { 89 Color clr = Color.Empty; 90 91 // Get color components count 92 int cCount = Depth / 8; 93 94 // Get start index of the specified pixel 95 //int i = ((y * Width) + x) * cCount; 96 int i = y * bitmapData.Stride + x * cCount; 97 98 if (i > Pixels.Length - cCount) 99 throw new IndexOutOfRangeException(); 100 101 if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha 102 { 103 byte b = Pixels[i]; 104 byte g = Pixels[i + 1]; 105 byte r = Pixels[i + 2]; 106 byte a = Pixels[i + 3]; // a 107 clr = Color.FromArgb(a, r, g, b); 108 } 109 if (Depth == 24) // For 24 bpp get Red, Green and Blue 110 { 111 byte b = Pixels[i]; 112 byte g = Pixels[i + 1]; 113 byte r = Pixels[i + 2]; 114 clr = Color.FromArgb(r, g, b); 115 } 116 if (Depth == 8) 117 // For 8 bpp get color value (Red, Green and Blue values are the same) 118 { 119 byte c = Pixels[i]; 120 clr = Color.FromArgb(c, c, c); 121 } 122 return clr; 123 } 124 125 /// <summary> 126 /// Set the color of the specified pixel 127 /// </summary> 128 /// <param name="x"></param> 129 /// <param name="y"></param> 130 /// <param name="color"></param> 131 public void SetPixel(int x, int y, Color color) 132 { 133 // Get color components count 134 int cCount = Depth / 8; 135 136 // Get start index of the specified pixel 137 //int i = ((y * Width) + x) * cCount; 138 int i = y * bitmapData.Stride + x * cCount; 139 140 if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha 141 { 142 Pixels[i] = color.B; 143 Pixels[i + 1] = color.G; 144 Pixels[i + 2] = color.R; 145 Pixels[i + 3] = color.A; 146 } 147 if (Depth == 24) // For 24 bpp set Red, Green and Blue 148 { 149 Pixels[i] = color.B; 150 Pixels[i + 1] = color.G; 151 Pixels[i + 2] = color.R; 152 } 153 if (Depth == 8) 154 // For 8 bpp set color value (Red, Green and Blue values are the same) 155 { 156 Pixels[i] = color.B; 157 } 158 } 159 160 public bool IsValidCoordinate(int x, int y) 161 { 162 return x >= 0 && x < this.Width && y > 0 && y < this.Height; 163 } 164 165 #region IDisposable Support 166 private bool disposedValue = false; // 要检测冗余调用 167 168 protected virtual void Dispose(bool disposing) 169 { 170 if (!disposedValue) 171 { 172 if (disposing) 173 { 174 // TODO: 释放托管状态(托管对象)。 175 } 176 177 // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。 178 // TODO: 将大型字段设置为 null。 179 UnlockBits(); 180 disposedValue = true; 181 } 182 } 183 184 // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。 185 // ~LockBitmap() { 186 // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 187 // Dispose(false); 188 // } 189 190 // 添加此代码以正确实现可处置模式。 191 void IDisposable.Dispose() 192 { 193 // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 194 Dispose(true); 195 // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。 196 // GC.SuppressFinalize(this); 197 } 198 #endregion 199 }
使用方式:
using (var bitmap = new LockBitmap(img)) { for (int i = 0; i < img.Width; i++) { for (int j = 0; j < img.Height; j++) { tempColor = bitmap.GetPixel(i, j); } } }
Stride 和 Width 的区别参考:http://blog.csdn.net/ustcxiangchun/article/details/25893883
这里有个问题:我用了 https://www.cnblogs.com/bomo/archive/2013/02/26/2934055.html 里面的指针法,然后 MVC 的属性【允许不安全的代码】也勾选了,但是发布 Release 版本无论怎样都发布不成功,只能发布 Debug 版本,如果有知道的怎么发布的 Release 版本的,希望评论告知一下,感谢。编译环境:VS2015 、ASP.NET MVC 5 、.NET Framework 4.5.2。尝试过的方式:web.config 配置 "/unsafe+"=true 没有用