Советы по разработке C ++ Cache - PullRequest
5 голосов
/ 17 июня 2011

У меня есть приложение на c ++ с несколькими типами изображений (RGB, Grey ...), и у каждого типа есть свойства, такие как Rotation или Scale. Каждый тип изображения генерируется с помощью некоторых вычислений из других типов. Например, вращение GrayImage генерируется вращением GrayImage, которое, в свою очередь, генерируется путем "поседения" RGBImage.

Я хотел бы разработать класс кэша с методами GetX(...), которые кэшируют различные изображения (и, возможно, все изображения в пути вычисления). Этот класс также знает, как генерировать каждое изображение, если его нет в кэше.

Класс должен удовлетворять некоторым ограничениям:

  1. Поскольку мы имеем дело с изображениями разных типов и представлений (RGB, GrayScale и т. Д.), Кэш должен возвращать конкретный класс для вызывающего кода, чтобы иметь возможность использовать его без какого-либо преобразования. Таким образом, механизм кэширования должен содержать различные структуры кэша, содержащие конкретные типы. (поправьте меня, если я ошибаюсь)

    map<...,RGBImage> 
    map<...,GrayImage> 
    

    например.

  2. Кэш должен быть гибким для изменений в вычислениях изображений. Изменения в коде допустимы, если они не слишком велики.

Текущая версия, которую я имею, прикрепляет структуру Key к каждому типу изображения. Там GrayKey, RGBKey и так далее. различные ключи содержат свойства, такие как Scale и Rotation, и могут иметь свойства, специфичные для изображения (например, toGrayConvertingMethod для GrayKey). Кеш содержит карты вида:

    map <XKey,XImage>

GetX(...) метод получает структуру Key в качестве параметра, запрашивая, например, Rotated GrayImage. Тем не менее, эта реализация заставляет кэш применять много логики для вычислений изображений. Он должен проверить, запрашивает ли GrayKey повернутое изображение или нет, и действовать соответственно. Я хотел бы «закодировать» это отношение вычисления изображения более элегантным способом, но, похоже, не могу его найти.

Есть предложения?

Большое спасибо.

Ответы [ 3 ]

2 голосов
/ 18 июня 2011

Возможно, вы могли бы что-то сделать, используя контейнер 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

Возможно, это не самое оптимальное решение, но я думаю, оно охватывает вариант использования, который вы упомянулив вашем комментарии.

#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 голос
/ 17 июня 2011

Рассматривали ли вы использование тонких аксессуаров для серого и поворота вашего цветного изображения? Универсальная библиотека изображений Adobe (теперь часть надстройки) использует некоторые умные итераторы таким образом

0 голосов
/ 17 июня 2011

Рассматривали ли вы использование STL контейнера ? Используйте карту или набор для хранения ссылок на изображения. Быстрый поиск, чтобы увидеть, если вы уже создали изображение.

...