Компьютерная графика - Растровые алгоритмы.pptx
- Количество слайдов: 37
Растровые алгоритмы Компьютерная графика Иванов Е. А. Кафедра МОИСи. И, МЭСИ, 2013
Растровые алгоритмы Так как в подавляющем большинстве графические устройства являются растровыми, то есть представляют изображение в виде прямоугольной матрицы пикселей (растра), то, естественно, возникает необходимость в растровых алгоритмах.
Растровые алгоритмы Хотя большинство графических библиотек содержат внутри себя достаточное количество простейших растровых алгоритмов, таких, как: переведение идеального объекта (отрезка, окружности и др. ) в их растровые образы; обработка растровых изображений, тем не менее часто возникает необходимость явного построения растровых алгоритмов.
Растровая развертка отрезка Достаточно важным понятием для растровой сетки является связность – возможность соединения двух пикселей растровой линией, то есть последовательным набором пикселей. При этом возникает вопрос, когда пиксели (x 1, y 1) и (x 2, y 2) можно считать соседними?
Растровая развертка отрезка Вводится два понятия связности: 4 -связность, когда пиксели считаются соседними, если либо их x-координаты, либо y-координаты отличаются на единицу, то есть |x 1 – x 2| + |y 1 – y 2| 1; 8 -связность, когда пиксели считаются соседними, если их x- и y-координаты отличаются не более чем на единицу, то есть |x 1 – x 2| 1, |y 1 – y 2| 1.
Растровая развертка отрезка Понятие 4 -связности является более сильным, чем 8 -связность: любые два 4 -связных пикселя являются и 8 -связными, но не наоборот.
Растровая развертка отрезка В качестве линии на растровой сетке выступает набор пикселей P 1, P 2, …, Pn, где любые два пикселя Pi , Pi+1 являются соседними.
Растровая развертка отрезка Так как понятие линии базируется на понятии связности, то естественным образом возникает понятие 4 - и 8 -связных линий. Поэтому, когда мы говорим о растровом представлении, например, отрезка, то следует ясно понимать, о каком именно представлении идет речь. При этом нужно иметь в виду, что растровое представление объекта не является единственным и возможны различные способы построения.
Растровая развертка отрезка Рассмотрим задачу построения растрового изображения отрезка, соединяющего точки (x 1, y 1) и (x 2, y 2). Для простоты будем считать, что 0 y 2 – y 1 x 2 – x 1. Тогда отрезок описывается следующим уравнением:
Растровая развертка отрезка Простейший алгоритм растрового представления отрезка имеет вид: void Line(int x 1, int y 1, int x 2, int y 2, int color) { double k = (y 2 -y 1)/(x 2 -x 1); double b = y 1 -k*x 1; for( int x = x 1; x <= x 2; x++ ) putpixel(x, round(k*x+b), color); } Здесь и далее putpixel – некоторая абстрактная функция, отображающая на экране точку заданного цвета по заданным координатам.
Растровая развертка отрезка Используя рекуррентное соотношение для вычисления y, можно упростить функцию, однако это не устраняет основного недостатка алгоритма – использования вещественных чисел для работы на целочисленной решетке. В 1965 году Брезенхэмом был предложен простой целочисленный алгоритм для растрового построения отрезка, первоначально предназначенный для использования в графопостроителях.
Растровая развертка отрезка При построении растрового изображения отрезка всегда выбирается ближайший по вертикали пиксель. При этом из двух точек A и B (см. рисунок) выбирается та, которая ближе к исходной прямой (в данном случае выбирается точка A, так как a0 значение y от предыдущей точки увеличивается на 1, а d – на 2( y - x). В противном случае значение y не изменяется, а значение d заменяется на 2 y.
Растровая развертка отрезка (пример) Реализация приведенного алгоритма представлена ниже void line(int x 1, int y 1, int x 2, int y 2) { int delta. X = abs(x 2 - x 1); int delta. Y = abs(y 2 - y 1); int sign. X = x 1 < x 2 ? 1 : -1; int sign. Y = y 1 < y 2 ? 1 : -1; int error = delta. X - delta. Y; for (; ; ) { putpixel(x 1, y 1); if(x 1 == x 2 && y 1 == y 2) break; int error 2 = error * 2; if(error 2 > -delta. Y) { error -= delta. Y; x 1 += sign. X; } if(error 2 < delta. X) { error += delta. X; y 1 += sign. Y; } } } Подробнее по ссылке в конце презентации
Растровая развёртка окружности Существует несколько очень простых, но не эффективных способов преобразования окружностей в растровую форму. Например, рассмотрим для простоты окружность с центром в начале координат. Её уравнение записывается как x 2 + y 2 = R 2. Решая это уравнение относительно y, получим
Растровая развёртка окружности Чтобы изобразить четвертую часть окружности, будем изменять x с единичным шагом от 0 до R и на каждом шаге вычислять y. Остальные четверти отображаются симметрично. Этот метод неэффективен поскольку в него входят операции умножения и извлечения квадратного корня; при значениях х близких к R появляются большие незаполненные промежутки, т. к. тангенс угла наклона касательной к окружности стремится к бесконечности.
Растровая развёртка окружности Вторым простым методом растровой развертки окружности является использование вычислений x и y по формулам x = R cos α, y = R sin α при пошаговом изменении угла α от 0° до 90°. Он так же неэффективен из-за вычислений sin и cos. Для упрощения алгоритма растровой развёртки стандартной окружности можно воспользоваться её симметрией относительно координатных осей и прямых y = ± x; в случае, когда центр окружности не совпадает с началом координат, эти прямые необходимо сдвинуть параллельно так, чтобы они прошли через центр окружности. Тем самым достаточно построить растровое представление для 1/8 части окружности, а все оставшиеся точки получить симметрией
Растровая развёртка окружности За основу можно взять часть окружности от 0 до 45° в направлении по часовой стрелке с исходной точкой построения (r, 0). В этом случае координата окружности x является монотонно убывающей функцией координаты y. Основная идея алгоритма заключается в выборе ближайшей точки при помощи управляющих переменных, значения которых можно вычислить в пошаговом режиме с использованием лишь небольшого числа сложений, вычитании и сдвигов.
Растровая развёртка окружности При выбранном направлении движения по окружности имеется только три возможности для расположения ближайшего пикселя: на единицу вправо, на единицу вниз и по диагонали вниз.
Растровая развёртка окружности Выбор варианта можно осуществить, вычислив расстояния до этих точек и выбрав минимальное из них:
Растровая развёртка окружности Алгоритм можно упростить, перейдя к анализу знаков величин Sh, Sv, Sd. При Sd < 0 диагональная точка лежит внутри окружности, поэтому ближайшими точками могут быть только диагональная и правая. Теперь достаточно проанализировать знак выражения =dv -dd. Если 0, выбираем горизонтальный шаг, в противном случае - диагональный. Если же Sd > 0, то определяем знак 1=dd-dv, и если 1 0, выбираем диагональный шаг, в противном случае - вертикальный. Затем вычисляется новое значение Sd, причем желательно минимизировать вычисления не только этой величины, но и величин , 1 на каждом шаге алгоритма. Путем несложных преобразований можно получить для первого шага алгоритма, что =2(Sd + y) – 1, 1 =2(Sd + x) - 1.
Растровая развёртка окружности После перехода в точку (x’, y’), x’=x+1, y’=y-1 по диагонали новое значение Sd вычисляется по формуле S’d = Sd + 2 x’ – 2 y’ + 2, при горизонтальном переходе x’=x+1, y’=y S’d = Sd + 2 x’ + 1 при вертикальном x’=x, y’=y-1 S’d = Sd – 2 y’ + 1
Растровая развёртка окружности Таким образом, алгоритм рисования этой части окружности можно считать полностью описанным (Инициализация точки – установка точки в заданные координаты)
Растровая развёртка окружности Все оставшиеся части окружности строятся параллельно: после получения очередной (x’, y’) точки можно инициализировать еще семь точек с координатами (-x’, y’), (-x’, -y’), (y’, x’), (y’, -x’), (-y’, x’) Данный алгоритм называется ”Алгоритм Брезенхэма дискретизации окружности”
Растровая развёртка окружности (пример) void circle(int x 0, int y 0, int radius) { int x = 0; int y = radius; int delta = 2 - 2 * radius; int error = 0; while(y >= 0) { putpixel(x 0 + x, y 0 + y); putpixel(x 0 + x, y 0 - y); putpixel(x 0 - x, y 0 + y); putpixel(x 0 - x, y 0 - y); error = 2 * (delta + y) - 1; if( delta < 0 && error <= 0 ) { ++x; delta += 2 * x + 1; continue; } error = 2 * (delta - x) - 1; if( delta > 0 && error > 0 ) { --y; delta += 1 - 2 * y; continue; } ++x; delta += 2 * (x - y); --y; } }
Отсечение отрезка Необходимость отсечь выводимое изображение по границам некоторой области встречается довольно часто (например, можно отсечь часть изображения, выходящую за поле вывода, чтобы не тратить вычислительные ресурсы на её обработку). В простейших ситуациях в качестве такой области, как правило, выступает прямоугольник. Ниже рассматривается достаточно простой и эффективный алгоритм отсечения отрезков по границе произвольного прямоугольника. Он заключается в разбиении всей плоскости на 9 областей прямыми, образующими прямоугольник. В каждой из этих областей все точки по отношению к прямоугольнику расположены одинаково.
Отсечение отрезка Алгоритм Сазерленда – Кохена отсечения отрезка по прямоугольной области. Определив, в какие области попали концы рассматриваемого отрезка, легко понять, где именно необходимо отсечение. Для этого каждой области сообщается 4 -битовый код, где установленный бит 0, точка лежит левее прямоугольника, бит 1, точка лежит правее прямоугольника, бит 2, точка лежит ниже прямоугольника, бит 3, точка лежит выше прямоугольника.
Отсечение отрезка Приведенная ниже программа реализует алгоритм Сазерленда – Кохена отсечения отрезка по прямоугольной области. void Swap (int &a, int &b) { int c; c=a; a=b; b=c; } int Out. Code(int x, int y, int X 1, int Y 1, int X 2, int Y 2) { int code=0; if (x
Отсечение отрезка while( !outside && !inside ) { if (code 1==0) { Swap(x 1, x 2); Swap(y 1, y 2); Swap(code 1, code 2); } if(code 1 & 0 x 01) { y 1+=(long)(y 2 -y 1)*(X 1 -x 1)/(x 2 -x 1); x 1=X 1; } else if(code 1 & 0 x 02) { x 1+=(long)(x 2 -x 1)*(Y 1 -y 1)/(y 2 -y 1); y 1=Y 1; } else if(code 1 & 0 x 04) { y 1+=(long)(y 2 -y 1)*(X 2 -x 1)/(x 2 -x 1); x 1=X 2; } else if(code 1 & 0 x 08) { x 1+=(long)(x 2 -x 1)*(Y 2 -y 1)/(y 2 -y 1); y 2=Y 2; } code 1=Out. Code(x 1, y 1, X 1, Y 1, X 2, Y 2); code 2=Out. Code(x 2, y 2, X 1, Y 1, X 2, Y 2); inside = (code 1|code 2) == 0; outside = (code 1&code 2) != 0; } line (x 1, y 1, x 2, y 2); } Здесь и далее line – некоторая абстрактная функция, отображающая на экране линию по заданным координатам ее начала и конца.
Закраска области, заданной цветом границы Рассмотрим область, ограниченную набором пикселей заданного цвета, и точку (x, y), лежащую внутри этой области. Задача заполнения области заданным цветом в случае, когда область не является выпуклой, может оказаться довольно сложной.
Закраска области, заданной цветом границы void Pixel. Fill(int x, int y, int Border. Color, int color) { int c=getpixel(x, y); // получение цвета пикселя if((c!=Border. Color) && (c!=color)) { putpixel(x, y, color); Pixel. Fill(x-1, y, Border. Color, color); Pixel. Fill(x+1, y, Border. Color, color); Pixel. Fill(x, y-1, Border. Color, color); Pixel. Fill(x, y+1, Border. Color, color); } }
Закраска области, заданной цветом границы Простейший алгоритм, показанный выше, хотя и абсолютно корректно заполняющий даже самые сложные области, является слишком неэффективным, так как уже для отрисованного пикселя функция вызывается еще три раза, и, кроме того, требует слишком большого стека из-за большой глубины рекурсии.
Метод построчного сканирования Поэтому для решения задачи закраски области предпочтительнее алгоритмы, способные обрабатывать сразу целые группы пикселей. Рассмотрим версию одного из самых популярных алгоритмов подобного типа, когда для заданной точки (x, y) определяется и заполняется максимальный отрезок [x 1, xr], содержащий эту точку и лежащий внутри области. После этого в поисках еще не заполненных пикселей проверяются отрезки, лежащие выше и ниже. Если такие пиксели находятся, то функция рекурсивно вызывается для их обработки.
Закраска области, заданной цветом границы Этот алгоритм эффективно работает даже для областей с дырками.
Закраска области, заданной цветом границы int linefill(HDC hdc, int x, int y, int dir, int Prev. XL, int Prev. XR, COLORREF color, COLORREF brdcolor) { int xl=x; int xr=x; COLORREF c; do { c = Get. Pixel(hdc, --xl, y); } while( ( c != brdcolor && c!=color ) || xl < 0 ); do { c = Get. Pixel(hdc, ++xr, y); } while( c != brdcolor && c!=color ); xr--; xl++;
Закраска области, заданной цветом границы line(hdc, xl, y, xr, y, color); for( x = xl; x <= xr; x++ ) { c = Get. Pixel(hdc, x, y+dir); if( c != brdcolor && c != color ) x = linefill(hdc, x, y+dir, xl, xr, color, brdcolor); } for( x = xl; x < Prev. XL; x++ ) { c = Get. Pixel(hdc, x, y-dir); if( c != brdcolor && c != color ) x = linefill(hdc, x, y-dir, xl, xr, color, brdcolor); } Продолжение
Закраска области, заданной цветом границы for( x = Prev. XR; x < xr; x++ ) { c = Get. Pixel(hdc, x, y-dir); if( c != brdcolor && c != color ) x = linefill(hdc, x, y-dir, xl, xr, color, brdcolor); } } return xr; void fill(HDC hdc, int x, int y, COLORREF color, COLORREF brdcolor) { linefill(hdc, x, y, 1, x, x, color, brdcolor); } Продолжение
Дополнительные материалы http: //ru. wikipedia. org/wiki/%D 0%90%D 0%BB%D 0%B 3%D 0%BE%D 1%80%D 0%B 8%D 1%82%D 0%BC_%D 0%91%D 1%80%D 0%B 5%D 0%B 7%D 0%B 5%D 0%BD%D 1%85%D 1%8 D%D 0%BC%D 0%B 0 (Алгоритм Брезенхэма) http: //en. wikipedia. org/wiki/Bresenham%27 s_circle_algorithm (Алгоритм Брезенхэма окружность) http: //alglib. sources. ru/articles/2 dgraph. php (Алгоритмы растровой графики) http: //www. fvn 2009. narod. ru/Manuscripts/schedule 8. htm