Compartilhar via


Passo a passo: Criando uma rede Processamento de imagens

Este documento demonstra como criar uma rede de blocos de mensagens assíncronas que executam o processamento de imagem.

A rede determina quais operações realizar em uma imagem na Base de suas características.Este exemplo usa o fluxo de dados modelo rota imagens através da rede.No modelo de fluxo de dados, componentes independentes de um programa se comunicar entre si através do envio de mensagens.Quando um componente recebe uma mensagem, ele pode realizar alguma ação e passar o resultado da ação para outro componente.Compare isso com o fluxo de controle modelo, no qual um aplicativo usa estruturas de controle, por exemplo, instruções condicionais, loops e assim por diante, para controlar a ordem das operações em um programa.

Uma rede baseada em fluxo de dados cria um pipeline de tarefas.Cada estágio do pipeline executa simultaneamente a parte da tarefa geral.Uma analogia para isso é uma linha de montagem para fabricação de automóvel.Como cada veículo passa por linha de montagem, uma estação de monta o quadro, outro instala o mecanismo e assim por diante.Permitindo que vários veículos ser montados simultaneamente, a linha de montagem fornece melhor throughput que montar veículos completos uma vez.

Pré-requisitos

Antes de iniciar esta explicação passo a passo, leia os seguintes documentos:

Também recomendamos que você entenda os fundamentos de GDI+ antes de iniciar esta explicação passo a passo.Para obter mais informações sobre GDI+, consulte GDI+.

Seções

Esta explicação passo a passo contém as seções a seguir:

  • Definir a funcionalidade de processamento de imagem

  • Criação de rede de processamento de imagem

  • O exemplo completo

Definir a funcionalidade de processamento de imagem

Esta seção mostra as funções de suporte a rede de processamento de imagem usa para trabalhar com imagens que são lidas do disco.

As seguintes funções, GetRGB e MakeColor, extrair e combinar os componentes individuais de determinada cor, respectivamente.

// Retrieves the red, green, and blue components from the given
// color value.
void GetRGB(DWORD color, BYTE& r, BYTE& g, BYTE& b)
{
   r = static_cast<BYTE>((color & 0x00ff0000) >> 16);
   g = static_cast<BYTE>((color & 0x0000ff00) >> 8);
   b = static_cast<BYTE>((color & 0x000000ff));
}

// Creates a single color value from the provided red, green, 
// and blue components.
DWORD MakeColor(BYTE r, BYTE g, BYTE b)
{
   return (r<<16) | (g<<8) | (b);
}

A seguinte função ProcessImage, chamadas de determinado std::function objeto para transformar o valor de cor de cada pixel em um GDI+Bitmap objeto.O ProcessImage função usa o concurrency::parallel_for algoritmo para processar cada linha do bitmap em paralelo.

// Calls the provided function for each pixel in a Bitmap object.
void ProcessImage(Bitmap* bmp, const function<void (DWORD&)>& f)
{
   int width = bmp->GetWidth();
   int height = bmp->GetHeight();

   // Lock the bitmap.
   BitmapData bitmapData;
   Rect rect(0, 0, bmp->GetWidth(), bmp->GetHeight());
   bmp->LockBits(&rect, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData);

   // Get a pointer to the bitmap data.
   DWORD* image_bits = (DWORD*)bitmapData.Scan0;

   // Call the function for each pixel in the image.
   parallel_for (0, height, [&, width](int y)
   {      
      for (int x = 0; x < width; ++x)
      {
         // Get the current pixel value.
         DWORD* curr_pixel = image_bits + (y * width) + x;

         // Call the function.
         f(*curr_pixel);
      }
   });

   // Unlock the bitmap.
   bmp->UnlockBits(&bitmapData);
}

As seguintes funções, Grayscale, Sepiatone, ColorMask, e Darken, chamar o ProcessImage função para transformar o valor de cor de cada pixel em um Bitmap objeto.Cada uma dessas funções usa uma expressão lambda para definir a transformação de cor de um pixel.

// Converts the given image to grayscale.
Bitmap* Grayscale(Bitmap* bmp) 
{
   ProcessImage(bmp, 
      [](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);

         // Set each color component to the average of 
         // the original components.
         BYTE c = (static_cast<WORD>(r) + g + b) / 3;
         color = MakeColor(c, c, c);
      }
   );
   return bmp;
}

// Applies sepia toning to the provided image.
Bitmap* Sepiatone(Bitmap* bmp) 
{
   ProcessImage(bmp, 
      [](DWORD& color) {
         BYTE r0, g0, b0;
         GetRGB(color, r0, g0, b0);

         WORD r1 = static_cast<WORD>((r0 * .393) + (g0 *.769) + (b0 * .189));
         WORD g1 = static_cast<WORD>((r0 * .349) + (g0 *.686) + (b0 * .168));
         WORD b1 = static_cast<WORD>((r0 * .272) + (g0 *.534) + (b0 * .131));

         color = MakeColor(min(0xff, r1), min(0xff, g1), min(0xff, b1));
      }
   );
   return bmp;
}

// Applies the given color mask to each pixel in the provided image.
Bitmap* ColorMask(Bitmap* bmp, DWORD mask)
{
   ProcessImage(bmp, 
      [mask](DWORD& color) {
         color = color & mask;
      }
   );
   return bmp;
}

// Darkens the provided image by the given amount.
Bitmap* Darken(Bitmap* bmp, unsigned int percent)
{
   if (percent > 100)
      throw invalid_argument("Darken: percent must less than 100.");

   double factor = percent / 100.0;

   ProcessImage(bmp, 
      [factor](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);
         r = static_cast<BYTE>(factor*r);
         g = static_cast<BYTE>(factor*g);
         b = static_cast<BYTE>(factor*b);
         color = MakeColor(r, g, b);
      }
   );
   return bmp;
}

A seguinte função GetColorDominance, também chamadas de ProcessImage função.No entanto, em vez de alterar o valor de cada cor, essa função usa concurrency::combinable objetos para calcular se o componente de cor vermelho, verde ou azul domina a imagem.

// Determines which color component (red, green, or blue) is most dominant
// in the given image and returns a corresponding color mask.
DWORD GetColorDominance(Bitmap* bmp)
{
   // The ProcessImage function processes the image in parallel.
   // The following combinable objects enable the callback function
   // to increment the color counts without using a lock.
   combinable<unsigned int> reds;
   combinable<unsigned int> greens;
   combinable<unsigned int> blues;

   ProcessImage(bmp, 
      [&](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);
         if (r >= g && r >= b)
            reds.local()++;
         else if (g >= r && g >= b)
            greens.local()++;
         else
            blues.local()++;
      }
   );

   // Determine which color is dominant and return the corresponding
   // color mask.

   unsigned int r = reds.combine(plus<unsigned int>());
   unsigned int g = greens.combine(plus<unsigned int>());
   unsigned int b = blues.combine(plus<unsigned int>());

   if (r + r >= g + b)
      return 0x00ff0000;
   else if (g + g >= r + b)
      return 0x0000ff00;
   else
      return 0x000000ff;
}

A seguinte função GetEncoderClsid, recupera o identificador de classe para determinado tipo MIME de um codificador.O aplicativo usa essa função para recuperar o codificador para um bitmap.

// Retrieves the class identifier for the given MIME type of an encoder.
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = nullptr;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == nullptr)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}

Top

Criação de rede de processamento de imagem

Esta seção descreve como criar uma rede de blocos de mensagens assíncronas que executam o processamento de imagem em cada JPEG imagem (. jpg) em um determinado diretório.A rede realiza as seguintes operações de processamento de imagem:

  1. Qualquer imagem de autoria de Tom, converta em tons de cinza.

  2. Para qualquer imagem que tem o vermelho como cor dominante, remova os componentes de verdes e azuis e escurecê-lo.

  3. Para qualquer imagem, aplique a coloração sépia.

A rede se aplica somente a primeira operação de processamento de imagem que corresponde a uma dessas condições.Por exemplo, se uma imagem é de autoria de Tom e tem o vermelho como sua cor dominante, a imagem só é convertida em tons de cinza.

Após a rede executa cada operação de processamento de imagem, ele salva a imagem de disco como um arquivo bitmap (. bmp).

As etapas a seguir mostram como criar uma função que implementa a rede de processamento de imagem e aplica essa rede para cada JPEG imagem em um determinado diretório.

Para criar a rede de processamento de imagem

  1. Criar uma função, ProcessImages, que leva o nome de um diretório no disco.

    void ProcessImages(const wstring& directory)
    {
    }
    
  2. No ProcessImages função, criar um countdown_event variável.O countdown_event classe é mostrado posteriormente nesta explicação passo a passo.

    // Holds the number of active image processing operations and 
    // signals to the main thread that processing is complete.
    countdown_event active(0);
    
  3. Criar um std::map objeto que associa um Bitmap o objeto com o nome de arquivo original.

    // Maps Bitmap objects to their original file names.
    map<Bitmap*, wstring> bitmap_file_names;
    
  4. Adicione o seguinte código para definir os membros da rede de processamento de imagem.

    //
    // Create the nodes of the network.
    //
    
    // Loads Bitmap objects from disk.
    transformer<wstring, Bitmap*> load_bitmap(
       [&](wstring file_name) -> Bitmap* {
          Bitmap* bmp = new Bitmap(file_name.c_str());
          if (bmp != nullptr)
             bitmap_file_names.insert(make_pair(bmp, file_name));
          return bmp;
       }
    );
    
    // Holds loaded Bitmap objects.
    unbounded_buffer<Bitmap*> loaded_bitmaps;
    
    // Converts images that are authored by Tom to grayscale.
    transformer<Bitmap*, Bitmap*> grayscale(
       [](Bitmap* bmp) {
          return Grayscale(bmp);
       },
       nullptr,
       [](Bitmap* bmp) -> bool {
          if (bmp == nullptr)
             return false;
    
          // Retrieve the artist name from metadata.
          UINT size = bmp->GetPropertyItemSize(PropertyTagArtist);
          if (size == 0)
             // Image does not have the Artist property.
             return false;
    
          PropertyItem* artistProperty = (PropertyItem*) malloc(size);
          bmp->GetPropertyItem(PropertyTagArtist, size, artistProperty);
          string artist(reinterpret_cast<char*>(artistProperty->value));
          free(artistProperty);
    
          return (artist.find("Tom ") == 0);
       }
    );
    
    // Removes the green and blue color components from images that have red as
    // their dominant color.
    transformer<Bitmap*, Bitmap*> colormask(
       [](Bitmap* bmp) {
          return ColorMask(bmp, 0x00ff0000);
       },
       nullptr,
       [](Bitmap* bmp) -> bool { 
          if (bmp == nullptr)
             return false;
          return (GetColorDominance(bmp) == 0x00ff0000);
       }
    );
    
    // Darkens the color of the provided Bitmap object.
    transformer<Bitmap*, Bitmap*> darken([](Bitmap* bmp) {
       return Darken(bmp, 50);
    });
    
    // Applies sepia toning to the remaining images.
    transformer<Bitmap*, Bitmap*> sepiatone(
       [](Bitmap* bmp) {
          return Sepiatone(bmp);
       },
       nullptr,
       [](Bitmap* bmp) -> bool { return bmp != nullptr; }
    );
    
    // Saves Bitmap objects to disk.
    transformer<Bitmap*, Bitmap*> save_bitmap([&](Bitmap* bmp) -> Bitmap* {
       // Replace the file extension with .bmp.
       wstring file_name = bitmap_file_names[bmp];
       file_name.replace(file_name.rfind(L'.') + 1, 3, L"bmp");
    
       // Save the processed image.
       CLSID bmpClsid;
       GetEncoderClsid(L"image/bmp", &bmpClsid);      
       bmp->Save(file_name.c_str(), &bmpClsid);
    
       return bmp;
    });
    
    // Deletes Bitmap objects.
    transformer<Bitmap*, Bitmap*> delete_bitmap([](Bitmap* bmp) -> Bitmap* {      
       delete bmp;
       return nullptr;
    });
    
    // Decrements the event counter.
    call<Bitmap*> decrement([&](Bitmap* _) {      
       active.signal();
    });
    
  5. Adicione o seguinte código para conectar à rede.

    //
    // Connect the network.
    //   
    
    load_bitmap.link_target(&loaded_bitmaps);
    
    loaded_bitmaps.link_target(&grayscale);
    loaded_bitmaps.link_target(&colormask);   
    colormask.link_target(&darken);
    loaded_bitmaps.link_target(&sepiatone);
    loaded_bitmaps.link_target(&decrement);
    
    grayscale.link_target(&save_bitmap);
    darken.link_target(&save_bitmap);
    sepiatone.link_target(&save_bitmap);
    
    save_bitmap.link_target(&delete_bitmap);
    delete_bitmap.link_target(&decrement);
    
  6. Adicione o seguinte código para enviar a cabeça da rede, o caminho completo de cada JPEG arquivo no diretório.

    // Traverse all files in the directory.
    wstring searchPattern = directory;
    searchPattern.append(L"\\*");
    
    WIN32_FIND_DATA fileFindData;
    HANDLE hFind = FindFirstFile(searchPattern.c_str(), &fileFindData);
    if (hFind == INVALID_HANDLE_VALUE) 
       return;
    do
    {
       if (!(fileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
       {
          wstring file = fileFindData.cFileName;
    
          // Process only JPEG files.
          if (file.rfind(L".jpg") == file.length() - 4)
          {
             // Form the full path to the file.
             wstring full_path(directory);
             full_path.append(L"\\");
             full_path.append(file);
    
             // Increment the count of work items.
             active.add_count();
    
             // Send the path name to the network.
             send(load_bitmap, full_path);
          }
       }
    }
    while (FindNextFile(hFind, &fileFindData) != 0); 
    FindClose(hFind);
    
  7. Aguarde o countdown_event variável para chegar a zero.

    // Wait for all operations to finish.
    active.wait();
    

A tabela a seguir descreve os membros da rede.

Membro

Descrição

load_bitmap

A concurrency::transformer objeto carrega um Bitmap de objeto do disco e adiciona uma entrada para o map objeto para associar a imagem com o nome de arquivo original.

loaded_bitmaps

A concurrency::unbounded_buffer objeto que envia as imagens carregadas para os filtros de processamento de imagem.

grayscale

A transformer objeto que converte as imagens são criadas por Tom de cinza.Ele usa os metadados da imagem para determinar seu autor.

colormask

A transformer objeto remove componentes de cor verde e azul de imagens que tenham vermelho como a cor dominante.

darken

A transformer objeto escurece imagens que tenham vermelho como a cor dominante.

sepiatone

A transformer objeto aplica o ajuste de tons de sépia para imagens que não são criadas pelo Tom e não são predominantemente vermelho.

save_bitmap

A transformer objeto salva o processados image disco como um bitmap.save_bitmaprecupera o nome do arquivo original do map de objeto e altera sua extensão de nome de arquivo para. bmp.

delete_bitmap

A transformer objeto libera a memória para as imagens.

decrement

A concurrency::call objeto que age como o nó terminal na rede.Ele diminui a countdown_event objeto para sinalizar para o aplicativo principal que uma imagem foi processada.

O loaded_bitmaps buffer de mensagem é importante porque, como um unbounded_buffer de objeto, ele oferece Bitmap objetos a vários receptores.Quando um bloco de destino aceita uma Bitmap o objeto, o unbounded_buffer objeto não oferece que Bitmap objeto para outros destinos.Portanto, a ordem na qual você vincular objetos para um unbounded_buffer o objeto é importante.O grayscale, colormask, e sepiatone mensagem blocos cada usam um filtro para aceitar somente certas Bitmap objetos.O decrement buffer de mensagem é um destino importante do loaded_bitmaps buffer de mensagem, pois ele aceita todos os Bitmap objetos rejeitados por buffers de mensagem.Um unbounded_buffer objeto é necessária para propagar as mensagens.Portanto, um unbounded_buffer bloqueia o objeto até um novo bloco de destino está vinculado e aceita a mensagem se nenhum bloco de destino atual aceita a mensagem.

Se seu aplicativo requer vários mensagem bloqueia o processo a mensagem, em vez de apenas o bloco de mensagem de um primeiro aceita a mensagem, você pode usar outro tipo de bloco de mensagem, como overwrite_buffer.O overwrite_buffer classe contém uma mensagem por vez, mas ele propaga a mensagem para cada um dos seus destinos.

A ilustração a seguir mostra a rede de processamento de imagem:

A rede de processamento de imagem

O countdown_event objeto neste exemplo permite que a rede de processamento de imagem informar o aplicativo principal quando todas as imagens foram processadas.O countdown_event classe usa uma concurrency::event objeto para sinalizar quando um valor de contador chega a zero.O principal aplicativo incrementa o contador toda vez que ele envia um nome de arquivo para a rede.O nó terminal de Decrementos rede contador após o processamento de cada imagem.Depois que o aplicativo principal percorre o diretório especificado, ele aguarda o countdown_event objeto para sinalizar que seu contador atingir zero.

A exemplo a seguir mostra a countdown_event classe:

// A synchronization primitive that is signaled when its 
// count reaches zero.
class countdown_event
{
public:
   countdown_event(unsigned int count = 0)
      : _current(static_cast<long>(count)) 
   {
      // Set the event if the initial count is zero.
      if (_current == 0L)
         _event.set();
   }

   // Decrements the event counter.
   void signal() {
      if(InterlockedDecrement(&_current) == 0L) {
         _event.set();
      }
   }

   // Increments the event counter.
   void add_count() {
      if(InterlockedIncrement(&_current) == 1L) {
         _event.reset();
      }
   }

   // Blocks the current context until the event is set.
   void wait() {
      _event.wait();
   }

private:
   // The current count.
   volatile long _current;
   // The event that is set when the counter reaches zero.
   event _event;

   // Disable copy constructor.
   countdown_event(const countdown_event&);
   // Disable assignment.
   countdown_event const & operator=(countdown_event const&);
};

Top

O exemplo completo

O código a seguir mostra o exemplo completo.O wmain função gerencia a GDI+ biblioteca e chama o ProcessImages funcionar ao processo o JPEG arquivos na Amostras de imagens diretório.

// image-processing-network.cpp
// compile with: /DUNICODE /EHsc image-processing-network.cpp /link gdiplus.lib
#include <windows.h>
#include <gdiplus.h>
#include <iostream>
#include <map>
#include <agents.h>
#include <ppl.h>

using namespace concurrency;
using namespace Gdiplus;
using namespace std;

// Retrieves the red, green, and blue components from the given
// color value.
void GetRGB(DWORD color, BYTE& r, BYTE& g, BYTE& b)
{
   r = static_cast<BYTE>((color & 0x00ff0000) >> 16);
   g = static_cast<BYTE>((color & 0x0000ff00) >> 8);
   b = static_cast<BYTE>((color & 0x000000ff));
}

// Creates a single color value from the provided red, green, 
// and blue components.
DWORD MakeColor(BYTE r, BYTE g, BYTE b)
{
   return (r<<16) | (g<<8) | (b);
}

// Calls the provided function for each pixel in a Bitmap object.
void ProcessImage(Bitmap* bmp, const function<void (DWORD&)>& f)
{
   int width = bmp->GetWidth();
   int height = bmp->GetHeight();

   // Lock the bitmap.
   BitmapData bitmapData;
   Rect rect(0, 0, bmp->GetWidth(), bmp->GetHeight());
   bmp->LockBits(&rect, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData);

   // Get a pointer to the bitmap data.
   DWORD* image_bits = (DWORD*)bitmapData.Scan0;

   // Call the function for each pixel in the image.
   parallel_for (0, height, [&, width](int y)
   {      
      for (int x = 0; x < width; ++x)
      {
         // Get the current pixel value.
         DWORD* curr_pixel = image_bits + (y * width) + x;

         // Call the function.
         f(*curr_pixel);
      }
   });

   // Unlock the bitmap.
   bmp->UnlockBits(&bitmapData);
}

// Converts the given image to grayscale.
Bitmap* Grayscale(Bitmap* bmp) 
{
   ProcessImage(bmp, 
      [](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);

         // Set each color component to the average of 
         // the original components.
         BYTE c = (static_cast<WORD>(r) + g + b) / 3;
         color = MakeColor(c, c, c);
      }
   );
   return bmp;
}

// Applies sepia toning to the provided image.
Bitmap* Sepiatone(Bitmap* bmp) 
{
   ProcessImage(bmp, 
      [](DWORD& color) {
         BYTE r0, g0, b0;
         GetRGB(color, r0, g0, b0);

         WORD r1 = static_cast<WORD>((r0 * .393) + (g0 *.769) + (b0 * .189));
         WORD g1 = static_cast<WORD>((r0 * .349) + (g0 *.686) + (b0 * .168));
         WORD b1 = static_cast<WORD>((r0 * .272) + (g0 *.534) + (b0 * .131));

         color = MakeColor(min(0xff, r1), min(0xff, g1), min(0xff, b1));
      }
   );
   return bmp;
}

// Applies the given color mask to each pixel in the provided image.
Bitmap* ColorMask(Bitmap* bmp, DWORD mask)
{
   ProcessImage(bmp, 
      [mask](DWORD& color) {
         color = color & mask;
      }
   );
   return bmp;
}

// Darkens the provided image by the given amount.
Bitmap* Darken(Bitmap* bmp, unsigned int percent)
{
   if (percent > 100)
      throw invalid_argument("Darken: percent must less than 100.");

   double factor = percent / 100.0;

   ProcessImage(bmp, 
      [factor](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);
         r = static_cast<BYTE>(factor*r);
         g = static_cast<BYTE>(factor*g);
         b = static_cast<BYTE>(factor*b);
         color = MakeColor(r, g, b);
      }
   );
   return bmp;
}

// Determines which color component (red, green, or blue) is most dominant
// in the given image and returns a corresponding color mask.
DWORD GetColorDominance(Bitmap* bmp)
{
   // The ProcessImage function processes the image in parallel.
   // The following combinable objects enable the callback function
   // to increment the color counts without using a lock.
   combinable<unsigned int> reds;
   combinable<unsigned int> greens;
   combinable<unsigned int> blues;

   ProcessImage(bmp, 
      [&](DWORD& color) {
         BYTE r, g, b;
         GetRGB(color, r, g, b);
         if (r >= g && r >= b)
            reds.local()++;
         else if (g >= r && g >= b)
            greens.local()++;
         else
            blues.local()++;
      }
   );

   // Determine which color is dominant and return the corresponding
   // color mask.

   unsigned int r = reds.combine(plus<unsigned int>());
   unsigned int g = greens.combine(plus<unsigned int>());
   unsigned int b = blues.combine(plus<unsigned int>());

   if (r + r >= g + b)
      return 0x00ff0000;
   else if (g + g >= r + b)
      return 0x0000ff00;
   else
      return 0x000000ff;
}

// Retrieves the class identifier for the given MIME type of an encoder.
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = nullptr;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == nullptr)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}

// A synchronization primitive that is signaled when its 
// count reaches zero.
class countdown_event
{
public:
   countdown_event(unsigned int count = 0)
      : _current(static_cast<long>(count)) 
   {
      // Set the event if the initial count is zero.
      if (_current == 0L)
         _event.set();
   }

   // Decrements the event counter.
   void signal() {
      if(InterlockedDecrement(&_current) == 0L) {
         _event.set();
      }
   }

   // Increments the event counter.
   void add_count() {
      if(InterlockedIncrement(&_current) == 1L) {
         _event.reset();
      }
   }

   // Blocks the current context until the event is set.
   void wait() {
      _event.wait();
   }

private:
   // The current count.
   volatile long _current;
   // The event that is set when the counter reaches zero.
   event _event;

   // Disable copy constructor.
   countdown_event(const countdown_event&);
   // Disable assignment.
   countdown_event const & operator=(countdown_event const&);
};

// Demonstrates how to set up a message network that performs a series of 
// image processing operations on each JPEG image in the given directory and
// saves each altered image as a Windows bitmap.
void ProcessImages(const wstring& directory)
{
   // Holds the number of active image processing operations and 
   // signals to the main thread that processing is complete.
   countdown_event active(0);

   // Maps Bitmap objects to their original file names.
   map<Bitmap*, wstring> bitmap_file_names;

   //
   // Create the nodes of the network.
   //

   // Loads Bitmap objects from disk.
   transformer<wstring, Bitmap*> load_bitmap(
      [&](wstring file_name) -> Bitmap* {
         Bitmap* bmp = new Bitmap(file_name.c_str());
         if (bmp != nullptr)
            bitmap_file_names.insert(make_pair(bmp, file_name));
         return bmp;
      }
   );

   // Holds loaded Bitmap objects.
   unbounded_buffer<Bitmap*> loaded_bitmaps;

   // Converts images that are authored by Tom to grayscale.
   transformer<Bitmap*, Bitmap*> grayscale(
      [](Bitmap* bmp) {
         return Grayscale(bmp);
      },
      nullptr,
      [](Bitmap* bmp) -> bool {
         if (bmp == nullptr)
            return false;

         // Retrieve the artist name from metadata.
         UINT size = bmp->GetPropertyItemSize(PropertyTagArtist);
         if (size == 0)
            // Image does not have the Artist property.
            return false;

         PropertyItem* artistProperty = (PropertyItem*) malloc(size);
         bmp->GetPropertyItem(PropertyTagArtist, size, artistProperty);
         string artist(reinterpret_cast<char*>(artistProperty->value));
         free(artistProperty);

         return (artist.find("Tom ") == 0);
      }
   );

   // Removes the green and blue color components from images that have red as
   // their dominant color.
   transformer<Bitmap*, Bitmap*> colormask(
      [](Bitmap* bmp) {
         return ColorMask(bmp, 0x00ff0000);
      },
      nullptr,
      [](Bitmap* bmp) -> bool { 
         if (bmp == nullptr)
            return false;
         return (GetColorDominance(bmp) == 0x00ff0000);
      }
   );

   // Darkens the color of the provided Bitmap object.
   transformer<Bitmap*, Bitmap*> darken([](Bitmap* bmp) {
      return Darken(bmp, 50);
   });

   // Applies sepia toning to the remaining images.
   transformer<Bitmap*, Bitmap*> sepiatone(
      [](Bitmap* bmp) {
         return Sepiatone(bmp);
      },
      nullptr,
      [](Bitmap* bmp) -> bool { return bmp != nullptr; }
   );

   // Saves Bitmap objects to disk.
   transformer<Bitmap*, Bitmap*> save_bitmap([&](Bitmap* bmp) -> Bitmap* {
      // Replace the file extension with .bmp.
      wstring file_name = bitmap_file_names[bmp];
      file_name.replace(file_name.rfind(L'.') + 1, 3, L"bmp");

      // Save the processed image.
      CLSID bmpClsid;
      GetEncoderClsid(L"image/bmp", &bmpClsid);      
      bmp->Save(file_name.c_str(), &bmpClsid);

      return bmp;
   });

   // Deletes Bitmap objects.
   transformer<Bitmap*, Bitmap*> delete_bitmap([](Bitmap* bmp) -> Bitmap* {      
      delete bmp;
      return nullptr;
   });

   // Decrements the event counter.
   call<Bitmap*> decrement([&](Bitmap* _) {      
      active.signal();
   });

   //
   // Connect the network.
   //   

   load_bitmap.link_target(&loaded_bitmaps);

   loaded_bitmaps.link_target(&grayscale);
   loaded_bitmaps.link_target(&colormask);   
   colormask.link_target(&darken);
   loaded_bitmaps.link_target(&sepiatone);
   loaded_bitmaps.link_target(&decrement);

   grayscale.link_target(&save_bitmap);
   darken.link_target(&save_bitmap);
   sepiatone.link_target(&save_bitmap);

   save_bitmap.link_target(&delete_bitmap);
   delete_bitmap.link_target(&decrement);

   // Traverse all files in the directory.
   wstring searchPattern = directory;
   searchPattern.append(L"\\*");

   WIN32_FIND_DATA fileFindData;
   HANDLE hFind = FindFirstFile(searchPattern.c_str(), &fileFindData);
   if (hFind == INVALID_HANDLE_VALUE) 
      return;
   do
   {
      if (!(fileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
      {
         wstring file = fileFindData.cFileName;

         // Process only JPEG files.
         if (file.rfind(L".jpg") == file.length() - 4)
         {
            // Form the full path to the file.
            wstring full_path(directory);
            full_path.append(L"\\");
            full_path.append(file);

            // Increment the count of work items.
            active.add_count();

            // Send the path name to the network.
            send(load_bitmap, full_path);
         }
      }
   }
   while (FindNextFile(hFind, &fileFindData) != 0); 
   FindClose(hFind);

   // Wait for all operations to finish.
   active.wait();
}

int wmain()
{
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR           gdiplusToken;

   // Initialize GDI+.
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

   // Perform image processing.
   // TODO: Change this path if necessary.
   ProcessImages(L"C:\\Users\\Public\\Pictures\\Sample Pictures");

   // Shutdown GDI+.
   GdiplusShutdown(gdiplusToken);
}

A ilustração a seguir mostra um exemplo de saída.Cada imagem de origem está acima de seu correspondente imagem modificada.

Exemplo de saída de exemplo

Farol criado por Tom Alphin e, portanto, é convertida em tons de cinza.Chrysanthemum, Deserto, Koala, e tulipas tenha vermelho como a cor dominante e, portanto, tiver removidos os componentes de cor azul e verde e são escurecidos.Hydrangeas, Jellyfish, e pingüins correspondem aos critérios padrão e, portanto, sépia toned.

Top

Ff398050.collapse_all(pt-br,VS.110).gifCompilando o código

Copie o código de exemplo e colá-lo em um projeto de Visual Studio ou colá-lo em um arquivo chamado network.cpp de processamento de imagem e execute o seguinte comando em uma janela de Prompt de comando Visual Studio.

cl.exe /DUNICODE /EHsc image-processing-network.cpp /link gdiplus.lib

Consulte também

Outros recursos

Passo a passo de tempo de execução de concorrência