您的位置:首页 > 其它

直线的扫描转换算法

2018-01-31 19:37 190 查看
这是第一篇博客,顺便深入学习一下markdown语法。

之后的图形学文章都使用C++实现。

目录

目录

数值微分法
算法理论

算法实现

中点画线法
算法理论

算法实现

Bresenham画线算法
算法理论

算法实现

数值微分法

数值微分法通过计算直线的斜率,计算每个x对应的y坐标,并取像素x,round(y)。本方法直观可行,但执行效率低。由于每步都需要进行浮点乘法运算与舍入运算,故需要增量的方法。

算法理论

首先计算直线的斜率:

k=Δy/Δxk
4000
=Δy/Δx接着推导增量方式计算y坐标:

yi+1=kxi+1+B=k(xi+Δx)+B=kxi+kΔx+B=yi+kΔx(1)(1)yi+1=kxi+1+B=k(xi+Δx)+B=kxi+kΔx+B=yi+kΔx由于Δx=1Δx=1,所以每次yy递增kk。

算法实现

利用openCV+Cpp进行实现。考虑了|k|∈[0,1]与|k|∉[0,1]|k|∈[0,1]与|k|∉[0,1]两种情形

#include<iostream>
#include<cmath>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

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(500, 1000, CV_8UC3, Scalar(255, 255, 255));
DDAline(imageROI, 0, 0,400, 200, Vec3b(0, 0, 0));
namedWindow("显示结果");
imshow("显示结果", imageROI);
waitKey();
}

void DDAline(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v){
double k = double(y1 - y0) / (x1 - x0);
if (k >= -1 && k <= 1){
double y = y0;
int x = x0;
for (x = x0; x <= x1; x++){
m.at<Vec3b>(int(y + 0.5),x ) = v;
y += k;
}
}
else{
double x = x0;
int y = y0;
k = 1 / k;
for (y = y0; y <= y1; y++){
m.at<Vec3b>(y,int(x+0.5)) = v;
x += k;
}
}
}




这里注意opencv中at(i0,i1)函数是操作第i0行第i1列,所以需要把xy反过来。

总体还是比较简单。cv配置、函数搞的比较麻烦。opencv还需多多熟练。

中点画线法

算法理论

首先假定k∈[0,1]k∈[0,1]之间。

根据(x0,y0)(x1,y1)(x0,y0)(x1,y1)两点坐标可以得到直线方程:

F(x,y)=ax+by+cF(x,y)=ax+by+c其中,a=y0−y1,b=x1−x0,c=x0y1−x1y0a=y0−y1,b=x1−x0,c=x0y1−x1y0

当F(x,y)>0F(x,y)>0时,点在直线上方。当确定P点位置后,下一个像素点处的中点M的判别式为:F(M)=F(x+1,y+0.5)=F(x,y)+a+0.5bF(M)=F(x+1,y+0.5)=F(x,y)+a+0.5b此时判断符号。若M点在直线上方,则选M下方的像素点。那么下一个需要判断的中点的判别式为:

F(xp+2,yp+0.5)=F(M)+aF(xp+2,yp+0.5)=F(M)+a如果M点在直线下方,则选上方的像素点,下一个中点的判别式为

F(xp+2,yp+1.5)=F(M)+a+bF(xp+2,yp+1.5)=F(M)+a+b而F(M)的初始值:

F(M0)=F(x0,y0)+a+0.5b=a+0.5bF(M0)=F(x0,y0)+a+0.5b=a+0.5b

算法实现

void MidpointLine(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v){
int a = y0 - y1;
int b = x1 - x0;
int d0 = 2*a + b;
int delta1 = 2*a;
int delta2 = 2*(a + b);
int x, y, d;
for (x = x0, y = y0, d = d0; x <= x1; x++){
if (d < 0){
y++;
d += delta2;
}
else{
d += delta1;
}
m.at<Vec3b>(y, x) = v;
}
}


由于把d的判别全部乘2了,可以避免小数的运算。斜率在其他范围内只要换换前后两点的顺序、换换x,y,就可以解决。

Bresenham画线算法

算法理论

还是首先假定k∈[0,1]k∈[0,1]。

假设x列的像素已确定,那么下一个像素列坐标为x+1,行下标为y或y+1,具体是哪个取决于误差项d大于0.5还是小于0.5。每次x增加1,则误差随之增加k。程序中设e=d-0.5,则可判断e的正负值。当d>1d>1时,则只需执行d=d−1d=d−1。

算法实现

void BresenhamLine(Mat& m, const int x0, const int y0, const int x1, const int y1, const Vec3b& v){
int x, y, dx, dy;
float k, e;
dx = x1 - x0;
dy = y1 - y0;
k = double(dy) / dx;
e = -0.5;
for (x = x0, y = y0; x <= x1; x++){
m.at<Vec3b>(y, x) = v;
e += k;
if (e >= 0){
y++;
e--;
}
}
}


以上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: