Nano Hash - криптовалюты, майнинг, программирование

Построение графиков с помощью glVertex2i() показывает артефакты при масштабировании с помощью glScaled()

Я использую OpenGL для построения гистограммы изображения RGB. Поскольку это 8-битная гистограмма изображения, мой набор данных содержит точки данных от нуля до 255.

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

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

Гистограмма без glScale

На изображении выше показана гистограмма, построенная с 256 точками данных без масштабирования с помощью glScaled().

График гистограммы с glScale

То же, что и выше, но в большем масштабе

На двух изображениях выше показана гистограмма, построенная с 256 точками данных И масштабированная с помощью glScaled(). Странные артефакты очевидны (отсутствуют данные?). Обратите внимание, что третья гистограмма имеет немного другую форму из-за изменения уровня освещенности.

Вот соответствующая часть моего кода инициализации OpenGL:

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// If this line is removed then the graph plots correctly
// m_scale_factor = width / 256
glScaled(m_scale_factor, 1.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT);

И вот соответствующая часть моего сюжетного кода:

glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);

glBegin(GL_LINE_STRIP);
for (int n = 0; n < m_histogram_X; n++)
{
    glColor4ub(255, 0, 0, 255);
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 255, 0, 255);
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);

    glColor4ub(0, 0, 255, 255);
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

...

На этом этапе я чувствую, что должен заявить, что я новичок в OpenGL, поэтому, возможно, я неправильно понял многие вещи OpenGL...

Мой вопрос: можно ли исправить эту проблему в OpenGL или мне придется увеличить количество точек данных с помощью какой-то интерполяции, а затем построить без масштабирования?

Я ценю любую предложенную помощь.

14.05.2019

  • Если вы новичок в OpenGL, я предлагаю вам изучить современный OpenGL. То, что вы изучаете, — это устаревший OpenGL, которому более 10 лет. См. learnopengl.com. 15.05.2019

Ответы:


1

Если вы разделите рендеринг на 3 прохода, вы сможете использовать треугольные полосы вместо линейных полос (которые должны заполнять промежутки).

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(255, 0, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_R[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 255, 0, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_G[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()

glBegin(GL_TRIANGLE_STRIP);
glColor4ub(0, 0, 255, 255);
for (int n = 0; n < m_histogram_X; n++)
{
    glVertex2i(n, m_Hist_Channel_B[n]);
    glVertex2i(n, GRAPH_HEIGHT);
}
glEnd()
15.05.2019
  • Это решило проблему для 8-битных гистограмм изображений и 16-битных. Спасибо. 15.05.2019

  • 2

    Отсутствуют данные. Просто вы используете примитив рисования таким образом, который не соответствует данным. GL_LINE_STRIP рисует одну длинную непрерывную линию, соединяя точки по мере их прохождения, а также интерполируя между установленными вами цветами.

    По сути, вы рисуете линию от синего к красному от последней синей ячейки до высоты следующей красной ячейки, затем от красной ячейки до зеленой ячейки и оттуда до синей ячейки. Затем вы переходите к следующей красной корзине и так далее. По сути, вы рисуете маленькие «шипы» с соединительными линиями между красным и синим. Конечно, будут пробелы, если пикселей по ширине для заполнения больше, чем бинов.

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

    По правде говоря, это не самый эффективный способ построения гистограммы. Гораздо лучший подход — загрузить данные гистограммы в одномерную текстуру, нарисовать один большой четырехугольник (или, что еще лучше, треугольник, заполняющий область просмотра, используя ножничное тестирование, чтобы обрезать область просмотра до прямоугольника) и для каждого фрагмента (примерно это пиксель, с кое-что еще) в фрагментном шейдере используйте координату X, чтобы найти бин из текстуры, вычтите координату Y из текстуры и передайте результат в функцию step или smoothstep GLSL для определения цвета для пикселя. Как бы странно это ни звучало для новичка, но рисование одного треугольника и выполнение всего остального во фрагментном шейдере более эффективно, чем отправка набора полигонов. Это также дает гораздо лучшее качество!

    Обновление — пример шейдера

    Практическое применение может быть реализовано с помощью такого шейдера (семантика Shadertoy):

    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        // Normalized pixel coordinates (from 0 to 1)
        vec2 uv = fragCoord/iResolution.xy;
    
        // read the histogram data from a sampler
        // on shadertoy there are no 1D textures, so we use a
        // single row of a 2D texture instead, and sweep up/down
        // over time to get a "dynamic" histogram.
        vec3 h = texture(iChannel0, vec2(uv.x, sin(0.01*iTime))).rgb;
    
        // discard fragments which are "outside" the histogram
        // also use this value later for the alpha channel
        float a = smoothstep( 0.000, 0.001, length(h));
        if( 0. == a ){ discard; }
    
        // Color the fragment. The smoothstep gives some antialiasing.
        // For perfect pixel coverage based antialiasing we'd have to
        // determine the slope of the histogram, to construct a tangent
        // and determine the distance to it, i.e. create a
        // Signed Distance Field
        fragColor = vec4(
            smoothstep(-0.001, 0.001, h.r - uv.y),
            smoothstep(-0.001, 0.001, h.g - uv.y),
            smoothstep(-0.001, 0.001, h.b - uv.y),
            a );
    
        // Instead of using the smoothstep and/or the SDF using a
        // multisampled buffer and performing a simple `y >= h` test
        // would yield probably a nicer result.
    }
    

    Тогда результат выглядит так

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

    14.05.2019
  • Простое построение трех цветов по отдельности, чтобы избежать изменения цвета между соседними вершинами, в значительной степени помогло бы решить проблему. 15.05.2019
  • @datenwolf Спасибо за ваш полезный комментарий. Как я уже сказал, я очень новичок в OpenGL, и то, что вы предлагаете, звучит относительно сложно для моего статуса новичка в GL. Мне нужно было бы потратить больше времени на изучение OpenGL, а время, к сожалению, имеет решающее значение. 15.05.2019
  • @Ben Voigt Я попробовал ваше предложение, и оно стало намного лучше, но, увы, все еще не идеально. Я думаю, по крайней мере сейчас, я буду интерполировать дополнительные точки данных и строить графики без glScaled(). Когда у меня будет время, я смогу узнать больше об OpenGL и реализовать решение, подобное описанному datenwolf. 15.05.2019
  • @Amanda: я обновил свой ответ кодом шейдера. Вам по-прежнему понадобится хост-код OpenGL для загрузки шейдера и гистограммы в текстуру. Но это должно дать вам идею. 16.05.2019
  • @datenwolf Большое спасибо за это, это ценится. Поэкспериментирую с вашей идеей. 17.05.2019
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..