29 julho, 2013 0 Comentários AUTOR: elemarjr CATEGORIAS: Sem categoria Tags:,

Image Effects com C++ AMP - Blur

Tempo de leitura: 1 minuto

Olá. Tudo certo?

Agora que já tratamos dos fundamentos de C++ AMP, está na hora de começar a botar as "mãos na massa" e fazer algo de útil.

Para começar, vamos criar um filtro simples de imagens: blur.

O código-fonte completo está disponível no github. Fique a vontade para acessar e criar alguns pull-requests. Nesse post, trago apenas a parte essencial do código (detalhes devem ser conferidos no github).

Aplicando Blur

Abaixo, um screenshot da aplicação que estamos desenvolvendo antes da aplicação do filtro.

blur_antes

Agora, um screenshot após aplicação do filtro:

blur_depois

Legal, não é?

Blur em C++ (sem usar C++ AMP)

No projeto, criei uma interface muito simples para representar os "filtros" que posso aplicar a uma imagem. Ela tem apenas um método, ProcessImage, que recebe um bitmap para ser processado e outro que deve receber o resultado do processamento. Abaixo, o código que aplica Blur usando C++ puro.

void ProcessImage(
	const Gdiplus::BitmapData& source,
	Gdiplus::BitmapData& dest
	)
{
	int bpp = Gdiplus::GetPixelFormatSize(dest.PixelFormat);
	const int DIST = 10;

	int w = source.Width;
	int h = source.Height;

	for (int x = 0; x < w; x++)
		for (int y = 0; y < h; y++)
		{
			int accumR = 0;
			int accumG = 0;
			int accumB = 0;
			int count = 0;
			for (int currentX = x - DIST ; currentX < (x + DIST); currentX++)
				if (currentX >= 0 && currentX < w)  
					for (int currentY = y - DIST ; currentY < (y + DIST); currentY++)
						if (currentY >= 0 && currentY < h) 
						{
							COLORREF pixel = GetPixel(static_cast<byte *>(source.Scan0), 
								currentX, currentY, dest.Stride, bpp);
							accumR += GetRValue(pixel);
							accumG += GetGValue(pixel);
							accumB += GetBValue(pixel);
							count ++;
						}

			SetPixel(static_cast<byte *>(dest.Scan0), x, y, dest.Stride, bpp, 
				RGB(accumR / count, accumG / count, accumB / count));
		}
}

A lógica é muito simples. Cada pixel é calculado considerando uma média de seus vizinhos.

Blur usando C++ AMP

Agora, o mesmo código anterior, porém, adaptado para C++ AMP.

void ProcessImage(
	const Gdiplus::BitmapData& source,
	Gdiplus::BitmapData& dest
	)
{
	using namespace concurrency;
	const int DIST = 10;
	const int w = source.Width;
	const int h = source.Height;

	array<ArgbPackedPixel, 2> asource(h, w), adest(h, w);

	CopyIn(source, asource);

	concurrency::parallel_for_each(asource.extent,
		[=, &asource, &adest](index<2> idx) restrict (amp) {
		RgbPixel accum;
		accum.r = 0; accum.g = 0; accum.b = 0;

		int count = 0;
		int x = idx[1];
		int y = idx[0];
		for (int currentX = x - DIST ; currentX < (x + DIST); currentX++)
			if (currentX >= 0 && currentX < w)
				for (int currentY = y - DIST ; currentY < (y + DIST); currentY++)
					if (currentY >= 0 && currentY < h)
					{
						RgbPixel pixel = UnpackPixel(asource&#91;index<2>(currentY, currentX)]);
						accum.r += pixel.r;
						accum.g += pixel.g;
						accum.b += pixel.b;
						count ++;
					}

		accum.r /= count; accum.g /= count; accum.b /= count;
		adest[idx] = PackPixel(accum);

	});

	CopyOut(adest, dest);
}

Comparando tempos

A primeira versão consome quase 6 segundos para completar seu trabalho (em meu computador). A segunda versão, com C++ AMP, é "instantânea".

Era isso. 🙂

0 Comentários

  1. Marcos 3 anos ago says:

    Excelente tópico, justo o que procurava para complementar minhas leituras sobre C++AMP.
    Algumas mudanças para utilizar com VS2013RC (C++AMP 2.0):
    Não precisa mais de ArgbPackedPixel e RgbPixel, pode usar o submódulo concurrency::graphics (usando ccg como atalho):
    ccg::texture src{ h, w, src_ptr, src_h_dot_pitch, 8U };//cpu para gpu
    ccg::texture dst{ h, w, 8U };
    ccg::texture_view dst_vw{ dst };//write_only_texture deprecated
    parallel_for_each(dst_vw.extent, [=, &src](index idx)...
    ...
    ccg::uint_4 accum{ 0 };
    ...
    auto pixel = src[cc::index(currentY, currentX)];
    ...
    accum.rgb /= count;
    accum.a = 0xff;
    dst_vw.set(idx, accum);
    ....
    ccg::copy(dst_vw, buffer_ptr, buffer_h_dot_pitch);//da gpu para cpu

    Responder