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;
}
В результате получается изображение, похожее на уменьшенную версию в общей форме и сложности с некоторыми незначительными артефактами и проблемами точности. Это не так хорошо, как стандартные методы обработки изображений, но работает идеально для меня. Надеюсь, это кому-нибудь поможет.