由于做调研报告和过年,耽搁了一段时间,赶紧补上。惭愧惭愧。
对于一般多边形,对一条扫描线,可以分为四个步骤:
1. 求交。求扫描线与多边形各边的交点。
2. 排序。交点按递增的顺序排序。
3. 交点配对。将各个交点配对,每对交点代表一个相交区间。
4. 区间填色。
对于扫描线与顶点相交的情况,当共享交点的两条边在扫描线的两边时,交点只算一个。当共享交点的两条边在扫描线的同一边时,交点作为0个或2个。在此规定,两顶点均高于扫描线时,取2个交点,顶点需要填充;两顶点均低于扫描线时,取0个交点,顶点不填充。
对于边界像素,规定落在右/上边界的像素不予填充,落在左下边界的像素予以填充。
即保证下闭上开,左闭右开。
再对上述四个步骤进行讨论。
求交点,活性边表的效率较高,可以利用上一条扫描线的活性边表构造下一条扫描线的活性边表。设边的直线方程为:
main.cpp
#include
#include
#include
#include"Polygon.h"
#include
#include"EdgeTable.h"
#define width 1000
#define height 500
using namespace cv;
using namespace std;
void BubbleSort(vector & row);
void InsertionSort(vector & row, vector & newrow);
void DrawRow(Mat& m, vector & row, int y);
void RefreshRow(vector<vector >& table, int y);
int main(){
Mat imageROI = Mat(height, width, CV_8UC3, Scalar(255, 255, 255));
int n,i,j;
cout << "Please input the number of edges" << endl;
cin >> n;
Polygon Poly(n);
cout << "Please input the coordinate of the vertexes" << endl;
Poly.Initialize();//definition of the polygon
EdgeTable NET(height+1);//initialization of new edge table
for (i = 0; i < Poly.PointTable.size(); i++){
if (i + 1 < Poly.PointTable.size())
j = i + 1;
else
j = 0;//to get the index of the points, and form a line
double x0 = Poly.PointTable[i].GetX();
double x1 = Poly.PointTable[j].GetX();
int y0 = Poly.PointTable[i].GetY();
int y1 = Poly.PointTable[j].GetY();
int ymin = min(y0, y1);
int ymax = max(y0, y1);
double x;//x is the x coordinate of the ymin!
if (ymin == y0){
x = x0;
}
else{
x = x1;
}
double deltax = double(-(x1 - x0)) / (y0 - y1);
NET.table[ymin].push_back({ x, deltax, ymax });
}//put edges of ymin=i into new edge table
int y = 0;//y=min scanner
EdgeTable AET(height+1);//initialization of active edge table
for (i = 0; i < height; i++){
BubbleSort(AET.table[i]);//sort the active table
InsertionSort(AET.table[i], NET.table[i]);//insert new edge into the active table, and sort
DrawRow(imageROI,AET.table[i],i);//draw the line
RefreshRow(AET.table,i);//erase nodes that y=ymax, and insert the row into next line
}
namedWindow("显示结果");
imshow("显示结果", imageROI);
waitKey();
}
void BubbleSort(vector & row){
EdgeNode tmp;
int i,j;
for (i = row.size() - 1; i >= 0; i--){
for (j = 0; j < i; j++){
if (row[j].x > row[j+1].x){
tmp = row[j];
row[j] = row[j+1];
row[j+1] = tmp;
}
}
}
return;
}
void InsertionSort(vector & row, vector & newrow){
int i, j;
EdgeNode tmp;
i = row.size();
row.insert(row.end(), newrow.begin(), newrow.end());
for (; i for (j = i; j > 0&&tmp.x1].x; j--){
row[j] = row[j-1];
}
row[j] = tmp;
}
}
void DrawRow(Mat& m,vector & row,int y){
bool NeedFill = false;
int x1 = 0;
int x2 = 0;
vector ::iterator iter;
for (iter = row.begin(); iter != row.end(); iter++){
x1 = x2;
x2 = (*iter).x;
if (NeedFill == true){
for (int x = x1; x <= x2; x++){
m.at(y, x) = Vec3b(0, 0, 0);
}
}
NeedFill = !NeedFill;
}
}
void RefreshRow(vector<vector >& table, int y){
vector ::iterator iter;
vector row;
EdgeNode tmp;
if (y <= height){
row = table[y];
for (iter = row.begin(); iter != row.end();){
if ((*iter).ymax == y){
iter=row.erase(iter);
}
else{
(*iter).x += (*iter).deltax;
iter++;//the travesal method of vector when erase() used
}
}
table[y + 1] = row;
return;
}
}
EdgeTable.h
#pragma once
#include
struct EdgeNode{
double x;
double deltax;
int ymax;
};
class EdgeTable
{
public:
EdgeTable(int n);
~EdgeTable();
std::vector<std::vector > table;
};
EdgeTable.cpp
#include "EdgeTable.h"
EdgeTable::EdgeTable(int n)
{
table = std::vector<std::vector >(n);
}
EdgeTable::~EdgeTable()
{
}
Polygon.h
#pragma once
#include
#include
#include
class MyPoint{
public:
MyPoint() :x(0), y(0){};
MyPoint(int a, int b) :x(a), y(b){};
void SetX(int X){ x = X; };
void SetY(int Y){ y = Y; };
int GetX(){ return x; };
int GetY(){ return y; };
private:
int x;
int y;
};
class Polygon
{
public:
Polygon(int n) :n(n){};
~Polygon(){};
void Initialize();
std::vector PointTable;
private:
int n;
};
Polygon.cpp
#include "Polygon.h"
#include
void Polygon::Initialize(){
int i, x, y;
for (i = 0; i < n; i++){
std::cin >> x >> y;
PointTable.push_back(MyPoint(x, y));
}
}
结果:
写得还是有点费劲。主要问题在于vector的使用还不熟悉,一开始甚至想自己写链表。其他如传引用还是拷贝构造,遍历时有erase怎么办,都花了一些时间。算法中新边表要加入ymin的x值也疏忽了。加油加油。
边填充算法的理论比较简单,没有使用特殊的数据结构,但在实现时出了一些不好解决的问题。
边标志算法分为两步,第一步对多边形的每条边进行直线扫描转换,即对多边形的每条边打上边标志。
第二步则进行填充。对每条与多边形相交的扫描线,从左往右进行扫描,若点在多边形内,置inside为ture,若在多边形外则置false。每次遇到边标志时,令inside=!inside。最后,当inside为真时,则将像素置为多边形色。
#include
#include
#include
#include
#include"Polygon.h"
#define width 1000
#define height 500
using namespace std;
using namespace cv;
void DDAline(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v);
int main(){
Mat imageROI = Mat(height, width, CV_8UC3, Scalar(255, 255, 255));
int n, i, j;
cout << "Please input the number of edges" << endl;
cin >> n;
Polygon Poly(n);
cout << "Please input the coordinate of the vertexes" << endl;
Poly.Initialize();//definition of the polygon
int x0, x1, y0, y1;
for (i = 0; i < Poly.PointTable.size(); i++){
if (i + 1 < Poly.PointTable.size()){
j = i + 1;
}
else{
j = 0;
}
x0 = Poly.PointTable[i].GetX();
y0 = Poly.PointTable[i].GetY();
x1 = Poly.PointTable[j].GetX();
y1 = Poly.PointTable[j].GetY();
if (x0 > x1){
swap(x0, x1);
swap(y0, y1);
}
DDAline(imageROI, x0, y0, x1, y1, Vec3b(0, 0, 0));
}
bool inside;
int x,y;
for (y = 0; y < height; y++){
int count = 0;
inside = false;
for (x = 0; x < width-1; x++){
if (imageROI.at(y, x) == Vec3b(0, 0, 0) && imageROI.at(y, x+1) != Vec3b(0, 0, 0)){
inside = !inside;
}
if (inside != false){
imageROI.at(y, x) = Vec3b(0, 0, 0);
}
}
}
namedWindow("显示结果");
imshow("显示结果", imageROI);
waitKey();
}
由图可见,算法实现的结果存在一定问题。
在顶点处,由于像素点只有一个,inside直接取负了,但还可通过对查找顶点、讨论边的另一个顶点位置等方法解决。
图上的(0,0)至(200,100)的边由直线扫描算法,每行均有两个像素点被置为黑色,故算法中加了一步,相邻的两个黑色像素点视为一个边标志。
但又出现了问题。如在y=1这行,两条边的边标志是相邻的,又被视为了一个边标志,造成整行都是黑的。
故个人认为该算法只是比较适合硬件实现,在这种结果中并不太适用。
有点深度优先算法的意思,但是只有上下两个方向作为分支,左右则直接处理了,一定程度上减少了搜索的深度。
初始任意取一个种子元素,当栈非空时进行以下四步
+ 栈顶像素出栈
+ 对包含该像素的左右区间进行填充
+ 对最左、最右像素标记为xl和xr
+ 对上下两个区间进行扫描,如果存在未填充像素,则将区间的最右端元素入栈
#include
#include
#include
#include
#include
#include"Polygon.h"
#define width 1000
#define height 500
using namespace std;
using namespace cv;
void DDAline(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v);
int main(){
Mat imageROI = Mat(height, width, CV_8UC3, Scalar(255, 255, 255));
int n, i, j;
cout << "Please input the number of edges" << endl;
cin >> n;
Polygon Poly(n);
cout << "Please input the coordinates of the vertexes" << endl;
Poly.Initialize();//definition of the polygon
int x0, x1, y0, y1;
for (i = 0; i < Poly.PointTable.size(); i++){
if (i + 1 < Poly.PointTable.size()){
j = i + 1;
}
else{
j = 0;
}
x0 = Poly.PointTable[i].GetX();
y0 = Poly.PointTable[i].GetY();
x1 = Poly.PointTable[j].GetX();
y1 = Poly.PointTable[j].GetY();
if (x0 > x1){
swap(x0, x1);
swap(y0, y1);
}
DDAline(imageROI, x0, y0, x1, y1, Vec3b(0, 0, 0));
}
//种子填充算法
int px, py;
cout << "input seed coordinate" << endl;
cin >> px >> py;
MyPoint seed = MyPoint(px, py);
stack pixelStack;
MyPoint tmp;
int x,y, xl, xr,xnextspan;
bool flag;
pixelStack.push(seed);//push the seed pixel
while (!pixelStack.empty()){
tmp = pixelStack.top();
pixelStack.pop();
x = tmp.GetX();
y = tmp.GetY();
if (imageROI.at(y, x) == Vec3b(0, 0, 0)){
continue;
}
imageROI.at(y, x) = Vec3b(0, 0, 0);
for (x0 = x + 1; imageROI.at(y, x0) != Vec3b(0, 0, 0); x0++){
imageROI.at(y, x0) = Vec3b(0, 0, 0);
}
xr = x0 - 1;//fill right pixels
for (x0 = x - 1; imageROI.at(y, x0) != Vec3b(0, 0, 0); x0--){
imageROI.at(y, x0) = Vec3b(0, 0, 0);
}
xl = x0 + 1;//fill left pixels
x0 = xl;
y = y + 1;
while (x0 <= xr){
flag = false;
while (imageROI.at(y, x0) != Vec3b(0, 0, 0)&&x0if (flag == false){
flag = true;
}
x0++;
}
if (flag == true){
if (x0 == xr&&imageROI.at(y, x0) != Vec3b(0, 0, 0)){
pixelStack.push(MyPoint(x0, y));
}
else{
pixelStack.push(MyPoint(x0 - 1, y));
}
flag = false;
}
xnextspan = x0;
while (imageROI.at(y, x0) == Vec3b(0, 0, 0) && x0 <= xr){
x0++;
}
if (xnextspan == x0)
x0++;
}//fill y=y+1
x0 = xl;
y = y - 2;
while (x0 <= xr){
flag = false;
while (imageROI.at(y, x0) != Vec3b(0, 0, 0)&&x0if (flag == false){
flag = true;
}
x0++;
}
if (flag == true){
if (x0 == xr&&imageROI.at(y, x0) != Vec3b(0, 0, 0)){
pixelStack.push(MyPoint(x0, y));
}
else{
pixelStack.push(MyPoint(x0 - 1, y));
}
flag = false;
}
xnextspan = x0;
while (imageROI.at(y, x0) == Vec3b(0, 0, 0) && x0 <= xr){
x0++;
}
if (xnextspan == x0)
x0++;
}//fill y=y-1
}
namedWindow("显示结果");
imshow("显示结果", imageROI);
waitKey();
}