Compartilhar via


Instruções passo a passo: criando uma rede de processamento de imagem

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

A rede determina quais operações em execução em uma imagem com base em suas características. Este exemplo usa o modelo de fluxo de dados para rotear imagens pela rede. No modelo de fluxo de dados, os componentes independentes de um programa um com o outro se comunicam enviando mensagens. Quando um componente recebe uma mensagem, pode executar qualquer ação e depois passe o resultado dessa ação a outro componente. Compare com o modelo de fluxo de controle , em que um aplicativo usa estruturas de controle, por exemplo, instruções condicionais, loop, e assim por diante, para controlar a ordem das operações em um programa.

Uma rede baseada no fluxo de dados cria um pipeline de tarefas. Cada fase de pipeline é executada simultaneamente a parte da tarefa total. Uma analogia a esta é uma cadeia de fabricação para a produção auto. Conforme cada veículo passa pela cadeia de produção, uma estação monta o quadro, outro instalar o mecanismo, e assim por diante. Habilitando os vários veículos a ser montados simultaneamente, a cadeia de produção fornece melhor transferência do que veículos completos de montagem um de cada vez.

Pré-requisitos

Leia os seguintes documentos antes de iniciar esta explicação passo a passo:

Recomendamos também inclui os fundamentos de GDI+ antes de começar este passo a passo. Para obter mais informações sobre como GDI+, consulte GDI+.

Seções

Essa explicação passo a passo contém as seguintes seções:

  • Definindo a funcionalidade de processamento de imagem

  • Criando a rede de processamento de imagem

  • O Exemplo Completo

Definindo a funcionalidade de processamento de imagem

Esta seção mostra o suporte que suportam a rede de processamento de imagem trabalhar com imagens que são lidos do disco.

As seguintes funções, GetRGB e MakeColor, extração e combinam os componentes individuais de cor especificada, 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, chama o objeto determinado de std::function para converter o valor da cor de cada x em um objeto de GDI+Bitmap . A função de ProcessImage usa o algoritmo de concurrency::parallel_for para processar em paralelo cada linha de bitmap.

// 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, chame a função de ProcessImage para converter o valor da cor de cada x em um objeto de Bitmap . Cada uma dessas funções a seguir usa uma expressão de lambda para definir a transformação cor de um x.

// 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 chama a função de ProcessImage . Porém, em vez de alterar o valor de cada cor, essa função usa objetos de concurrency::combinable para computar se o componente, verde, vermelho ou azul de cor corresponde ao mestre 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 da classe para o 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
}

[Superior]

Criando a rede de processamento de imagem

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

  1. Para qualquer imagem que é criada por Tom, converta à escala de cinza.

  2. Para qualquer imagem que tenha vermelho como a cor dominante, remova os componentes verde e azul e escureça-os em.

  3. Para qualquer outra imagem, aplique a tonificação de sepia.

A rede aplica apenas a primeira operação de processamento que corresponde a uma destas condições. Por exemplo, se uma imagem é criada por Tom e red como a cor dominante, a imagem é convertida apenas à escala de cinza.

Depois que a cada rede executa a operação de processamento, salva a imagem no disco como um arquivo de bitmap (.bmp).

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

Para criar a rede de processamento de imagem

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

    void ProcessImages(const wstring& directory)
    {
    }
    
  2. Na função de ProcessImages , crie uma variável de countdown_event . A classe de countdown_event é exibida mais adiante neste 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. Crie um objeto de std::map que associa um objeto de Bitmap pelo nome de arquivo original.

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

    // 
    // 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 código a seguir para conectar-se a 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 ao início da rede o caminho completo de todos os arquivos de JPEG 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 a variável de countdown_event para alcançar zero.

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

A tabela a seguir descreve os membros de rede.

Membro

Descrição

load_bitmap

Um objeto de concurrency::transformer que carrega um objeto de Bitmap de disco e adiciona uma entrada ao objeto de map para associar a imagem com seu nome de arquivo original.

loaded_bitmaps

Um objeto de concurrency::unbounded_buffer que envia as imagens carregadas ao processamento de imagem filtros.

grayscale

Um objeto de transformer que converte de imagens que são criadas por Tom à escala de cinza. Usa os metadados de imagem para determinar o autor.

colormask

Um objeto de transformer que remove os componentes verde e azul de cor das imagens que têm vermelho como a cor dominante.

darken

Um objeto de transformer esmaece que as imagens que têm vermelho como a cor dominante.

sepiatone

Um objeto de transformer que aplica o sepia que tonifica imagens que não são criadas por Tom e não são frequente vermelhas.

save_bitmap

Um objeto de transformer que salva image processado no disco como um bitmap. save_bitmap recupera o nome de arquivo original do objeto de map e alterar sua extensão de nome de arquivo como .bmp.

delete_bitmap

Um objeto de transformer que libera memória para as imagens.

decrement

Um objeto de concurrency::call que atue como o nó terminal na rede. Diminui o objeto de countdown_event ao sinal para o aplicativo principal que uma imagem esteve processada.

O buffer de mensagem de loaded_bitmaps é importante porque, como um objeto de unbounded_buffer , oferece objetos de Bitmap a vários destinatários. Quando um bloco de destino aceita um objeto de Bitmap , o objeto de unbounded_buffer não oferece que objeto de Bitmap com qualquer outro destinos. Consequentemente, a ordem na qual você vincular objetos a um objeto de unbounded_buffer é importante. grayscale, colormask, e a mensagem de sepiatone bloqueiam usam cada um filtro só aceitar determinado Bitmap objeto. O buffer de mensagem de decrement é importante um destino de buffer de mensagem de loaded_bitmaps como aceita todos os objetos de Bitmap que são rejeitados por outros buffers de mensagem. Um objeto de unbounded_buffer é necessário propagar mensagens na ordem. Consequentemente, blocos de um objeto de unbounded_buffer até um novo pacote de destino ele esteja vinculado e aceitar a mensagem se nenhum pacote atual de destino aceita essa mensagem.

Se seu aplicativo requer que os blocos de mensagens múltiplas processa a mensagem, em vez de apenas um bloco de mensagem que aceita primeiro a mensagem, você pode usar outra mensagem em blocos, como overwrite_buffer. A classe de overwrite_buffer contém uma mensagem de cada vez, mas propaga essa mensagem a cada um de seus destinos.

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

Rede de processamento de imagem

O objeto de countdown_event neste exemplo permite a rede de processamento de imagem para informar o aplicativo principal quando todas as imagens foram processadas. A classe de countdown_event usa um objeto de concurrency::event para sinalizar quando alcança o contador zero de um valor. O aplicativo principal incrementa o contador sempre que envia um nome de arquivo na rede. O nó terminal de rede diminui o contador depois que cada foto foi processada. Depois que o aplicativo principal atravessa o diretório especificado, aguarda o objeto de countdown_event para sinalizar que seu contador atingiu zero.

O exemplo a seguir mostra a classe de countdown_event :

// 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&);
};

[Superior]

O Exemplo Completo

O código a seguir mostra o exemplo completo. A função de wmain gerencia a biblioteca de GDI+ e chama a função de ProcessImages para processar os arquivos de JPEG no diretório de Imagens de exemplo .

// 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 a saída de exemplo. Cada foto de origem está acima da imagem alterada correspondentes.

Exemplo de saída de exemplo

Farol é criado por Alphin Tom e como consequência convertido na escala de cinza. Crisântemo, Deserto, Koala, e Tulips têm vermelho como a cor dominante e tem como consequência os componentes da cor verde e azul e são removidos escurecidos. Hortênsias, Medusa, e correspondência de Penguins os critérios padrão e são consequentemente sepia tonificado.

[Superior]

Compilando o código

Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado image-processing-network.cpp e execute o comando a seguir em uma janela de prompt de comando do Visual Studio.

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

Consulte também

Outros recursos

Instruções passo a passo do Tempo de Execução de Simultaneidade