【问题标题】:convert grayscale image to false colors / pseudo colors in c# or c++在 C# 或 C++ 中将灰度图像转换为假色/伪色
【发布时间】:2017-03-08 01:50:37
【问题描述】:

您好,我正在尝试使用 C# 或 C++ 将灰度图像 (​​Bitmap) 转换为假色/伪色。

做研究我刚刚找到了两个未完成的答案,例如http://www.emgu.com/forum/viewtopic.php?f=7&t=4132https://stackoverflow.com/a/20468006/2798895

什么是伪色可以在这里找到:https://en.wikipedia.org/wiki/False_color#Pseudocolor

有什么建议可以解决这个问题吗?

【问题讨论】:

    标签: c# c++ image-processing colors


    【解决方案1】:

    通常的方法是对值进行基于表格的转换以获取颜色。

    举个简单的例子,让我们假设一个简单的双色从冷端的蓝色(我假设强度为 0)到热端的红色(我假设强度为256)。

    为此,我们可能会写一个类似这样的转换表:

    struct triplet {
        char r, g, b;
    };
    
    static const int count = 256;
    
    std::vector<triplet> table;
    
    for (int i=0; i<count; i++) 
        table.push_back({i, 0, count-i});
    

    有了这个,我们可以很容易地将输入从强度转换为伪色:

    pseudocolor = table[intensity];
    

    如果您想要更复杂的渐变,例如从蓝色到绿色到黄色到红色,您可能希望从 HLS 或 HSV 等颜色模型开始,然后从该颜色模型转换为 RGB 以生成你的颜色值。这使得在通过一系列阴影移动时计算具有大致相同饱和度和亮度水平的值变得更加容易。

    【讨论】:

      【解决方案2】:

      我这样做的基本方法是将图像转换为灰度调色板格式,然后给它一个改变色调的调色板。我得到了所有这些工具集(事实上,我已经做到了)。我看看能不能找到相关代码。

      要制作灰度调色图像,你应该可以get the original image's pixels out somehow,如果它们已经是灰色的,任何 R/G/B 组件都可以。将它们存储在字节数组中并从中烘焙调色板图像。最简单的方法是使用Image.LockBits 函数,它可以直接访问支持图像的数组。这是我的工具功能:

      /// <summary>
      /// Creates a bitmap based on data, width, height, stride and pixel format.
      /// </summary>
      /// <param name="sourceData">Byte array of raw source data</param>
      /// <param name="width">Width of the image</param>
      /// <param name="height">Height of the image</param>
      /// <param name="stride">Scanline length inside the data</param>
      /// <param name="pixelFormat">Pixel format</param>
      /// <param name="palette">Color palette</param>
      /// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
      /// <returns>The new image</returns>
      public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
      {
          Bitmap newImage = new Bitmap(width, height, pixelFormat);
          BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
          Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
          // Compensate for possible negative stride on BMP format.
          Boolean isFlipped = targetData.Stride < 0;
          Int32 targetStride = Math.Abs(targetData.Stride);
          Int64 scan0 = targetData.Scan0.ToInt64();
          for (Int32 y = 0; y < height; y++)
              Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
          newImage.UnlockBits(targetData);
          // Fix negative stride on BMP format.
          if (isFlipped)
              newImage.RotateFlip(RotateFlipType.Rotate180FlipX);
          // For indexed images, set the palette.
          if ((pixelFormat & PixelFormat.Indexed) != 0 && palette != null)
          {
              ColorPalette pal = newImage.Palette;
              for (Int32 i = 0; i < pal.Entries.Length; i++)
              {
                  if (i < palette.Length)
                      pal.Entries[i] = palette[i];
                  else if (defaultColor.HasValue)
                      pal.Entries[i] = defaultColor.Value;
                  else
                      break;
              }
              newImage.Palette = pal;
          }
          return newImage;
      }
      

      现在,要获得漂亮的颜色,您当然需要调色板。为此,我使用 Rich Newman 的 HSLColor 类:

      https://richnewman.wordpress.com/about/code-listings-and-diagrams/hslcolor-class/

      请注意,我对这段代码做了一个小的修改,并将私有 const “scale” 变成了 public static “SCALE”,以便在我的操作中轻松访问它。

      /// <summary>
      /// Generates a colour palette of the given bits per pixel containing a hue rotation of the given range.
      /// </summary>
      /// <param name="bpp">Bits per pixel of the image the palette is for.</param>
      /// <param name="blackOnZero">Replace the first colour on the palette with black.</param>
      /// <param name="addTransparentZero">Make the first colour on the palette transparent.</param>
      /// <param name="reverseGenerated">Reverse the generated range. This happens after the generating, and before the operations on the first index/</param>
      /// <param name="startHue">Start hue range. Value from 0 to 240.</param>
      /// <param name="endHue">End hue range. Value from 0 to 240. Must be higher then startHue.</param>
      /// <param name="inclusiveEnd">True to include the end hue in the palette. If you generate a full hue range, this can be set to False to avoid getting a duplicate red colour on it.</param>
      /// <returns>The generated palette, as array of System.Drawing.Color objects.</returns>
      public static Color[] GenerateRainbowPalette(Int32 bpp, Boolean blackOnZero, Boolean addTransparentZero, Boolean reverseGenerated, Int32 startHue, Int32 endHue, Boolean inclusiveEnd)
      {
          Int32 colors = 1 << bpp;
          Color[] pal = new Color[colors];
          Double step = (Double)(endHue - startHue) / (inclusiveEnd ? colors - 1 : colors);
          Double start = startHue;
          Double satValue = ColorHSL.SCALE;
          Double lumValue = 0.5 * ColorHSL.SCALE;
          for (Int32 i = 0; i < colors; i++)
          {
              Double curStep =  start + step * i;
              pal[i] = new ColorHSL(curStep, satValue, lumValue);
          }
          if (reverseGenerated)
          {
              Color[] entries = pal.Reverse().ToArray();
              for (Int32 i = 0; i < pal.Length; i++)
                  pal[i] = entries[i];
          }
          if (blackOnZero)
              pal[0] = Color.Black;
          // make color 0 transparent
          if (addTransparentZero)
              pal[0] = Color.FromArgb(0, pal[0]);
          return pal;
      }
      

      一个完整的色调旋转会从红色到黄色到青色到绿色到蓝色到紫色再回到红色。但是,为了对伪色产生良好的效果,您不希望最高值与最低值相似,因此最好从蓝色变为红色。这意味着,限制生成的范围,并反转最终的调色板,这就是我在函数中添加这些选项的原因。

      蓝色到红色彩虹的最终调用是这样的:

      Color[] heightmap = PaletteUtils.GenerateRainbowPalette(8, false, false, true, 0, 160, true);
      

      应用在游戏地形高度图上的结果:

      【讨论】:

        猜你喜欢
        • 2018-04-18
        • 2013-01-12
        • 2019-12-03
        • 2013-12-26
        • 2011-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多