C#:如何将数值范围[50,600]的一维数组转换为灰度位图?
解决16位灰度图像生成问题
为啥之前的代码出问题
你用Format16bppGrayScale创建的是16位灰度图,但SetPixel对这种格式的支持很差——你传的单值会被错误解析成ARGB通道,导致显示成蓝色。另外,16位灰度的有效值范围是0到65535,你那50-600的数值直接用肯定不对,得先映射到这个区间,而且不能用8位RGB的Color.FromArgb(val,val,val),因为那要求值必须在0-255之间。
正确的做法
直接操作位图的内存缓冲区,别用SetPixel,同时把你的数值线性映射到16位灰度的范围里。
完整代码示例
// 替换成你实际的宽度和高度 int imgWidth = xL; int imgHeight = yL; float minPixelVal = 50; float maxPixelVal = 600; float valueRange = maxPixelVal - minPixelVal; // 创建16位灰度位图 var grayBitmap = new Bitmap(imgWidth, imgHeight, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale); // 锁定位图内存,准备写入数据 var bitmapData = grayBitmap.LockBits( new System.Drawing.Rectangle(0, 0, imgWidth, imgHeight), System.Drawing.Imaging.ImageLockMode.WriteOnly, grayBitmap.PixelFormat); // 准备存储像素的字节数组(16位灰度每个像素占2字节) int stride = bitmapData.Stride; int totalBytes = stride * imgHeight; byte[] pixelBuffer = new byte[totalBytes]; int arrIndex = 0; for (int y = 0; y < imgHeight; y++) { for (int x = 0; x < imgWidth; x++) { // 把原数组的数值映射到0-65535的16位灰度范围 float originalVal = arr[arrIndex]; ushort gray16 = (ushort)(((originalVal - minPixelVal) / valueRange) * 65535); // 把16位值转成字节(小端存储,适配Windows系统) byte[] grayBytes = BitConverter.GetBytes(gray16); // 计算当前像素在缓冲区的位置 int bufferIndex = y * stride + x * 2; pixelBuffer[bufferIndex] = grayBytes[0]; pixelBuffer[bufferIndex + 1] = grayBytes[1]; arrIndex++; } } // 把像素数据复制到位图内存里 System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, 0, bitmapData.Scan0, totalBytes); // 解锁内存 grayBitmap.UnlockBits(bitmapData); // 按需旋转图像(如果原方向不对) grayBitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); // 保存图像,建议用PNG避免压缩损失 grayBitmap.Save("output_gray.png", System.Drawing.Imaging.ImageFormat.Png);
注意点
- 映射逻辑:代码里是把50对应纯黑(0),600对应纯白(65535),这样能最大化利用灰度范围,对比度最高。如果不想让50变成纯黑,也可以改映射公式,比如直接
gray16 = (ushort)(originalVal * (65535f / maxPixelVal)),但这样对比度会低一些。 - 效率问题:
LockBits比SetPixel快得多,尤其是图像尺寸大的时候,后者会慢到离谱。 - 字节顺序:Windows系统默认是小端存储,所以
BitConverter.GetBytes生成的字节顺序刚好符合要求,不用额外调整。
内容的提问来源于stack exchange,提问作者Harsh Bhadoria
相关产品推荐
相关产品推荐

