8

更新:より多くのコードを含む以下の追加の質問を参照してください。

画像をぼかすためのカテゴリをコーディングしようとしています。私の出発点は、ここにあるJeffLaMarcheのサンプルです。これは(他の人が提案した修正後)正常に機能しますが、私の要件には1桁遅すぎます。3GSでは、適切なぼかしを行うのに3秒かかる可能性があり、これを0.5未満に下げたいと思います。フルスクリーンの場合は秒(速いほど良い)。

彼はAccelerateフレームワークをパフォーマンスの向上として言及しているので、私はこれ、特にAppleDocumenationによるとvDSP_f3x3を見て最後の日を過ごしました。

3x3カーネルで2次元畳み込みを実行することにより、画像をフィルタリングします。単精度。

完璧-私は適切なフィルターマトリックスを持っていて、画像を持っています...しかし、これは私が困惑するところです。

vDSP_f3x3は、画像データが(float *)であると想定していますが、私の画像は;からのものです。

srcData = (unsigned char *)CGBitmapContextGetData (context);

コンテキストはkCGImageAlphaPremultipliedFirstを使用したCGBitmapContextCreateから取得されるため、私のsrcDataは実際にはコンポーネントあたり8ビットのARGBです。

私が本当に必要としているのはfloatコンポーネントのコンテキストだと思いますが、Quartzのドキュメントによると、kCGBitMapFloatComponentsはMac OSでのみ利用可能であり、iOSでは利用できません:-(

私が持っている整数コンポーネントをvDSP_f3x3が必要とするfloatコンポーネントに変換するaccelateフレームワークを使用する本当に速い方法はありますか?自分でできるということですが、それを実行してから畳み込みを行ってから元に戻すまでに、畳み込みを行った方がよいので、今よりもさらに遅くなっているのではないかと思います。

多分私は間違ったアプローチをしていますか?

vDSPを使用してiPhoneで画像処理を行ったヒントはありますか?私が見つけることができるドキュメントは、非常に参照指向であり、この種のことに関してはあまり初心者に優しいものではありません。

誰かが本当に速いぼかし(そして高品質、解像度を下げてから私が見たものを再スケーリングしてズボンに見えるものではない)のリファレンスを持っているなら、それは素晴らしいでしょう!

編集:

@Jasonに感謝します。私はこれを実行し、ほぼ機能していますが、問題は、画像がぼやけても、呼び出すたびに1ピクセル左にシフトすることです。また、画像が白黒になるように見えますが、それは別のことかもしれません。

このコードには、明らかに正しくないものとして飛び出しているものがありますか?私はまだそれを最適化しておらず、少しラフですが、たたみ込みコードが十分に明確であることを願っています。

CGImageRef CreateCGImageByBlurringImage(CGImageRef inImage, NSUInteger pixelRadius, NSUInteger gaussFactor)
{
unsigned char *srcData, *finalData;

CGContextRef context = CreateARGBBitmapContext(inImage);
if (context == NULL) 
    return NULL;

size_t width = CGBitmapContextGetWidth(context);
size_t height = CGBitmapContextGetHeight(context);
size_t bpr = CGBitmapContextGetBytesPerRow(context);

int componentsPerPixel = 4; // ARGB

CGRect rect = {{0,0},{width,height}}; 
CGContextDrawImage(context, rect, inImage); 

// Now we can get a pointer to the image data associated with the bitmap
// context.

srcData = (unsigned char *)CGBitmapContextGetData (context);

if (srcData != NULL)
{

    size_t dataSize = bpr * height;
    finalData = malloc(dataSize);
    memcpy(finalData, srcData, dataSize);

    //Generate Gaussian kernel

    float *kernel;  

    // Limit the pixelRadius

    pixelRadius = MIN(MAX(1,pixelRadius), 248);
    int kernelSize = pixelRadius * 2 + 1;

    kernel = malloc(kernelSize * sizeof *kernel);

    int gauss_sum =0;

    for (int i = 0; i < pixelRadius; i++)
    {
        kernel[i] = 1 + (gaussFactor*i);
        kernel[kernelSize - (i + 1)] = 1 + (gaussFactor * i);
        gauss_sum += (kernel[i] + kernel[kernelSize - (i + 1)]);
    }

    kernel[(kernelSize - 1)/2] = 1 + (gaussFactor*pixelRadius);

    gauss_sum += kernel[(kernelSize-1)/2];

    // Scale the kernel

    for (int i=0; i<kernelSize; ++i) {
        kernel[i] = kernel[i]/gauss_sum;
    }

    float * srcAsFloat,* resultAsFloat;

    srcAsFloat = malloc(width*height*sizeof(float)*componentsPerPixel);
    resultAsFloat = malloc(width*height*sizeof(float)*componentsPerPixel);

   // Convert uint source ARGB to floats

    vDSP_vfltu8(srcData,1,srcAsFloat,1,width*height*componentsPerPixel);

    // Convolve (hence the -1) with the kernel

    vDSP_conv(srcAsFloat, 1, &kernel[kernelSize-1],-1, resultAsFloat, 1, width*height*componentsPerPixel, kernelSize);

    // Copy the floats back to ints

    vDSP_vfixu8(resultAsFloat, 1, finalData, 1, width*height*componentsPerPixel);

    free(resultAsFloat);
    free(srcAsFloat);

}

size_t bitmapByteCount = bpr * height;

CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, finalData, bitmapByteCount, &providerRelease);

CGImageRef cgImage = CGImageCreate(width, height, CGBitmapContextGetBitsPerComponent(context),
                                   CGBitmapContextGetBitsPerPixel(context), CGBitmapContextGetBytesPerRow(context), CGBitmapContextGetColorSpace(context), CGBitmapContextGetBitmapInfo(context), 
                                   dataProvider, NULL, true, kCGRenderingIntentDefault);

CGDataProviderRelease(dataProvider);
CGContextRelease(context); 


return cgImage;
}

vDSP_conv行をコメントアウトし、次の行を次のように変更する場合は、これを追加する必要があります。

       vDSP_vfixu8(srcAsFloat, 1, finalData, 1, width*height*componentsPerPixel);

その後、予想どおり、私の結果は元のソースのクローンになります。カラーで、左にシフトされていません。これは、問題が発生しているのは畳み込みであることを意味しますが、どこにあるのかわかりません:-(

思考:実際にこれについて考えると、畳み込みは入力ピクセルがARGB形式であることを知る必要があるように思われます。そうしないと、畳み込みは値を乗算し、その意味についての知識がありません(つまり、複数のR * Bなど) 。これは、私が思うに白黒の結果が得られる理由を説明しますが、シフトは説明しません。繰り返しになりますが、ここにある私の素朴なバージョンよりも多くのものが必要かもしれないと思います...

最終的な考え:左へのシフトはフィルターの自然な結果だと思います。画像のサイズを確認し、場合によってはそれを埋める必要があります...したがって、コードは実際に機能していると思います。

4

5 に答える 5

12

Accelerateフレームワークは単純なシリアルコードよりも高速ですが、それを使用して画像をぼかすことで最高のパフォーマンスが得られることはおそらくないでしょう。

私の提案は、OpenGL ES 2.0シェーダー(このAPIをサポートするデバイス用)を使用して、2パスボックスブラーを実行することです。私のベンチマークに基づくと、GPUはiPhone 4のCPUの14〜28倍の速度でこれらの種類の画像操作操作を処理できますが、Appleが最良の場合にAccelerateフレームワークについて報告しているのはおそらく4.5倍です。

このためのいくつかのコードは、この質問と、 GPU Pro 2ブックの「モバイルデバイスに対する後処理の影響」の章(サンプルコードはここにあります)で説明されています。画像をテクスチャに配置し、ピクセル間の値を読み取ることにより、GPUでの双一次フィルタリングにより、無料でぼかしが得られます。これは、いくつかの高速ルックアップおよび平均化操作と組み合わせることができます。

処理のために画像をGPUにフィードするための開始プロジェクトが必要な場合は、こちらの記事のサンプルアプリケーションを使用できる可能性があります。このサンプルアプリケーションは、AVFoundationビデオフレームをテクスチャとして処理シェーダーに渡しますが、特定の画像データを送信してぼかし操作を実行するように変更することができます。私のglReadPixels()コードを使用して、後で使用するためにぼやけた画像を取得できるはずです。

私は最初にこの回答を書いたので、GPUでこれらの種類の操作を行うためのオープンソースの画像およびビデオ処理フレームワークを作成しました。フレームワークにはいくつかの異なるぼかしタイプがあり、それらはすべて画像やライブビデオに非常にすばやく適用できます。GPUImageGaussianBlurFilterは、標準の9ヒットガウスぼかしを適用し、iPhone 4の640x480フレームのビデオに対して16ミリ秒で実行されます。GPUImageFastBlurFilterは、ハードウェアフィルタリングを使用する修正された9ヒットガウスぼかしであり、2.0ミリ秒で実行されます。その同じビデオフレーム。同様に、5ピクセルのボックスを使用し、同じハードウェア上の同じ画像に対して1.9ミリ秒で実行されるGPUImageBoxBlurFilterがあります。パフォーマンスの調整が少し必要ですが、メジアンとバイラテラルのブラーフィルターもあります。

私のベンチマークでは、特にライブビデオのフィルタリングに関しては、Accelerateはこれらの種類の速度に近づいていません。

于 2011-05-10T19:01:03.010 に答える
9

フィルタリングを実行するために変換するfloat必要があります。これは、高速化された関数が取るものであるためです。さらに、追加の処理を実行する場合は、はるかに柔軟です。2次元畳み込み(フィルター)の計算時間は、変換に費やされる時間よりも短い可能性があります。vDSP_vfltu8()uint8データをfloatにすばやく変換する 関数を見てください。vDSP_vfixu8()uint8に変換し直します。

ぼかしを実行するには、おそらく3x3よりも大きな畳み込みカーネルが必要vDSP_imgfir()になるため、任意のカーネルサイズを使用する関数を使用することをお勧めします。

編集への応答:

いくつかのこと:

  1. 各カラーチャンネルで個別にフィルタリングを実行する必要があります。つまり、R、G、およびBコンポーネントを(floatタイプの)独自の画像に分割し、それらをフィルタリングしてから、ARGB画像に再多重化する必要があります。

  2. vDSP_conv1次元の畳み込みを計算しますが、画像をぼかすには、実際には2次元の畳み込みが必要です。 vDSP_imgfir基本的に2次元畳み込みを計算します。このためには、2Dカーネルも必要になります。カーネルを生成するための2次元ガウス関数の式を調べることができます。
    注:カーネルが分離可能である場合(ガウス)、実際には1次元畳み込みを使用して2次元畳み込みを実行できます。これが何を意味するのかについては説明しませんが、基本的には、列全体で1次元畳み込みを実行してから、結果の行全体で1次元畳み込みを実行する必要があります。あなたが何をしているのかを知らない限り、私はこのルートに行きません。

于 2011-05-09T16:07:22.570 に答える
3

これを実装することを検討している場合の将来の参照用にしないでください:私はあなたのためにそれを行いました!

参照: https ://github.com/gdawg/uiimage-dsp

vDSPとAccelerateフレームワークを使用してGaussian/Box Blur/Sharpenを追加するUIImageカテゴリの場合。

于 2011-05-18T14:51:35.483 に答える
3

したがって、ジェイソンの優れた助けを借りて私自身の質問に答えるために、他の誰かを助ける場合に備えて、最終的な作業コードフラグメントが参照用にここに提供されています。ご覧のとおり、戦略は、ソースARGB(パフォーマンスのためにAを無視し、データがXRGBであると想定)を3つのfloat配列に分割し、フィルターを適用してから、結果を再多重化することです。

それは御馳走になります-しかしそれは痛々しいほど遅いです。私は16x16の大きなカーネルを使用して大きなぼかしを取得していますが、3GSではフルスクリーン画像に約5秒かかるため、実行可能なソリューションにはなりません。

次のステップは代替案を検討することです...しかし、私を立ち上げて実行してくれてありがとう。

    vDSP_vfltu8(srcData+1,4,srcAsFloatR,1,pixels);
    vDSP_vfltu8(srcData+2,4,srcAsFloatG,1,pixels);
    vDSP_vfltu8(srcData+3,4,srcAsFloatB,1,pixels);

    // Now apply the filter to each of the components. For a gaussian blur with a 16x16 kernel
    // this turns out to be really slow!

    vDSP_imgfir (srcAsFloatR, height, width, kernel,resultAsFloatR, frows, fcols);
    vDSP_imgfir (srcAsFloatG, height, width, kernel,resultAsFloatG, frows, fcols);
    vDSP_imgfir (srcAsFloatB, height, width, kernel,resultAsFloatB, frows, fcols);

    // Now re-multiplex the final image from the processed float data

    vDSP_vfixu8(resultAsFloatR, 1, finalData+1, 4, pixels);
    vDSP_vfixu8(resultAsFloatG, 1, finalData+2, 4, pixels);
    vDSP_vfixu8(resultAsFloatB, 1, finalData+3, 4, pixels);
于 2011-05-10T10:06:06.913 に答える
2

なぜvDSPを使用して画像フィルタリングを行うのですか?vImageConvolve_ARGB8888()を試してください。vImageは、Accelerate.frameworkの画像処理コンポーネントです。

于 2014-01-21T02:22:37.440 に答える