C ++缓存设计建议

时间:2011-06-17 17:29:54

标签: c++ class-design

我有一个带有多种图像类型(RGB,Gray ...)的c ++应用程序,每种类型都有旋转或缩放等属性。每种图像类型都是通过其他类型的计算生成的。例如,通过旋转GrayImage生成轮换GrayImage,而RGBImage又是通过“灰化”GetX(...)生成的。{/ p>

我想设计一个带有方法map<...,RGBImage> map<...,GrayImage> 的缓存类,缓存各种图像(可能还有计算路径中的所有图像)。 该类还知道如何生成每个图像,以防它不在缓存中。

班级必须满足一些限制条件:

  1. 由于我们处理的是不同类型和表示的图像(RGB,GrayScale等),因此缓存必须为调用代码返回一个具体类,以便能够在不进行某种转换的情况下使用它。因此,缓存机制必须包含包含具体类型的不同缓存结构。 (如果我错了,请修理我)

    Key
    例如,

  2. 缓存必须能够灵活应对图像计算的变化。代码的更改只要不太大就可以接受。

  3. 当前版本我为每种图像类型附加了GrayKey结构。 有RGBKeyGrayKey等等。各种键包含缩放和旋转等属性,并且可以具有特定于图像的属性(例如, map <XKey,XImage> 的toGrayConvertingMethod)。 缓存包含以下形式的地图:

    GetX(...)

    Key方法接收{{1}}结构作为参数,例如请求Rotated GrayImage。 但是,此实现强制缓存应用大量逻辑来计算图像。必须检查GrayKey是否请求旋转图像并采取相应措施。 我想以更优雅的方式“编码”这个图像计算关系,但似乎找不到它。

    有什么建议吗?

    非常感谢。

3 个答案:

答案 0 :(得分:2)

也许你可以使用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旋转,所以你需要做的就是旋转)

代码被注释以解释它的作用,但基本上,它使用模板递归按顺序搜索索引,一旦索引找到一些匹配,它就会尝试按相关性顺序对它们进行排序,并返回最佳匹配。要添加其他属性,您需要执行以下操作:

  1. 将该媒体资源添加到ImageCacheItem
  2. 将属性的比较添加到ImageCacheSimilarity
  3. (可选)将与其匹配的另一个索引添加到ImageCache typedef
  4. 它可能不是最佳解决方案,但我认为它涵盖了您在评论中提到的用例。

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

答案 1 :(得分:1)

您是否考虑过使用薄型存取器进行灰色和旋转彩色图像? Adobe的通用映像库(现在是boost的一部分)使用了一些聪明的迭代器

答案 2 :(得分:0)

您考虑使用STL container吗?使用地图或集来存储对图像的引用。快速查找以查看您是否已创建图像。