[英]C++ Cache Design Advice
我有一個c ++應用程序,有幾種圖像類型(RGB,灰色......),每種類型都有像旋轉或縮放等屬性。 每種圖像類型都是通過其他類型的計算生成的。 例如,通過旋轉GrayImage
生成旋轉的GrayImage
,而GrayImage
又通過“灰化” RGBImage
生成。
我想設計一個緩存類,其方法GetX(...)
緩存各種圖像(可能還有計算路徑中的所有圖像)。 如果不在緩存中,該類還將知道如何生成每個圖像。
該類必須滿足一些約束:
由於我們處理的是不同類型和表示的圖像(RGB,GrayScale等),因此緩存必須為調用代碼返回一個具體類,以便能夠在不使用某種類型的情況下使用它。 因此,緩存機制必須包含包含具體類型的不同緩存結構。 (如果我錯了,請修理我)
map<...,RGBImage> map<...,GrayImage>
例如。
緩存必須靈活地適應圖像計算的變化。 代碼更改是可以接受的,只要它們不是太大。
我當前的版本為每個圖像類型附加了一個Key
結構。 有GrayKey
, RGBKey
等等。 各種鍵包含縮放和旋轉等屬性,並且可以具有特定於圖像的屬性(例如,用於GrayKey
)。 緩存保存表單的地圖:
map <XKey,XImage>
GetX(...)
方法接收Key
結構作為參數,例如請求Rotated GrayImage。 但是,此實現強制緩存應用大量邏輯來計算圖像。 必須檢查GrayKey是否請求旋轉圖像並采取相應措施。 我想以更優雅的方式“編碼”這個圖像計算關系,但似乎找不到。
有什么建議?
非常感謝。
也許你可以使用Boost.MultiIndex
容器做些什么? 它允許您創建一個存儲圖像數據的類型,以及如何操作它的詳細信息,然后根據您想要的任何鍵組合查找值。 如果您之前沒有使用它,它可能看起來有點令人生畏,但我在下面附上了一個例子。
顯然,我的例子只處理緩存機制的存儲/檢索部分,如果你將它們粘在一起,如果查找失敗就可以生成圖像的東西,它應該做你想要的一切。 擴展它也很容易...需要查找額外的參數? 您只需要為ImageCache
typedef添加另一個索引。
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/shared_array.hpp>
#include <algorithm>
#include <iostream>
#include <utility>
// A cache item, stores the image data, and any values we need to
// index on.
struct ImageCacheItem
{
enum RgbMode { RGB_MODE_COLOUR, RGB_MODE_GREYSCALE };
// Im not sure how much copying goes on in the container,
// so using a smart pointer to prevent copying large amounts
// of data.
boost::shared_array<char> imageBuffer;
double rotation;
double scale;
RgbMode rgbMode;
ImageCacheItem(double r, double s)
: rotation(r), scale(s)
{
}
};
// These are "tag" structures, they are used as part of the
// multi_index_container as a way to distinguish between indicies.
struct ByRotation {};
struct ByScale {};
struct ByRgb {};
struct ByRotationScale {};
// Typedef of the container itself.
typedef boost::multi_index_container<
ImageCacheItem, // The data type for the container.
// Note there is no "key" type, as the key values
// are extracted from the data items theselves.
boost::multi_index::indexed_by<
// Define an index for the rotation value
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ByRotation>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, rotation)
>,
// Define an index for the scale value
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ByScale>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, scale)
>,
// Define an index for the rgb value
boost::multi_index::hashed_non_unique<
boost::multi_index::tag<ByRgb>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, ImageCacheItem::RgbMode, rgbMode)
>,
// Define an index by rotation + scale
boost::multi_index::hashed_unique<
boost::multi_index::tag<ByRotationScale>,
boost::multi_index::composite_key<
ImageCacheItem,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, rotation),
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, scale)
>
>
>
> ImageCache;
// Utility typedefs, you'll want these to shorten the iterator
// data types when you're looking things up (see main).
typedef ImageCache::index<ByRotation>::type ImageCacheByRotation;
typedef ImageCache::index<ByScale>::type ImageCacheByScale;
typedef ImageCache::index<ByRgb>::type ImageCacheByRgb;
typedef ImageCache::index<ByRotationScale>::type ImageCacheByRotationScale;
int main()
{
// Create the cache and add time "images" to it.
ImageCache cache;
cache.insert(ImageCacheItem(10, 10));
cache.insert(ImageCacheItem(10, 20));
cache.insert(ImageCacheItem(20, 20));
// look up the images with scale of 20.
typedef ImageCacheByScale::iterator ScaleIter;
std::pair<ScaleIter, ScaleIter> scaleResult = cache.get<ByScale>().equal_range(20);
std::cout << "Found " << std::distance(scaleResult.first, scaleResult.second) << " Results" << std::endl;
// look up the image with rotation = 10 && scale = 20.
ImageCacheByRotationScale::iterator rsResult = cache.get<ByRotationScale>().find(boost::make_tuple(10, 20));
std::cout << "Found " << (rsResult != cache.get<ByRotationScale>().end() ? 1 : 0) << " Results" << std::endl;
return 0;
}
編輯:這是一個很大的...
我已經嘗試擴展上面的示例,以便在緩存中找到最接近您搜索內容的圖像,但是有偏差,所以如果您想要旋轉45,並且比例為10,如果沒有精確匹配則發現,它會傾向於一個屬性相同的結果,另一個屬性為0(即10的縮放,但是0旋轉,所以你需要做的就是旋轉)。
代碼被注釋以解釋它的作用,但基本上,它使用模板遞歸按順序搜索索引,一旦索引找到一些匹配,它就會嘗試按相關性排序它們,並返回最佳匹配。 要添加其他屬性,您需要執行以下操作:
ImageCacheItem
ImageCacheSimilarity
ImageCache
typedef 它可能不是最佳解決方案,但我認為它涵蓋了您在評論中提到的用例。
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/mpl/list.hpp>
#include <boost/optional.hpp>
#include <boost/ref.hpp>
#include <boost/shared_array.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <utility>
#include <vector>
#include <typeinfo>
// A cache item, stores the image data, and any values we need to
// index on.
struct ImageCacheItem
{
enum RgbMode { RGB_MODE_COLOUR, RGB_MODE_GREYSCALE };
// Im not sure how much copying goes on in the container,
// so using a smart pointer to prevent copying large amounts
// of data.
boost::shared_array<char> imageBuffer;
double rotation;
double scale;
RgbMode rgbMode;
ImageCacheItem(double r, double s)
: rotation(r), scale(s)
{
}
};
// Calculates the similarity between two ImageCacheItem objects.
int ImageCacheSimilarity(const ImageCacheItem& item, const ImageCacheItem& target)
{
const double EPSILON = 0.0000001;
int score = 0;
// 2 points for an exact match
// 1 point if the value is 0 (e.g. not rotated, so can be used as a starting point)
// -1 point otherwise
score += (std::fabs(item.rotation - target.rotation) < EPSILON)
? 2
: ((std::fabs(item.rotation) < EPSILON) ? 1 : -1);
score += (std::fabs(item.scale - target.scale) < EPSILON)
? 2
: ((std::fabs(item.scale) < EPSILON) ? 1 : -1);
score += (item.rgbMode == target.rgbMode) ? 2 : 0;
return score;
}
// Orders ImageCacheItem objects based on their similarity to a target value.
struct ImageCacheCmp
{
const ImageCacheItem& target;
ImageCacheCmp(const ImageCacheItem& t)
: target(t)
{
}
bool operator()(const ImageCacheItem& a, const ImageCacheItem& b)
{
return (ImageCacheSimilarity(a, target) > ImageCacheSimilarity(b, target));
}
};
// These are "tag" structures, they are used as part of the
// multi_index_container as a way to distinguish between indicies.
struct ByRotation {};
struct ByScale {};
struct ByRgb {};
struct ByRotationScale {};
// Typedef of the container itself.
typedef boost::multi_index_container<
ImageCacheItem, // The data type for the container.
// Note there is no "key" type, as the key values
// are extracted from the data items theselves.
boost::multi_index::indexed_by<
// The order of indicies here will affect performance, put the
// ones that match against the most fields first. Its not required
// to make it work, but it will reduce the number of matches to
// compare against later on.
// Define an index by rotation + scale
boost::multi_index::hashed_unique<
boost::multi_index::tag<ByRotationScale>,
boost::multi_index::composite_key<
ImageCacheItem,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, rotation),
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, scale)
>
>,
// Define an index for the rotation value
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ByRotation>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, rotation)
>,
// Define an index for the scale value
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ByScale>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, double, scale)
>,
// Define an index for the rgb value
boost::multi_index::hashed_non_unique<
boost::multi_index::tag<ByRgb>,
BOOST_MULTI_INDEX_MEMBER(ImageCacheItem, ImageCacheItem::RgbMode, rgbMode)
>
>
> ImageCache;
// Type of the vector used when collecting index results. It stores
// references to the values in the cache to minimise copying.
typedef std::vector<boost::reference_wrapper<const ImageCacheItem> > ImageCacheResults;
// Utility class for overload resolution
template <int I>
struct Int2Type
{
enum { value = I };
};
void FindMatches(
const ImageCacheItem& item,
const ImageCache& cache,
ImageCacheResults& results,
const Int2Type<boost::mpl::size<ImageCache::index_type_list>::type::value>&)
{
// End of template recursion
}
template <int I>
void FindMatches(
const ImageCacheItem& item,
const ImageCache& cache,
ImageCacheResults& results,
const Int2Type<I>&)
{
// Get the index being searched
typedef typename ImageCache::nth_index<I>::type Index;
// This type knows how to extract the relevant bits of ImageCacheItem
// for this particular index.
typename Index::key_from_value keyExtractor;
// Look for matches in the index.
std::pair<typename Index::const_iterator, typename Index::const_iterator> iter =
cache.get<I>().equal_range(keyExtractor(item));
// If we found any results, add them to 'results', otherwise
// continue to the next index.
if (iter.first != iter.second)
{
results.reserve(std::distance(iter.first, iter.second));
for ( ; iter.first != iter.second; ++iter.first)
{
results.push_back(boost::cref(*iter.first));
}
}
else
{
FindMatches(item, cache, results, Int2Type<I + 1>());
}
}
boost::optional<ImageCacheItem> FindClosestImage(const ImageCacheItem& item, const ImageCache& cache)
{
// Find exact/partial matches according to the indicies.
ImageCacheResults results;
FindMatches(item, cache, results, Int2Type<0>());
// If no matches were found, return an empty value
if (results.empty())
{
return boost::optional<ImageCacheItem>();
}
// We got this far, so we must have some candiates, the problem is
// we dont know which is the best match, so here we sort the results
// based on proximity to the "item". However, we are only interested
// in the best match, so do a partial_sort.
std::partial_sort(results.begin(), results.begin() + 1, results.end(), ImageCacheCmp(item));
return results.front().get();
}
int main()
{
// Create the cache and add some "images" to it.
ImageCache cache;
cache.insert(ImageCacheItem(10, 20));
cache.insert(ImageCacheItem(10, 10));
cache.insert(ImageCacheItem(10, 2));
cache.insert(ImageCacheItem(20, 20));
cache.insert(ImageCacheItem(30, 20));
cache.insert(ImageCacheItem(30, 0));
// Look for an image similar to rotation = 30 && scale = 2.
boost::optional<ImageCacheItem> result = FindClosestImage(ImageCacheItem(30, 2), cache);
// Have to check if result is value before usage, it would be empty
// if not match is found.
if (result)
{
std::cout << "Found (" << result->rotation
<< ", " << result->scale << ")"
<< std::endl;
}
else
{
std::cout << "No Results" << std::endl;
}
return 0;
}
您是否考慮過使用薄型存取器進行灰色和旋轉彩色圖像? Adobe的通用圖像庫(現在是boost的一部分)使用了一些聰明的迭代器
你考慮過使用STL容器嗎? 使用地圖或集來存儲對圖像的引用。 快速查找以查看您是否已創建圖像。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.