C# 无法显示具有正确颜色映射的 RAW16 灰度图像
C# unable to display RAW16 grayscale image with correct color mapping
修改本link中提供的代码:
我写了这个:
private void btnLoad_Click(object sender, EventArgs e)
{
if (System.IO.File.Exists(txtPicture.Text))
{
byte[] _data = System.IO.File.ReadAllBytes(txtPicture.Text);
var _rgbData = Convert16BitGrayScaleToRgb16(_data, 160, 120);
var _bmp = CreateBitmapFromBytes(_rgbData, 160, 120);
pbFrame.Image = _bmp;
}
}
private static void Convert16bitGSToRGB(UInt16 color, out byte red, out byte green, out byte blue)
{
red = (byte)(color & 0x31);
green = (byte)((color & 0x7E0) >> 5);
blue = (byte)((color & 0xF800) >> 11);
}
private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 6;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex + 1];
byte lobyte = inBuffer[inIndex];
//R
outBuffer[outIndex] = lobyte;
outBuffer[outIndex + 1] = hibyte;
//G
outBuffer[outIndex + 2] = lobyte;
outBuffer[outIndex + 3] = hibyte;
//B
outBuffer[outIndex + 4] = lobyte;
outBuffer[outIndex + 5] = hibyte;
}
}
return outBuffer;
}
private static byte[] Convert16BitGrayScaleToRgb16(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 2;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex];
byte lobyte = inBuffer[inIndex+1];
outBuffer[outIndex] = lobyte;
outBuffer[outIndex+1] = hibyte;
}
}
return outBuffer;
}
private static byte[] Convert16BitGrayScaleToRgb24(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 3;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex];
byte lobyte = inBuffer[inIndex + 1];
byte r, g, b;
UInt16 color = (UInt16)(hibyte << 8 | lobyte);
Convert16bitGSToRGB(color, out r, out g, out b);
outBuffer[outIndex] = r;
outBuffer[outIndex + 1] = g;
outBuffer[outIndex + 2] = b;
}
}
return outBuffer;
}
private static Bitmap CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
{
//Create an image that will hold the image data
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format16bppRgb565);
//Get a reference to the images pixel data
Rectangle dimension = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Copy the pixel data into the bitmap structure
Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);
bmp.UnlockBits(picData);
return bmp;
}
但转换的结果仍然不是satisfying/correct。这是我应该得到的图片:
正在转换文件link在此处:
这是使用 Convert16BitGrayScaleToRgb48
:
的结果
这是使用 Convert16BitGrayScaleToRgb16
的结果:
这是使用 Convert16BitGrayScaleToRgb24
:
的结果
很明显颜色重映射是错误的,但我无法理解问题出在哪里。
此外,我还发现 picturebox 并没有准确显示它存储的内容。上数第二张图片(Convert16BitGrayScaleToRgb48
结果)是图片框显示的内容,而下图是我将图片保存为 PNG 格式后得到的图片:
我认为 RAW16 灰度应该是指包含 16 位灰度值或在 565 或 555 地图上编码的 RGB 灰度值的 2 个字节。但是 none 这些假说似乎与真实的东西相符。
有人提示如何转换源文件中提供的值以获得与第一个一样的图片(使用 ImageJ 从同一来源获得)?
我使用 GIMP 找到了一个可能的提示。如果我通过这个应用程序加载原始文件(更改 .data
and/or 中的扩展名强制将其加载为 RAW)并将其设置为 160x120 16bpp BigEndian
我得到一个几乎黑色的框架(!),但如果我改变压缩范围的水平,围绕唯一的小峰值(大约 12,0 黑色 - 13,0 白色)图像结果正确。更改字节顺序非常简单,可以稍微压缩动态范围,但我正在努力。
这次经历的第一个教训是“不要相信你的眼睛”:-)。
我努力的最终结果是这三种方法:
public static void GetMinMax(byte[] data, out UInt16 min, out UInt16 max, bool big_endian = true)
{
if (big_endian)
min = max = (UInt16)((data[0] << 8) | data[1]);
else
min = max = (UInt16)((data[1] << 8) | data[0]);
for (int i = 0; i < (data.Length - 1); i += 2)
{
UInt16 _value;
if (big_endian)
_value = (UInt16)((data[i] << 8) | data[i + 1]);
else
_value = (UInt16)((data[i + 1] << 8) | data[i]);
if (_value < min)
min = _value;
if (_value > max)
max = _value;
}
}
public static void CompressRange(byte MSB, byte LSB, UInt16 min, UInt16 max, out byte color, Polarity polarity)
{
UInt16 _value = (UInt16)((MSB << 8) | LSB);
_value -= min;
switch (polarity)
{
case Polarity.BlackHot:
_value = (UInt16)((_value * 255) / (max - min));
_value = (UInt16)(255 - _value);
break;
default:
case Polarity.WhiteHot:
_value = (UInt16)((_value * 255) / (max - min));
break;
}
color = (byte)(_value & 0xff);
}
public static byte[] Convert16BitGrayScaleToRgb24(byte[] inBuffer, int width, int height, UInt16 min, UInt16 max, bool big_endian = true, Polarity polarity = Polarity.WhiteHot)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 3;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte;
byte lobyte;
if (big_endian)
{
hibyte = inBuffer[inIndex];
lobyte = inBuffer[inIndex + 1];
}
else
{
hibyte = inBuffer[inIndex + 1];
lobyte = inBuffer[inIndex];
}
byte gray;
CompressRange(hibyte, lobyte, min, max, out gray, polarity);
outBuffer[outIndex] = gray;
outBuffer[outIndex + 1] = gray;
outBuffer[outIndex + 2] = gray;
}
}
return outBuffer;
}
这些允许加载附加到原始问题的文件并将其显示在标准 WindowsForm PictureBox
上。使用 48bpp 格式会导致某些图形卡上的图像质量下降(至少我的是这样)。
顺便说一句 GetMinMax
计算当前帧的最小最大值,而不考虑环境的历史记录。这意味着,如果您打算使用此功能来显示图片序列(就像我一样),则 FOV 中平均温度的强烈变化将使整个图像产生不同的 曝光 在丢失图片的一些细节。在这种情况下,我建议计算当前帧的最小值-最大值,但不要在 Convert16BitGrayScaleToRgb24
中使用它,而是对两个值使用 移动平均值 。
修改本link中提供的代码:
我写了这个:
private void btnLoad_Click(object sender, EventArgs e)
{
if (System.IO.File.Exists(txtPicture.Text))
{
byte[] _data = System.IO.File.ReadAllBytes(txtPicture.Text);
var _rgbData = Convert16BitGrayScaleToRgb16(_data, 160, 120);
var _bmp = CreateBitmapFromBytes(_rgbData, 160, 120);
pbFrame.Image = _bmp;
}
}
private static void Convert16bitGSToRGB(UInt16 color, out byte red, out byte green, out byte blue)
{
red = (byte)(color & 0x31);
green = (byte)((color & 0x7E0) >> 5);
blue = (byte)((color & 0xF800) >> 11);
}
private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 6;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex + 1];
byte lobyte = inBuffer[inIndex];
//R
outBuffer[outIndex] = lobyte;
outBuffer[outIndex + 1] = hibyte;
//G
outBuffer[outIndex + 2] = lobyte;
outBuffer[outIndex + 3] = hibyte;
//B
outBuffer[outIndex + 4] = lobyte;
outBuffer[outIndex + 5] = hibyte;
}
}
return outBuffer;
}
private static byte[] Convert16BitGrayScaleToRgb16(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 2;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex];
byte lobyte = inBuffer[inIndex+1];
outBuffer[outIndex] = lobyte;
outBuffer[outIndex+1] = hibyte;
}
}
return outBuffer;
}
private static byte[] Convert16BitGrayScaleToRgb24(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 3;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex];
byte lobyte = inBuffer[inIndex + 1];
byte r, g, b;
UInt16 color = (UInt16)(hibyte << 8 | lobyte);
Convert16bitGSToRGB(color, out r, out g, out b);
outBuffer[outIndex] = r;
outBuffer[outIndex + 1] = g;
outBuffer[outIndex + 2] = b;
}
}
return outBuffer;
}
private static Bitmap CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
{
//Create an image that will hold the image data
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format16bppRgb565);
//Get a reference to the images pixel data
Rectangle dimension = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Copy the pixel data into the bitmap structure
Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);
bmp.UnlockBits(picData);
return bmp;
}
但转换的结果仍然不是satisfying/correct。这是我应该得到的图片:
正在转换文件link在此处:
这是使用 Convert16BitGrayScaleToRgb48
:
这是使用 Convert16BitGrayScaleToRgb16
的结果:
这是使用 Convert16BitGrayScaleToRgb24
:
很明显颜色重映射是错误的,但我无法理解问题出在哪里。
此外,我还发现 picturebox 并没有准确显示它存储的内容。上数第二张图片(Convert16BitGrayScaleToRgb48
结果)是图片框显示的内容,而下图是我将图片保存为 PNG 格式后得到的图片:
我认为 RAW16 灰度应该是指包含 16 位灰度值或在 565 或 555 地图上编码的 RGB 灰度值的 2 个字节。但是 none 这些假说似乎与真实的东西相符。
有人提示如何转换源文件中提供的值以获得与第一个一样的图片(使用 ImageJ 从同一来源获得)?
我使用 GIMP 找到了一个可能的提示。如果我通过这个应用程序加载原始文件(更改 .data
and/or 中的扩展名强制将其加载为 RAW)并将其设置为 160x120 16bpp BigEndian
我得到一个几乎黑色的框架(!),但如果我改变压缩范围的水平,围绕唯一的小峰值(大约 12,0 黑色 - 13,0 白色)图像结果正确。更改字节顺序非常简单,可以稍微压缩动态范围,但我正在努力。
这次经历的第一个教训是“不要相信你的眼睛”:-)。
我努力的最终结果是这三种方法:
public static void GetMinMax(byte[] data, out UInt16 min, out UInt16 max, bool big_endian = true)
{
if (big_endian)
min = max = (UInt16)((data[0] << 8) | data[1]);
else
min = max = (UInt16)((data[1] << 8) | data[0]);
for (int i = 0; i < (data.Length - 1); i += 2)
{
UInt16 _value;
if (big_endian)
_value = (UInt16)((data[i] << 8) | data[i + 1]);
else
_value = (UInt16)((data[i + 1] << 8) | data[i]);
if (_value < min)
min = _value;
if (_value > max)
max = _value;
}
}
public static void CompressRange(byte MSB, byte LSB, UInt16 min, UInt16 max, out byte color, Polarity polarity)
{
UInt16 _value = (UInt16)((MSB << 8) | LSB);
_value -= min;
switch (polarity)
{
case Polarity.BlackHot:
_value = (UInt16)((_value * 255) / (max - min));
_value = (UInt16)(255 - _value);
break;
default:
case Polarity.WhiteHot:
_value = (UInt16)((_value * 255) / (max - min));
break;
}
color = (byte)(_value & 0xff);
}
public static byte[] Convert16BitGrayScaleToRgb24(byte[] inBuffer, int width, int height, UInt16 min, UInt16 max, bool big_endian = true, Polarity polarity = Polarity.WhiteHot)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 3;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte;
byte lobyte;
if (big_endian)
{
hibyte = inBuffer[inIndex];
lobyte = inBuffer[inIndex + 1];
}
else
{
hibyte = inBuffer[inIndex + 1];
lobyte = inBuffer[inIndex];
}
byte gray;
CompressRange(hibyte, lobyte, min, max, out gray, polarity);
outBuffer[outIndex] = gray;
outBuffer[outIndex + 1] = gray;
outBuffer[outIndex + 2] = gray;
}
}
return outBuffer;
}
这些允许加载附加到原始问题的文件并将其显示在标准 WindowsForm PictureBox
上。使用 48bpp 格式会导致某些图形卡上的图像质量下降(至少我的是这样)。
顺便说一句 GetMinMax
计算当前帧的最小最大值,而不考虑环境的历史记录。这意味着,如果您打算使用此功能来显示图片序列(就像我一样),则 FOV 中平均温度的强烈变化将使整个图像产生不同的 曝光 在丢失图片的一些细节。在这种情况下,我建议计算当前帧的最小值-最大值,但不要在 Convert16BitGrayScaleToRgb24
中使用它,而是对两个值使用 移动平均值 。