Как отсортировать пиксели и реализовать linearGradient в гистограмме webgl?

Я пытался реализовать гистограмму для изображения с помощью webgl. Я могу сделать это в javascript HTML5 Canvas, взяв массив длиной 255 и увеличив значения пикселей каждого индекса до 0-255 и используя createLinearGradient для создания гистограммы.

for(var c = 0; c < 256; c++){
    histogram[c] = 0;
}
var ctx = document.getElementById('canvas').getContext('2d');
var pixels= ctx.getImageData(0, 0, width, height).data;
for (var i = 0, j = 0; i < u8a.length; i++, j = j + 4) {
    histogram[pixels[j]]++; //increasing pixel index for histogram
}

А для реализации того же в WebGL я имею в виду этот jsfiddle, но я вижу гистограмму не гладкий и не упорядоченный.

Итак, я застрял здесь с двумя вещами с WebGL:

-Как мы можем отсортировать значения пикселей от 0,0 (0) до 1,0 (255) в вышеупомянутой скрипке?

-Как сделать гистограмму гладкой в ​​webgl с/без линейного градиента?

Код :

precision mediump float;
uniform sampler2D u_histTexture;
uniform vec2 u_resolution;
uniform sampler2D u_maxTexture;
void main() {
    // get the max color constants
    vec4 maxColor = texture2D(u_maxTexture, vec2(0));
    // compute our current UV position
    vec2 uv = gl_FragCoord.xy / u_resolution;
    // Get the history for this color
    // (note: since u_histTexture is 256x1 uv.y is irrelevant
   vec4 hist = texture2D(u_histTexture, uv);
   // scale by maxColor so scaled goes from 0 to 1 with 1 = maxColor
   vec4 scaled = hist / maxColor;
   // 1 > maxColor, 0 otherwise
   vec4 color = step(uv.yyyy, scaled);
   float rr = 0.2989 * color.r + 0.5870 * color.g + 0.1140 * color.b;
       gl_FragColor = vec4(rr, rr, rr, 1);
}

Гистограмма в диапазоне от 0 до 255 или от 0 до 65536, если изображение 16-битное


person graphics123    schedule 20.11.2018    source источник
comment
?? Сортировать гистограмму! На самом деле это не гистограмма, если она отсортирована. В любом случае, ваш вопрос очень неясен, что вы подразумеваете под гладкостью и сортировкой? можете ли вы предоставить изображение того, что вы хотите, включая несортированные и отсортированные гистограммы, поскольку трудно догадаться, что вы имеете в виду?   -  person Blindman67    schedule 20.11.2018
comment
от сортировки я имею в виду, что он должен отображать от 0,0 до 1,0 от самого темного до самого светлого (от черного до белого) в формате оттенков серого. Я скоро добавлю ожидаемое изображение   -  person graphics123    schedule 20.11.2018
comment
добавлено изображение, пожалуйста, проверьте, в настоящее время я использую createLinearGradient of Html5 Canvas для этого результата   -  person graphics123    schedule 20.11.2018
comment
Чтобы создать градиент, измените последнюю строку на gl_FragColor = vec4(rr, rr, rr, 1.0) * vec4(uv.xxx,1.0);, она умножит цвет на позицию x (0-1). Что касается сортировки, все еще неясно, что вы имеете в виду.   -  person Blindman67    schedule 20.11.2018
comment
Хорошо, я попробовал ваше изменение, и оно выглядит хорошо. Спасибо. Для сортировки, пожалуйста, проверьте изображение, которое я добавил, оно начинается с более темных оттенков и переходит к более светлым оттенкам. Надеюсь, это ясно.   -  person graphics123    schedule 20.11.2018
comment
Изображение не дает понятия, что отсортировано?   -  person Blindman67    schedule 20.11.2018
comment
Разве вы не видите более яркие цвета слева и более светлые справа?   -  person graphics123    schedule 20.11.2018
comment
Я вижу волнистую линию и от темного слева к светлому справа. Я не вижу яркого в светлое (хотя я не знаю, в чем разница между ярким и светлым, может быть, вы имеете в виду насыщенность для яркого??), Код, который вы показываете (ссылка), имеет темное в светлое (слева направо) Это и есть гистограмма, ось x — это яркость/яркость, ось y — счет. Можете ли вы показать сортировку, которую вы используете для 2D-контекстной версии.   -  person Blindman67    schedule 20.11.2018
comment
Для 2D я использовал массив из 256 элементов, начиная с 0 до 255. Поэтому при повторении значений пикселей в коде выше в посте я просто увеличиваю индекс. Следовательно, если во всем изображении есть 100 0 в качестве значения пикселя, я делаю индекс от 0 до 100 (код: гистограмма [0] = 100), и то же самое применяется до 255 (код: гистограмма [n] = pixelCount). А чтобы сделать гистограмму гладкой, я использовал colorStop, т.е. `var myGradient = histCtx.createLinearGradient(0, 0, histCanvas.width, 0); myGradient.addColorStop (0, 'черный'); myGradient.addColorStop(1, 'белый'); drawHistogramPath(histCtx, myGradient); `   -  person graphics123    schedule 21.11.2018


Ответы (1)


Лучшее, что я могу понять, это то, что вы не хотите сортировать, а хотите сделать черно-белую гистограмму (все каналы r, g, b вместе взятые).

Для этого вам нужен только один проход, который суммирует все каналы, а не 4 прохода, как сейчас.

Вам нужно внести изменения в следующие шейдеры и заменить некоторые вызовы рендеринга в javascript.

Измените «hist-vs» на

attribute float pixelId;
uniform vec2 u_resolution;
uniform sampler2D u_texture;
void main() {
  vec2 pixel = vec2(mod(pixelId, u_resolution.x), floor(pixelId / u_resolution.x));
  vec2 uv = (pixel + 0.5) / u_resolution;
  vec4 color = texture2D(u_texture, uv);
  float colorSum = (color.r + color.g + color.b) / 3.0 ; 
  gl_Position = vec4((colorSum * 255.0 + 0.5) / 256.0 * 2.0 - 1.0, 0.5, 0, 1);
  gl_PointSize = 1.0;
}

Измените «max-vs» на

precision mediump float;
uniform sampler2D u_texture;
void main() {
  vec4 maxColor = vec4(0);
  for (int i = 0; i < 256; i++) {
    vec2 uv = vec2((float(i) + 0.5) / 256.0, 0.5);
    maxColor = max(maxColor, vec4(texture2D(u_texture, uv).rgb, 1));
  }
  gl_FragColor = maxColor;
}

Измените «show-fs» на

precision mediump float;    
uniform sampler2D u_histTexture;
uniform vec2 u_resolution;
uniform sampler2D u_maxTexture;    
void main() {
  vec3 maxColor = texture2D(u_maxTexture, vec2(0)).rgb;
  vec2 uv = gl_FragCoord.xy / u_resolution;
  vec3 hist = texture2D(u_histTexture, uv).rgb;
  gl_FragColor = vec4(step(uv.yyy, hist / maxColor) * uv.x, 1);
}

Изменения в JavaScript

Затем в Javascript вместо того, чтобы вызывать первый шейдер 4 раза, вам нужно вызвать его только один раз. Также, поскольку форма маски не нужна, вам не нужно передавать ее шейдеру.

Цикл for, который первоначально выглядел как

  for (var channel = 0; channel < 4; ++channel) {
    gl.colorMask(channel === 0, channel === 1, channel === 2, channel === 3);
    twgl.setUniforms(histProgramInfo, {
      u_texture: tex,
      u_colorMult: [
        channel === 0 ? 1 : 0,
        channel === 1 ? 1 : 0,
        channel === 2 ? 1 : 0,
        channel === 3 ? 1 : 0,
      ],
      u_resolution: [img.width, img.height],
    });
    twgl.drawBufferInfo(gl, gl.POINTS, pixelIdBufferInfo);
  }

замените все эти строки на

gl.colorMask(true, true, true, false);
twgl.setUniforms(histProgramInfo, {  u_texture: tex,  u_resolution: [img.width, img.height]});
twgl.drawBufferInfo(gl, gl.POINTS, pixelIdBufferInfo);
person Blindman67    schedule 21.11.2018
comment
Это решение работает, как и ожидалось, но оно не создает линейный градиент, как на изображении, которое я прикрепил (см. черную линию, покрывающую гистограмму сверху). Итак, означает ли это, что OpenGL не может отображать linearGradient так, как мы получаем с помощью Canvas в HTML5 createLinearGradient? - person graphics123; 22.11.2018
comment
@ subhfyu546754 Вам нужно использовать правильную терминологию. Черная линия — это штрих, и она не имеет ничего общего с градиентом. - person Blindman67; 22.11.2018
comment
да это инсульт я не заметил. Но как нам добиться этого в webgl в этом сценарии. Я могу видеть много примеров в Интернете, но не могли бы вы дать некоторое представление о том, как реализовать обводку в этом случае? - person graphics123; 22.11.2018
comment
@subhfyu546754 Предоставленная скрипка не способствует добавлению контура, а показанный вами штрих выходит за пределы холста webGl. Вы можете легко использовать 2D APIglobalCompositeOperation (gCO) и визуализированное изображение WebGL в качестве маски для рисования контура, но вам потребуется изменить вывод WebGL, чтобы включить прозрачные пиксели. Затем создайте новый холст, который на один пиксель больше со всех сторон, нарисуйте маску на 1 пиксель вверх, вниз, влево и вправо, установите для gCO значение source-over, нарисуйте черный прямоугольник поверх холста, установите gCO на source-over и отрисуйте. маска в центре. - person Blindman67; 22.11.2018
comment
Хорошо, я попробую это. Спасибо за идею. Принимаю это как ответ - person graphics123; 26.11.2018