title: "openGL实现图形学扫描线种子填充算法"

date: 2018-06-11T19:41:30+08:00

tags: ["图形学"]

categories: ["C++"]


先上效果图

白色的起始种子点

代码



#include <GL/glut.h>
#include <cmath>
#include <set>
#include <vector>
#include <unistd.h> //sleep函数
#include <iostream>
#include <algorithm> //find函数,查找vector中元素
#include <stack> using namespace std; //规格化为0.05的倍数
inline GLdouble normal(GLdouble x)
{
return (round(x * 20) / 20);
} typedef struct Point {
GLdouble x, y; Point(GLdouble a = 0, GLdouble b = 0)
{
x = a, y = b;
} // set会对插入的元素自动排序,需要重载运算符.定义排序规则
//
//
// 重载运算符的要求
// 1. 若A<B为真,则B<A为假
// 2. 若A<B,B<C --> A<C
// 3. A<A永远为假
// set中判断元素是否相等
// if(!(A<B||B<A)) --> A=B
bool operator<(const Point &a) const
{
return ((x - a.x) < -0.01 || ((x - a.x) < 0.01 && (y - a.y) < -0.01));
} bool operator==(const Point &a) const
{
return (abs(x - a.x) < 0.01 && abs(y - a.y) < 0.01);
}
} point; void drawGrid(); void initGraphBorder(); void drawGraphices(); void myDisplay(); void DDA(point A, point B); void initGraphBorder(); point first_seed;
unsigned long m;
static int n = 40;
static GLfloat pointSize = 12.5;
set<point> graphBorder; // 图形边界数组<set>可以快速查找
//set虽然可以快速查找,但却会打乱顺序,不方便实现填充动画
vector<point> graphFill; // 要填充的数组
vector<point> graphVertex; //存储图形定点数组,按顺序
stack<point> seed; //存储种子 GLdouble p = float(2.0 / n); // 每个格子的大小 //画网格坐标
void drawGrid()
{
glColor3d(1, 1, 1); //网格用白色表示
glLineWidth(1);
glBegin(GL_LINES); for (int i = 0; i <= n; i++) {
//画竖线
glVertex2d(-1 + 1.0 / n + i * 2.0 / n, -1);
glVertex2d(-1 + 1.0 / n + i * 2.0 / n, 1);
//画横线
glVertex2d(-1, -1 + 1.0 / n + i * 2.0 / n);
glVertex2d(1, -1 + 1.0 / n + i * 2.0 / n);
} glEnd();
} //DDA算法
void DDA(point A, point B)
{
cout << "连接(" << A.x << ',' << A.y << ") 到 (" << B.x << ',' << B.y << ")\n"; if (A.x > B.x) {
swap(A, B);
} A.x = normal(A.x);
A.y = normal(A.y);
B.x = normal(B.x);
B.y = normal(B.y);
double delta_x = B.x - A.x;
double delta_y = B.y - A.y;
double k = delta_y / delta_x;
double x = A.x, y = A.y; if (k > -1 && k < 1) {
//x是最大位移
//cout << k << endl;
while (true) {
if ((x - B.x) > 0.01)break; graphBorder.insert(point(normal(x), normal(y)));
x += p;
y += (p * k);
}
}
else if (k >= 1) {
//Y是最大位移
while (true) {
if ((y - B.y) > 0.01) break; graphBorder.insert(point(normal(x), normal(y)));
y += p;
x += (p / k);
}
}
else {
while (true) {
if ((B.y - y) > 0.01) break; graphBorder.insert(point(normal(x), normal(y)));
y -= p;
x -= (p / k);
}
}
} //初始化图形边界数组
void initGraphBorder()
{
// graphVertex存储图形的顶点
for (auto it = graphVertex.begin(); it != graphVertex.end(); it++) {
if (it == graphVertex.end() - 1) {
//最后一个点连接第一个点
DDA(*it, *graphVertex.begin());
}
else {
//连接it1和it2xr
DDA(*it, *(it + 1));
}
}
} /*********************************
* 1. 初始化:堆栈置空,将种子seed(x,y)入栈
* 2. 出栈: 若栈空则结束,否则将栈顶元素出栈,以y作为当前扫描线
* 3. 填充并确定新种子点所在区域:从当前种子点出发,沿y扫描线向左右方向填充
* 直到遇到边界像素。标记当前区段的左右端点坐标为Xl, Xr.
* 4. 确定新种子点:检查[Xl-1,Xr]和[Xl+1,Xr]区域,若存在非边界,未填充的
* 像素,则把每一区间的最右像素作为种子点压栈。返回第2步
**********************************/ // 沿扫描线的区域填充
// 从种子堆栈<stack>seed中取出种子点
// 把要填充的点加入<vector>graphFill
void floodFillSet()
{
while (!seed.empty()) {
point s = seed.top(); //当前种子点
seed.pop();
cout << "当前种子点 " << s.x << " " << s.y << endl;
//填充种子点及左右连续区域
point t = s, xl, xr; //填充左侧连续区域,并找到区域最左边界
while (true) {
//到达边界退出
if (t.x < -1)break;
graphFill.push_back(t);
t.x -= p;
//查找是否已填充
auto it1 = find(graphFill.begin(), graphFill.end(), t);
//查找是否是边界
auto it2 = graphBorder.find(t); if (it1 != graphFill.end() || it2 != graphBorder.end()) { //已填充或已达边界
xl = t;
xl.x += p;
break;
}
} t = s; //填充右侧区域
while (true) {
if (t.x > 1)break; graphFill.push_back(t);
t.x += p;
//查找是否已填充
auto it1 = find(graphFill.begin(), graphFill.end(), t);
//查找是否是边界
auto it2 = graphBorder.find(t); if (it1 != graphFill.end() || it2 != graphBorder.end()) { //已填充或已达边界
xr = t;
xr.x -= p;
break;
}
} //在下一行找种子点
for (int d : { 1, -1 }) {//先上再下
bool status = false; //标记一段空白区域的开始
t = xl;
t.y += (p * d); //移到下一行或上一行 while ((t.x - xr.x) < 0.07) {
auto it1 = find(graphFill.begin(), graphFill.end(), t);
auto it2 = graphBorder.find(t); //找空白区域开始点
//如果当前点是空白区域,且空白区域未开始 则s=true
if (!status && it1 == graphFill.end() && it2 == graphBorder.end()) {
status = true;
} //区域开始且遇到边界或填充区域
if (status && (it1 != graphFill.end() || it2 != graphBorder.end())) { //到达边界或已填充颜色,且左侧有空白区域
status = false;
//添加左侧一点为种子点
seed.push(point(t.x - p, t.y));
} // 到达最右区间xr,即便不是边界,只要s=true也应标注最右种子点
if (status && abs(t.x - xr.x) < 0.01) { //注意浮点数比较大小
status = false;
seed.push(t);
} t.x += p;
}
}
}
} //画待填充的图形边界
void drawGraphices()
{
glColor3f(0.5, 0.5, 0.2);
glBegin(GL_POINTS); //C++11的新特性,可以遍历vector map list or {1,2,3}
for (auto it : graphBorder) {
glVertex2d(it.x, it.y);
} glEnd();
} //画填充像素
void drawFloodFill(unsigned long m)
{
glColor3f(0.4, 0.8, 0.1);
glBegin(GL_POINTS); for (unsigned long i = 0; i < m && i < graphFill.size(); i++) {
glVertex2d(graphFill[i].x, graphFill[i].y);
//myDisplay();
} glEnd();
} //绘图
void myDisplay()
{
glClearColor(0.1f, 0.3f, 0.33f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画图形边界
drawGraphices();
//画m个填充像素
drawFloodFill(m);
glColor3d(1, 0, 0);
glPointSize(pointSize);
//加个白点,起始种子点
glColor3d(1, 1, 1);
glBegin(GL_POINTS);
glVertex2d(first_seed.x, first_seed.y);
glEnd();
//画网格
drawGrid();
glFlush();
} int main(int argc, char *argv[])
{
//存入要填充的图形顶点 8
//(-0.7,0.6) (0.7,0.6) (0.7,0) (0.3,0) (0.3,-0.5) (-0.3,-0.5)
// (-0.3,0) (-0.7,0)
cout << "请输入顶点坐标,(-1,1) 输入>1 结束 \n";
double x, y; while (cin >> x) {
if (x > 1)
break;
else
cin >> y; graphVertex.emplace_back(point(x, y));
} cout << "输入初始种子点\n";
cin >> x >> y;
first_seed.x = x;
first_seed.y = y;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
//初始化边界集
initGraphBorder();
//扫描线种子填充
seed.push(first_seed);
floodFillSet();
glutCreateWindow("扫描线种子填充");
glutDisplayFunc(&myDisplay); for (m = 0; m < graphFill.size(); m++) {
usleep(10000);
myDisplay();
} glutMainLoop();
return 0;
}

输入样例

-0.7 0.7
-0.35 0.7
-0.35 0
0.35 0
0.35 0.7
0.7 0.7
0.7 -0.7
-0.7 -0.7
2
0 -0.2

openGL实现图形学扫描线种子填充算法的更多相关文章

  1. CGA填充算法之种子填充算法

    CGA填充算法之种子填充算法 平面区域填充算法是计算机图形学领域的一个很重要的算法,区域填充即给出一个区域的边界 (也可以是没有边界,只是给出指定颜色),要求将边界范围内的所有象素单元都修改成指定的颜 ...

  2. 种子填充算法描述及C++代码实现

    项目需要看了种子填充算法,改进了算法主要去除面积小的部分.种子填充算法分为两种,简单的和基于扫描线的方法,简单的算法如下描述(笔者针对的是二值图像): (1)从上到下,从左到有,依次扫描每个像素: ( ...

  3. JAVA实现种子填充算法

    种子填充算法原理在网上很多地方都能找到,这篇是继上篇扫描线算法后另一种填充算法,直接上实现代码啦0.0 我的实现只是实现了种子填充算法,但是运行效率不快,如果大佬有改进方法,欢迎和我交流,谢谢! 最后 ...

  4. Atitit Seed-Filling种子填充算法attilax总结

    Atitit Seed-Filling种子填充算法attilax总结 种子填充的原理,4联通与8联通区域的选择.. 三个队列 waitProcessPixList tempPixList Proces ...

  5. UVA 572 -- Oil Deposits(DFS求连通块+种子填充算法)

    UVA 572 -- Oil Deposits(DFS求连通块) 图也有DFS和BFS遍历,由于DFS更好写,所以一般用DFS寻找连通块. 下述代码用一个二重循环来找到当前格子的相邻8个格子,也可用常 ...

  6. Open gl 的不规则图形的4联通种子递归填充和扫描线种子递归填充算法实现

    实验题目:不规则区域的填充算法 实验目的:验证不规则区域的填充算法 实验内容:利用VC与OpenGL,实现不规则区域的填充算法. 1.必做:实现简单递归的不规则区域填充算法. 2.选做:针对简单递归算 ...

  7. [OpenGL] 不规则区域的填充算法

    不规则区域的填充算法 一.简单递归 利用Dfs实现简单递归填充. 核心代码: // 简单深度搜索填充 (四连通) void DfsFill(int x, int y) { || y < || x ...

  8. 图形学入门(3)——区域填充算法(region filling)

    继续图形学之旅,我们已经解决了如何画线和画圆的问题,接下来要解决的是,如何往一个区域内填充颜色?对一个像素填充颜色只需调用SetPixel之类的函数就行了,所以这个问题其实就是:如何找到一个区域内的所 ...

  9. [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)

    上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...

随机推荐

  1. datatables隐藏列与createdRow渲染bootstrapSwitch形成的BUG

    背景: 昨天写了一个页面用于规则库的增删改查. 数据使用datatables渲染,后端返回数据由前端进行一次性渲染和分页. 隐藏列: 排序的ID不展示,但是排序又想按照ID来排,所以把ID单独作为一列 ...

  2. Java-正则使用

    Java-正则使用 注意 在Java中由于string的设计,导致斜杠是特殊的字符,所以如若想要在正则中使用斜杠,则需要两个斜杠来表示 eg: \d 需要写成: \\d ,两外 \\\\ 表示匹配单个 ...

  3. [BZOJ1984][Luogu4315]月下“毛景树”

    题目大意 给出一棵 n 个点的无根树,待边权,要求维护一下操作: 修改某条边的边权 修改点 u 到点 v 路径上所有边的边权 点 u 到点 v 路径上所有边的边权加上某个值 查询点 u 到点 v 路径 ...

  4. 【洛谷】SAC E#1 Factorial

    别人可以眼杀我却研究了一个小时看了题解才懂的数学题 输入: n, k 输出: n!在k进制下后缀0的个数 n,k <= 10^12 将 n! 表示成 x×2y5z 的形式,其中 x mod 2 ...

  5. php的imagick模块扩展

    imagick模块介绍       ImageMagick是一个用于查看.编辑位图文件以及进行图像格式转换的开放源代码软件套装.它可以读取.编辑超过100种图象格式,可用来替换GD库. 安装 在加载模 ...

  6. [LeetCode] Maximum Depth of N-ary Tree N叉树的最大深度

    Given a n-ary tree, find its maximum depth. The maximum depth is the number of nodes along the longe ...

  7. NIOS_UART

    1.Fifoed avalon UART带缓冲区,使用非常方便,google下载,google上有的技术资料,百度上找不到,以为没有这个事情: 2.两种UART如果想用操作寄存器的方式操作,需要在al ...

  8. ldd可执行程序时返回not a dynamic executable

    原因:32位程序放在64位机器上或64位程序放在32位程序上了 解决方法:如果是32位程序放在64位系统上则安装yum -y install libstdc++.i686,64位则是yum -y in ...

  9. 对不可描述的软件安装sfbo插件

    0 后来...突然有一天,我就需要sfbo了. 1 安装 yum search "不可描述插件"是空的,只能用源码安装. 官方信息可以链接到这里,obfs. 1.1 编译安装 gi ...

  10. [dev][ipsec] netlink是什么

    介绍: https://www.linuxjournal.com/article/7356 大纲: man手册 http://man7.org/linux/man-pages/man7/netlink ...