Megosztás a következőn keresztül:


Útmutató: Image-Processing-hálózat létrehozása

Ez a dokumentum bemutatja, hogyan hozható létre a képfeldolgozást végző aszinkron üzenetblokkok hálózata.

A hálózat a jellemzői alapján határozza meg, hogy a rendszerképen mely műveleteket kell végrehajtani. Ez a példa az adatfolyam-modellel irányítja a képeket a hálózaton keresztül. Az adatfolyam-modellben a program független összetevői üzenetek küldésével kommunikálnak egymással. Amikor egy összetevő üzenetet kap, végrehajthat néhány műveletet, majd átadhatja a művelet eredményét egy másik összetevőnek. Hasonlítsa össze ezt a vezérlőfolyamat-modellel , amelyben az alkalmazás vezérlőstruktúrákat, például feltételes utasításokat, hurkokat stb. használ a programok műveleteinek sorrendjének szabályozásához.

Az adatfolyamon alapuló hálózat feladatfolyamatot hoz létre. A folyamat minden szakasza egyidejűleg elvégzi a teljes tevékenység egy részét. Ehhez hasonló az autógyártáshoz használt szerelősor. Ahogy minden jármű áthalad a szerelősoron, az egyik állomás összeszereli a keretet, egy másik telepíti a motort, és így tovább. Több jármű egyidejű összeszerelésének lehetővé tételével a szerelősor jobb átviteli sebességet biztosít, mint a teljes járművek egyenkénti összeszerelése.

Előfeltételek

Az útmutató megkezdése előtt olvassa el a következő dokumentumokat:

Azt is javasoljuk, hogy az útmutató megkezdése előtt ismerje meg a GDI+ alapjait.

Szakaszok

Ez az útmutató a következő szakaszokat tartalmazza:

Képfeldolgozási funkciók definiálása

Ez a szakasz azokat a támogatási függvényeket mutatja be, amelyeket a képfeldolgozó hálózat a lemezről beolvasott rendszerképek használatához használ.

Az alábbi függvények, GetRGB majd MakeColoraz adott szín egyes összetevőinek kinyerése és kombinálása.

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

Az alábbi függvény ProcessImagemeghívja a megadott std::függvényobjektumot , hogy átalakítsa a GDI+ Bitmap objektum egyes képpontjainak színértékét. A ProcessImage függvény a concurrency::parallel_for algoritmust használja a bitkép sorainak párhuzamos feldolgozására.

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

Az alábbi függvények GrayscaleSepiatoneColorMaskDarkenmeghívják a függvényt az ProcessImage objektum egyes képpontjainak Bitmap színértékének átalakításához. Mindegyik függvény egy lambda kifejezéssel határozza meg egy képpont színátalakítását.

// 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 következő függvény GetColorDominanceis meghívja a függvényt ProcessImage . Ahelyett azonban, hogy módosítaná az egyes színek értékét, ez a függvény egyidejűséget használ::kombinálható objektumok annak kiszámításához, hogy a piros, a zöld vagy a kék színösszetevő uralja-e a képet.

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

Az alábbi függvény GetEncoderClsidegy kódoló adott MIME-típusának osztályazonosítóját kéri le. Az alkalmazás ezzel a függvénnyel kéri le a kódolót egy bitképhez.

// 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
}

[Felső]

A képfeldolgozó hálózat létrehozása

Ez a szakasz azt ismerteti, hogyan hozhat létre olyan aszinkron üzenetblokkokból álló hálózatot, amely képfeldolgozást végez egy adott könyvtárban lévő összes JPEG (.jpg) lemezképen. A hálózat a következő képfeldolgozási műveleteket hajtja végre:

  1. A Tom által készített összes kép esetében alakítsa át szürkeárnyalatossá.

  2. Minden olyan képnél, amelynek domináns színe piros, távolítsa el a zöld és a kék összetevőket, majd sötétítse el.

  3. Bármely más kép esetében alkalmazzon szépia tónust.

A hálózat csak az első képfeldolgozási műveletet alkalmazza, amely megfelel az egyik feltételnek. Ha például egy képet Tom szerkesztett, és domináns színe piros, a rendszer csak szürkeárnyalatossá alakítja a képet.

Miután a hálózat végrehajtotta az egyes képfeldolgozási műveleteket, bitképfájlként (.bmp) menti a lemezre.

Az alábbi lépések bemutatják, hogyan hozhat létre egy függvényt, amely implementálja ezt a képfeldolgozó hálózatot, és hogyan alkalmazza ezt a hálózatot egy adott könyvtárban lévő összes JPEG-lemezképre.

A képfeldolgozó hálózat létrehozása

  1. Hozzon létre egy függvényt, ProcessImagesamely a lemezen lévő könyvtár nevét veszi fel.

    void ProcessImages(const wstring& directory)
    {
    }
    
  2. A függvényben ProcessImages hozzon létre egy változót countdown_event . Az countdown_event osztály később jelenik meg ebben az útmutatóban.

    // Holds the number of active image processing operations and 
    // signals to the main thread that processing is complete.
    countdown_event active(0);
    
  3. Hozzon létre egy std::map objektumot, amely társít egy Bitmap objektumot az eredeti fájl nevéhez.

    // Maps Bitmap objects to their original file names.
    map<Bitmap*, wstring> bitmap_file_names;
    
  4. Adja hozzá a következő kódot a képfeldolgozó hálózat tagjainak meghatározásához.

     //
     // 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. Adja hozzá a következő kódot a hálózat csatlakoztatásához.

    //
    // 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. Adja hozzá a következő kódot, amely a könyvtárban található egyes JPEG-fájlok teljes elérési útját küldi el a hálózat vezetőjének.

    // 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. Várja meg, amíg a változó eléri a countdown_event nullát.

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

Az alábbi táblázat a hálózat tagjait ismerteti.

Tag Leírás
load_bitmap Párhuzamosság::átalakító objektum, amely betölt egy Bitmap objektumot a lemezről, és hozzáad egy bejegyzést a map objektumhoz, hogy az képet az eredeti fájlnevével társítsa.
loaded_bitmaps Concurrency::unbounded_buffer objektum, amely a betöltött képeket a képfeldolgozási szűrők számára küldi.
grayscale Egy transformer objektum, amely a Tom által készített képeket szürkeárnyalatossá alakítja. A kép metaadatait használja a szerző meghatározásához.
colormask Egy transformer objektum, amely eltávolítja a zöld és kék színösszetevőket a domináns színként piros színnel rendelkező képekről.
darken Egy transformer objektum, amely sötétíti a képeket, amelyek piros, mint a domináns szín.
sepiatone Olyan transformer objektum, amely szépia tónust alkalmaz azokra a képekre, amelyeket nem Tom készített, és amelyek túlnyomórészt nem vörösek.
save_bitmap Egy transformer objektum, amely bitképként menti a feldolgozott image lemezt. save_bitmap lekéri az eredeti fájlnevet az map objektumból, és a fájlnévkiterjesztést .bmp-ra változtatja.
delete_bitmap Egy transformer objektum, amely felszabadítja a képek memóriáját.
decrement A concurrency::call objektum, amely a hálózat végső csomópontjaként működik. Az objektumot countdown_event csökkenti, hogy jelezze a fő alkalmazás számára a képfeldolgozás megtörténtét.

Az loaded_bitmaps üzenetpuffer azért fontos, mert mint unbounded_buffer objektum, több fogadónak kínál Bitmap objektumokat. Ha egy célblokk elfogad egy Bitmap objektumot, az unbounded_buffer objektum nem ajánlja fel az Bitmap objektumot más céloknak. Ezért fontos, hogy milyen sorrendben csatolja az objektumokat egy unbounded_buffer objektumhoz. A grayscale, colormask, és sepiatone üzenetblokkok mindegyike szűrőt használ, hogy csak bizonyos Bitmap objektumokat fogadjon el. Az decrement üzenetpuffer az loaded_bitmaps üzenetpuffer kiemelt célja, mert elfogadja az összes Bitmap objektumot, amelyek más üzenetpufferek elutasítanak. Az unbounded_buffer üzenetek sorrendjének propagálásához objektum szükséges. Ezért egy unbounded_buffer objektum blokkolja az üzenetet, amíg egy új célblokkot hozzá nem kapcsolnak, és elfogadja azt, ha az aktuális célblokk nem fogadja el.

Ha az alkalmazás megköveteli, hogy több üzenetblokk dolgozza fel az üzenetet, ahelyett, hogy csak azt az üzenetblokkot használná, amely először elfogadja az üzenetet, használhat egy másik üzenetblokktípust, például overwrite_buffer. Az overwrite_buffer osztály egyszerre egy üzenetet tárol, de propagálja az üzenetet az egyes célokra.

Az alábbi ábrán a képfeldolgozó hálózat látható:

Képfeldolgozó hálózat.

A countdown_event példában szereplő objektum lehetővé teszi, hogy a képfeldolgozó hálózat tájékoztassa a fő alkalmazást az összes kép feldolgozásakor. Az countdown_event osztály egy egyidejűség::eseményobjektum használatával jelzi, ha egy számláló értéke eléri a nullát. A fő alkalmazás minden alkalommal növeli a számlálót, amikor fájlnevet küld a hálózatnak. A hálózat terminálcsomópontja az egyes rendszerképek feldolgozása után csökkenti a számlálót. Miután a fő alkalmazás bejárta a megadott könyvtárat, megvárja, amíg az countdown_event objektum jelzi, hogy a számláló elérte a nullát.

Az alábbi példa az osztályt countdown_event mutatja be:

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

[Felső]

A teljes példa

Az alábbi kód a teljes példát mutatja be. A wmain függvény kezeli a GDI+ kódtárat, és meghívja a ProcessImages függvényt a JPEG-fájlok könyvtárban való Sample Pictures feldolgozására.

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

Az alábbi ábrán a mintakimenet látható. Minden forráskép a megfelelő módosított rendszerkép felett található.

Mintakimenet a példához.

Lighthouse Tom Alphin készítette, ezért szürkeárnyalatossá vált. Chrysanthemum, Desertés KoalaTulips a piros a domináns szín, ezért a kék és a zöld színösszetevők el vannak távolítva, és sötétednek. Hydrangeas, Jellyfishés Penguins megfelel az alapértelmezett feltételeknek, ezért szépia tónusú.

[Felső]

A kód összeállítása

Másolja ki a példakódot, és illessze be egy Visual Studio-projektbe, vagy illessze be egy elnevezett image-processing-network.cpp fájlba, majd futtassa a következő parancsot egy Visual Studio parancssori ablakban.

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

Lásd még

Egyidejűségi futtatókörnyezeti útmutatók