C# 2D масштабирование масштаба

решаемая

Я работаю над 2D процедурным островным генератором. До сих пор я использовал несколько шумовых функций и смешивание для достижения желаемой формы островков, но в настоящее время они имеют размер только 300 x 200 пикселей. Я хочу масштабировать данные изображения (НЕ ИЗОБРАЖЕНИЕ) до нужного размера карты где-то в диапазоне 400 000 x 400 000. Я рассмотрел простое изменение шумовых функций на более низкую частоту, что привело к увеличению островков, но я также смешиваю их с генерируемым градиентом, который уже является вычислительно тяжелым. Увеличение размера моего сгенерированного градиента сильно повредило бы время генерации.

Форма моего острова / изображение: http://4.bp.blogspot.com/-ed7nK7jwcJc/UxYjNMxUF-I/AAAAAAAAASc/sG-BygA4usA/s1600/PopulationMap1.png

Данные за островом представляют собой массив чисел от 0 до 1, представляющих воду / пляж / сушу. Все, что мне нужно, это масштабировать эти данные до диапазона, необходимого для продолжения создания дорог и так далее. По сути, каждый пиксель является плиткой, и мне нужно где-то около 400 КБ по ширине от нынешних 300 по ширине. Я понимаю, что, вероятно, слишком мало входных данных для такого масштабирования, но я все же хотел бы попробовать это.

Я ознакомился со всеми методами фильтрации и интерполяции изображений, но не могу понять ни один из них. Я заметил, что этот вопрос уже задавался интерполяцией массива, но я не могу понять, как заставить его работать на сетке размером более 4х4, но ОП точно описывает то, что я ищу.

В основном линейная или бикубическая фильтрация / масштабирование на большой сетке с плавающей запятой. Мне просто нужен пример кода, чтобы набросать процесс..

Если ничего не помогает, я переключу свой градиент на более простой подход и просто сгенерирую все данные карты в том размере, с которого я хочу, чтобы это началось. Но я действительно хотел бы попробовать масштабировать его. Заранее спасибо.

РЕДАКТИРОВАТЬ - Я дошел до того, что у меня есть список пар взвешенных значений для каждой плитки, окружающей точку, к которой я обращаюсь. Поэтому, если бы я запрашивал значение в 5, 5, 5, 5, я бы получил значение и вес плиток, окружающих (и включая) 5,5. Итак, у меня есть список из 9 {0.9, 1.27} { 0.3, 0.77} и т. Д. Пар значений, где первая - это элемент массива, а вторая - его вес (обратно пропорционально расстоянию между ним и запрашиваемой точкой). Сейчас я пытаюсь найти способ сложения всех этих пар взвешенных значений для достижения окончательного значения. Я почти уверен, что это связано с какой-то интерполяцией, но я не совсем понимаю, как это сделать. Если я смогу пройти через эту часть, то я должен быть в порядке с остальными.

1 ответ

Решение

Я выяснил метод, который хорошо работает для меня. Я постараюсь объяснить свое решение здесь на тот случай, если кому-то еще понадобится помощь по этой теме. Обратите внимание, что это, вероятно, не лучшее решение, но оно выполняет свою работу.

Я начал с моего исходного массива float[,] sourceArray и больший массив, который я хочу масштабировать до float[,] targetArray

Затем я перебираю каждую плитку (элемент массива) в моем целевом массиве и получаю процентное значение между его центром и общей высотой и шириной цели масштаба. Затем я вызываю метод, который возвращает вычисленное значение, передавая процент, исходный массив, ширину исходного массива и высоту исходного массива.

for(float x = 0, x < scaleWidth, x++)
{
    for(float y = 0, y < scaleHeight, y++)
    {
        Vector2 percentage = new Vector2((x+0.5)/scaleWidth, (y+0.5)/scaleHeight);
        targetArray[x,y] = GetScaleValueAt(percentage, sourceArray, sourceWidth, sourceHeight);
    }
}

мой GetScaleValueAt() Метод возвращает запрошенное, правильно масштабированное значение. Сначала он находит соответствующее местоположение в исходном массиве на основе процента. Также он находит плитку, внутри которой находится локация.

public float GetScaleValueAt(float percent, float[,] source, int width, int height)
{
    Vector2 location = new Vector2(width * percent.X, height * percent.Y);
    Point centerTile = new Point((int)Math.Floor(loc.X), (int)Math.Floor(loc.Y));\

Затем я использую другой метод, чтобы получить все плитки рядом с центральной (включая центральную плитку)

    List<Point> tiles = GetIncNeighborhood(centerTile.X, centerTile.Y, width, height);

Затем я перебираю этот список и заполняю второй список WeightedPairэто просто класс или структура с двумя значениями с плавающей запятой - float value, weight которые доступны. weight рассчитывается по обратному расстоянию между location и каждая плитка. Я также сохраняю сумму взвешенных значений.

    List<WeightedPair> weightValues = new List<WeightedPair>();
    float weightedTotal = 0;
    foreach(Point p in tiles)
    {
        Vector2 distance = new Vector2();
        distance.X = System.Math.Abs(loc.X - p.X);
        distance.Y = System.Math.Abs(loc.Y - p.Y);
        float inverseDist = 1 / distance.Length();

        float value = source[p.X, p.Y];

        weightedTotal += invDist;
        weightValues.Add(new WeightedPair(value, invDist));
    }

Наконец я перебираю weightValues список и рассчитать значение, умножив каждый value в соответствии с weight/weightedTotal,

    float finalValue = 0;
    foreach(WeightedPair w in weightValues)
    {
        finalValue += w.value * (w.weight/weightedTotal);
    }
    return finalValue;
}

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

Другие вопросы по тегам