创建图像处理网络
此部分介绍如何创建对给定目录中的每个 JPEG (.jpg) 图像执行图像处理的异步消息块网络。 网络执行以下图像处理操作:
-
对于 Tom 创作的任何图像,转换为灰度。
-
对于任何以红色作为主色的图像,移除绿色和蓝色分量,然后将其变暗。
-
对于任何其他图像,应用棕色调。
网络仅应用与其中一个条件匹配的第一个图像处理操作。 例如,如果图像由 Tom 创作,并且将红色作为其主色,则图像仅转换为灰度。
网络执行每个图像处理操作后,它会将图像作为位图 (.bmp) 文件保存到磁盘。
以下步骤演示如何创建实现此图像处理网络的函数,并将该网络应用于给定目录中的每个 JPEG 图像。
创建图像处理网络
// 1. 创建函数 ProcessImages,它会采用磁盘上某个目录的名称。
void ProcessImages(const wstring& directory)
{
}
// 2. 在 ProcessImages 函数中,创建 countdown_event 变量。 本演练稍后会对 countdown_event 类进行介绍。
// Holds the number of active image processing operations and
// signals to the main thread that processing is complete.
countdown_event active(0);
// 3. 创建将 Bitmap 对象与其原始文件名关联的 std::map 对象。
// Maps Bitmap objects to their original file names.
map<Bitmap*, wstring> bitmap_file_names;
// 4. 添加以下代码以定义图像处理网络的成员。
//
// 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. 添加以下代码以连接网络。
//
// 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. 添加以下代码以向网络头发送目录中每个 JPEG 文件的完整路径。
// 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. 等待 countdown_event 变量达到零。
// Wait for all operations to finish.
active.wait();
图像网络成员
下表描述了网络的成员:
loaded_bitmaps 消息缓冲区非常重要,因为作为 unbounded_buffer 对象,它可向多个接收方提供 Bitmap 对象。 当目标块接受 Bitmap 对象时,unbounded_buffer 对象不会向任何其他目标提供该 Bitmap 对象。 因此,将对象链接到 unbounded_buffer 对象的顺序非常重要。 grayscale、colormask 和 sepiatone 消息块各自都使用筛选器,以便仅接受特定 Bitmap 对象。 decrement 消息缓冲区是 loaded_bitmaps 消息缓冲区的重要目标,因为它接受其他消息缓冲区所拒绝的所有 Bitmap 对象。 需要 unbounded_buffer 对象以便按顺序传播消息。 因此,unbounded_buffer 对象会在新目标块链接到它之前阻塞,并在没有当前目标块接受该消息时接受消息。
如果应用程序需要多个消息块处理消息,而不只是第一个接受消息的消息块,则可以使用另一种消息块类型,例如 overwrite_buffer。 overwrite_buffer 类一次保存一个消息,但它会将该消息传播到其每个目标。
下表描述了网络的成员。
此示例中的 countdown_event 对象使图像处理网络能够在处理了所有图像后告知主应用程序。 countdown_event 类使用 concurrency::event 对象在计数器值达到零时发送信号。 主应用程序在每次将文件名发送到网络时使计数器递增。 网络终端节点在处理每个图像后使计数器递减。 在主应用程序遍历指定目录后,它会等待 countdown_event 对象发出指示其计数器已达到零的信号。
下面的示例展示了 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&);
};