2011-09-13 11 views
1

私の現在のプロジェクトでは、より大きなサイズの別のイメージに含まれるイメージのピクセルの正確な位置を見つける必要があります。小さな画像は決して回転または伸張されません(ピクセルごとに一致する必要があります)が、輝度が異なる場合があり、画像の一部のピクセルが歪む可能性があります。私の最初の試合はCPUでやっていたが、遅すぎた。計算は非常に平行ですので、私はGPUを使用することに決めました。私はちょうどCUDAを学び始め、私の最初のCUDAアプリを書いた。私のコードは動作しますが、GPUでもまだ遅いです。大きな画像が1024x1280のサイズを持ち、128x128が小さい場合、プログラムはGeForce GTX 560iで2000msの計算を行います。私は200ミリ秒未満で結果を得る必要があります。将来私はおそらくより複雑なアルゴリズムが必要になるでしょう。だから、私はむしろより多くの計算パワーリザーブを持っています。問題は、スピードアップを達成するためにコードを最適化する方法です。CUDAを使用して別のイメージでイメージをすばやく見つける方法は?

CUDAImageLib.dll:

#include "cuda_runtime.h" 
#include "device_launch_parameters.h" 

#include <stdio.h> 
#include <cutil.h> 

//#define SUPPORT_ALPHA 

__global__ void ImageSearch_kernel(float* BufferOut, float* BufferB, float* BufferS, unsigned int bw, unsigned int bh, unsigned int sw, unsigned int sh) 
{ 
    unsigned int bx = threadIdx.x + blockIdx.x * blockDim.x; 
    unsigned int by = threadIdx.y + blockIdx.y * blockDim.y; 
    float diff = 0; 
    for (unsigned int y = 0; y < sh; ++y) 
    { 
     for (unsigned int x = 0; x < sw; ++x) 
     { 
      unsigned int as = (x + y * sw) * 4; 
      unsigned int ab = (x + bx + (y + by) * bw) * 4; 
#ifdef SUPPORT_ALPHA 
      diff += ((abs(BufferS[as] - BufferB[ab]) + abs(BufferS[as + 1] - BufferB[ab + 1]) + abs(BufferS[as + 2] - BufferB[ab + 2])) * BufferS[as + 3] * BufferB[ab + 3]); 
#else 
      diff += abs(BufferS[as] - BufferB[ab]); 
      diff += abs(BufferS[as + 1] - BufferB[ab + 1]); 
      diff += abs(BufferS[as + 2] - BufferB[ab + 2]);  
#endif 
     } 
    } 
    BufferOut[bx + (by * (bw - sw))] = diff; 
} 

extern "C" int __declspec(dllexport) __stdcall ImageSearchGPU(float* BufferOut, float* BufferB, float* BufferS, int bw, int bh, int sw, int sh) 
{ 
    int aBytes = (bw * bh) * 4 * sizeof(float); 
    int bBytes = (sw * sh) * 4 * sizeof(float); 
    int cBytes = ((bw - sw) * (bh - sh)) * sizeof(float); 

    dim3 threadsPerBlock(32, 32); 
    dim3 numBlocks((bw - sw)/threadsPerBlock.x, (bh - sh)/threadsPerBlock.y); 

    float *dev_B = 0; 
    float *dev_S = 0; 
    float *dev_Out = 0; 

    unsigned int timer = 0; 
    float sExecutionTime = 0; 

    cudaError_t cudaStatus; 

    // Choose which GPU to run on, change this on a multi-GPU system. 
    cudaStatus = cudaSetDevice(0); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?"); 
     goto Error; 
    } 

    // Allocate GPU buffers for three vectors (two input, one output) . 
    cudaStatus = cudaMalloc((void**)&dev_Out, cBytes); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMalloc failed!"); 
     goto Error; 
    } 

    cudaStatus = cudaMalloc((void**)&dev_B, aBytes); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMalloc failed!"); 
     goto Error; 
    } 

    cudaStatus = cudaMalloc((void**)&dev_S, bBytes); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMalloc failed!"); 
     goto Error; 
    } 

    // Copy input vectors from host memory to GPU buffers. 
    cudaStatus = cudaMemcpy(dev_B, BufferB, aBytes, cudaMemcpyHostToDevice); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMemcpy failed!"); 
     goto Error; 
    } 

    cudaStatus = cudaMemcpy(dev_S, BufferS, bBytes, cudaMemcpyHostToDevice); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMemcpy failed!"); 
     goto Error; 
    } 

    cutCreateTimer(&timer); 
    cutStartTimer(timer); 

    // Launch a kernel on the GPU with one thread for each element. 
    ImageSearch_kernel<<<numBlocks, threadsPerBlock>>>(dev_Out, dev_B, dev_S, bw, bh, sw, sh); 

    // cudaDeviceSynchronize waits for the kernel to finish, and returns 
    // any errors encountered during the launch. 
    cudaStatus = cudaDeviceSynchronize(); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus); 
     goto Error; 
    } 

    cutStopTimer(timer); 
    sExecutionTime = cutGetTimerValue(timer); 

    // Copy output vector from GPU buffer to host memory. 
    cudaStatus = cudaMemcpy(BufferOut, dev_Out, cBytes, cudaMemcpyDeviceToHost); 
    if (cudaStatus != cudaSuccess) { 
     fprintf(stderr, "cudaMemcpy failed!"); 
     goto Error; 
    } 

Error: 
    cudaFree(dev_Out); 
    cudaFree(dev_B); 
    cudaFree(dev_S); 
    return (int)sExecutionTime; 
} 

extern "C" int __declspec(dllexport) __stdcall FindMinCPU(float* values, int count) 
{ 
    int minIndex = 0; 
    float minValue = 3.4e+38F; 
    for (int i = 0; i < count; ++i) 
    { 
     if (values[i] < minValue) 
     { 
      minValue = values[i]; 
      minIndex = i; 
     } 
    } 
    return minIndex; 
} 

C#のテストアプリケーション:

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

namespace TestCUDAImageSearch 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using(Bitmap big = new Bitmap("Big.png"), small = new Bitmap("Small.png")) 
      { 
       Console.WriteLine("Big " + big.Width + "x" + big.Height + " Small " + small.Width + "x" + small.Height); 

       Stopwatch sw = new Stopwatch(); 
       sw.Start(); 
       Point point = CUDAImageLIb.ImageSearch(big, small); 
       sw.Stop(); 
       long t = sw.ElapsedMilliseconds; 
       Console.WriteLine("Image found at " + point.X + "x" + point.Y); 
       Console.WriteLine("total time=" + t + "ms  kernel time=" + CUDAImageLIb.LastKernelTime + "ms"); 
      } 
      Console.WriteLine("Hit key"); 
      Console.ReadKey(); 
     } 
    } 
} 



//#define SUPPORT_HSB 

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

namespace TestCUDAImageSearch 
{ 
    public static class CUDAImageLIb 
    { 
     [DllImport("CUDAImageLib.dll")] 
     private static extern int ImageSearchGPU(float[] bufferOut, float[] bufferB, float[] bufferS, int bw, int bh, int sw, int sh); 

     [DllImport("CUDAImageLib.dll")] 
     private static extern int FindMinCPU(float[] values, int count); 

     private static int _lastKernelTime = 0; 

     public static int LastKernelTime 
     { 
      get { return _lastKernelTime; } 
     } 

     public static Point ImageSearch(Bitmap big, Bitmap small) 
     { 
      int bw = big.Width; 
      int bh = big.Height; 
      int sw = small.Width; 
      int sh = small.Height; 
      int mx = (bw - sw); 
      int my = (bh - sh); 

      float[] diffs = new float[mx * my]; 
      float[] b = ImageToFloat(big); 
      float[] s = ImageToFloat(small); 
      _lastKernelTime = ImageSearchGPU(diffs, b, s, bw, bh, sw, sh); 
      int minIndex = FindMinCPU(diffs, diffs.Length); 
      return new Point(minIndex % mx, minIndex/mx); 
     } 

     public static List<Point> ImageSearch(Bitmap big, Bitmap small, float maxDeviation) 
     { 
      int bw = big.Width; 
      int bh = big.Height; 
      int sw = small.Width; 
      int sh = small.Height; 
      int mx = (bw - sw); 
      int my = (bh - sh); 
      int nDiff = mx * my; 

      float[] diffs = new float[nDiff]; 
      float[] b = ImageToFloat(big); 
      float[] s = ImageToFloat(small); 
      _lastKernelTime = ImageSearchGPU(diffs, b, s, bw, bh, sw, sh); 

      List<Point> points = new List<Point>(); 
      for(int i = 0; i < nDiff; ++i) 
      { 
       if (diffs[i] < maxDeviation) 
       { 
        points.Add(new Point(i % mx, i/mx)); 
       } 
      } 
      return points; 
     } 

#if SUPPORT_HSB 

     private static float[] ImageToFloat(Bitmap img) 
     { 
      int w = img.Width; 
      int h = img.Height; 
      float[] pix = new float[w * h * 4]; 
      int i = 0; 
      for (int y = 0; y < h; ++y) 
      { 
       for (int x = 0; x < w; ++x) 
       { 
        Color c = img.GetPixel(x, y); 
        pix[i] = c.GetHue()/360;     
        pix[i + 1] = c.GetSaturation();     
        pix[i + 2] = c.GetBrightness();      
        pix[i + 3] = c.A; 
        i += 4; 
       } 
      } 
      return pix; 
     } 
#else 
     private static float[] ImageToFloat(Bitmap bmp) 
     { 
      int w = bmp.Width; 
      int h = bmp.Height; 
      int n = w * h; 
      float[] pix = new float[n * 4]; 

      System.Diagnostics.Debug.Assert(bmp.PixelFormat == PixelFormat.Format32bppArgb); 
      Rectangle r = new Rectangle(0, 0, w, h); 
      BitmapData bmpData = bmp.LockBits(r, ImageLockMode.ReadOnly, bmp.PixelFormat); 
      System.Diagnostics.Debug.Assert(bmpData.Stride > 0); 
      int[] pixels = new int[n]; 
      System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, pixels, 0, n); 
      bmp.UnlockBits(bmpData); 

      int j = 0; 
      for (int i = 0; i < n; ++i) 
      { 
       pix[j] = (pixels[i] & 255)/255.0f; 
       pix[j + 1] = ((pixels[i] >> 8) & 255)/255.0f; 
       pix[j + 2] = ((pixels[i] >> 16) & 255)/255.0f; 
       pix[j + 3] = ((pixels[i] >> 24) & 255)/255.0f; 
       j += 4; 
      } 
      return pix; 
     } 
#endif 
    } 
} 

答えて

2

あなたは

  • テクスチャキャッシュを使用することにより、例えば、より高速なメモリアクセスを使用して計算をスピードアップすることができ大きな画像の場合
  • 共有メモリまたは定数小さい画像またはその一部のためのキャッシュ。

しかし、あなたの本当の問題は、あなたの比較の全体的なアプローチです。すべての可能な場所で画素ごとに画像を比較することは決して効率的ではありません。あまりにも多くの作業が必要です。まず、小さな画像が含まれる可能性があり、大きな画像で面白い画像領域を選択し、それだけで画像を表す何かによって、より高速な比較メカニズムを探すこれら

  • で検索

    1. に方法を見つけることを考える必要がありますピクセル値ではありません。少ないデータで表現を計算することで、画像を比較することができます。カラーヒストグラム、または積分画像である。
  • 2

    あなたが話しているように見えるのはよく知られている問題です:Template matching。最も簡単な方法は、画像(より大きな画像)をテンプレート(より小さな画像)と畳み込むことです。あなたは2つの方法のいずれかで畳み込みを実装することができます。

    1)CUDA SDKの畳み込みの例を変更します(とにかくやっていることに似ています)。

    2)畳み込みを実装するには、FFTを使用します。 Ref。 Convolution theorem。覚えておく必要があります

    % MATLAB format 
    L = size(A) + size(B) - 1; 
    conv2(A, B) = IFFT2(FFT2(A, L) .* FFT2(B, L)); 
    

    2次元FFTを実装するには、カフを使用できます(適切な埋め込み後)。要素の賢明な乗算を行うカーネルを作成し、逆FFTを実行する前に結果を正規化する必要があります(CUFFTが正規化しないため)。

    (1024×1280と128×128)のサイズについては、少なくとも(1024 + 128-1)×(1280 + 128-1)= 1151×1407に入力する必要があります。しかし、(パディングされた)入力が2の累乗である場合、FFTは最も高速です。したがって、サイズの大きな画像と小さい画像の両方を2048 x 2048のサイズにパディングする必要があります。

    関連する問題