İ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.
Önkoş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, ProcessImage
bir 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 , , Sepiatone
ColorMask
, ve Darken
iş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, GetColorDominance
iş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, GetEncoderClsid
bir 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:
Tom tarafından yazılan herhangi bir resim için gri tonlamalıya dönüştürün.
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.
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
Disk üzerindeki bir dizinin adını alan bir işlev
ProcessImages
oluşturun.void ProcessImages(const wstring& directory) { }
işlevinde
ProcessImages
bircountdown_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);
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;
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(); });
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);
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);
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 | Açıklama |
---|---|
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
, colormask
ve 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_buffer
baş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:
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 ProcessImages
Sample 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.
Lighthouse
Tom Alphin tarafından yazıldığından gri tonlamalıya dönüştürülür. Chrysanthemum
, Desert
ve Koala
Tulips
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
, Jellyfish
ve 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