Java/Processing - масштабировать матрицу изображения с оберточными краями

Мне было интересно, как эффективно реализовать следующую процедуру масштабирования изображения в Java или Processing. При масштабировании границы изображения оборачиваются по краям экрана. Я хочу применить то же самое во время выполнения к массиву Pixels() в программе Processing. (чтобы сохранить эту независимость от обработки - Pixels() - это не что иное, как метод, который возвращает все пиксели на моем текущем экране в массиве).

(Обратите внимание, что этот пример был сделан в MaxMsp/Jitter с использованием модуля jit.rota, который, похоже, использует очень эффективную реализацию).

немасштабированный

уменьшенный

Может кто-нибудь помочь мне с тем, как начать? Я предполагаю, что это должно быть сочетание уменьшения масштаба изображения и создания его вспомогательных копий, но для меня это звучит не очень эффективно. Приведенный выше пример отлично работает с видео даже с самыми экстремальными настройками.

2 ответа

Один вариант, который я думаю, будет быстрым, это использование базового фрагментного шейдера.

К счастью, у вас есть пример, довольно близкий к тому, что вам нужно, который поставляется с Обработка через Файл> Примеры> Темы> Шейдеры> Бесконечные плитки

Я не смогу эффективно предоставить достойное руководство для начала и конца, но на веб-сайте Processing есть исчерпывающее руководство по PShader, если вы начинаете с нуля.

Действительно грубая суть того, что вам нужно:

  • шейдеры - это программы, которые работают очень быстро и распараллелены на GPU, разделены на две части: вершинные шейдеры (в основном, с трехмерной геометрией), фрагментные шейдеры (в основном, с "фрагментами"(которые собираются стать пикселями на экране)). Вы захотите поиграть с фрагментным шейдером
  • Язык называется GLSL и немного отличается (меньше типов, более строгий, более простой синтаксис), но не является абсолютно чуждым (похожий тип объявления переменных C, функций, условий, циклов и т. Д.)
  • если вы хотите сделать переменную из GLSL-программы доступной в Processing, вы ставите ее перед ключевым словом uniform
  • используйте textureWrap(REPEAT), чтобы обернуть края
  • чтобы масштабировать изображение и обернуть его, вам нужно масштабировать координаты выборки текстуры:

Вот как выглядит шейдерный скроллер Infinite Tiles:

//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------

uniform float time;
uniform vec2 resolution;
uniform sampler2D tileImage;

#define TILES_COUNT_X 4.0

void main() {
  vec2 pos = gl_FragCoord.xy - vec2(4.0 * time);
  vec2 p = (resolution - TILES_COUNT_X * pos) / resolution.x;
  vec3 col = texture2D (tileImage, p).xyz;
  gl_FragColor = vec4 (col, 1.0);
}

Вы можете немного упростить это, поскольку вам не нужно прокручивать. Кроме того, вместо вычитания и умножения (- TILES_COUNT_X * pos), вы можете просто умножить:

//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------

uniform float scale;
uniform vec2 resolution;
uniform sampler2D tileImage;

void main() {
  vec2 pos = gl_FragCoord.xy * vec2(scale);
  vec2 p = (resolution - pos) / resolution.x;
  vec3 col = texture2D (tileImage, p).xyz;
  gl_FragColor = vec4 (col, 1.0);
}

Обратите внимание, я изменил time переменная, чтобы стать scale поэтому код обработки, обращающийся к этой единой переменной, также должен измениться:

//-------------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//-------------------------------------------------------------

PImage tileTexture;
PShader tileShader;

void setup() {
  size(640, 480, P2D);
  textureWrap(REPEAT);
  tileTexture = loadImage("penrose.jpg");
  loadTileShader();
}

void loadTileShader() {  
  tileShader = loadShader("scroller.glsl");
  tileShader.set("resolution", float(width), float(height));  
  tileShader.set("tileImage", tileTexture);
}

void draw() {
  tileShader.set("scale", map(mouseX,0,width,-3.0,3.0));
  shader(tileShader);
  rect(0, 0, width, height);
}

Переместите мышь, чтобы изменить масштаб: масштабирование фрагмента шейдера

масштабирование фрагмента шейдера

Обновление Вы можете поиграть с очень похожим шейдером здесь:

Я действительно придумал решение - но затем реализую метод Джорджа, поскольку разница в скорости с использованием шейдеров, кажется, того стоит!

public void scalePixels(double wRatio,double hRatio, PGraphics viewPort) {
    viewPort.loadPixels();
    int[] PixelsArrayNew = viewPort.pixels.clone();
    double x_ratio = wRatio ;
    double y_ratio = hRatio ;
    double px, py ;
    for (int i=0;i<viewPort.height;i++) {
        for (int j=0;j<viewPort.width;j++) {
            px = Math.floor(j%(wRatio*viewPort.width)/x_ratio) ;
            py = Math.floor(i%(hRatio*viewPort.height)/y_ratio) ;
            viewPort.pixels[(int)(i*viewPort.width)+j] = PixelsArrayNew[(int)((py*viewPort.width)+px)] ;
        }
    }
    viewPort.updatePixels();    
}
Другие вопросы по тегам