2016-10-25 20 views
2

こんにちは、私は、グレースケール画像(Bitmap)をC#またはC++を使用して偽色/疑似色に変換しています。C#またはC++でグレースケール画像を偽色/疑似色に変換する

研究中私はちょうどこれについて2つの未完の答えが見つかりました。 http://www.emgu.com/forum/viewtopic.php?f=7&t=4132https://stackoverflow.com/a/20468006/2798895

何偽色はここで見つけることができますされていますhttps://en.wikipedia.org/wiki/False_color#Pseudocolor

任意の提案をどのようにこの問題にアプローチするために?

答えて

1

一般的な方法は、値をテーブルベースで変換して色を得ることです。

簡単な例として、コールドエンドの青色(ここでは強度0とします)からホットエンドまでの赤色までのシンプルな2色のフェードを仮定しましょう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に変換してカラーを生成することができます値。これにより、一定範囲のシェードを移動しながら、ほぼ同じレベルの彩度と明るさを持つ値を計算するのがはるかに簡単になります。

0

基本的なやりかたは、イメージをグレースケールのパレットフォーマットに変換してから、色相を変更するパレットを与えるだけです。私はそれらすべてのツールセットを手に入れました。私は関連するコードを見つけることができるかどうかがわかります。

グレースケールのパレット画像を作成するには、元の画像のピクセルを何とか得ることができます。すでに灰色の場合、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); 
    CopyToMemory(targetData.Scan0, sourceData, 0, sourceData.Length, stride, targetData.Stride); 
    newImage.UnlockBits(targetData); 
    // 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; 
} 

public static void CopyToMemory(IntPtr target, Byte[] bytes, Int32 startPos, Int32 length, Int32 origStride, Int32 targetStride) 
{ 
    Int32 sourcePos = startPos; 
    IntPtr destPos = target; 
    Int32 minStride = Math.Min(origStride, targetStride); 
    while (length >= targetStride) 
    { 
     Marshal.Copy(bytes, sourcePos, destPos, minStride); 
     length -= origStride; 
     sourcePos += origStride; 
     destPos = new IntPtr(destPos.ToInt64() + targetStride); 
    } 
    if (length > 0) 
    { 
     Marshal.Copy(bytes, sourcePos, destPos, length); 
    } 
} 

ここで素晴らしい色を得るためには、もちろんパレットが必要です。私はこのコードを少し適応を行い、代わりに簡単にアクセスするためのpublic static「SCALE」に民間のconst「スケール」を作っ

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

注:このため、私はリッチニューマンのHSLColorクラスを使用します私の操作のために。

/// <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); 

Height map in hue colours

関連する問題