21

私は Java で画像を扱っています。100 以上の画像 (.png) 形式を設計しました。それらはすべて透明で黒色の描画でした。

問題は、図面の色を変更するように求められたことです(黒から)。

画像のビットマップ(ピクセル)を変更するグーグルで切り取られた多くのコードを検索しましたが、正確なピクセルを一致させ、画像が透明モードの場合に特別に置き換えるために何をしなければならないかを推測していません。以下は.Net(C#)のコードです

        Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height);
        for (int i = 0; i < scrBitmap.Width; i++)
        {
            for (int j = 0; j < scrBitmap.Height; j++)
            {                    
                originalColor = scrBitmap.GetPixel(i, j);
                if(originalColor = Color.Black)
                  newBitmap.SetPixel(i, j, Color.Red);
            }
        }            
        return newBitmap;

しかし、まったく一致していませんでした。デバッグしました。ファイル全体で、Color (originalColor) 変数の Red、Green、Blue パラメータの値がありませんでした。

誰でも助けることができますか?

4

4 に答える 4

17

パフォーマンスについて話す前に、コードを確認しましょう。

var originalColor = scrBitmap.GetPixel(i, j);
if (originalColor = Color.Black)
    newBitmap.SetPixel(i, j, Color.Red);

ここには 2 つのエラーがあります。

  1. と比較するのではなくColor.Black、に割り当て Color.BlackますoriginalColor
  2. 透過性を処理しません。

透明度を確認するには、Colorオブジェクトではなく R、G、B の値を比較する必要があります。次のように変更しましょう。

var originalColor = scrBitmap.GetPixel(i, j);
if (originalColor.R == 0 && originalColor.G == 0 && originalColor.B == 0)
    newBitmap.SetPixel(i, j, Color.FromArgb(originalColor.A, Color.Red));

これで動作することがわかりますが、各画像を処理するのに非常に長い時間がかかり、GetPixelかなりSetPixel遅いです (主な理由は、呼び出しごとにすべてをチェックして計算するためです)。ビットマップ データを直接処理する方がはるかに優れています。画像形式が事前にわかっている場合 (そして画像ごとに固定されている場合) は、コードを少し追加するだけではるかに高速に実行できます。

static unsafe Bitmap ReplaceColor(Bitmap source,
                                  Color toReplace,
                                  Color replacement)
{
  const int pixelSize = 4; // 32 bits per pixel

  Bitmap target = new Bitmap(
    source.Width,
    source.Height,
    PixelFormat.Format32bppArgb);

  BitmapData sourceData = null, targetData = null;

  try
  {
    sourceData = source.LockBits(
      new Rectangle(0, 0, source.Width, source.Height),
      ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    targetData = target.LockBits(
      new Rectangle(0, 0, target.Width, target.Height),
      ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    for (int y = 0; y < source.Height; ++y)
    {
      byte* sourceRow = (byte*)sourceData.Scan0 + (y * sourceData.Stride);
      byte* targetRow = (byte*)targetData.Scan0 + (y * targetData.Stride);

      for (int x = 0; x < source.Width; ++x)
      {
        byte b = sourceRow[x * pixelSize + 0];
        byte g = sourceRow[x * pixelSize + 1];
        byte r = sourceRow[x * pixelSize + 2];
        byte a = sourceRow[x * pixelSize + 3];

        if (toReplace.R == r && toReplace.G == g && toReplace.B == b)
        {
          r = replacement.R;
          g = replacement.G;
          b = replacement.B;
        }

        targetRow[x * pixelSize + 0] = b;
        targetRow[x * pixelSize + 1] = g;
        targetRow[x * pixelSize + 2] = r;
        targetRow[x * pixelSize + 3] = a;
      }
    }
  }
  finally
  {
    if (sourceData != null)
      source.UnlockBits(sourceData);

    if (targetData != null)
      target.UnlockBits(targetData);
  }

  return target;
}

もちろん、これはさらに最適化することができ、さまざまな形式を処理する必要がある場合があります (ピクセル形式のこのリストとそのレイアウトに関するこの記事を参照てください) が、ビットマップを操作するための出発点と考えてください。

完全を期すために、これはビットマップ データに直接アクセスしない場合の同等の色です。これは非常に遅いため、めったに使用しないでください。

static Bitmap ReplaceColor(Bitmap source,
                           Color toReplace,
                           Color replacement)
{
    var target = new Bitmap(source.Width, source.Height);

    for (int x = 0; x < source.Width; ++x)
    {
        for (int y = 0; y < source.Height; ++y)
        {
            var color = source.GetPixel(x, y);
            target.SetPixel(x, y, color == toReplace ? replacement : color);
        }
    }

    return target;
}

また、比較の際にアルファ チャネルを考慮することに注意してください (たとえば、50% の透明な緑は 30% の透明な緑と同じ色ではありません)。アルファを無視するには、次のようなものを使用できます。

if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B)

最後に、置換するピクセルが少ないことがわかっている場合は、元の画像の生のコピーを作成して (Graphics.FromImageコンテキストを作成し、ビットマップに描画するために使用します)、置換がある場合にのみsource呼び出すことができます。SetPixel()IMO ここでの最適化はまったく役に立ちません。パフォーマンスが必要な場合は、最初のソリューションを使用してください...

于 2013-06-20T08:02:01.810 に答える
7

色を効率的に置き換える 1 つの方法は、リマップ テーブルを使用することです。次の例では、ピクチャ ボックス内に画像が描画されます。Paint イベントでは、色 Color.Black が Color.Blue に変更されます。

    private void pictureBox_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        using (Bitmap bmp = new Bitmap("myImage.png"))
        {

            // Set the image attribute's color mappings
            ColorMap[] colorMap = new ColorMap[1];
            colorMap[0] = new ColorMap();
            colorMap[0].OldColor = Color.Black;
            colorMap[0].NewColor = Color.Blue;
            ImageAttributes attr = new ImageAttributes();
            attr.SetRemapTable(colorMap);
            // Draw using the color map
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            g.DrawImage(bmp, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr);
        }
    }

詳細: http://msdn.microsoft.com/en-us/library/4b4dc1kz%28v=vs.110%29.aspx

于 2014-11-24T09:29:04.657 に答える
1

これはすべてのピクセルに対して計算するわけではないため、別の解決策を提供します。

その短くて簡単です。変換時間は 62 ミリ秒です。

public Bitmap Color(Bitmap original)
        {
            //create a blank bitmap the same size as original
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);

            //get a graphics object from the new Image
            Graphics g = Graphics.FromImage(newBitmap);

            //create the color you want ColorMatrix
            //now is set to red, but with different values 
            //you can get anything you want.
            ColorMatrix colorMatrix = new ColorMatrix(
                new float[][]
                {

                    new float[] {1f, .0f, .0f, 0, 0},
                    new float[] {1f, .0f, .0f, 0, 0},
                    new float[] {1f, .0f, .0f, 0, 0},
                    new float[] {0, 0, 0, 1, 0},
                    new float[] {0, 0, 0, 0, 1}
                });

            //create some image attributes
            ImageAttributes attributes = new ImageAttributes();

            //set the color matrix attribute
            attributes.SetColorMatrix(colorMatrix);

            //draw original image on the new image using the color matrix
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
                0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

            //release sources used
            g.Dispose();
            return newBitmap;
        }
于 2015-03-02T22:43:16.087 に答える