Aracılığıyla paylaş


İzlenecek yol: Görüntü İşleme Ağı Oluşturma

Bu belgede, görüntü işleme gerçekleştiren zaman uyumsuz ileti bloklarından oluşan bir ağın nasıl oluşturulacağı gösterilmektedir.

Ağ, özellikleri temelinde bir görüntü üzerinde hangi işlemlerin gerçekleştirileceğini belirler. Bu örnek, görüntüleri ağ üzerinden yönlendirmek için veri akışı modelini kullanır. Veri akışı modelinde, bir programın bağımsız bileşenleri ileti göndererek birbirleriyle iletişim kurar. Bir bileşen bir ileti aldığında, bazı eylemler gerçekleştirebilir ve ardından bu eylemin sonucunu başka bir bileşene geçirebilir. Bunu, uygulamanın bir programdaki işlemlerin sırasını denetlemek için koşullu deyimler, döngüler vb. denetim yapılarını kullandığı denetim akışı modeliyle karşılaştırın.

Veri akışını temel alan bir ağ, bir görev işlem hattı oluşturur. İşlem hattının her aşaması, genel görevin bir bölümünü eşzamanlı olarak gerçekleştirir. Buna bir benzetme, otomobil üretimi için bir montaj hattıdır. Her araç montaj hattından geçerken, bir istasyon çerçeveyi monte eder, başka bir istasyon motoru yükler, vb. Montaj hattı, birden çok aracın aynı anda monte edilmesini sağlayarak, aynı anda tam araçların montajından daha iyi aktarım hızı sağlar.

Ön koşullar

Bu izlenecek yolu başlatmadan önce aşağıdaki belgeleri okuyun:

Bu kılavuza başlamadan önce GDI+ ile ilgili temel bilgileri de anlamanız önerilir.

Bölümler

Bu izlenecek yol aşağıdaki bölümleri içerir:

Görüntü İşleme İşlevselliğini Tanımlama

Bu bölümde, görüntü işleme ağının diskten okunan görüntülerle çalışmak için kullandığı destek işlevleri gösterilir.

Aşağıdaki işlevler GetRGB ve MakeColor, verilen rengin tek tek bileşenlerini sırasıyla ayıklar ve birleştirir.

// 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şağıdaki işlevi, ProcessImagebir GDI+ Bit Eşlem nesnesindeki her pikselin renk değerini dönüştürmek için verilen std::function nesnesini çağırır. İşlev, ProcessImage bit eşlem satırının her satırını paralel olarak işlemek için concurrency::p arallel_for algoritmasını kullanır.

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

Aşağıdaki , , SepiatoneColorMask, ve DarkenişlevleriGrayscale, bir Bitmap nesnedeki ProcessImage her pikselin renk değerini dönüştürmek için işlevini çağırır. Bu işlevlerin her biri bir pikselin renk dönüşümlerini tanımlamak için bir lambda ifadesi kullanır.

// 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şağıdaki işlevi, GetColorDominanceişlevini de çağırır ProcessImage . Ancak bu işlev, her rengin değerini değiştirmek yerine eşzamanlılık::combinable nesnelerini kullanarak görüntüye kırmızı, yeşil veya mavi renk bileşeninin hakim olup olmadığını hesaplar.

// 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şağıdaki işlevi, GetEncoderClsidbir kodlayıcının verilen MIME türü için sınıf tanımlayıcısını alır. Uygulama, bit eşlem için kodlayıcıyı almak için bu işlevi kullanır.

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

[Üst]

Görüntü İşleme Ağı Oluşturma

Bu bölümde, belirli bir dizindeki her JPEG (.jpg) görüntüsünde görüntü işleme gerçekleştiren zaman uyumsuz ileti bloklarından oluşan bir ağın nasıl oluşturulacağı açıklanmaktadır. Ağ aşağıdaki görüntü işleme işlemlerini gerçekleştirir:

  1. Tom tarafından yazılan herhangi bir resim için gri tonlamalıya dönüştürün.

  2. Baskın renk olarak kırmızı olan tüm görüntüler için yeşil ve mavi bileşenleri kaldırın ve koyulaştırın.

  3. Başka herhangi bir görüntü için sepya tonlama uygulayın.

Ağ yalnızca bu koşullardan biriyle eşleşen ilk görüntü işleme işlemini uygular. Örneğin, bir resim Tom tarafından yazılmışsa ve baskın rengi kırmızıysa, görüntü yalnızca gri tonlamalıya dönüştürülür.

Ağ her görüntü işleme işlemini gerçekleştirdikten sonra görüntüyü bir bit eşlem (.bmp) dosyası olarak diske kaydeder.

Aşağıdaki adımlar, bu görüntü işleme ağını uygulayan ve bu ağı belirli bir dizindeki her JPEG görüntüsüne uygulayan bir işlevin nasıl oluşturulacağını gösterir.

Görüntü işleme ağını oluşturmak için

  1. Disk üzerindeki bir dizinin adını alan bir işlev ProcessImagesoluşturun.

    void ProcessImages(const wstring& directory)
    {
    }
    
  2. işlevinde ProcessImages bir countdown_event değişken oluşturun. Sınıfı countdown_event , bu kılavuzda daha sonra gösterilir.

    // Holds the number of active image processing operations and 
    // signals to the main thread that processing is complete.
    countdown_event active(0);
    
  3. Bir nesneyi özgün dosya adıyla ilişkilendiren bir Bitmap std::map nesnesi oluşturun.

    // Maps Bitmap objects to their original file names.
    map<Bitmap*, wstring> bitmap_file_names;
    
  4. Görüntü işleme ağının üyelerini tanımlamak için aşağıdaki kodu ekleyin.

     //
     // 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. Ağa bağlanmak için aşağıdaki kodu ekleyin.

    //
    // 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. Dizinindeki her JPEG dosyasının tam yolunu ağın başına göndermek için aşağıdaki kodu ekleyin.

    // 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. Değişkenin countdown_event sıfıra ulaşmasını bekleyin.

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

Aşağıdaki tabloda ağ üyeleri açıklanmaktadır.

Üye Tanım
load_bitmap Bir nesneyi diskten yükleyen Bitmap ve görüntüyü özgün dosya adıyla ilişkilendirmek için nesneye map bir girdi ekleyen concurrency::transformer nesnesi.
loaded_bitmaps Yüklenen görüntüleri görüntü işleme filtrelerine gönderen concurrency::unbounded_buffer nesnesi.
grayscale transformer Tom tarafından yazılan görüntüleri gri tonlamalıya dönüştüren nesne. Yazarını belirlemek için görüntünün meta verilerini kullanır.
colormask transformer Baskın renk olarak kırmızı olan görüntülerden yeşil ve mavi renk bileşenlerini kaldıran nesne.
darken Baskın transformer renk olarak kırmızı olan görüntüleri koyulaştıran bir nesne.
sepiatone transformer Tom tarafından yazılmamış ve ağırlıklı olarak kırmızı olmayan resimlere sepya tonlama uygulayan bir nesne.
save_bitmap diske transformer işleneni image bit eşlem olarak kaydeden nesne. save_bitmap nesneden map özgün dosya adını alır ve dosya adı uzantısını .bmp olarak değiştirir.
delete_bitmap transformer Görüntüler için belleği boşaltan bir nesne.
decrement Ağdaki terminal düğümü gibi davranan bir eşzamanlılık::call nesnesi. Nesneyi, ana uygulamaya bir görüntünün işlendiğini belirten sinyal vermek üzere geri countdown_event alır.

İleti loaded_bitmaps arabelleği önemlidir çünkü nesne unbounded_buffer olarak nesneleri birden çok alıcıya sunar Bitmap . Hedef blok bir Bitmap nesneyi kabul ettiğinde, unbounded_buffer nesne bu Bitmap nesneyi diğer hedeflere sunmaz. Bu nedenle, nesneleri bir unbounded_buffer nesneye bağlama sırası önemlidir. grayscale, colormaskve sepiatone iletilerinin her birinin yalnızca belirli Bitmap nesneleri kabul etmek için bir filtre kullanmasını engeller. İleti decrement arabelleği, diğer ileti arabellekleri tarafından reddedilen tüm Bitmap nesneleri kabul ettiğinden ileti arabelleğinin önemli bir hedefidirloaded_bitmaps. unbounded_buffer İletileri sırayla yaymak için bir nesne gerekir. Bu nedenle, bir unbounded_buffer nesne, yeni bir hedef blok ona bağlanana kadar engeller ve geçerli bir hedef bloğu bu iletiyi kabul ederse iletiyi kabul eder.

Uygulamanız, iletiyi ilk kabul eden ileti bloğu yerine birden çok ileti bloğunun iletiyi işlemesini gerektiriyorsa, gibi overwrite_bufferbaşka bir ileti bloğu türü kullanabilirsiniz. sınıfı overwrite_buffer bir kerede bir ileti tutar, ancak bu iletiyi hedeflerinin her birine yayılır.

Aşağıdaki çizimde görüntü işleme ağı gösterilmektedir:

Image processing network.

countdown_event Bu örnekteki nesne, görüntü işleme ağının tüm görüntüler işlendiğinde ana uygulamayı bilgilendirmesini sağlar. sınıfı, countdown_event bir sayaç değeri sıfıra ulaştığında sinyal vermek için eşzamanlılık::event nesnesi kullanır. Ana uygulama, ağa her dosya adı gönderdiğinde sayacı artırır. Ağın terminal düğümü, her görüntü işlendikten sonra sayacın azalmasını sağlar. Ana uygulama belirtilen dizinden geçtikten sonra, nesnesinin sayacının countdown_event sıfıra ulaştığını belirtmesini bekler.

Aşağıdaki örnekte sınıfı gösterilmektedir 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&);
};

[Üst]

Tam Örnek

Aşağıdaki kod, tam örneği gösterir. wmain işlevi GDI+ kitaplığını yönetir ve dizinindeki ProcessImagesSample Pictures JPEG dosyalarını işlemek için işlevini çağırır.

// 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şağıdaki çizimde örnek çıktı gösterilmektedir. Her kaynak görüntü, karşılık gelen değiştirilmiş görüntüsünün üzerindedir.

Sample output for the example.

Lighthouse Tom Alphin tarafından yazıldığından gri tonlamalıya dönüştürülür. Chrysanthemum, Desertve KoalaTulips baskın renk olarak kırmızıya sahiptir ve bu nedenle mavi ve yeşil renk bileşenleri kaldırılır ve karartılır. Hydrangeas, Jellyfishve Penguins varsayılan ölçütleri eşleştirin ve bu nedenle sepya tonları kullanılır.

[Üst]

Kod Derleniyor

Örnek kodu kopyalayıp bir Visual Studio projesine yapıştırın veya adlı image-processing-network.cpp bir dosyaya yapıştırın ve ardından bir Visual Studio Komut İstemi penceresinde aşağıdaki komutu çalıştırın.

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

Ayrıca bkz.

Eşzamanlılık Çalışma Zamanı İzlenecek Yollar