zoukankan      html  css  js  c++  java
  • 实域填充算法

    一、综述

    两种实域填充方法:有序边表和种子算法。

    在MFC环境中实现上述两种算法绘制凹凸多边形并从计算效率上,对两种算法进行比较。

    二、程序框架

    MFC程序
    - cgDrawFillpolyView.h为视图层的头文件,负责声明各种成员变量和成员函数;
    - cgDrawFillpolyView.cpp为视图层的源文件,负责实现并规划调用填充算法、计算时间求值。
    - CSelectControl.h为窗口面板中的按键及文本定义成员变量及成员函数。
    - CSelectControl.cpp实现面板的功能,如点击按键绘制填充多边形、显示计算时长等。

    三、算法描述

    1. 有序边表算法

    基本原理:用水平扫描线从上到下(或从下到上)扫描由多条首尾相连的线段构成的多边形,每根扫描线与多边形的某些边产生一系列交点。将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜色画水平直线。

    (1)在实现中首先需要对多边形的每一条边进行处理操作,因为是采用水平线扫描填充,所以对于水平线不做处理。对其余边根据端点的y值大小进行排序,保存yMax,yMin,Xa(相对应的x坐标)以及Dx(斜率的倒数)。

    (2)在进行扫描的过程中,很重要的一部分操作便是求交边集指针的移动。初始状态位0,在扫描线开始向下移动后,调用Include()函数,检查是否有边进入扫描线交集(即判断所有y最大值大于扫描线当前y值的边线),此时将m_End++,即尾指针向后移动。在Include()函数中,也会调整起始点位置,将Dx调整为位移量。

    (3)之后调用UpdateXvalue()函数,判断是否有边退出求交边集。如果没有边退出,则移动x,并根据x值大小进行排序。有边退出,更新数组,删除该边,m_Begin++,即头指针向后移动。

    (4)调用pFillScan(pDC)函数,进行填充。

    (5)m_Scan--,回到第二步进行循环,直到m_Begin==m_End。

    2. 种子算法

    (1)初始化:堆栈置空。将种子点(x,y)入栈。

    (2)出栈:若栈空则结束。否则取栈顶元素(x,y),以y作为当前扫描线。

    (3)填充并确定种子点所在区段:从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xl和xr。

    (4)并确定新的种子点:在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第(2)步。

    四、处理流程

    主要处理流程为:

    在cgDrawFillpolyView.h中定义

    void Fillpolygon(int pNumbers, CPoint *points, CDC *pDC);

    void ScanLineFill(int x, int y, COLORREF color, CDC* pDC);等

    在cgDrawFillpolyView.cpp中完成初始赋值、函数具体实现,函数计算时间

    在IDD_SELECTcONTROL中新添加button且添加类向导,添加staticText

    通过CSelectControl完成两个填充函数调用,计算用时的展示

    五、运行结果

    1. 当点击“有序边表”绘图

    img

    1. 当点击“种子”绘图

    img

    1. 当控制所绘图形相同或相似时,通过时间了解计算效率

    img

    六、实验总结

    1. debug有序边表填充 j误写成i

    img

    通过本次实验,首先我对VS的MFC有了一个初步了解与掌握

    1. 对于区域填充、图像填充等算法,有了更深刻的认识,也有提高手速,对mfc功能进一步熟练掌握。
    2. 难点:
    • 难以调用种子填充,不知如何取出一个多边形内部点,考虑到多边形形状不规律性,且不希望用端点实现,最后处理如下:

      img

    • 最初想用边界颜色来作为种子填充的判别条件之一,但是由于对setROP2(2)了解不足,很难取到边界颜色,改用不利用边界颜色判定方法,耗时不短,后来意识到非常容易直接取最后双击点颜色即可。

    • 计算效率比对,想利用同一个多边形轮廓,不同填充方法,进行更直观的展示,多方研究未能实现,只能先选择方法再绘制相似图形统计计算时间

    收获良多,谢谢。

    核心代码:

    扫描线种子填充:

    int SetRP(int x, int y, COLORREF newColor, COLORREF oldColor, CDC *pDC) {
    
    ​    while (pDC->GetPixel(CPoint(x, y)) == oldColor) {
    ​       pDC->SetPixel(x, y, newColor);
    ​       x++;
    ​    }
    
    ​    return x - 1;
    
    }
    
    int SetLP(int x, int y, COLORREF newColor, COLORREF oldColor, CDC *pDC) {
    
    ​    while (pDC->GetPixel(CPoint(x - 1, y)) == oldColor) {
    ​       --x;
    ​       pDC->SetPixel(x, y, newColor);
    ​    }
    
    ​    return x + 1;
    
    }
    
    
    void NewLineSeed(stack<CPoint> *p, int xl, int xr, int y, COLORREF newColor, COLORREF oldColor, CDC *pDC) {
    
    ​    int x, e;
    
    ​    for (x = xl + 1, e = xr + 1; x < e; x++) {
    ​       //找出每一个区间的最右像素,入栈
    
    ​       if (pDC->GetPixel(CPoint(x, y)) != oldColor) {
    ​           if (pDC->GetPixel(CPoint(x - 1, y)) == oldColor)
    ​              p->push(CPoint(x - 1, y));
    ​       }
    
    ​    }
    
    ​    //把rx所在点入栈
    ​    if (pDC->GetPixel(CPoint(x - 1, y)) == oldColor)
    
    ​       p->push(CPoint(x - 1, y));
    
    }
    
    void Ccg2020YBHFillpolyView::ScanLineFill(int x, int y, COLORREF color, CDC *pDC)
    
    {
    
    ​    int pRight, pLeft;
    ​    stack<CPoint> p;
    ​    int oldColor = pDC->GetPixel(x, y);  //给定种子
    
    ​    p.push(CPoint(x, y));
    
    ​    while (!p.empty()) {
    ​       CPoint p1 = p.top();  //栈顶像素出栈
    ​       p.pop();
    
    ​       pRight = SetRP(p1.x, p1.y, color, oldColor, pDC);  //向左向右进行填充
    ​       pLeft = SetLP(p1.x, p1.y, color, oldColor, pDC);
    
    ​       //上下两条扫描线处理
    ​       NewLineSeed(&p, pLeft, pRight, p1.y + 1, color, oldColor, pDC);
    ​       NewLineSeed(&p, pLeft, pRight, p1.y - 1, color, oldColor, pDC);
    
    ​    }
    
    }
    

    有序边表法填充:

    void Ccg2020YBHFillpolyView::Fillpolygon(int pNumbers, CPoint *points, CDC *pDC) {
    
    ​    m_edgeNumbers = 0;
    ​    pLoadPolygon(pNumbers, points);  // Polygon Loading, calculates every edge's m_yMax[],m_yMin[],m_Xa[],m_Dx[]
    ​                                 //求交边集范围,因为数组已经根据y值大小进行边的排序,所以end向后移动即代表有边进入,start向后移动,代表有边退出
    ​    m_Begin = m_End = 0;
    ​    m_Scan = (int)m_yMax[0];     //从顶向下扫描
    
    ​    pInclude();            //检查是否有边进入扫描线
    ​    pUpdateXvalue();          //检查是否有边退出扫描线
    
    ​    while (m_Begin != m_End) {
    ​       pFillScan(pDC);
    ​       m_Scan--;
    ​       pInclude();
    ​       pUpdateXvalue();
    
    ​    }
    
    }
    
    void Ccg2020YBHFillpolyView::pLoadPolygon(int pNumbers, CPoint *points) {
    
    ​    float x1, y1, x2, y2;
    ​    x1 = points[0].x;  y1 = points[0].y + 0.5;
    
    ​    for (int i = 1; i < pNumbers; i++) {
    
    ​       x2 = points[i].x;  y2 = points[i].y + 0.5;
    
    ​       if (abs(int(y2 - y1)) >= 0) //水平线不做处理
    ​       {
    ​           pInsertLine(x1, y1, x2, y2);
    ​           x1 = x2;   
    ​           y1 = y2;
    ​       }
    
    ​       else
    ​           x2 = x1;
    ​    }
    
    }
    
    void Ccg2020YBHFillpolyView::pInsertLine(float x1, float y1, float x2, float y2) {
    ​    int i;
    ​    float Ymax, Ymin;
    
    ​    Ymax = (y2 > y1) ? y2 : y1;
    ​    Ymin = (y2 < y1) ? y2 : y1;
    ​    i = m_edgeNumbers;
    
    ​    //根据y值的大小,进行排序插入,大的在前面
    ​    while (i != 0 && m_yMax[i - 1] < Ymax) {
    
    ​       m_yMax[i] = m_yMax[i - 1];
    ​       m_yMin[i] = m_yMin[i - 1];
    ​       m_Xa[i] = m_Xa[i - 1];
    ​       m_Dx[i] = m_Dx[i - 1];
    ​       i--;
    
    ​    }
    
    ​    m_yMax[i] = Ymax;
    ​    m_yMin[i] = Ymin;
    
    ​    if (y2 > y1) m_Xa[i] = x2;    //根据y大小确定Xa的值,y大的会先于扫描线相交
    ​    else     m_Xa[i] = x1;
    
    ​    m_Dx[i] = (x2 - x1) / (y2 - y1);   //斜率的倒数
    ​    m_edgeNumbers++;
    
    }
    
    void Ccg2020YBHFillpolyView::pInclude() {
    
    ​    //end向后移动,找出所有边最高点y值大于当前扫描线的边,看是否有新的边进入交集
    ​    while (m_End < m_edgeNumbers && m_yMax[m_End] >= m_Scan) {
    
    ​       //有边进入,调整起始点位置,然后将Dx调整为位移量
    ​       m_Xa[m_End] = m_Xa[m_End] + (-0.5) * m_Dx[m_End];
    ​       m_Dx[m_End] = -m_Dx[m_End];
    ​       m_End++;
    
    ​    }
    
    }
    
    void Ccg2020YBHFillpolyView::pUpdateXvalue() {
    
    ​    int i, start = m_Begin;
    
    ​    for (i = start; i < m_End; i++) {
    
    ​       if (m_Scan > m_yMin[i]) {
    ​           //当前边没有退出,则移动x,然后在进行排序
    ​           m_Xa[i] += m_Dx[i];
    ​           pXsort(m_Begin, i);
    
    ​       }
    ​       else {
    
    ​           //有边退出,更新数组,然后begin++
    ​           for (int j = i; j > m_Begin; j--) {
    
    ​              m_yMin[j] = m_yMin[j - 1];
    ​              m_Xa[j] = m_Xa[j - 1];
    ​              m_Dx[j] = m_Dx[j - 1];
    
    ​           }
    
    ​           m_Begin++;
    
    ​       }
    
    ​    }
    
    }
    
    void Ccg2020YBHFillpolyView::pXsort(int Begin, int i) {
    
    ​    float temp;
    
    ​    while (i > Begin&&m_Xa[i] < m_Xa[i - 1])
    
    ​    {
    
    ​       temp = m_Xa[i]; m_Xa[i] = m_Xa[i - 1]; m_Xa[i - 1] = temp;
    ​       temp = m_Dx[i]; m_Dx[i] = m_Dx[i - 1]; m_Dx[i - 1] = temp;
    ​       temp = m_yMin[i]; m_yMin[i] = m_yMin[i - 1]; m_yMin[i - 1] = temp;
    ​       i--;
    
    ​    }
    
    }
    
    void Ccg2020YBHFillpolyView::pFillScan(CDC *pDC) {
    
    ​    //   int x, y;
    ​    pDC->SetROP2(2);
    
    ​    for (int i = m_Begin; i < m_End; i += 2)
    ​    {
    
    ​       pDC->MoveTo(m_Xa[i], m_Scan);
    ​       pDC->LineTo(m_Xa[i + 1], m_Scan);
    
    ​       /*y = m_Scan;
    ​       for (int x = m_Xa[i]; x<m_Xa[i + 1]; x++)
    ​           if (m_patternData[y % 7][x % 8])
    ​              pDC->SetPixel(x, y, RGB(255, 0, 0));*/
    ​    }
    
    }
    
  • 相关阅读:
    送走2015,迎来2016
    Android-Volley网络通信框架(StringRequest &amp; JsonObjectRequest)
    Mac上配置 Ruby on Rails和Git
    学习Javascript闭包(Closure)
    cocos2d-x 学习资源整理(持续更新...)
    android自己定义刷新类控件
    awk条件语句
    Leetcode 236 Lowest Common Ancestor of a Binary Tree
    Linux查看当前正在执行的进程
    Thinking in UML 学习笔记(三)——UML核心视图之类图
  • 原文地址:https://www.cnblogs.com/gu-qiu/p/14053069.html
Copyright © 2011-2022 走看看