4

非常に類似している(1つは別の編集バージョンである)2つの画像をピクセルごとに比較し、違いを新しいファイルに書き出すだけです。

for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        pix1 = src.GetPixel(x, y);
        pix2 = comp.GetPixel(x, y);
        if (pix1 != pix2)
        {
            dest.SetPixel(x, y, pix1);
        }
    }
}

srccompは比較する2つの画像であり、dest単なる新しい画像です。かなり時間がかかります。

これを行うためのより速い方法は何ですか?
たぶん、それを比較するために実際にピクセルを取得する必要はありませんか?

4

3 に答える 3

5

ピクセルを比較するには、それらを読み取る必要があります。ただし、GetPixel()これを行うには非常に時間がかかる方法であり、ごく少量のデータをチェックする場合を除いて、お勧めしません。

パフォーマンスを向上させるための最善の方法は、安全でないコードを使用し、代わりにポインターを使用することです。インターネット上にはこれのサンプルがたくさんあります。以下は私が見つけたもので、問題を少し説明し、これに対する2つの異なる解決策を提供します。

http://davidthomasbernal.com/blog/2008/03/13/c-image-processing-performance-unsafe-vs-safe-code-part-i

彼がいくつかのベンチマークと完全なソースへのリンクを持っているパート2も必ずチェックしてください。

于 2012-07-26T04:39:06.850 に答える
3

このコードは私が使っているものと似ています。絶対に、この場合、安全でないコードが唯一の方法です。メモリの周りのピクセルでいっぱいのビットマップをマーシャリングする必要はありません、そしてあなたは比較のためにそれを2回しなければならないでしょう!

ただし、ピクセルを比較することは、2つの数値を比較することと見なす必要があります。ピクセルは色の赤、緑、青の各コンポーネントで3バイトであるため、もちろんアルファ(多くの場合無視されます)は、UInt32の代わりに値を比較することをお勧めします。これには、ある数値(1ピクセル)を別の数値と比較するという利点があります。それらが同じである場合は、次に進みます。

これを機能させるには、バイト*ではなくUInt32*が必要です。第二に、上記のコードは、私が見ることができるストライドを考慮していません。これは、画像の各水平線が実際にはピクセル自体の合計とは異なるバイト数を使用する可能性がある場所です。24ビット(rgb)とアルファ(a)を扱っている可能性が高いため、ピクセルはすでに整列しているはずなので、上記のコードは機能するはずです。しかし、それが100%の確率で行われるという保証はありません。

私は本当にうるさいように聞こえるかもしれませんが、パフォーマンスはあなたにとって何かを意味すると思います-そしてゲームのバックグラウンドから来ると、それは私にも影響します。

(上記のリンクからパート1に移動しました。Karl-JohanSjögrenに感謝します)

BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);

この行に到達するときは、本当に注意する必要があります。ロックしているPixelFormatは、ソース画像のPixelFormatの1つと一致している必要があります。そうしないと、期待どおりに動作しない可能性があります。

LockBitsから、掘り下げる必要のあるBitmapDataを取得します。

byte* scan0 = (byte*)bData.Scan0.ToPointer();

これは私が参照している行です-これをUInt32*にするので、命令ごとに1バイトを読み取るのではなく、UInt32として一度に4バイトを読み取ります。

このメモリに配列としてアクセスします。ストライドが1Yピクセルに相当することを認識して、1次元配列へのオフセットを計算します。したがって、ストライドにYを掛けます。ただし、同じになります。トラップ私はこの時点で何度も持っています!

int stride = bData.Stride / 4;  // the width is expressed in bytes, yet we need it as UInt32's.  As there's 4 bytes per UInt32, this makes sense of the divide by 4.

したがって、ピクセルの読み取りは次のように行うことができます。

int x = 123;
int y = 321;
UInt32 pixelColour = scan0[(y * stride) + x];

最後に、完了したらビットマップのロックを解除することを忘れないでください。忘れやすいもの。また、変更したピクセルを別のビットマップに保存する場合は、同じ方法でピクセルを書き込む必要があります。ピクセルを読み取るための配列としてポインターを使用する私の例は、ピクセルを書き込むために同じことを行う方法が明らかであるはずだと思います。

もう1つの考え-Jpegやその他の不可逆圧縮画像を比較しようとしていないことを願っています-変更は非常に予測不可能であり、あなたの考えとはまったく異なる可能性があるためです。代わりに、BMPやPNGなどの損失のない画像形式を使用する必要があります。

これが誰かを助けることを願っています。

于 2014-06-28T08:38:46.790 に答える
2

このクラスを見ることができます:これは、ピクセルを比較するためのポインターに基づく高速メソッドを提供するオープンソースコードです。リンクが見つからなかったので、コードを投稿します。

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace SampleGrabberNET
{
    public unsafe class UnsafeBitmap
    {
        Bitmap bitmap;

  // three elements used for MakeGreyUnsafe
  int width;
  BitmapData bitmapData = null;
    Byte* pBase = null;

  public UnsafeBitmap(Bitmap bitmap)
  {
     this.bitmap = new Bitmap(bitmap);
  }

        public UnsafeBitmap(int width, int height)
    {
        this.bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        }

  public void Dispose()
  {
     bitmap.Dispose();
  }

  public Bitmap Bitmap
  {
 get
     {
    return(bitmap);
     }
  }

  private Point PixelSize
  {
     get
     {
    GraphicsUnit unit = GraphicsUnit.Pixel;
    RectangleF bounds = bitmap.GetBounds(ref unit);

    return new Point((int) bounds.Width, (int) bounds.Height);
     }
  }

  public void LockBitmap()
  {
     GraphicsUnit unit = GraphicsUnit.Pixel;
     RectangleF boundsF = bitmap.GetBounds(ref unit);
     Rectangle bounds = new Rectangle((int) boundsF.X,
    (int) boundsF.Y,
    (int) boundsF.Width,
    (int) boundsF.Height);

     // Figure out the number of bytes in a row
     // This is rounded up to be a multiple of 4
     // bytes, since a scan line in an image must always be a multiple of 4 bytes
     // in length.
     width = (int) boundsF.Width * sizeof(PixelData);
if (width % 4 != 0)
     {
    width = 4 * (width / 4 + 1);
 }
     bitmapData =
    bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

     pBase = (Byte*) bitmapData.Scan0.ToPointer();
  }

  public PixelData GetPixel(int x, int y)
  {
            PixelData returnValue = *PixelAt(x, y);
     return returnValue;
  }

        public void SetPixel(int x, int y, PixelData colour)
        {
            PixelData* pixel = PixelAt(x, y);
            *pixel = colour;
        }

  public void UnlockBitmap()
  {
     bitmap.UnlockBits(bitmapData);
     bitmapData = null;
     pBase = null;
  }
        public PixelData* PixelAt(int x, int y)
        {
            return (PixelData*)(pBase + y * width + x * sizeof(PixelData));
        }
    }
    public struct PixelData
    {
        public byte blue;
        public byte green;
        public byte red;
    }

}

于 2012-07-26T09:24:55.313 に答える