有没有一种方法可以在索引颜色位图中提取像素的索引(C#)?
Is there a way of extracting the index of a pixel in an indexed colour Bitmap (C#)?
我已经将带有唯一调色板的索引彩色图像 (8bppI) 加载到 C# 程序中,我需要访问该图像中颜色的 index。但是,唯一的访问函数似乎是 Bitmap.GetPixel(x,y),其中 returns 是一种颜色,而不是索引。当相同的颜色被重新插入到具有相同格式和调色板的位图中时,颜色信息显然被 误解为 作为索引,一切都变得糟糕起来。为了清楚地说明问题,这里是代码的简化版本:
public void CreateTerrainMap() {
visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualizationLock = new LockBitmap(visualization);
Lock();
// "TerrainIndex.bmp" is a 256x256 indexed colour image (8bpp) with a unique palette.
// Debugging confirms that this image loads with its palette and format intact
Bitmap terrainColours = new Bitmap("Resources\TerrainIndex.bmp");
visualization.Palette = terrainColours.Palette;
Color c;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (Terrain[x, y] < SeaLevel) {
c = Color.FromArgb(15); // Counterintuitively, this actually gives index 15 (represented as (0,0,0,15))
} else {
heatIndex = <some number between 0 and 255>;
rainIndex = <some number between 0 and 255>;
if (IsCoastal(x, y)) {
c = Color.FromArgb(35); // Counterintuitively, this actually gives index 35 (represented as (0,0,0,35))
} else {
// This returns an argb colour rather than an index...
c = terrainColours.GetPixel(rainIndex, heatIndex);
}
}
// ...and this seemingly decides that the blue value is actually an index and sets the wrong colour entirely
visualizationLock.SetPixel(x, y, c);
}
}
}
TerrainIndex 如下所示:
TerrainIndex.bmp
调色板看起来像这样:Palette
输出应如下所示:Good
但它看起来像这样:Bad
请注意,海洋(索引 15)和海岸(索引 35)看起来是正确的,但其他一切都来自调色板的错误部分。
我找不到任何有关在 C# 中使用索引颜色位图的有用信息。我真的希望有人能向我解释我可能做错了什么,或者指出正确的方向。
我根据我的评论创建了一个答案。所以 "native" 解决方案是这样的(需要允许不安全代码):
Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
BitmapData visualizationData = visualization.LockBits(new Rectangle(Point.Empty, visualization.Size),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
unsafe
{
byte* row = (byte*)visualizationData.Scan0;
for (int y = 0; y < visualizationData.Height; y++)
{
for (int x = 0; x < visualizationData.Width; x++)
{
// here you set the 8bpp palette index directly
row[x] = GetHeatIndex(x, y);
}
row += visualizationData.Stride;
}
}
}
finally
{
visualization.UnlockBits(visualizationData);
}
或者,您可以使用 these 库,然后:
using KGySoft.Drawing;
using KGySoft.Drawing.Imaging;
// ...
Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
using (IWritableBitmapData visualizationData = visualization.GetWritableBitmapData())
{
for (int y = 0; y < visualizationData.Height; y++)
{
IWritableBitmapDataRow row = visualizationData[y];
for (int x = 0; x < visualizationData.Width; x++)
{
// setting pixel by palette index
row.SetColorIndex(x, GetHeatIndex(x, y));
// or: directly by raw data (8bpp means 1 byte per pixel)
row.WriteRaw<byte>(x, GetHeatIndex(x, y));
// or: by color (automatically applies the closest palette index)
row.SetColor(x, GetHeatColor(x, y));
}
}
}
编辑:
对于阅读 pixels/indices,您可以使用 terrainColors.GetReadableBitmapData()
,因此您将能够以非常相似的方式使用 rowTerrain.GetColorIndex(x)
或 rowTerrain.ReadRaw<byte>(x)
。
我已经将带有唯一调色板的索引彩色图像 (8bppI) 加载到 C# 程序中,我需要访问该图像中颜色的 index。但是,唯一的访问函数似乎是 Bitmap.GetPixel(x,y),其中 returns 是一种颜色,而不是索引。当相同的颜色被重新插入到具有相同格式和调色板的位图中时,颜色信息显然被 误解为 作为索引,一切都变得糟糕起来。为了清楚地说明问题,这里是代码的简化版本:
public void CreateTerrainMap() {
visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualizationLock = new LockBitmap(visualization);
Lock();
// "TerrainIndex.bmp" is a 256x256 indexed colour image (8bpp) with a unique palette.
// Debugging confirms that this image loads with its palette and format intact
Bitmap terrainColours = new Bitmap("Resources\TerrainIndex.bmp");
visualization.Palette = terrainColours.Palette;
Color c;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (Terrain[x, y] < SeaLevel) {
c = Color.FromArgb(15); // Counterintuitively, this actually gives index 15 (represented as (0,0,0,15))
} else {
heatIndex = <some number between 0 and 255>;
rainIndex = <some number between 0 and 255>;
if (IsCoastal(x, y)) {
c = Color.FromArgb(35); // Counterintuitively, this actually gives index 35 (represented as (0,0,0,35))
} else {
// This returns an argb colour rather than an index...
c = terrainColours.GetPixel(rainIndex, heatIndex);
}
}
// ...and this seemingly decides that the blue value is actually an index and sets the wrong colour entirely
visualizationLock.SetPixel(x, y, c);
}
}
}
TerrainIndex 如下所示: TerrainIndex.bmp
调色板看起来像这样:Palette
输出应如下所示:Good
但它看起来像这样:Bad
请注意,海洋(索引 15)和海岸(索引 35)看起来是正确的,但其他一切都来自调色板的错误部分。
我找不到任何有关在 C# 中使用索引颜色位图的有用信息。我真的希望有人能向我解释我可能做错了什么,或者指出正确的方向。
我根据我的评论创建了一个答案。所以 "native" 解决方案是这样的(需要允许不安全代码):
Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
BitmapData visualizationData = visualization.LockBits(new Rectangle(Point.Empty, visualization.Size),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
unsafe
{
byte* row = (byte*)visualizationData.Scan0;
for (int y = 0; y < visualizationData.Height; y++)
{
for (int x = 0; x < visualizationData.Width; x++)
{
// here you set the 8bpp palette index directly
row[x] = GetHeatIndex(x, y);
}
row += visualizationData.Stride;
}
}
}
finally
{
visualization.UnlockBits(visualizationData);
}
或者,您可以使用 these 库,然后:
using KGySoft.Drawing;
using KGySoft.Drawing.Imaging;
// ...
Bitmap visualization = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
visualization.Palette = GetVisualizationPalette();
using (IWritableBitmapData visualizationData = visualization.GetWritableBitmapData())
{
for (int y = 0; y < visualizationData.Height; y++)
{
IWritableBitmapDataRow row = visualizationData[y];
for (int x = 0; x < visualizationData.Width; x++)
{
// setting pixel by palette index
row.SetColorIndex(x, GetHeatIndex(x, y));
// or: directly by raw data (8bpp means 1 byte per pixel)
row.WriteRaw<byte>(x, GetHeatIndex(x, y));
// or: by color (automatically applies the closest palette index)
row.SetColor(x, GetHeatColor(x, y));
}
}
}
编辑:
对于阅读 pixels/indices,您可以使用 terrainColors.GetReadableBitmapData()
,因此您将能够以非常相似的方式使用 rowTerrain.GetColorIndex(x)
或 rowTerrain.ReadRaw<byte>(x)
。